]>
Commit | Line | Data |
---|---|---|
7fafc032 | 1 | /* |
1e03b754 | 2 | * Copyright (C) 2004-2009 Kay Sievers <kay.sievers@vrfy.org> |
2f6cbd19 | 3 | * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> |
bb38678e SJR |
4 | * Copyright (C) 2009 Canonical Ltd. |
5 | * Copyright (C) 2009 Scott James Remnant <scott@netsplit.com> | |
7fafc032 | 6 | * |
55e9959b KS |
7 | * This program is free software: you can redistribute it and/or modify |
8 | * it under the terms of the GNU General Public License as published by | |
9 | * the Free Software Foundation, either version 2 of the License, or | |
10 | * (at your option) any later version. | |
7fafc032 | 11 | * |
55e9959b KS |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | * GNU General Public License for more details. | |
7fafc032 | 16 | * |
55e9959b KS |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
7fafc032 KS |
19 | */ |
20 | ||
a695feae | 21 | #include <stddef.h> |
7fafc032 KS |
22 | #include <signal.h> |
23 | #include <unistd.h> | |
24 | #include <errno.h> | |
25 | #include <stdio.h> | |
26 | #include <stdlib.h> | |
c3804728 | 27 | #include <stdbool.h> |
7fafc032 | 28 | #include <string.h> |
085cce37 | 29 | #include <ctype.h> |
085cce37 | 30 | #include <fcntl.h> |
0b3dfb3d | 31 | #include <time.h> |
b52a01ee | 32 | #include <getopt.h> |
78230c0d | 33 | #include <dirent.h> |
820fc48f | 34 | #include <sys/time.h> |
1e03b754 KS |
35 | #include <sys/prctl.h> |
36 | #include <sys/socket.h> | |
37 | #include <sys/signalfd.h> | |
138068d6 | 38 | #include <sys/select.h> |
3210a72b | 39 | #include <sys/poll.h> |
138068d6 | 40 | #include <sys/wait.h> |
dc117daa | 41 | #include <sys/stat.h> |
c895fd00 | 42 | #include <sys/ioctl.h> |
01618658 | 43 | #include <sys/inotify.h> |
761dfddc | 44 | #include <sys/utsname.h> |
7fafc032 KS |
45 | |
46 | #include "udev.h" | |
7fafc032 | 47 | |
d59f11e1 KS |
48 | #define UDEVD_PRIORITY -4 |
49 | #define UDEV_PRIORITY -2 | |
50 | ||
c3804728 | 51 | static bool debug; |
9e8fe79b | 52 | |
7d563a17 KS |
53 | static void log_fn(struct udev *udev, int priority, |
54 | const char *file, int line, const char *fn, | |
55 | const char *format, va_list args) | |
56 | { | |
57 | if (debug) { | |
820fc48f KS |
58 | char buf[1024]; |
59 | struct timeval tv; | |
60 | struct timezone tz; | |
61 | ||
62 | vsnprintf(buf, sizeof(buf), format, args); | |
63 | gettimeofday(&tv, &tz); | |
64 | fprintf(stderr, "%llu.%06u [%u] %s: %s", | |
65 | (unsigned long long) tv.tv_sec, (unsigned int) tv.tv_usec, | |
66 | (int) getpid(), fn, buf); | |
7d563a17 KS |
67 | } else { |
68 | vsyslog(priority, format, args); | |
69 | } | |
70 | } | |
71 | ||
c3804728 | 72 | static bool debug_trace; |
d7ddce18 | 73 | static struct udev_rules *rules; |
f503f6b2 | 74 | static struct udev_queue_export *udev_queue_export; |
d59f11e1 | 75 | static struct udev_ctrl *udev_ctrl; |
1e03b754 KS |
76 | static struct udev_monitor *monitor; |
77 | static int worker_watch[2]; | |
78 | static pid_t settle_pid; | |
c3804728 KS |
79 | static bool stop_exec_queue; |
80 | static bool reload_config; | |
87d55ff6 KS |
81 | static int children; |
82 | static int children_max; | |
d412a685 | 83 | static sigset_t orig_sigmask; |
0bc74ea7 | 84 | static struct udev_list_node event_list; |
1e03b754 | 85 | static struct udev_list_node worker_list; |
c3804728 | 86 | static bool udev_exit; |
1e03b754 KS |
87 | static volatile sig_atomic_t worker_exit; |
88 | ||
89 | enum poll_fd { | |
90 | FD_CONTROL, | |
91 | FD_NETLINK, | |
92 | FD_INOTIFY, | |
93 | FD_SIGNAL, | |
94 | FD_WORKER, | |
95 | }; | |
96 | ||
97 | static struct pollfd pfd[] = { | |
98 | [FD_NETLINK] = { .events = POLLIN }, | |
99 | [FD_WORKER] = { .events = POLLIN }, | |
100 | [FD_SIGNAL] = { .events = POLLIN }, | |
101 | [FD_INOTIFY] = { .events = POLLIN }, | |
102 | [FD_CONTROL] = { .events = POLLIN }, | |
103 | }; | |
104 | ||
105 | enum event_state { | |
106 | EVENT_UNDEF, | |
107 | EVENT_QUEUED, | |
108 | EVENT_RUNNING, | |
109 | }; | |
110 | ||
111 | struct event { | |
112 | struct udev_list_node node; | |
113 | struct udev *udev; | |
114 | struct udev_device *dev; | |
115 | enum event_state state; | |
116 | int exitcode; | |
117 | unsigned long long int delaying_seqnum; | |
118 | unsigned long long int seqnum; | |
119 | const char *devpath; | |
120 | size_t devpath_len; | |
121 | const char *devpath_old; | |
19711e19 KS |
122 | dev_t devnum; |
123 | bool is_block; | |
1e03b754 KS |
124 | }; |
125 | ||
126 | static struct event *node_to_event(struct udev_list_node *node) | |
7e027927 KS |
127 | { |
128 | char *event; | |
129 | ||
130 | event = (char *)node; | |
1e03b754 KS |
131 | event -= offsetof(struct event, node); |
132 | return (struct event *)event; | |
133 | } | |
134 | ||
135 | enum worker_state { | |
136 | WORKER_UNDEF, | |
137 | WORKER_RUNNING, | |
138 | WORKER_IDLE, | |
139 | WORKER_KILLED, | |
140 | }; | |
141 | ||
142 | struct worker { | |
143 | struct udev_list_node node; | |
bc113de9 KS |
144 | struct udev *udev; |
145 | int refcount; | |
1e03b754 KS |
146 | pid_t pid; |
147 | struct udev_monitor *monitor; | |
148 | enum worker_state state; | |
149 | struct event *event; | |
150 | }; | |
151 | ||
152 | /* passed from worker to main process */ | |
153 | struct worker_message { | |
154 | pid_t pid; | |
155 | int exitcode; | |
156 | }; | |
157 | ||
158 | static struct worker *node_to_worker(struct udev_list_node *node) | |
159 | { | |
160 | char *worker; | |
161 | ||
162 | worker = (char *)node; | |
163 | worker -= offsetof(struct worker, node); | |
164 | return (struct worker *)worker; | |
7e027927 KS |
165 | } |
166 | ||
1e03b754 | 167 | static void event_queue_delete(struct event *event) |
fc465079 | 168 | { |
7e027927 | 169 | udev_list_node_remove(&event->node); |
7a770250 | 170 | |
a2f2270e | 171 | /* mark as failed, if "add" event returns non-zero */ |
4b06c409 | 172 | if (event->exitcode != 0 && strcmp(udev_device_get_action(event->dev), "remove") != 0) |
f503f6b2 | 173 | udev_queue_export_device_failed(udev_queue_export, event->dev); |
7a770250 | 174 | else |
f503f6b2 | 175 | udev_queue_export_device_finished(udev_queue_export, event->dev); |
aa8734ff | 176 | |
45798927 | 177 | info(event->udev, "seq %llu done with %i\n", udev_device_get_seqnum(event->dev), event->exitcode); |
aa8734ff | 178 | udev_device_unref(event->dev); |
1e03b754 | 179 | free(event); |
aa8734ff | 180 | } |
7a770250 | 181 | |
619b97ff | 182 | static void event_sig_handler(int signum) |
aa8734ff | 183 | { |
1e03b754 KS |
184 | switch (signum) { |
185 | case SIGALRM: | |
619b97ff | 186 | _exit(1); |
1e03b754 KS |
187 | break; |
188 | case SIGTERM: | |
c3804728 | 189 | worker_exit = true; |
1e03b754 KS |
190 | break; |
191 | } | |
192 | } | |
193 | ||
bc113de9 KS |
194 | static struct worker *worker_ref(struct worker *worker) |
195 | { | |
196 | worker->refcount++; | |
197 | return worker; | |
198 | } | |
199 | ||
1e03b754 KS |
200 | static void worker_unref(struct worker *worker) |
201 | { | |
bc113de9 KS |
202 | worker->refcount--; |
203 | if (worker->refcount > 0) | |
204 | return; | |
205 | ||
206 | udev_list_node_remove(&worker->node); | |
1e03b754 | 207 | udev_monitor_unref(worker->monitor); |
87d55ff6 | 208 | children--; |
bc113de9 | 209 | info(worker->udev, "worker [%u] cleaned up\n", worker->pid); |
1e03b754 | 210 | free(worker); |
fc465079 KS |
211 | } |
212 | ||
1e03b754 | 213 | static void worker_new(struct event *event) |
7fafc032 | 214 | { |
1e03b754 KS |
215 | struct worker *worker; |
216 | struct udev_monitor *worker_monitor; | |
90c210eb | 217 | pid_t pid; |
aa8734ff | 218 | struct sigaction act; |
ce449f89 | 219 | |
1e03b754 KS |
220 | /* listen for new events */ |
221 | worker_monitor = udev_monitor_new_from_netlink(event->udev, NULL); | |
222 | if (worker_monitor == NULL) | |
223 | return; | |
224 | /* allow the main daemon netlink address to send devices to the worker */ | |
225 | udev_monitor_allow_unicast_sender(worker_monitor, monitor); | |
226 | udev_monitor_enable_receiving(worker_monitor); | |
227 | ||
228 | worker = calloc(1, sizeof(struct worker)); | |
229 | if (worker == NULL) | |
230 | return; | |
bc113de9 KS |
231 | /* worker + event reference */ |
232 | worker->refcount = 2; | |
233 | worker->udev = event->udev; | |
0bc74ea7 | 234 | |
90c210eb KS |
235 | pid = fork(); |
236 | switch (pid) { | |
1e03b754 | 237 | case 0: { |
adda4c68 | 238 | sigset_t sigmask; |
1e03b754 | 239 | struct udev_device *dev; |
adda4c68 KS |
240 | struct pollfd pmon = { |
241 | .fd = udev_monitor_get_fd(worker_monitor), | |
242 | .events = POLLIN, | |
243 | }; | |
1e03b754 | 244 | |
f503f6b2 | 245 | udev_queue_export_unref(udev_queue_export); |
9290143d | 246 | udev_monitor_unref(monitor); |
d59f11e1 | 247 | udev_ctrl_unref(udev_ctrl); |
1e03b754 KS |
248 | close(pfd[FD_SIGNAL].fd); |
249 | close(worker_watch[READ_END]); | |
9060b066 KS |
250 | udev_log_close(); |
251 | udev_log_init("udevd-work"); | |
085cce37 | 252 | setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); |
b437ec95 | 253 | |
aa8734ff KS |
254 | /* set signal handlers */ |
255 | memset(&act, 0x00, sizeof(act)); | |
619b97ff | 256 | act.sa_handler = event_sig_handler; |
aa8734ff KS |
257 | sigemptyset (&act.sa_mask); |
258 | act.sa_flags = 0; | |
1e03b754 | 259 | sigaction(SIGTERM, &act, NULL); |
aa8734ff KS |
260 | sigaction(SIGALRM, &act, NULL); |
261 | ||
adda4c68 KS |
262 | /* unblock SIGALRM */ |
263 | sigfillset(&sigmask); | |
264 | sigdelset(&sigmask, SIGALRM); | |
265 | sigprocmask(SIG_SETMASK, &sigmask, NULL); | |
266 | /* SIGTERM is unblocked in ppoll() */ | |
267 | sigdelset(&sigmask, SIGTERM); | |
aa8734ff | 268 | |
1e03b754 KS |
269 | /* request TERM signal if parent exits */ |
270 | prctl(PR_SET_PDEATHSIG, SIGTERM); | |
aa8734ff | 271 | |
1e03b754 KS |
272 | /* initial device */ |
273 | dev = event->dev; | |
aa8734ff | 274 | |
adda4c68 | 275 | do { |
1e03b754 | 276 | struct udev_event *udev_event; |
f7c5b04f | 277 | struct worker_message msg = {}; |
1e03b754 | 278 | int err; |
f7c5b04f | 279 | int failed = 0; |
aa8734ff | 280 | |
820fc48f | 281 | info(event->udev, "seq %llu running\n", udev_device_get_seqnum(dev)); |
1e03b754 KS |
282 | udev_event = udev_event_new(dev); |
283 | if (udev_event == NULL) | |
284 | _exit(3); | |
0bc74ea7 | 285 | |
1e03b754 KS |
286 | /* set timeout to prevent hanging processes */ |
287 | alarm(UDEV_EVENT_TIMEOUT); | |
288 | ||
289 | /* apply rules, create node, symlinks */ | |
290 | err = udev_event_execute_rules(udev_event, rules); | |
291 | ||
292 | /* rules may change/disable the timeout */ | |
293 | if (udev_device_get_event_timeout(dev) >= 0) | |
294 | alarm(udev_device_get_event_timeout(dev)); | |
295 | ||
f46d2e54 KS |
296 | if (err == 0) |
297 | failed = udev_event_execute_run(udev_event, &orig_sigmask); | |
1e03b754 | 298 | |
1e03b754 KS |
299 | alarm(0); |
300 | ||
301 | /* apply/restore inotify watch */ | |
302 | if (err == 0 && udev_event->inotify_watch) { | |
303 | udev_watch_begin(udev_event->udev, dev); | |
304 | udev_device_update_db(dev); | |
305 | } | |
5ae82640 | 306 | |
1e03b754 KS |
307 | /* send processed event back to libudev listeners */ |
308 | udev_monitor_send_device(worker_monitor, NULL, dev); | |
11625409 | 309 | |
f46d2e54 | 310 | /* send udevd the result of the event execution */ |
f7c5b04f KS |
311 | if (err != 0) |
312 | msg.exitcode = err; | |
313 | else if (failed != 0) | |
314 | msg.exitcode = failed; | |
1e03b754 KS |
315 | msg.pid = getpid(); |
316 | send(worker_watch[WRITE_END], &msg, sizeof(struct worker_message), 0); | |
317 | ||
adda4c68 KS |
318 | info(event->udev, "seq %llu processed with %i\n", udev_device_get_seqnum(dev), err); |
319 | udev_event_unref(udev_event); | |
320 | udev_device_unref(dev); | |
321 | dev = NULL; | |
322 | ||
323 | /* wait for more device messages or signal from udevd */ | |
324 | while (!worker_exit) { | |
325 | int fdcount; | |
326 | ||
327 | fdcount = ppoll(&pmon, 1, NULL, &sigmask); | |
328 | if (fdcount < 0) | |
329 | continue; | |
330 | ||
331 | if (pmon.revents & POLLIN) { | |
332 | dev = udev_monitor_receive_device(worker_monitor); | |
333 | if (dev != NULL) | |
334 | break; | |
335 | } | |
336 | } | |
337 | } while (dev != NULL); | |
1e03b754 KS |
338 | |
339 | udev_monitor_unref(worker_monitor); | |
9060b066 | 340 | udev_log_close(); |
c895fd00 | 341 | exit(0); |
1e03b754 | 342 | } |
90c210eb | 343 | case -1: |
1e03b754 KS |
344 | udev_monitor_unref(worker_monitor); |
345 | event->state = EVENT_QUEUED; | |
346 | free(worker); | |
aa8734ff | 347 | err(event->udev, "fork of child failed: %m\n"); |
2f6cbd19 | 348 | break; |
90c210eb | 349 | default: |
1e03b754 KS |
350 | /* close monitor, but keep address around */ |
351 | udev_monitor_disconnect(worker_monitor); | |
352 | worker->monitor = worker_monitor; | |
353 | worker->pid = pid; | |
354 | worker->state = WORKER_RUNNING; | |
355 | worker->event = event; | |
356 | event->state = EVENT_RUNNING; | |
357 | udev_list_node_append(&worker->node, &worker_list); | |
87d55ff6 | 358 | children++; |
12bc9c54 | 359 | info(event->udev, "seq %llu forked new worker [%u]\n", udev_device_get_seqnum(event->dev), pid); |
1e03b754 | 360 | break; |
90c210eb | 361 | } |
7fafc032 KS |
362 | } |
363 | ||
665ee17d | 364 | static void event_run(struct event *event, bool force) |
fc465079 | 365 | { |
1e03b754 KS |
366 | struct udev_list_node *loop; |
367 | ||
368 | udev_list_node_foreach(loop, &worker_list) { | |
369 | struct worker *worker = node_to_worker(loop); | |
370 | ssize_t count; | |
7baada47 | 371 | |
1e03b754 KS |
372 | if (worker->state != WORKER_IDLE) |
373 | continue; | |
0f624f16 | 374 | |
1e03b754 KS |
375 | count = udev_monitor_send_device(monitor, worker->monitor, event->dev); |
376 | if (count < 0) { | |
a073cfa8 | 377 | err(event->udev, "worker [%u] did not accept message %zi (%m), kill it\n", worker->pid, count); |
1e03b754 | 378 | kill(worker->pid, SIGKILL); |
bc113de9 | 379 | worker->state = WORKER_KILLED; |
1e03b754 KS |
380 | continue; |
381 | } | |
bc113de9 KS |
382 | worker_ref(worker); |
383 | worker->event = event; | |
384 | worker->state = WORKER_RUNNING; | |
385 | event->state = EVENT_RUNNING; | |
1e03b754 KS |
386 | return; |
387 | } | |
388 | ||
87d55ff6 KS |
389 | if (!force && children >= children_max) { |
390 | info(event->udev, "maximum number (%i) of children reached\n", children); | |
1e03b754 KS |
391 | return; |
392 | } | |
393 | ||
394 | /* start new worker and pass initial device */ | |
395 | worker_new(event); | |
396 | } | |
397 | ||
398 | static void event_queue_insert(struct udev_device *dev) | |
399 | { | |
400 | struct event *event; | |
401 | ||
402 | event = calloc(1, sizeof(struct event)); | |
403 | if (event == NULL) | |
404 | return; | |
405 | ||
406 | event->udev = udev_device_get_udev(dev); | |
407 | event->dev = dev; | |
408 | event->seqnum = udev_device_get_seqnum(dev); | |
409 | event->devpath = udev_device_get_devpath(dev); | |
410 | event->devpath_len = strlen(event->devpath); | |
411 | event->devpath_old = udev_device_get_devpath_old(dev); | |
19711e19 KS |
412 | event->devnum = udev_device_get_devnum(dev); |
413 | event->is_block = (strcmp("block", udev_device_get_subsystem(dev)) == 0); | |
1e03b754 KS |
414 | |
415 | udev_queue_export_device_queued(udev_queue_export, dev); | |
416 | info(event->udev, "seq %llu queued, '%s' '%s'\n", udev_device_get_seqnum(dev), | |
417 | udev_device_get_action(dev), udev_device_get_subsystem(dev)); | |
418 | ||
419 | event->state = EVENT_QUEUED; | |
0bc74ea7 | 420 | udev_list_node_append(&event->node, &event_list); |
c7c00276 | 421 | |
fc465079 | 422 | /* run all events with a timeout set immediately */ |
1e03b754 | 423 | if (udev_device_get_timeout(dev) > 0) { |
665ee17d | 424 | event_run(event, true); |
fc465079 KS |
425 | return; |
426 | } | |
fc465079 KS |
427 | } |
428 | ||
adda4c68 | 429 | static void worker_kill(struct udev *udev, int retain) |
1e03b754 KS |
430 | { |
431 | struct udev_list_node *loop; | |
432 | int max; | |
433 | ||
87d55ff6 | 434 | if (children <= retain) |
1e03b754 KS |
435 | return; |
436 | ||
87d55ff6 | 437 | max = children - retain; |
1e03b754 KS |
438 | |
439 | udev_list_node_foreach(loop, &worker_list) { | |
440 | struct worker *worker = node_to_worker(loop); | |
441 | ||
442 | if (max-- <= 0) | |
443 | break; | |
444 | ||
445 | if (worker->state == WORKER_KILLED) | |
446 | continue; | |
447 | ||
448 | worker->state = WORKER_KILLED; | |
449 | kill(worker->pid, SIGTERM); | |
450 | } | |
451 | } | |
452 | ||
e3196993 | 453 | /* lookup event for identical, parent, child device */ |
19711e19 | 454 | static bool is_devpath_busy(struct event *event) |
7fafc032 | 455 | { |
7e027927 | 456 | struct udev_list_node *loop; |
1e03b754 | 457 | size_t common; |
7b6571a9 | 458 | |
0bc74ea7 KS |
459 | /* check if queue contains events we depend on */ |
460 | udev_list_node_foreach(loop, &event_list) { | |
1e03b754 | 461 | struct event *loop_event = node_to_event(loop); |
7e027927 | 462 | |
0bc74ea7 | 463 | /* we already found a later event, earlier can not block us, no need to check again */ |
1e03b754 | 464 | if (loop_event->seqnum < event->delaying_seqnum) |
0bc74ea7 KS |
465 | continue; |
466 | ||
467 | /* event we checked earlier still exists, no need to check again */ | |
1e03b754 | 468 | if (loop_event->seqnum == event->delaying_seqnum) |
19711e19 | 469 | return true; |
0bc74ea7 KS |
470 | |
471 | /* found ourself, no later event can block us */ | |
1e03b754 | 472 | if (loop_event->seqnum >= event->seqnum) |
fc630eda KS |
473 | break; |
474 | ||
19711e19 KS |
475 | /* check major/minor */ |
476 | if (major(event->devnum) != 0 && event->devnum == loop_event->devnum && event->is_block == loop_event->is_block) | |
477 | return true; | |
478 | ||
a2f2270e | 479 | /* check our old name */ |
19711e19 KS |
480 | if (event->devpath_old != NULL && strcmp(loop_event->devpath, event->devpath_old) == 0) { |
481 | event->delaying_seqnum = loop_event->seqnum; | |
482 | return true; | |
483 | } | |
a2f2270e | 484 | |
1e03b754 KS |
485 | /* compare devpath */ |
486 | common = MIN(loop_event->devpath_len, event->devpath_len); | |
487 | ||
488 | /* one devpath is contained in the other? */ | |
489 | if (memcmp(loop_event->devpath, event->devpath, common) != 0) | |
490 | continue; | |
491 | ||
492 | /* identical device event found */ | |
493 | if (loop_event->devpath_len == event->devpath_len) { | |
494 | event->delaying_seqnum = loop_event->seqnum; | |
19711e19 | 495 | return true; |
c3b145a3 | 496 | } |
1e03b754 KS |
497 | |
498 | /* parent device event found */ | |
499 | if (event->devpath[common] == '/') { | |
500 | event->delaying_seqnum = loop_event->seqnum; | |
19711e19 | 501 | return true; |
1e03b754 KS |
502 | } |
503 | ||
504 | /* child device event found */ | |
505 | if (loop_event->devpath[common] == '/') { | |
506 | event->delaying_seqnum = loop_event->seqnum; | |
19711e19 | 507 | return true; |
1e03b754 KS |
508 | } |
509 | ||
510 | /* no matching device */ | |
511 | continue; | |
80513ea3 | 512 | } |
1e03b754 | 513 | |
19711e19 | 514 | return false; |
7fafc032 KS |
515 | } |
516 | ||
1e03b754 | 517 | static void events_start(struct udev *udev) |
7fafc032 | 518 | { |
7e027927 | 519 | struct udev_list_node *loop; |
8ab44e3f | 520 | |
1e03b754 KS |
521 | udev_list_node_foreach(loop, &event_list) { |
522 | struct event *event = node_to_event(loop); | |
0bc74ea7 | 523 | |
1e03b754 | 524 | if (event->state != EVENT_QUEUED) |
0bc74ea7 KS |
525 | continue; |
526 | ||
527 | /* do not start event if parent or child event is still running */ | |
19711e19 | 528 | if (is_devpath_busy(event)) { |
1e03b754 | 529 | dbg(udev, "delay seq %llu (%s)\n", event->seqnum, event->devpath); |
fc465079 KS |
530 | continue; |
531 | } | |
532 | ||
665ee17d | 533 | event_run(event, false); |
1e03b754 KS |
534 | } |
535 | } | |
536 | ||
537 | static void worker_returned(void) | |
538 | { | |
88cbfb09 | 539 | for (;;) { |
1e03b754 KS |
540 | struct worker_message msg; |
541 | ssize_t size; | |
542 | struct udev_list_node *loop; | |
543 | ||
544 | size = recv(pfd[FD_WORKER].fd, &msg, sizeof(struct worker_message), MSG_DONTWAIT); | |
545 | if (size != sizeof(struct worker_message)) | |
546 | break; | |
547 | ||
548 | /* lookup worker who sent the signal */ | |
549 | udev_list_node_foreach(loop, &worker_list) { | |
550 | struct worker *worker = node_to_worker(loop); | |
d1cc6562 | 551 | |
1e03b754 KS |
552 | if (worker->pid != msg.pid) |
553 | continue; | |
554 | ||
555 | /* worker returned */ | |
556 | worker->event->exitcode = msg.exitcode; | |
557 | event_queue_delete(worker->event); | |
558 | worker->event = NULL; | |
adda4c68 KS |
559 | if (worker->state != WORKER_KILLED) |
560 | worker->state = WORKER_IDLE; | |
bc113de9 | 561 | worker_unref(worker); |
1e03b754 | 562 | break; |
d1cc6562 | 563 | } |
e825b59b | 564 | } |
88f4b648 KS |
565 | } |
566 | ||
3b47c739 | 567 | /* receive the udevd message from userspace */ |
d59f11e1 | 568 | static void handle_ctrl_msg(struct udev_ctrl *uctrl) |
7fafc032 | 569 | { |
d59f11e1 KS |
570 | struct udev *udev = udev_ctrl_get_udev(uctrl); |
571 | struct udev_ctrl_msg *ctrl_msg; | |
572 | const char *str; | |
573 | int i; | |
7b1cbec9 | 574 | |
d59f11e1 KS |
575 | ctrl_msg = udev_ctrl_receive_msg(uctrl); |
576 | if (ctrl_msg == NULL) | |
b437ec95 | 577 | return; |
4a231017 | 578 | |
d59f11e1 KS |
579 | i = udev_ctrl_get_set_log_level(ctrl_msg); |
580 | if (i >= 0) { | |
581 | info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); | |
582 | udev_set_log_priority(udev, i); | |
adda4c68 | 583 | worker_kill(udev, 0); |
0028653c KS |
584 | } |
585 | ||
d59f11e1 | 586 | if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { |
7d563a17 | 587 | info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); |
c3804728 | 588 | stop_exec_queue = true; |
d59f11e1 KS |
589 | } |
590 | ||
591 | if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { | |
7d563a17 | 592 | info(udev, "udevd message (START_EXEC_QUEUE) received\n"); |
c3804728 | 593 | stop_exec_queue = false; |
d59f11e1 KS |
594 | } |
595 | ||
596 | if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) { | |
7d563a17 | 597 | info(udev, "udevd message (RELOAD_RULES) received\n"); |
c3804728 | 598 | reload_config = true; |
3b47c739 | 599 | } |
d59f11e1 KS |
600 | |
601 | str = udev_ctrl_get_set_env(ctrl_msg); | |
602 | if (str != NULL) { | |
aa8734ff KS |
603 | char *key; |
604 | ||
605 | key = strdup(str); | |
606 | if (key != NULL) { | |
607 | char *val; | |
608 | ||
609 | val = strchr(key, '='); | |
610 | if (val != NULL) { | |
611 | val[0] = '\0'; | |
612 | val = &val[1]; | |
613 | if (val[0] == '\0') { | |
614 | info(udev, "udevd message (ENV) received, unset '%s'\n", key); | |
615 | udev_add_property(udev, key, NULL); | |
616 | } else { | |
617 | info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); | |
618 | udev_add_property(udev, key, val); | |
619 | } | |
d59f11e1 | 620 | } else { |
aa8734ff | 621 | err(udev, "wrong key format '%s'\n", key); |
d59f11e1 | 622 | } |
aa8734ff | 623 | free(key); |
d59f11e1 | 624 | } |
adda4c68 | 625 | worker_kill(udev, 0); |
d59f11e1 KS |
626 | } |
627 | ||
87d55ff6 | 628 | i = udev_ctrl_get_set_children_max(ctrl_msg); |
d59f11e1 | 629 | if (i >= 0) { |
87d55ff6 KS |
630 | info(udev, "udevd message (SET_MAX_CHILDREN) received, children_max=%i\n", i); |
631 | children_max = i; | |
d59f11e1 | 632 | } |
bb38678e SJR |
633 | |
634 | settle_pid = udev_ctrl_get_settle(ctrl_msg); | |
635 | if (settle_pid > 0) { | |
636 | info(udev, "udevd message (SETTLE) received\n"); | |
1e03b754 KS |
637 | kill(settle_pid, SIGUSR1); |
638 | settle_pid = 0; | |
bb38678e | 639 | } |
d59f11e1 | 640 | udev_ctrl_msg_unref(ctrl_msg); |
88f4b648 | 641 | } |
4a231017 | 642 | |
bd284db1 SJR |
643 | /* read inotify messages */ |
644 | static int handle_inotify(struct udev *udev) | |
645 | { | |
4daa146b | 646 | int nbytes, pos; |
bd284db1 SJR |
647 | char *buf; |
648 | struct inotify_event *ev; | |
bd284db1 | 649 | |
1e03b754 | 650 | if ((ioctl(pfd[FD_INOTIFY].fd, FIONREAD, &nbytes) < 0) || (nbytes <= 0)) |
bd284db1 SJR |
651 | return 0; |
652 | ||
653 | buf = malloc(nbytes); | |
654 | if (buf == NULL) { | |
45798927 | 655 | err(udev, "error getting buffer for inotify\n"); |
1e03b754 | 656 | return -1; |
bd284db1 SJR |
657 | } |
658 | ||
1e03b754 | 659 | nbytes = read(pfd[FD_INOTIFY].fd, buf, nbytes); |
bd284db1 | 660 | |
80be8c48 | 661 | for (pos = 0; pos < nbytes; pos += sizeof(struct inotify_event) + ev->len) { |
047f88bc | 662 | struct udev_device *dev; |
bd284db1 | 663 | |
80be8c48 | 664 | ev = (struct inotify_event *)(buf + pos); |
b8e96d67 | 665 | if (ev->len) { |
79e1912b KS |
666 | const char *s; |
667 | ||
668 | info(udev, "inotify event: %x for %s\n", ev->mask, ev->name); | |
669 | s = strstr(ev->name, ".rules"); | |
670 | if (s == NULL) | |
671 | continue; | |
672 | if (strlen(s) != strlen(".rules")) | |
673 | continue; | |
c3804728 | 674 | reload_config = true; |
b8e96d67 SJR |
675 | continue; |
676 | } | |
677 | ||
047f88bc SJR |
678 | dev = udev_watch_lookup(udev, ev->wd); |
679 | if (dev != NULL) { | |
79e1912b | 680 | info(udev, "inotify event: %x for %s\n", ev->mask, udev_device_get_devnode(dev)); |
bd284db1 SJR |
681 | if (ev->mask & IN_CLOSE_WRITE) { |
682 | char filename[UTIL_PATH_SIZE]; | |
683 | int fd; | |
684 | ||
047f88bc | 685 | info(udev, "device %s closed, synthesising 'change'\n", udev_device_get_devnode(dev)); |
065db052 | 686 | util_strscpyl(filename, sizeof(filename), udev_device_get_syspath(dev), "/uevent", NULL); |
bd284db1 SJR |
687 | fd = open(filename, O_WRONLY); |
688 | if (fd < 0 || write(fd, "change", 6) < 0) | |
689 | info(udev, "error writing uevent: %m\n"); | |
690 | close(fd); | |
691 | } | |
692 | if (ev->mask & IN_IGNORED) | |
047f88bc SJR |
693 | udev_watch_end(udev, dev); |
694 | ||
695 | udev_device_unref(dev); | |
bd284db1 | 696 | } |
b8e96d67 | 697 | |
bd284db1 SJR |
698 | } |
699 | ||
1e03b754 | 700 | free(buf); |
4aca304e | 701 | return 0; |
bd284db1 SJR |
702 | } |
703 | ||
45798927 | 704 | static void handle_signal(struct udev *udev, int signo) |
7fafc032 | 705 | { |
1e03b754 KS |
706 | switch (signo) { |
707 | case SIGINT: | |
708 | case SIGTERM: | |
c3804728 | 709 | udev_exit = true; |
1e03b754 KS |
710 | break; |
711 | case SIGCHLD: | |
88cbfb09 | 712 | for (;;) { |
1e03b754 | 713 | pid_t pid; |
45798927 | 714 | int status; |
1e03b754 | 715 | struct udev_list_node *loop, *tmp; |
5a73b25f | 716 | |
45798927 | 717 | pid = waitpid(-1, &status, WNOHANG); |
1e03b754 KS |
718 | if (pid <= 0) |
719 | break; | |
7fafc032 | 720 | |
1e03b754 KS |
721 | udev_list_node_foreach_safe(loop, tmp, &worker_list) { |
722 | struct worker *worker = node_to_worker(loop); | |
7e027927 | 723 | |
1e03b754 KS |
724 | if (worker->pid != pid) |
725 | continue; | |
2f6cbd19 | 726 | |
bc113de9 KS |
727 | info(udev, "worker [%u] exit\n", pid); |
728 | if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) { | |
adda4c68 | 729 | err(udev, "worker [%u] unexpectedly returned with status 0x%04x\n", pid, status); |
bc113de9 KS |
730 | if (worker->event != NULL) { |
731 | err(udev, "worker [%u] failed while handling '%s'\n", pid, worker->event->devpath); | |
732 | worker->event->exitcode = -32; | |
733 | event_queue_delete(worker->event); | |
734 | /* drop reference from running event */ | |
735 | worker_unref(worker); | |
736 | } | |
1e03b754 | 737 | } |
1e03b754 | 738 | worker_unref(worker); |
1e03b754 KS |
739 | break; |
740 | } | |
741 | } | |
742 | break; | |
743 | case SIGHUP: | |
c3804728 | 744 | reload_config = true; |
1e03b754 | 745 | break; |
f27125f9 | 746 | } |
747 | } | |
748 | ||
761dfddc KS |
749 | static void static_dev_create_from_modules(struct udev *udev) |
750 | { | |
751 | struct utsname kernel; | |
752 | char modules[UTIL_PATH_SIZE]; | |
753 | char buf[4096]; | |
754 | FILE *f; | |
755 | ||
756 | uname(&kernel); | |
757 | util_strscpyl(modules, sizeof(modules), "/lib/modules/", kernel.release, "/modules.devname", NULL); | |
758 | f = fopen(modules, "r"); | |
759 | if (f == NULL) | |
760 | return; | |
761 | ||
762 | while (fgets(buf, sizeof(buf), f) != NULL) { | |
763 | char *s; | |
764 | const char *modname; | |
765 | const char *devname; | |
766 | const char *devno; | |
767 | int maj, min; | |
768 | char type; | |
769 | mode_t mode; | |
770 | char filename[UTIL_PATH_SIZE]; | |
771 | ||
772 | if (buf[0] == '#') | |
773 | continue; | |
774 | ||
775 | modname = buf; | |
776 | s = strchr(modname, ' '); | |
777 | if (s == NULL) | |
778 | continue; | |
779 | s[0] = '\0'; | |
780 | ||
781 | devname = &s[1]; | |
782 | s = strchr(devname, ' '); | |
783 | if (s == NULL) | |
784 | continue; | |
785 | s[0] = '\0'; | |
786 | ||
787 | devno = &s[1]; | |
788 | s = strchr(devno, ' '); | |
789 | if (s == NULL) | |
790 | s = strchr(devno, '\n'); | |
791 | if (s != NULL) | |
792 | s[0] = '\0'; | |
793 | if (sscanf(devno, "%c%u:%u", &type, &maj, &min) != 3) | |
794 | continue; | |
795 | ||
796 | if (type == 'c') | |
797 | mode = 0600 | S_IFCHR; | |
798 | else if (type == 'b') | |
799 | mode = 0600 | S_IFBLK; | |
800 | else | |
801 | continue; | |
802 | ||
803 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/", devname, NULL); | |
804 | util_create_path(udev, filename); | |
805 | udev_selinux_setfscreatecon(udev, filename, mode); | |
806 | info(udev, "mknod '%s' %c%u:%u\n", filename, type, maj, min); | |
807 | if (mknod(filename, mode, makedev(maj, min)) < 0 && errno == EEXIST) | |
808 | utimensat(AT_FDCWD, filename, NULL, 0); | |
809 | udev_selinux_resetfscreatecon(udev); | |
810 | } | |
811 | ||
812 | fclose(f); | |
813 | } | |
814 | ||
cb9a0eee KS |
815 | static int copy_dir(struct udev *udev, DIR *dir_from, DIR *dir_to, int maxdepth) |
816 | { | |
817 | struct dirent *dent; | |
818 | ||
819 | for (dent = readdir(dir_from); dent != NULL; dent = readdir(dir_from)) { | |
820 | struct stat stats; | |
821 | ||
822 | if (dent->d_name[0] == '.') | |
823 | continue; | |
824 | if (fstatat(dirfd(dir_from), dent->d_name, &stats, AT_SYMLINK_NOFOLLOW) != 0) | |
825 | continue; | |
826 | ||
827 | if (S_ISBLK(stats.st_mode) || S_ISCHR(stats.st_mode)) { | |
828 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, stats.st_mode & 0777); | |
829 | if (mknodat(dirfd(dir_to), dent->d_name, stats.st_mode, stats.st_rdev) == 0) { | |
830 | fchmodat(dirfd(dir_to), dent->d_name, stats.st_mode & 0777, 0); | |
831 | fchownat(dirfd(dir_to), dent->d_name, stats.st_uid, stats.st_gid, 0); | |
832 | } else { | |
833 | utimensat(dirfd(dir_to), dent->d_name, NULL, 0); | |
834 | } | |
835 | udev_selinux_resetfscreatecon(udev); | |
836 | } else if (S_ISLNK(stats.st_mode)) { | |
837 | char target[UTIL_PATH_SIZE]; | |
838 | ssize_t len; | |
839 | ||
840 | len = readlinkat(dirfd(dir_from), dent->d_name, target, sizeof(target)); | |
841 | if (len <= 0 || len == (ssize_t)sizeof(target)) | |
842 | continue; | |
843 | target[len] = '\0'; | |
844 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFLNK); | |
845 | if (symlinkat(target, dirfd(dir_to), dent->d_name) < 0 && errno == EEXIST) | |
846 | utimensat(dirfd(dir_to), dent->d_name, NULL, AT_SYMLINK_NOFOLLOW); | |
847 | udev_selinux_resetfscreatecon(udev); | |
848 | } else if (S_ISDIR(stats.st_mode)) { | |
849 | DIR *dir2_from, *dir2_to; | |
850 | ||
851 | if (maxdepth == 0) | |
852 | continue; | |
853 | ||
854 | udev_selinux_setfscreateconat(udev, dirfd(dir_to), dent->d_name, S_IFDIR|0755); | |
855 | mkdirat(dirfd(dir_to), dent->d_name, 0755); | |
856 | udev_selinux_resetfscreatecon(udev); | |
857 | ||
858 | dir2_to = fdopendir(openat(dirfd(dir_to), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); | |
859 | if (dir2_to == NULL) | |
860 | continue; | |
861 | ||
862 | dir2_from = fdopendir(openat(dirfd(dir_from), dent->d_name, O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC)); | |
863 | if (dir2_from == NULL) { | |
864 | closedir(dir2_to); | |
865 | continue; | |
866 | } | |
867 | ||
868 | copy_dir(udev, dir2_from, dir2_to, maxdepth-1); | |
869 | ||
870 | closedir(dir2_to); | |
871 | closedir(dir2_from); | |
872 | } | |
873 | } | |
874 | ||
875 | return 0; | |
876 | } | |
877 | ||
761dfddc | 878 | static void static_dev_create_links(struct udev *udev, DIR *dir) |
cb9a0eee KS |
879 | { |
880 | struct stdlinks { | |
881 | const char *link; | |
882 | const char *target; | |
883 | }; | |
884 | static const struct stdlinks stdlinks[] = { | |
885 | { "core", "/proc/kcore" }, | |
4bfa5cf5 | 886 | { "fd", "/proc/self/fd" }, |
cb9a0eee KS |
887 | { "stdin", "/proc/self/fd/0" }, |
888 | { "stdout", "/proc/self/fd/1" }, | |
889 | { "stderr", "/proc/self/fd/2" }, | |
890 | }; | |
891 | unsigned int i; | |
cb9a0eee | 892 | |
cb9a0eee | 893 | for (i = 0; i < ARRAY_SIZE(stdlinks); i++) { |
761dfddc KS |
894 | udev_selinux_setfscreateconat(udev, dirfd(dir), stdlinks[i].link, S_IFLNK); |
895 | if (symlinkat(stdlinks[i].target, dirfd(dir), stdlinks[i].link) < 0 && errno == EEXIST) | |
896 | utimensat(dirfd(dir), stdlinks[i].link, NULL, AT_SYMLINK_NOFOLLOW); | |
cb9a0eee KS |
897 | udev_selinux_resetfscreatecon(udev); |
898 | } | |
761dfddc KS |
899 | } |
900 | ||
901 | static void static_dev_create_from_devices(struct udev *udev, DIR *dir) | |
902 | { | |
903 | DIR *dir_from; | |
cb9a0eee | 904 | |
cb9a0eee | 905 | dir_from = opendir(LIBEXECDIR "/devices"); |
761dfddc KS |
906 | if (dir_from == NULL) |
907 | return; | |
908 | copy_dir(udev, dir_from, dir, 8); | |
909 | closedir(dir_from); | |
910 | } | |
911 | ||
912 | static void static_dev_create(struct udev *udev) | |
913 | { | |
914 | DIR *dir; | |
cb9a0eee | 915 | |
761dfddc KS |
916 | dir = opendir(udev_get_dev_path(udev)); |
917 | if (dir == NULL) | |
918 | return; | |
919 | ||
920 | static_dev_create_links(udev, dir); | |
921 | static_dev_create_from_devices(udev, dir); | |
922 | ||
923 | closedir(dir); | |
cb9a0eee KS |
924 | } |
925 | ||
926 | static int mem_size_mb(void) | |
927 | { | |
928 | FILE *f; | |
929 | char buf[4096]; | |
930 | long int memsize = -1; | |
931 | ||
932 | f = fopen("/proc/meminfo", "r"); | |
933 | if (f == NULL) | |
934 | return -1; | |
935 | ||
936 | while (fgets(buf, sizeof(buf), f) != NULL) { | |
937 | long int value; | |
938 | ||
939 | if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { | |
940 | memsize = value / 1024; | |
941 | break; | |
942 | } | |
943 | } | |
944 | ||
945 | fclose(f); | |
946 | return memsize; | |
947 | } | |
948 | ||
59345311 | 949 | int main(int argc, char *argv[]) |
c2cf4012 | 950 | { |
7d563a17 | 951 | struct udev *udev; |
3904a758 | 952 | int fd; |
3c189886 | 953 | FILE *f; |
1e03b754 | 954 | sigset_t mask; |
a15f42c4 | 955 | const char *value; |
c3804728 | 956 | int daemonize = false; |
5f03ed8a | 957 | int resolve_names = 1; |
b52a01ee | 958 | static const struct option options[] = { |
033e9f8c KS |
959 | { "daemon", no_argument, NULL, 'd' }, |
960 | { "debug-trace", no_argument, NULL, 't' }, | |
961 | { "debug", no_argument, NULL, 'D' }, | |
962 | { "help", no_argument, NULL, 'h' }, | |
963 | { "version", no_argument, NULL, 'V' }, | |
5f03ed8a | 964 | { "resolve-names", required_argument, NULL, 'N' }, |
b52a01ee KS |
965 | {} |
966 | }; | |
e3396a2d | 967 | int rc = 1; |
53921bfa | 968 | |
7d563a17 KS |
969 | udev = udev_new(); |
970 | if (udev == NULL) | |
971 | goto exit; | |
972 | ||
9060b066 | 973 | udev_log_init("udevd"); |
7d563a17 | 974 | udev_set_log_fn(udev, log_fn); |
aa8734ff | 975 | info(udev, "version %s\n", VERSION); |
c3b1fa66 | 976 | udev_selinux_init(udev); |
95a6f4c8 | 977 | |
88cbfb09 | 978 | for (;;) { |
7d563a17 KS |
979 | int option; |
980 | ||
c70560fe | 981 | option = getopt_long(argc, argv, "dDthV", options, NULL); |
b52a01ee KS |
982 | if (option == -1) |
983 | break; | |
984 | ||
985 | switch (option) { | |
986 | case 'd': | |
c3804728 | 987 | daemonize = true; |
b52a01ee | 988 | break; |
c7c00276 | 989 | case 't': |
c3804728 | 990 | debug_trace = true; |
c7c00276 | 991 | break; |
c70560fe | 992 | case 'D': |
c3804728 | 993 | debug = true; |
7d563a17 KS |
994 | if (udev_get_log_priority(udev) < LOG_INFO) |
995 | udev_set_log_priority(udev, LOG_INFO); | |
9e8fe79b | 996 | break; |
5f03ed8a SJR |
997 | case 'N': |
998 | if (strcmp (optarg, "early") == 0) { | |
999 | resolve_names = 1; | |
9032f119 SJR |
1000 | } else if (strcmp (optarg, "late") == 0) { |
1001 | resolve_names = 0; | |
5f03ed8a SJR |
1002 | } else if (strcmp (optarg, "never") == 0) { |
1003 | resolve_names = -1; | |
1004 | } else { | |
9032f119 SJR |
1005 | fprintf(stderr, "resolve-names must be early, late or never\n"); |
1006 | err(udev, "resolve-names must be early, late or never\n"); | |
5f03ed8a SJR |
1007 | goto exit; |
1008 | } | |
1009 | break; | |
b52a01ee | 1010 | case 'h': |
6469c772 KS |
1011 | printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] " |
1012 | "[--resolve-names=early|late|never] [--version]\n"); | |
841e168c MS |
1013 | goto exit; |
1014 | case 'V': | |
01618658 | 1015 | printf("%s\n", VERSION); |
e3396a2d | 1016 | goto exit; |
b52a01ee KS |
1017 | default: |
1018 | goto exit; | |
561d4c5a | 1019 | } |
561d4c5a | 1020 | } |
40caaeec | 1021 | |
fc89fe7e KS |
1022 | if (getuid() != 0) { |
1023 | fprintf(stderr, "root privileges required\n"); | |
7d563a17 | 1024 | err(udev, "root privileges required\n"); |
fc89fe7e KS |
1025 | goto exit; |
1026 | } | |
1027 | ||
5edec024 MS |
1028 | /* make sure std{in,out,err} fd's are in a sane state */ |
1029 | fd = open("/dev/null", O_RDWR); | |
1030 | if (fd < 0) { | |
1031 | fprintf(stderr, "cannot open /dev/null\n"); | |
7d563a17 | 1032 | err(udev, "cannot open /dev/null\n"); |
5edec024 | 1033 | } |
5edec024 MS |
1034 | if (write(STDOUT_FILENO, 0, 0) < 0) |
1035 | dup2(fd, STDOUT_FILENO); | |
1036 | if (write(STDERR_FILENO, 0, 0) < 0) | |
1037 | dup2(fd, STDERR_FILENO); | |
1038 | ||
d59f11e1 KS |
1039 | /* init control socket, bind() ensures, that only one udevd instance is running */ |
1040 | udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); | |
1041 | if (udev_ctrl == NULL) { | |
1042 | fprintf(stderr, "error initializing control socket"); | |
1043 | err(udev, "error initializing udevd socket"); | |
1044 | rc = 1; | |
1045 | goto exit; | |
1046 | } | |
d59f11e1 KS |
1047 | if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { |
1048 | fprintf(stderr, "error binding control socket, seems udevd is already running\n"); | |
1049 | err(udev, "error binding control socket, seems udevd is already running\n"); | |
1050 | rc = 1; | |
833b3c68 KS |
1051 | goto exit; |
1052 | } | |
1e03b754 | 1053 | pfd[FD_CONTROL].fd = udev_ctrl_get_fd(udev_ctrl); |
833b3c68 | 1054 | |
1e03b754 KS |
1055 | monitor = udev_monitor_new_from_netlink(udev, "kernel"); |
1056 | if (monitor == NULL || udev_monitor_enable_receiving(monitor) < 0) { | |
e3396a2d | 1057 | fprintf(stderr, "error initializing netlink socket\n"); |
7d563a17 | 1058 | err(udev, "error initializing netlink socket\n"); |
833b3c68 KS |
1059 | rc = 3; |
1060 | goto exit; | |
1061 | } | |
1e03b754 KS |
1062 | udev_monitor_set_receive_buffer_size(monitor, 128*1024*1024); |
1063 | pfd[FD_NETLINK].fd = udev_monitor_get_fd(monitor); | |
1064 | ||
1065 | pfd[FD_INOTIFY].fd = udev_watch_init(udev); | |
1066 | if (pfd[FD_INOTIFY].fd < 0) { | |
1067 | fprintf(stderr, "error initializing inotify\n"); | |
1068 | err(udev, "error initializing inotify\n"); | |
1069 | rc = 4; | |
1070 | goto exit; | |
1071 | } | |
1072 | ||
1073 | if (udev_get_rules_path(udev) != NULL) { | |
1074 | inotify_add_watch(pfd[FD_INOTIFY].fd, udev_get_rules_path(udev), | |
6f1892dc | 1075 | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1e03b754 KS |
1076 | } else { |
1077 | char filename[UTIL_PATH_SIZE]; | |
081be002 | 1078 | struct stat statbuf; |
1e03b754 | 1079 | |
6133f343 | 1080 | inotify_add_watch(pfd[FD_INOTIFY].fd, LIBEXECDIR "/rules.d", |
6f1892dc | 1081 | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1e03b754 | 1082 | inotify_add_watch(pfd[FD_INOTIFY].fd, SYSCONFDIR "/udev/rules.d", |
6f1892dc | 1083 | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1e03b754 KS |
1084 | |
1085 | /* watch dynamic rules directory */ | |
1086 | util_strscpyl(filename, sizeof(filename), udev_get_dev_path(udev), "/.udev/rules.d", NULL); | |
081be002 KS |
1087 | if (stat(filename, &statbuf) != 0) { |
1088 | util_create_path(udev, filename); | |
1089 | udev_selinux_setfscreatecon(udev, filename, S_IFDIR|0755); | |
1090 | mkdir(filename, 0755); | |
1091 | udev_selinux_resetfscreatecon(udev); | |
1092 | } | |
1e03b754 | 1093 | inotify_add_watch(pfd[FD_INOTIFY].fd, filename, |
6f1892dc | 1094 | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1e03b754 KS |
1095 | } |
1096 | udev_watch_restore(udev); | |
1097 | ||
1098 | /* block and listen to all signals on signalfd */ | |
1099 | sigfillset(&mask); | |
d412a685 | 1100 | sigprocmask(SIG_SETMASK, &mask, &orig_sigmask); |
1e03b754 KS |
1101 | pfd[FD_SIGNAL].fd = signalfd(-1, &mask, 0); |
1102 | if (pfd[FD_SIGNAL].fd < 0) { | |
1103 | fprintf(stderr, "error getting signalfd\n"); | |
1104 | err(udev, "error getting signalfd\n"); | |
1105 | rc = 5; | |
1106 | goto exit; | |
1107 | } | |
1108 | ||
1109 | /* unnamed socket from workers to the main daemon */ | |
26347a4c | 1110 | if (socketpair(AF_LOCAL, SOCK_DGRAM|SOCK_CLOEXEC, 0, worker_watch) < 0) { |
1e03b754 KS |
1111 | fprintf(stderr, "error getting socketpair\n"); |
1112 | err(udev, "error getting socketpair\n"); | |
1113 | rc = 6; | |
1114 | goto exit; | |
1115 | } | |
1116 | pfd[FD_WORKER].fd = worker_watch[READ_END]; | |
833b3c68 | 1117 | |
5f03ed8a | 1118 | rules = udev_rules_new(udev, resolve_names); |
d7ddce18 KS |
1119 | if (rules == NULL) { |
1120 | err(udev, "error reading rules\n"); | |
1121 | goto exit; | |
1122 | } | |
1e03b754 | 1123 | |
f503f6b2 AJ |
1124 | udev_queue_export = udev_queue_export_new(udev); |
1125 | if (udev_queue_export == NULL) { | |
1126 | err(udev, "error creating queue file\n"); | |
1127 | goto exit; | |
1128 | } | |
90cd961e | 1129 | |
561d4c5a | 1130 | if (daemonize) { |
f15515b5 KS |
1131 | pid_t pid; |
1132 | ||
1133 | pid = fork(); | |
1134 | switch (pid) { | |
1135 | case 0: | |
f15515b5 KS |
1136 | break; |
1137 | case -1: | |
659353f5 | 1138 | err(udev, "fork of daemon failed: %m\n"); |
833b3c68 | 1139 | rc = 4; |
f15515b5 KS |
1140 | goto exit; |
1141 | default: | |
2f64aa40 | 1142 | rc = 0; |
833b3c68 | 1143 | goto exit; |
f15515b5 KS |
1144 | } |
1145 | } | |
1146 | ||
3c189886 KS |
1147 | f = fopen("/dev/kmsg", "w"); |
1148 | if (f != NULL) { | |
1149 | fprintf(f, "<6>udev: starting version " VERSION "\n"); | |
1150 | fclose(f); | |
1151 | } | |
1e03b754 | 1152 | |
d59f11e1 | 1153 | /* redirect std{out,err} */ |
0bc74ea7 KS |
1154 | if (!debug && !debug_trace) { |
1155 | dup2(fd, STDIN_FILENO); | |
5edec024 | 1156 | dup2(fd, STDOUT_FILENO); |
d59f11e1 KS |
1157 | dup2(fd, STDERR_FILENO); |
1158 | } | |
5edec024 MS |
1159 | if (fd > STDERR_FILENO) |
1160 | close(fd); | |
e3396a2d | 1161 | |
3904a758 | 1162 | /* set scheduling priority for the daemon */ |
085cce37 KS |
1163 | setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); |
1164 | ||
3904a758 | 1165 | chdir("/"); |
74adec7d | 1166 | umask(022); |
3bc7c84c | 1167 | setsid(); |
3904a758 KS |
1168 | |
1169 | /* OOM_DISABLE == -17 */ | |
1170 | fd = open("/proc/self/oom_adj", O_RDWR); | |
45798927 | 1171 | if (fd < 0) { |
659353f5 | 1172 | err(udev, "error disabling OOM: %m\n"); |
45798927 | 1173 | } else { |
3904a758 KS |
1174 | write(fd, "-17", 3); |
1175 | close(fd); | |
1176 | } | |
1177 | ||
0bc74ea7 KS |
1178 | /* in trace mode run one event after the other */ |
1179 | if (debug_trace) { | |
87d55ff6 | 1180 | children_max = 1; |
0bc74ea7 | 1181 | } else { |
f051e340 | 1182 | int memsize = mem_size_mb(); |
1e03b754 | 1183 | |
f051e340 | 1184 | if (memsize > 0) |
87d55ff6 | 1185 | children_max = 128 + (memsize / 8); |
f051e340 | 1186 | else |
87d55ff6 | 1187 | children_max = 128; |
f051e340 | 1188 | } |
1e03b754 | 1189 | |
0bc74ea7 | 1190 | /* possibly overwrite maximum limit of executed events */ |
87d55ff6 | 1191 | value = getenv("UDEVD_MAX_CHILDREN"); |
0bc74ea7 | 1192 | if (value) |
87d55ff6 KS |
1193 | children_max = strtoul(value, NULL, 10); |
1194 | info(udev, "initialize children_max to %u\n", children_max); | |
a15f42c4 | 1195 | |
761dfddc KS |
1196 | static_dev_create(udev); |
1197 | static_dev_create_from_modules(udev); | |
1198 | udev_rules_apply_static_dev_perms(rules); | |
1199 | ||
1e03b754 KS |
1200 | udev_list_init(&event_list); |
1201 | udev_list_init(&worker_list); | |
1202 | ||
63cc8f04 | 1203 | while (!udev_exit) { |
e9a77fd8 | 1204 | int fdcount; |
1e03b754 | 1205 | int timeout; |
3210a72b | 1206 | |
1e03b754 | 1207 | /* set timeout to kill idle workers */ |
87d55ff6 | 1208 | if (udev_list_is_empty(&event_list) && children > 2) |
1e03b754 KS |
1209 | timeout = 3 * 1000; |
1210 | else | |
1211 | timeout = -1; | |
1212 | /* wait for events */ | |
1213 | fdcount = poll(pfd, ARRAY_SIZE(pfd), timeout); | |
1214 | if (fdcount < 0) | |
1215 | continue; | |
021a294c | 1216 | |
1e03b754 KS |
1217 | /* timeout - kill idle workers */ |
1218 | if (fdcount == 0) | |
adda4c68 | 1219 | worker_kill(udev, 2); |
e9a77fd8 | 1220 | |
1e03b754 KS |
1221 | /* event has finished */ |
1222 | if (pfd[FD_WORKER].revents & POLLIN) | |
1223 | worker_returned(); | |
e9a77fd8 | 1224 | |
1e03b754 KS |
1225 | /* get kernel uevent */ |
1226 | if (pfd[FD_NETLINK].revents & POLLIN) { | |
1227 | struct udev_device *dev; | |
3210a72b | 1228 | |
1e03b754 KS |
1229 | dev = udev_monitor_receive_device(monitor); |
1230 | if (dev != NULL) | |
1231 | event_queue_insert(dev); | |
1232 | else | |
1233 | udev_device_unref(dev); | |
2f6cbd19 | 1234 | } |
e5a2989e | 1235 | |
1e03b754 KS |
1236 | /* start new events */ |
1237 | if (!udev_list_is_empty(&event_list) && !stop_exec_queue) | |
1238 | events_start(udev); | |
aa8734ff | 1239 | |
1e03b754 KS |
1240 | /* get signal */ |
1241 | if (pfd[FD_SIGNAL].revents & POLLIN) { | |
1242 | struct signalfd_siginfo fdsi; | |
1243 | ssize_t size; | |
aa8734ff | 1244 | |
1e03b754 KS |
1245 | size = read(pfd[FD_SIGNAL].fd, &fdsi, sizeof(struct signalfd_siginfo)); |
1246 | if (size == sizeof(struct signalfd_siginfo)) | |
45798927 | 1247 | handle_signal(udev, fdsi.ssi_signo); |
021a294c | 1248 | } |
e5a2989e | 1249 | |
1e03b754 KS |
1250 | /* device node and rules directory inotify watch */ |
1251 | if (pfd[FD_INOTIFY].revents & POLLIN) | |
4aca304e | 1252 | handle_inotify(udev); |
c895fd00 | 1253 | |
1e03b754 KS |
1254 | /* |
1255 | * get control message | |
1256 | * | |
1257 | * This needs to be after the inotify handling, to make sure, | |
1258 | * that the settle signal is send back after the possibly generated | |
1259 | * "change" events by the inotify device node watch. | |
1260 | */ | |
1261 | if (pfd[FD_CONTROL].revents & POLLIN) | |
1262 | handle_ctrl_msg(udev_ctrl); | |
3210a72b | 1263 | |
e3396a2d | 1264 | /* rules changed, set by inotify or a HUP signal */ |
c895fd00 | 1265 | if (reload_config) { |
d7ddce18 KS |
1266 | struct udev_rules *rules_new; |
1267 | ||
adda4c68 | 1268 | worker_kill(udev, 0); |
5f03ed8a | 1269 | rules_new = udev_rules_new(udev, resolve_names); |
d7ddce18 KS |
1270 | if (rules_new != NULL) { |
1271 | udev_rules_unref(rules); | |
1272 | rules = rules_new; | |
1273 | } | |
1e03b754 | 1274 | reload_config = 0; |
bb38678e | 1275 | } |
53921bfa | 1276 | } |
1e03b754 | 1277 | |
f503f6b2 | 1278 | udev_queue_export_cleanup(udev_queue_export); |
e3396a2d | 1279 | rc = 0; |
53921bfa | 1280 | exit: |
f503f6b2 | 1281 | udev_queue_export_unref(udev_queue_export); |
d7ddce18 | 1282 | udev_rules_unref(rules); |
d59f11e1 | 1283 | udev_ctrl_unref(udev_ctrl); |
1e03b754 KS |
1284 | if (pfd[FD_SIGNAL].fd >= 0) |
1285 | close(pfd[FD_SIGNAL].fd); | |
1286 | if (worker_watch[READ_END] >= 0) | |
1287 | close(worker_watch[READ_END]); | |
1288 | if (worker_watch[WRITE_END] >= 0) | |
1289 | close(worker_watch[WRITE_END]); | |
1290 | udev_monitor_unref(monitor); | |
c3b1fa66 | 1291 | udev_selinux_exit(udev); |
e598c573 | 1292 | udev_unref(udev); |
9060b066 | 1293 | udev_log_close(); |
833b3c68 | 1294 | return rc; |
7fafc032 | 1295 | } |