]>
Commit | Line | Data |
---|---|---|
7fafc032 | 1 | /* |
53921bfa | 2 | * udevd.c - hotplug event serializer |
7fafc032 | 3 | * |
7fafc032 KS |
4 | * Copyright (C) 2004 Kay Sievers <kay.sievers@vrfy.org> |
5 | * | |
6 | * | |
7 | * This program is free software; you can redistribute it and/or modify it | |
8 | * under the terms of the GNU General Public License as published by the | |
9 | * Free Software Foundation version 2 of the License. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, but | |
12 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License along | |
17 | * with this program; if not, write to the Free Software Foundation, Inc., | |
18 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
19 | * | |
20 | */ | |
21 | ||
8e2229c4 | 22 | #include <pthread.h> |
a695feae | 23 | #include <stddef.h> |
7fafc032 | 24 | #include <sys/types.h> |
90c210eb | 25 | #include <sys/wait.h> |
7fafc032 KS |
26 | #include <signal.h> |
27 | #include <unistd.h> | |
28 | #include <errno.h> | |
29 | #include <stdio.h> | |
30 | #include <stdlib.h> | |
31 | #include <string.h> | |
a695feae | 32 | #include <time.h> |
1c5c245e | 33 | #include <fcntl.h> |
53921bfa KS |
34 | #include <sys/types.h> |
35 | #include <sys/socket.h> | |
36 | #include <sys/un.h> | |
7fafc032 | 37 | |
a695feae | 38 | #include "list.h" |
7fafc032 | 39 | #include "udev.h" |
35b7d88c | 40 | #include "udev_version.h" |
7fafc032 KS |
41 | #include "udevd.h" |
42 | #include "logging.h" | |
43 | ||
35b7d88c | 44 | |
53921bfa KS |
45 | static pthread_mutex_t msg_lock; |
46 | static pthread_mutex_t msg_active_lock; | |
47 | static pthread_cond_t msg_active; | |
48 | static pthread_mutex_t exec_lock; | |
49 | static pthread_mutex_t exec_active_lock; | |
50 | static pthread_cond_t exec_active; | |
51 | static pthread_mutex_t running_lock; | |
52 | static pthread_attr_t thr_attr; | |
53 | static int expected_seqnum = 0; | |
7fafc032 | 54 | |
a695feae | 55 | LIST_HEAD(msg_list); |
53921bfa | 56 | LIST_HEAD(exec_list); |
35b7d88c | 57 | LIST_HEAD(running_list); |
7fafc032 | 58 | |
7fafc032 | 59 | |
53921bfa | 60 | static void msg_dump_queue(void) |
7fafc032 | 61 | { |
53921bfa | 62 | struct hotplug_msg *msg; |
7fafc032 | 63 | |
53921bfa KS |
64 | list_for_each_entry(msg, &msg_list, list) |
65 | dbg("sequence %d in queue", msg->seqnum); | |
35b7d88c KS |
66 | } |
67 | ||
53921bfa | 68 | static void msg_dump(struct hotplug_msg *msg) |
35b7d88c | 69 | { |
53921bfa KS |
70 | dbg("sequence %d, '%s', '%s', '%s'", |
71 | msg->seqnum, msg->action, msg->devpath, msg->subsystem); | |
35b7d88c KS |
72 | } |
73 | ||
53921bfa | 74 | static struct hotplug_msg *msg_create(void) |
35b7d88c | 75 | { |
53921bfa | 76 | struct hotplug_msg *new_msg; |
35b7d88c | 77 | |
53921bfa KS |
78 | new_msg = malloc(sizeof(struct hotplug_msg)); |
79 | if (new_msg == NULL) { | |
80 | dbg("error malloc"); | |
81 | return NULL; | |
82 | } | |
53921bfa | 83 | return new_msg; |
35b7d88c KS |
84 | } |
85 | ||
8e2229c4 KS |
86 | static void msg_delete(struct hotplug_msg *msg) |
87 | { | |
88 | if (msg != NULL) | |
89 | free(msg); | |
90 | } | |
91 | ||
53921bfa KS |
92 | /* orders the message in the queue by sequence number */ |
93 | static void msg_queue_insert(struct hotplug_msg *msg) | |
35b7d88c | 94 | { |
53921bfa | 95 | struct hotplug_msg *loop_msg; |
35b7d88c | 96 | |
53921bfa KS |
97 | /* sort message by sequence number into list*/ |
98 | list_for_each_entry(loop_msg, &msg_list, list) | |
99 | if (loop_msg->seqnum > msg->seqnum) | |
100 | break; | |
101 | list_add_tail(&msg->list, &loop_msg->list); | |
102 | dbg("queued message seq %d", msg->seqnum); | |
35b7d88c | 103 | |
53921bfa KS |
104 | /* store timestamp of queuing */ |
105 | msg->queue_time = time(NULL); | |
35b7d88c | 106 | |
53921bfa KS |
107 | /* signal queue activity to manager */ |
108 | pthread_mutex_lock(&msg_active_lock); | |
109 | pthread_cond_signal(&msg_active); | |
110 | pthread_mutex_unlock(&msg_active_lock); | |
7fafc032 | 111 | |
53921bfa | 112 | return ; |
7fafc032 KS |
113 | } |
114 | ||
53921bfa KS |
115 | /* forks event and removes event from run queue when finished */ |
116 | static void *run_threads(void * parm) | |
7fafc032 | 117 | { |
90c210eb | 118 | pid_t pid; |
53921bfa | 119 | struct hotplug_msg *msg; |
90c210eb | 120 | |
53921bfa | 121 | msg = parm; |
a695feae KS |
122 | setenv("ACTION", msg->action, 1); |
123 | setenv("DEVPATH", msg->devpath, 1); | |
90c210eb KS |
124 | |
125 | pid = fork(); | |
126 | switch (pid) { | |
127 | case 0: | |
33db4b8d | 128 | /* child */ |
35b7d88c | 129 | execl(UDEV_BIN, "udev", msg->subsystem, NULL); |
33db4b8d KS |
130 | dbg("exec of child failed"); |
131 | exit(1); | |
90c210eb KS |
132 | break; |
133 | case -1: | |
33db4b8d | 134 | dbg("fork of child failed"); |
53921bfa | 135 | goto exit; |
90c210eb | 136 | default: |
53921bfa KS |
137 | /* wait for exit of child */ |
138 | dbg("==> exec seq %d [%d] working at '%s'", | |
139 | msg->seqnum, pid, msg->devpath); | |
140 | wait(NULL); | |
141 | dbg("<== exec seq %d came back", msg->seqnum); | |
90c210eb | 142 | } |
53921bfa KS |
143 | |
144 | exit: | |
145 | /* remove event from run list */ | |
146 | pthread_mutex_lock(&running_lock); | |
147 | list_del_init(&msg->list); | |
148 | pthread_mutex_unlock(&running_lock); | |
149 | ||
8e2229c4 | 150 | msg_delete(msg); |
53921bfa KS |
151 | |
152 | /* signal queue activity to exec manager */ | |
153 | pthread_mutex_lock(&exec_active_lock); | |
154 | pthread_cond_signal(&exec_active); | |
155 | pthread_mutex_unlock(&exec_active_lock); | |
156 | ||
157 | pthread_exit(0); | |
7fafc032 KS |
158 | } |
159 | ||
53921bfa KS |
160 | /* returns already running task with devpath */ |
161 | static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg) | |
7fafc032 | 162 | { |
53921bfa KS |
163 | struct hotplug_msg *loop_msg; |
164 | struct hotplug_msg *tmp_msg; | |
35b7d88c | 165 | |
53921bfa KS |
166 | list_for_each_entry_safe(loop_msg, tmp_msg, &running_list, list) |
167 | if (strncmp(loop_msg->devpath, msg->devpath, sizeof(loop_msg->devpath)) == 0) | |
168 | return loop_msg; | |
169 | return NULL; | |
7fafc032 KS |
170 | } |
171 | ||
53921bfa KS |
172 | /* queue management executes the events and delays events for the same devpath */ |
173 | static void *exec_queue_manager(void * parm) | |
7fafc032 | 174 | { |
53921bfa KS |
175 | struct hotplug_msg *loop_msg; |
176 | struct hotplug_msg *tmp_msg; | |
a695feae | 177 | struct hotplug_msg *msg; |
53921bfa KS |
178 | pthread_t run_tid; |
179 | ||
180 | while (1) { | |
181 | pthread_mutex_lock(&exec_lock); | |
182 | list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, list) { | |
183 | msg = running_with_devpath(loop_msg); | |
184 | if (msg == NULL) { | |
185 | /* move event to run list */ | |
186 | pthread_mutex_lock(&running_lock); | |
187 | list_move_tail(&loop_msg->list, &running_list); | |
188 | pthread_mutex_unlock(&running_lock); | |
189 | ||
190 | pthread_create(&run_tid, &thr_attr, run_threads, (void *) loop_msg); | |
191 | ||
192 | dbg("moved seq %d to running list", loop_msg->seqnum); | |
193 | } else { | |
194 | dbg("delay seq %d, cause seq %d already working on '%s'", | |
195 | loop_msg->seqnum, msg->seqnum, msg->devpath); | |
196 | } | |
197 | } | |
198 | pthread_mutex_unlock(&exec_lock); | |
199 | ||
200 | /* wait for activation, new events or childs coming back */ | |
201 | pthread_mutex_lock(&exec_active_lock); | |
202 | pthread_cond_wait(&exec_active, &exec_active_lock); | |
203 | pthread_mutex_unlock(&exec_active_lock); | |
204 | } | |
205 | } | |
206 | ||
207 | /* move message from incoming to exec queue */ | |
208 | static void msg_move_exec(struct list_head *head) | |
209 | { | |
210 | list_move_tail(head, &exec_list); | |
211 | /* signal queue activity to manager */ | |
212 | pthread_mutex_lock(&exec_active_lock); | |
213 | pthread_cond_signal(&exec_active); | |
214 | pthread_mutex_unlock(&exec_active_lock); | |
215 | } | |
216 | ||
217 | /* queue management thread handles the timeouts and dispatches the events */ | |
218 | static void *msg_queue_manager(void * parm) | |
219 | { | |
220 | struct hotplug_msg *loop_msg; | |
a695feae | 221 | struct hotplug_msg *tmp_msg; |
53921bfa KS |
222 | time_t msg_age = 0; |
223 | struct timespec tv; | |
a695feae | 224 | |
53921bfa KS |
225 | while (1) { |
226 | dbg("msg queue manager, next expected is %d", expected_seqnum); | |
227 | pthread_mutex_lock(&msg_lock); | |
228 | pthread_mutex_lock(&exec_lock); | |
a695feae | 229 | recheck: |
53921bfa KS |
230 | list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, list) { |
231 | /* move event with expected sequence to the exec list */ | |
232 | if (loop_msg->seqnum == expected_seqnum) { | |
233 | msg_move_exec(&loop_msg->list); | |
234 | expected_seqnum++; | |
235 | dbg("moved seq %d to exec, next expected is %d", | |
236 | loop_msg->seqnum, expected_seqnum); | |
237 | continue; | |
238 | } | |
a695feae | 239 | |
53921bfa KS |
240 | /* move event with expired timeout to the exec list */ |
241 | msg_age = time(NULL) - loop_msg->queue_time; | |
242 | if (msg_age > EVENT_TIMEOUT_SEC-1) { | |
243 | msg_move_exec(&loop_msg->list); | |
244 | expected_seqnum = loop_msg->seqnum+1; | |
245 | dbg("moved seq %d to exec, reset next expected to %d", | |
246 | loop_msg->seqnum, expected_seqnum); | |
247 | goto recheck; | |
248 | } else { | |
249 | break; | |
250 | } | |
a695feae | 251 | } |
35b7d88c | 252 | |
53921bfa KS |
253 | msg_dump_queue(); |
254 | pthread_mutex_unlock(&exec_lock); | |
255 | pthread_mutex_unlock(&msg_lock); | |
35b7d88c | 256 | |
53921bfa KS |
257 | /* wait until queue gets active or next message timeout expires */ |
258 | pthread_mutex_lock(&msg_active_lock); | |
259 | ||
260 | if (list_empty(&msg_list) == 0) { | |
261 | tv.tv_sec = time(NULL) + EVENT_TIMEOUT_SEC - msg_age; | |
262 | tv.tv_nsec = 0; | |
263 | dbg("next event expires in %li seconds", | |
264 | EVENT_TIMEOUT_SEC - msg_age); | |
265 | pthread_cond_timedwait(&msg_active, &msg_active_lock, &tv); | |
266 | } else { | |
267 | pthread_cond_wait(&msg_active, &msg_active_lock); | |
268 | } | |
269 | pthread_mutex_unlock(&msg_active_lock); | |
7fafc032 | 270 | } |
7fafc032 KS |
271 | } |
272 | ||
53921bfa KS |
273 | /* every connect creates a thread which gets the msg, queues it and exits */ |
274 | static void *client_threads(void * parm) | |
7fafc032 | 275 | { |
53921bfa KS |
276 | int sock; |
277 | struct hotplug_msg *msg; | |
278 | int retval; | |
a695feae | 279 | |
53921bfa KS |
280 | sock = (int) parm; |
281 | ||
282 | msg = msg_create(); | |
283 | if (msg == NULL) { | |
284 | dbg("unable to store message"); | |
285 | goto exit; | |
7fafc032 | 286 | } |
7fafc032 | 287 | |
53921bfa KS |
288 | retval = recv(sock, msg, sizeof(struct hotplug_msg), 0); |
289 | if (retval < 0) { | |
290 | dbg("unable to receive message"); | |
291 | goto exit; | |
292 | } | |
a695feae | 293 | |
53921bfa KS |
294 | if (strncmp(msg->magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) { |
295 | dbg("message magic '%s' doesn't match, ignore it", msg->magic); | |
8e2229c4 | 296 | msg_delete(msg); |
53921bfa KS |
297 | goto exit; |
298 | } | |
a695feae | 299 | |
53921bfa KS |
300 | pthread_mutex_lock(&msg_lock); |
301 | msg_queue_insert(msg); | |
302 | pthread_mutex_unlock(&msg_lock); | |
303 | ||
304 | exit: | |
305 | close(sock); | |
306 | pthread_exit(0); | |
a695feae | 307 | } |
1c5c245e | 308 | |
53921bfa | 309 | static void sig_handler(int signum) |
7fafc032 | 310 | { |
53921bfa KS |
311 | switch (signum) { |
312 | case SIGINT: | |
313 | case SIGTERM: | |
314 | unlink(UDEVD_LOCK); | |
8e2229c4 | 315 | unlink(UDEVD_SOCK); |
53921bfa KS |
316 | exit(20 + signum); |
317 | break; | |
318 | default: | |
319 | dbg("unhandled signal"); | |
7fafc032 | 320 | } |
33db4b8d | 321 | } |
7fafc032 | 322 | |
1c5c245e GKH |
323 | static int one_and_only(void) |
324 | { | |
53921bfa KS |
325 | char string[50]; |
326 | int lock_file; | |
1c5c245e | 327 | |
53921bfa | 328 | lock_file = open(UDEVD_LOCK, O_RDWR | O_CREAT, 0x640); |
1c5c245e | 329 | if (lock_file < 0) |
a695feae | 330 | return -1; |
53921bfa | 331 | |
1c5c245e GKH |
332 | /* see if we can lock */ |
333 | if (lockf(lock_file, F_TLOCK, 0) < 0) { | |
53921bfa | 334 | dbg("file is already locked, exit"); |
1c5c245e | 335 | close(lock_file); |
a695feae | 336 | return -1; |
1c5c245e GKH |
337 | } |
338 | ||
339 | snprintf(string, sizeof(string), "%d\n", getpid()); | |
340 | write(lock_file, string, strlen(string)); | |
a695feae | 341 | |
1c5c245e GKH |
342 | return 0; |
343 | } | |
344 | ||
33db4b8d KS |
345 | int main(int argc, char *argv[]) |
346 | { | |
53921bfa KS |
347 | int ssock; |
348 | int csock; | |
349 | struct sockaddr_un saddr; | |
350 | struct sockaddr_un caddr; | |
351 | socklen_t clen; | |
352 | pthread_t cli_tid; | |
353 | pthread_t mgr_msg_tid; | |
354 | pthread_t mgr_exec_tid; | |
355 | int retval; | |
356 | ||
1c5c245e GKH |
357 | /* only let one version of the daemon run at any one time */ |
358 | if (one_and_only() != 0) | |
359 | exit(0); | |
360 | ||
7fafc032 KS |
361 | signal(SIGINT, sig_handler); |
362 | signal(SIGTERM, sig_handler); | |
7fafc032 | 363 | |
53921bfa KS |
364 | memset(&saddr, 0x00, sizeof(saddr)); |
365 | saddr.sun_family = AF_LOCAL; | |
8e2229c4 | 366 | strcpy(saddr.sun_path, UDEVD_SOCK); |
53921bfa | 367 | |
8e2229c4 | 368 | unlink(UDEVD_SOCK); |
53921bfa KS |
369 | ssock = socket(AF_LOCAL, SOCK_STREAM, 0); |
370 | if (ssock == -1) { | |
371 | dbg("error getting socket"); | |
372 | exit(1); | |
373 | } | |
374 | ||
375 | retval = bind(ssock, &saddr, sizeof(saddr)); | |
376 | if (retval < 0) { | |
377 | dbg("bind failed\n"); | |
378 | goto exit; | |
379 | } | |
380 | ||
381 | retval = listen(ssock, SOMAXCONN); | |
382 | if (retval < 0) { | |
383 | dbg("listen failed\n"); | |
384 | goto exit; | |
385 | } | |
386 | ||
387 | pthread_mutex_init(&msg_lock, NULL); | |
388 | pthread_mutex_init(&msg_active_lock, NULL); | |
389 | pthread_mutex_init(&exec_lock, NULL); | |
390 | pthread_mutex_init(&exec_active_lock, NULL); | |
391 | pthread_mutex_init(&running_lock, NULL); | |
392 | ||
393 | /* set default attributes for created threads */ | |
394 | pthread_attr_init(&thr_attr); | |
395 | pthread_attr_setdetachstate(&thr_attr, PTHREAD_CREATE_DETACHED); | |
8e2229c4 | 396 | pthread_attr_setstacksize(&thr_attr, 16 * 1024); |
53921bfa KS |
397 | |
398 | /* init queue management */ | |
399 | pthread_create(&mgr_msg_tid, &thr_attr, msg_queue_manager, NULL); | |
400 | pthread_create(&mgr_exec_tid, &thr_attr, exec_queue_manager, NULL); | |
401 | ||
402 | clen = sizeof(caddr); | |
403 | /* main loop */ | |
404 | while (1) { | |
405 | csock = accept(ssock, &caddr, &clen); | |
406 | if (csock < 0) { | |
53921bfa | 407 | dbg("client accept failed\n"); |
8e2229c4 | 408 | continue; |
53921bfa KS |
409 | } |
410 | pthread_create(&cli_tid, &thr_attr, client_threads, (void *) csock); | |
411 | } | |
412 | exit: | |
413 | close(ssock); | |
8e2229c4 | 414 | unlink(UDEVD_SOCK); |
53921bfa | 415 | exit(1); |
7fafc032 | 416 | } |