]>
Commit | Line | Data |
---|---|---|
7fafc032 | 1 | /* |
b3518c16 | 2 | * Copyright (C) 2004-2006 Kay Sievers <kay.sievers@vrfy.org> |
2f6cbd19 | 3 | * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> |
7fafc032 | 4 | * |
7fafc032 KS |
5 | * This program is free software; you can redistribute it and/or modify it |
6 | * under the terms of the GNU General Public License as published by the | |
7 | * Free Software Foundation version 2 of the License. | |
8 | * | |
9 | * This program is distributed in the hope that it will be useful, but | |
10 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
12 | * General Public License for more details. | |
13 | * | |
14 | * You should have received a copy of the GNU General Public License along | |
15 | * with this program; if not, write to the Free Software Foundation, Inc., | |
27b77df4 | 16 | * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
7fafc032 KS |
17 | * |
18 | */ | |
19 | ||
01618658 KS |
20 | #include "config.h" |
21 | ||
a695feae | 22 | #include <stddef.h> |
7fafc032 KS |
23 | #include <signal.h> |
24 | #include <unistd.h> | |
25 | #include <errno.h> | |
26 | #include <stdio.h> | |
27 | #include <stdlib.h> | |
28 | #include <string.h> | |
085cce37 KS |
29 | #include <ctype.h> |
30 | #include <dirent.h> | |
31 | #include <fcntl.h> | |
40caaeec | 32 | #include <syslog.h> |
0b3dfb3d | 33 | #include <time.h> |
b52a01ee | 34 | #include <getopt.h> |
138068d6 KS |
35 | #include <sys/select.h> |
36 | #include <sys/wait.h> | |
53921bfa KS |
37 | #include <sys/types.h> |
38 | #include <sys/socket.h> | |
39 | #include <sys/un.h> | |
dc117daa | 40 | #include <sys/stat.h> |
c895fd00 | 41 | #include <sys/ioctl.h> |
62821d0d | 42 | #include <linux/types.h> |
88f4b648 | 43 | #include <linux/netlink.h> |
01618658 KS |
44 | #ifdef HAVE_INOTIFY |
45 | #include <sys/inotify.h> | |
46 | #endif | |
7fafc032 KS |
47 | |
48 | #include "udev.h" | |
c895fd00 | 49 | #include "udev_rules.h" |
eef7c9a3 | 50 | #include "udev_selinux.h" |
7fafc032 | 51 | |
d59f11e1 KS |
52 | #define UDEVD_PRIORITY -4 |
53 | #define UDEV_PRIORITY -2 | |
54 | ||
55 | /* maximum limit of forked childs */ | |
56 | #define UDEVD_MAX_CHILDS 256 | |
57 | /* start to throttle forking if maximum number of running childs in our session is reached */ | |
58 | #define UDEVD_MAX_CHILDS_RUNNING 16 | |
59 | ||
c70560fe | 60 | static int debug; |
9e8fe79b | 61 | |
7d563a17 KS |
62 | static void log_fn(struct udev *udev, int priority, |
63 | const char *file, int line, const char *fn, | |
64 | const char *format, va_list args) | |
65 | { | |
66 | if (debug) { | |
67 | fprintf(stderr, "[%d] %s: ", (int) getpid(), fn); | |
68 | vfprintf(stderr, format, args); | |
69 | } else { | |
70 | vsyslog(priority, format, args); | |
71 | } | |
72 | } | |
73 | ||
74 | struct udevd_uevent_msg { | |
75 | struct udev *udev; | |
76 | struct list_head node; | |
77 | pid_t pid; | |
78 | int exitstatus; | |
79 | time_t queue_time; | |
80 | char *action; | |
81 | char *devpath; | |
82 | char *subsystem; | |
83 | char *driver; | |
84 | dev_t devt; | |
85 | unsigned long long seqnum; | |
86 | char *devpath_old; | |
87 | char *physdevpath; | |
88 | unsigned int timeout; | |
89 | char *envp[UEVENT_NUM_ENVP+1]; | |
90 | char envbuf[]; | |
91 | }; | |
92 | ||
93 | static int debug_trace; | |
1aa1e248 | 94 | static struct udev_rules rules; |
d59f11e1 | 95 | static struct udev_ctrl *udev_ctrl; |
239cc98b KS |
96 | static int uevent_netlink_sock = -1; |
97 | static int inotify_fd = -1; | |
085cce37 | 98 | static pid_t sid; |
13f24d59 | 99 | |
f1ff8d7b | 100 | static int signal_pipe[2] = {-1, -1}; |
5cab7caa | 101 | static volatile int sigchilds_waiting; |
63cc8f04 | 102 | static volatile int udev_exit; |
c895fd00 | 103 | static volatile int reload_config; |
f27125f9 | 104 | static int run_exec_q; |
3b47c739 | 105 | static int stop_exec_q; |
a15f42c4 KS |
106 | static int max_childs; |
107 | static int max_childs_running; | |
7d563a17 | 108 | static char udev_log_env[32]; |
40caaeec | 109 | |
40caaeec KS |
110 | static LIST_HEAD(exec_list); |
111 | static LIST_HEAD(running_list); | |
a15f42c4 | 112 | |
c895fd00 KS |
113 | static void asmlinkage udev_event_sig_handler(int signum) |
114 | { | |
115 | if (signum == SIGALRM) | |
116 | exit(1); | |
117 | } | |
118 | ||
b3518c16 | 119 | static int udev_event_process(struct udevd_uevent_msg *msg) |
c895fd00 KS |
120 | { |
121 | struct sigaction act; | |
7d563a17 | 122 | struct udevice *udevice; |
c895fd00 KS |
123 | int i; |
124 | int retval; | |
125 | ||
126 | /* set signal handlers */ | |
127 | memset(&act, 0x00, sizeof(act)); | |
128 | act.sa_handler = (void (*)(int)) udev_event_sig_handler; | |
129 | sigemptyset (&act.sa_mask); | |
130 | act.sa_flags = 0; | |
131 | sigaction(SIGALRM, &act, NULL); | |
132 | ||
3e5e8332 KS |
133 | /* reset to default */ |
134 | act.sa_handler = SIG_DFL; | |
135 | sigaction(SIGINT, &act, NULL); | |
136 | sigaction(SIGTERM, &act, NULL); | |
137 | sigaction(SIGCHLD, &act, NULL); | |
138 | sigaction(SIGHUP, &act, NULL); | |
139 | ||
c895fd00 | 140 | /* trigger timeout to prevent hanging processes */ |
bf50425b | 141 | alarm(UDEV_EVENT_TIMEOUT); |
c895fd00 | 142 | |
1aa1e248 | 143 | /* reconstruct event environment from message */ |
c895fd00 KS |
144 | for (i = 0; msg->envp[i]; i++) |
145 | putenv(msg->envp[i]); | |
146 | ||
7d563a17 KS |
147 | udevice = udev_device_init(msg->udev); |
148 | if (udevice == NULL) | |
1aa1e248 | 149 | return -1; |
7d563a17 KS |
150 | strlcpy(udevice->action, msg->action, sizeof(udevice->action)); |
151 | sysfs_device_set_values(udevice->udev, udevice->dev, msg->devpath, msg->subsystem, msg->driver); | |
152 | udevice->devpath_old = msg->devpath_old; | |
153 | udevice->devt = msg->devt; | |
1aa1e248 | 154 | |
7d563a17 | 155 | retval = udev_device_event(&rules, udevice); |
c895fd00 | 156 | |
bf50425b | 157 | /* rules may change/disable the timeout */ |
7d563a17 KS |
158 | if (udevice->event_timeout >= 0) |
159 | alarm(udevice->event_timeout); | |
bf50425b | 160 | |
c895fd00 | 161 | /* run programs collected by RUN-key*/ |
7d563a17 KS |
162 | if (retval == 0 && !udevice->ignore_device && udev_get_run(msg->udev)) |
163 | retval = udev_rules_run(udevice); | |
c895fd00 | 164 | |
7d563a17 | 165 | udev_device_cleanup(udevice); |
f4fc0136 | 166 | return retval; |
c895fd00 KS |
167 | } |
168 | ||
7a770250 KS |
169 | enum event_state { |
170 | EVENT_QUEUED, | |
171 | EVENT_FINISHED, | |
172 | EVENT_FAILED, | |
173 | }; | |
174 | ||
b3518c16 | 175 | static void export_event_state(struct udevd_uevent_msg *msg, enum event_state state) |
7a770250 KS |
176 | { |
177 | char filename[PATH_SIZE]; | |
178 | char filename_failed[PATH_SIZE]; | |
9c6ad9fb | 179 | size_t start; |
7a770250 | 180 | |
a2f2270e | 181 | /* location of queue file */ |
7d563a17 | 182 | snprintf(filename, sizeof(filename), "%s/.udev/queue/%llu", udev_get_dev_path(msg->udev), msg->seqnum); |
7a770250 | 183 | |
a2f2270e | 184 | /* location of failed file */ |
7d563a17 | 185 | strlcpy(filename_failed, udev_get_dev_path(msg->udev), sizeof(filename_failed)); |
7a770250 | 186 | strlcat(filename_failed, "/", sizeof(filename_failed)); |
01618658 | 187 | start = strlcat(filename_failed, ".udev/failed/", sizeof(filename_failed)); |
9c6ad9fb | 188 | strlcat(filename_failed, msg->devpath, sizeof(filename_failed)); |
ce398745 | 189 | path_encode(&filename_failed[start], sizeof(filename_failed) - start); |
7a770250 KS |
190 | |
191 | switch (state) { | |
192 | case EVENT_QUEUED: | |
193 | unlink(filename_failed); | |
7d563a17 KS |
194 | delete_path(msg->udev, filename_failed); |
195 | create_path(msg->udev, filename); | |
196 | selinux_setfscreatecon(msg->udev, filename, NULL, S_IFLNK); | |
ce398745 | 197 | symlink(msg->devpath, filename); |
7d563a17 | 198 | selinux_resetfscreatecon(msg->udev); |
ce398745 | 199 | break; |
7a770250 | 200 | case EVENT_FINISHED: |
a2f2270e KS |
201 | if (msg->devpath_old != NULL) { |
202 | /* "move" event - rename failed file to current name, do not delete failed */ | |
203 | char filename_failed_old[PATH_SIZE]; | |
204 | ||
7d563a17 | 205 | strlcpy(filename_failed_old, udev_get_dev_path(msg->udev), sizeof(filename_failed_old)); |
a2f2270e | 206 | strlcat(filename_failed_old, "/", sizeof(filename_failed_old)); |
01618658 | 207 | start = strlcat(filename_failed_old, ".udev/failed/", sizeof(filename_failed_old)); |
a2f2270e KS |
208 | strlcat(filename_failed_old, msg->devpath_old, sizeof(filename_failed_old)); |
209 | path_encode(&filename_failed_old[start], sizeof(filename) - start); | |
210 | ||
211 | if (rename(filename_failed_old, filename_failed) == 0) | |
7d563a17 | 212 | info(msg->udev, "renamed devpath, moved failed state of '%s' to %s'\n", |
a2f2270e KS |
213 | msg->devpath_old, msg->devpath); |
214 | } else { | |
215 | unlink(filename_failed); | |
7d563a17 | 216 | delete_path(msg->udev, filename_failed); |
a2f2270e | 217 | } |
7a770250 | 218 | |
ce398745 | 219 | unlink(filename); |
7d563a17 | 220 | delete_path(msg->udev, filename); |
ce398745 KS |
221 | break; |
222 | case EVENT_FAILED: | |
a2f2270e | 223 | /* move failed event to the failed directory */ |
7d563a17 | 224 | create_path(msg->udev, filename_failed); |
ce398745 | 225 | rename(filename, filename_failed); |
1b75f109 | 226 | |
a2f2270e | 227 | /* clean up possibly empty queue directory */ |
7d563a17 | 228 | delete_path(msg->udev, filename); |
ce398745 | 229 | break; |
7a770250 | 230 | } |
ce398745 KS |
231 | |
232 | return; | |
7a770250 KS |
233 | } |
234 | ||
b3518c16 | 235 | static void msg_queue_delete(struct udevd_uevent_msg *msg) |
fc465079 KS |
236 | { |
237 | list_del(&msg->node); | |
7a770250 | 238 | |
a2f2270e | 239 | /* mark as failed, if "add" event returns non-zero */ |
7a770250 KS |
240 | if (msg->exitstatus && strcmp(msg->action, "add") == 0) |
241 | export_event_state(msg, EVENT_FAILED); | |
242 | else | |
243 | export_event_state(msg, EVENT_FINISHED); | |
244 | ||
fc465079 KS |
245 | free(msg); |
246 | } | |
247 | ||
b3518c16 | 248 | static void udev_event_run(struct udevd_uevent_msg *msg) |
7fafc032 | 249 | { |
90c210eb | 250 | pid_t pid; |
f4fc0136 | 251 | int retval; |
90c210eb KS |
252 | |
253 | pid = fork(); | |
254 | switch (pid) { | |
255 | case 0: | |
33db4b8d | 256 | /* child */ |
833b3c68 | 257 | close(uevent_netlink_sock); |
d59f11e1 | 258 | udev_ctrl_unref(udev_ctrl); |
90cd961e | 259 | if (inotify_fd >= 0) |
c895fd00 | 260 | close(inotify_fd); |
c895fd00 KS |
261 | close(signal_pipe[READ_END]); |
262 | close(signal_pipe[WRITE_END]); | |
f602ccf0 | 263 | logging_close(); |
c895fd00 | 264 | logging_init("udevd-event"); |
085cce37 | 265 | setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); |
b437ec95 | 266 | |
f4fc0136 | 267 | retval = udev_event_process(msg); |
7d563a17 | 268 | info(msg->udev, "seq %llu finished with %i\n", msg->seqnum, retval); |
c895fd00 KS |
269 | |
270 | logging_close(); | |
f4fc0136 KS |
271 | if (retval) |
272 | exit(1); | |
c895fd00 | 273 | exit(0); |
90c210eb | 274 | case -1: |
7d563a17 | 275 | err(msg->udev, "fork of child failed: %s\n", strerror(errno)); |
ebfc1acd | 276 | msg_queue_delete(msg); |
2f6cbd19 | 277 | break; |
90c210eb | 278 | default: |
2f6cbd19 | 279 | /* get SIGCHLD in main loop */ |
7d563a17 | 280 | info(msg->udev, "seq %llu forked, pid [%d], '%s' '%s', %ld seconds old\n", |
0b3dfb3d | 281 | msg->seqnum, pid, msg->action, msg->subsystem, time(NULL) - msg->queue_time); |
2f6cbd19 | 282 | msg->pid = pid; |
90c210eb | 283 | } |
7fafc032 KS |
284 | } |
285 | ||
b3518c16 | 286 | static void msg_queue_insert(struct udevd_uevent_msg *msg) |
fc465079 | 287 | { |
7baada47 KS |
288 | char filename[PATH_SIZE]; |
289 | int fd; | |
290 | ||
fc465079 | 291 | msg->queue_time = time(NULL); |
7baada47 | 292 | |
0f624f16 | 293 | export_event_state(msg, EVENT_QUEUED); |
7d563a17 | 294 | info(msg->udev, "seq %llu queued, '%s' '%s'\n", msg->seqnum, msg->action, msg->subsystem); |
0f624f16 | 295 | |
7d563a17 | 296 | strlcpy(filename, udev_get_dev_path(msg->udev), sizeof(filename)); |
01618658 | 297 | strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); |
7baada47 | 298 | fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
90cd961e | 299 | if (fd >= 0) { |
7baada47 KS |
300 | char str[32]; |
301 | int len; | |
302 | ||
303 | len = sprintf(str, "%llu\n", msg->seqnum); | |
304 | write(fd, str, len); | |
305 | close(fd); | |
306 | } | |
fc465079 | 307 | |
c7c00276 KS |
308 | /* run one event after the other in debug mode */ |
309 | if (debug_trace) { | |
310 | list_add_tail(&msg->node, &running_list); | |
311 | udev_event_run(msg); | |
312 | waitpid(msg->pid, NULL, 0); | |
313 | msg_queue_delete(msg); | |
314 | return; | |
315 | } | |
316 | ||
fc465079 KS |
317 | /* run all events with a timeout set immediately */ |
318 | if (msg->timeout != 0) { | |
319 | list_add_tail(&msg->node, &running_list); | |
320 | udev_event_run(msg); | |
321 | return; | |
322 | } | |
323 | ||
324 | list_add_tail(&msg->node, &exec_list); | |
325 | run_exec_q = 1; | |
326 | } | |
327 | ||
f051e340 KS |
328 | static int mem_size_mb(void) |
329 | { | |
9f1f67b1 KS |
330 | FILE* f; |
331 | char buf[4096]; | |
332 | long int memsize = -1; | |
f051e340 | 333 | |
9f1f67b1 KS |
334 | f = fopen("/proc/meminfo", "r"); |
335 | if (f == NULL) | |
f051e340 | 336 | return -1; |
f051e340 | 337 | |
9f1f67b1 KS |
338 | while (fgets(buf, sizeof(buf), f) != NULL) { |
339 | long int value; | |
f051e340 | 340 | |
9f1f67b1 KS |
341 | if (sscanf(buf, "MemTotal: %ld kB", &value) == 1) { |
342 | memsize = value / 1024; | |
343 | break; | |
344 | } | |
345 | } | |
f051e340 | 346 | |
fdc9a0b5 | 347 | fclose(f); |
9f1f67b1 | 348 | return memsize; |
f051e340 KS |
349 | } |
350 | ||
351 | static int cpu_count(void) | |
352 | { | |
9f1f67b1 KS |
353 | FILE* f; |
354 | char buf[4096]; | |
f051e340 KS |
355 | int count = 0; |
356 | ||
9f1f67b1 KS |
357 | f = fopen("/proc/stat", "r"); |
358 | if (f == NULL) | |
f051e340 | 359 | return -1; |
f051e340 | 360 | |
9f1f67b1 KS |
361 | while (fgets(buf, sizeof(buf), f) != NULL) { |
362 | if (strncmp(buf, "cpu", 3) == 0 && isdigit(buf[3])) | |
f051e340 | 363 | count++; |
f051e340 KS |
364 | } |
365 | ||
9f1f67b1 | 366 | fclose(f); |
f051e340 KS |
367 | if (count == 0) |
368 | return -1; | |
369 | return count; | |
370 | } | |
371 | ||
085cce37 KS |
372 | static int running_processes(void) |
373 | { | |
9f1f67b1 KS |
374 | FILE* f; |
375 | char buf[4096]; | |
376 | int running = -1; | |
085cce37 | 377 | |
9f1f67b1 KS |
378 | f = fopen("/proc/stat", "r"); |
379 | if (f == NULL) | |
085cce37 | 380 | return -1; |
085cce37 | 381 | |
9f1f67b1 KS |
382 | while (fgets(buf, sizeof(buf), f) != NULL) { |
383 | int value; | |
085cce37 | 384 | |
9f1f67b1 KS |
385 | if (sscanf(buf, "procs_running %u", &value) == 1) { |
386 | running = value; | |
387 | break; | |
388 | } | |
389 | } | |
085cce37 | 390 | |
9f1f67b1 | 391 | fclose(f); |
085cce37 KS |
392 | return running; |
393 | } | |
394 | ||
395 | /* return the number of process es in our session, count only until limit */ | |
396 | static int running_processes_in_session(pid_t session, int limit) | |
397 | { | |
398 | DIR *dir; | |
399 | struct dirent *dent; | |
400 | int running = 0; | |
401 | ||
402 | dir = opendir("/proc"); | |
403 | if (!dir) | |
404 | return -1; | |
405 | ||
406 | /* read process info from /proc */ | |
407 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
408 | int f; | |
409 | char procdir[64]; | |
410 | char line[256]; | |
411 | const char *pos; | |
412 | char state; | |
413 | pid_t ppid, pgrp, sess; | |
414 | int len; | |
415 | ||
416 | if (!isdigit(dent->d_name[0])) | |
417 | continue; | |
418 | ||
419 | snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name); | |
420 | procdir[sizeof(procdir)-1] = '\0'; | |
421 | ||
422 | f = open(procdir, O_RDONLY); | |
423 | if (f == -1) | |
424 | continue; | |
425 | ||
b4f192f0 | 426 | len = read(f, line, sizeof(line)-1); |
085cce37 KS |
427 | close(f); |
428 | ||
429 | if (len <= 0) | |
430 | continue; | |
431 | else | |
432 | line[len] = '\0'; | |
433 | ||
434 | /* skip ugly program name */ | |
435 | pos = strrchr(line, ')') + 2; | |
436 | if (pos == NULL) | |
437 | continue; | |
438 | ||
439 | if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4) | |
440 | continue; | |
441 | ||
442 | /* count only processes in our session */ | |
443 | if (sess != session) | |
444 | continue; | |
445 | ||
446 | /* count only running, no sleeping processes */ | |
447 | if (state != 'R') | |
448 | continue; | |
449 | ||
450 | running++; | |
451 | if (limit > 0 && running >= limit) | |
452 | break; | |
453 | } | |
454 | closedir(dir); | |
455 | ||
456 | return running; | |
457 | } | |
458 | ||
7b6571a9 KS |
459 | static int compare_devpath(const char *running, const char *waiting) |
460 | { | |
461 | int i; | |
462 | ||
63f61c5c | 463 | for (i = 0; i < PATH_SIZE; i++) { |
7b6571a9 KS |
464 | /* identical device event found */ |
465 | if (running[i] == '\0' && waiting[i] == '\0') | |
466 | return 1; | |
467 | ||
468 | /* parent device event found */ | |
469 | if (running[i] == '\0' && waiting[i] == '/') | |
470 | return 2; | |
471 | ||
472 | /* child device event found */ | |
473 | if (running[i] == '/' && waiting[i] == '\0') | |
474 | return 3; | |
475 | ||
476 | /* no matching event */ | |
477 | if (running[i] != waiting[i]) | |
478 | break; | |
479 | } | |
480 | ||
481 | return 0; | |
482 | } | |
483 | ||
fc630eda KS |
484 | /* lookup event for identical, parent, child, or physical device */ |
485 | static int devpath_busy(struct udevd_uevent_msg *msg, int limit) | |
7fafc032 | 486 | { |
b3518c16 | 487 | struct udevd_uevent_msg *loop_msg; |
a15f42c4 | 488 | int childs_count = 0; |
7b6571a9 | 489 | |
fc630eda KS |
490 | /* check exec-queue which may still contain delayed events we depend on */ |
491 | list_for_each_entry(loop_msg, &exec_list, node) { | |
492 | /* skip ourself and all later events */ | |
493 | if (loop_msg->seqnum >= msg->seqnum) | |
494 | break; | |
495 | ||
a2f2270e KS |
496 | /* check our old name */ |
497 | if (msg->devpath_old != NULL) | |
498 | if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) | |
499 | return 2; | |
500 | ||
fc630eda KS |
501 | /* check identical, parent, or child device event */ |
502 | if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { | |
7d563a17 | 503 | dbg(msg->udev, "%llu, device event still pending %llu (%s)\n", |
fc630eda | 504 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); |
a2f2270e | 505 | return 3; |
fc630eda KS |
506 | } |
507 | ||
c3b145a3 MK |
508 | /* check for our major:minor number */ |
509 | if (msg->devt && loop_msg->devt == msg->devt && | |
510 | strcmp(msg->subsystem, loop_msg->subsystem) == 0) { | |
7d563a17 | 511 | dbg(msg->udev, "%llu, device event still pending %llu (%d:%d)\n", msg->seqnum, |
c3b145a3 MK |
512 | loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); |
513 | return 4; | |
514 | } | |
515 | ||
fc630eda KS |
516 | /* check physical device event (special case of parent) */ |
517 | if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) | |
518 | if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { | |
7d563a17 | 519 | dbg(msg->udev, "%llu, physical device event still pending %llu (%s)\n", |
fc630eda | 520 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); |
c3b145a3 | 521 | return 5; |
fc630eda KS |
522 | } |
523 | } | |
524 | ||
1113044b | 525 | /* check run queue for still running events */ |
f8a178a3 | 526 | list_for_each_entry(loop_msg, &running_list, node) { |
a15f42c4 | 527 | if (limit && childs_count++ > limit) { |
7d563a17 | 528 | dbg(msg->udev, "%llu, maximum number (%i) of childs reached\n", msg->seqnum, childs_count); |
a15f42c4 KS |
529 | return 1; |
530 | } | |
80513ea3 | 531 | |
a2f2270e KS |
532 | /* check our old name */ |
533 | if (msg->devpath_old != NULL) | |
534 | if (strcmp(loop_msg->devpath , msg->devpath_old) == 0) | |
535 | return 2; | |
536 | ||
fc630eda | 537 | /* check identical, parent, or child device event */ |
a15f42c4 | 538 | if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { |
7d563a17 | 539 | dbg(msg->udev, "%llu, device event still running %llu (%s)\n", |
a15f42c4 | 540 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); |
a2f2270e | 541 | return 3; |
a15f42c4 | 542 | } |
79721e0a | 543 | |
c3b145a3 MK |
544 | /* check for our major:minor number */ |
545 | if (msg->devt && loop_msg->devt == msg->devt && | |
546 | strcmp(msg->subsystem, loop_msg->subsystem) == 0) { | |
7d563a17 | 547 | dbg(msg->udev, "%llu, device event still running %llu (%d:%d)\n", msg->seqnum, |
c3b145a3 MK |
548 | loop_msg->seqnum, major(loop_msg->devt), minor(loop_msg->devt)); |
549 | return 4; | |
550 | } | |
551 | ||
fc630eda | 552 | /* check physical device event (special case of parent) */ |
79721e0a | 553 | if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) |
a15f42c4 | 554 | if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { |
7d563a17 | 555 | dbg(msg->udev, "%llu, physical device event still running %llu (%s)\n", |
a15f42c4 | 556 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); |
c3b145a3 | 557 | return 5; |
a15f42c4 | 558 | } |
80513ea3 | 559 | } |
a15f42c4 | 560 | return 0; |
7fafc032 KS |
561 | } |
562 | ||
fc630eda | 563 | /* serializes events for the identical and parent and child devices */ |
7d563a17 | 564 | static void msg_queue_manager(struct udev *udev) |
7fafc032 | 565 | { |
b3518c16 KS |
566 | struct udevd_uevent_msg *loop_msg; |
567 | struct udevd_uevent_msg *tmp_msg; | |
085cce37 KS |
568 | int running; |
569 | ||
8ab44e3f KS |
570 | if (list_empty(&exec_list)) |
571 | return; | |
572 | ||
085cce37 | 573 | running = running_processes(); |
7d563a17 | 574 | dbg(udev, "%d processes runnning on system\n", running); |
085cce37 | 575 | if (running < 0) |
a15f42c4 | 576 | running = max_childs_running; |
53921bfa | 577 | |
f8a178a3 | 578 | list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) { |
085cce37 | 579 | /* check running processes in our session and possibly throttle */ |
a15f42c4 KS |
580 | if (running >= max_childs_running) { |
581 | running = running_processes_in_session(sid, max_childs_running+10); | |
7d563a17 | 582 | dbg(udev, "at least %d processes running in session\n", running); |
a15f42c4 | 583 | if (running >= max_childs_running) { |
7d563a17 | 584 | dbg(udev, "delay seq %llu, too many processes already running\n", loop_msg->seqnum); |
085cce37 KS |
585 | return; |
586 | } | |
587 | } | |
588 | ||
fc630eda KS |
589 | /* serialize and wait for parent or child events */ |
590 | if (devpath_busy(loop_msg, max_childs) != 0) { | |
7d563a17 | 591 | dbg(udev, "delay seq %llu (%s)\n", loop_msg->seqnum, loop_msg->devpath); |
fc465079 KS |
592 | continue; |
593 | } | |
594 | ||
595 | /* move event to run list */ | |
596 | list_move_tail(&loop_msg->node, &running_list); | |
597 | udev_event_run(loop_msg); | |
598 | running++; | |
7d563a17 | 599 | dbg(udev, "moved seq %llu to running list\n", loop_msg->seqnum); |
53921bfa KS |
600 | } |
601 | } | |
602 | ||
7d563a17 | 603 | static struct udevd_uevent_msg *get_msg_from_envbuf(struct udev *udev, const char *buf, int buf_size) |
88f4b648 KS |
604 | { |
605 | int bufpos; | |
606 | int i; | |
b3518c16 | 607 | struct udevd_uevent_msg *msg; |
220dac4c | 608 | char *physdevdriver_key = NULL; |
5780be9e KS |
609 | int maj = 0; |
610 | int min = 0; | |
88f4b648 | 611 | |
b3518c16 | 612 | msg = malloc(sizeof(struct udevd_uevent_msg) + buf_size); |
88f4b648 KS |
613 | if (msg == NULL) |
614 | return NULL; | |
b3518c16 | 615 | memset(msg, 0x00, sizeof(struct udevd_uevent_msg) + buf_size); |
7d563a17 | 616 | msg->udev = udev; |
88f4b648 KS |
617 | |
618 | /* copy environment buffer and reconstruct envp */ | |
619 | memcpy(msg->envbuf, buf, buf_size); | |
620 | bufpos = 0; | |
3b47c739 | 621 | for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) { |
88f4b648 KS |
622 | int keylen; |
623 | char *key; | |
624 | ||
625 | key = &msg->envbuf[bufpos]; | |
626 | keylen = strlen(key); | |
627 | msg->envp[i] = key; | |
628 | bufpos += keylen + 1; | |
7d563a17 | 629 | dbg(udev, "add '%s' to msg.envp[%i]\n", msg->envp[i], i); |
88f4b648 KS |
630 | |
631 | /* remember some keys for further processing */ | |
632 | if (strncmp(key, "ACTION=", 7) == 0) | |
633 | msg->action = &key[7]; | |
ebfc1acd | 634 | else if (strncmp(key, "DEVPATH=", 8) == 0) |
88f4b648 | 635 | msg->devpath = &key[8]; |
ebfc1acd | 636 | else if (strncmp(key, "SUBSYSTEM=", 10) == 0) |
88f4b648 | 637 | msg->subsystem = &key[10]; |
254efc14 KS |
638 | else if (strncmp(key, "DRIVER=", 7) == 0) |
639 | msg->driver = &key[7]; | |
ebfc1acd | 640 | else if (strncmp(key, "SEQNUM=", 7) == 0) |
88f4b648 | 641 | msg->seqnum = strtoull(&key[7], NULL, 10); |
a2f2270e KS |
642 | else if (strncmp(key, "DEVPATH_OLD=", 12) == 0) |
643 | msg->devpath_old = &key[12]; | |
ebfc1acd | 644 | else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) |
88f4b648 | 645 | msg->physdevpath = &key[12]; |
220dac4c KS |
646 | else if (strncmp(key, "PHYSDEVDRIVER=", 14) == 0) |
647 | physdevdriver_key = key; | |
ebfc1acd | 648 | else if (strncmp(key, "MAJOR=", 6) == 0) |
5780be9e | 649 | maj = strtoull(&key[6], NULL, 10); |
ebfc1acd | 650 | else if (strncmp(key, "MINOR=", 6) == 0) |
5780be9e | 651 | min = strtoull(&key[6], NULL, 10); |
ebfc1acd | 652 | else if (strncmp(key, "TIMEOUT=", 8) == 0) |
88f4b648 KS |
653 | msg->timeout = strtoull(&key[8], NULL, 10); |
654 | } | |
5780be9e | 655 | msg->devt = makedev(maj, min); |
88f4b648 | 656 | msg->envp[i++] = "UDEVD_EVENT=1"; |
220dac4c KS |
657 | |
658 | if (msg->driver == NULL && msg->physdevpath == NULL && physdevdriver_key != NULL) { | |
659 | /* for older kernels DRIVER is empty for a bus device, export PHYSDEVDRIVER as DRIVER */ | |
660 | msg->envp[i++] = &physdevdriver_key[7]; | |
661 | msg->driver = &physdevdriver_key[14]; | |
662 | } | |
663 | ||
88f4b648 KS |
664 | msg->envp[i] = NULL; |
665 | ||
0ec819d9 | 666 | if (msg->devpath == NULL || msg->action == NULL) { |
7d563a17 | 667 | info(udev, "DEVPATH or ACTION missing, ignore message\n"); |
e825b59b KS |
668 | free(msg); |
669 | return NULL; | |
670 | } | |
88f4b648 KS |
671 | return msg; |
672 | } | |
673 | ||
3b47c739 | 674 | /* receive the udevd message from userspace */ |
d59f11e1 | 675 | static void handle_ctrl_msg(struct udev_ctrl *uctrl) |
7fafc032 | 676 | { |
d59f11e1 KS |
677 | struct udev *udev = udev_ctrl_get_udev(uctrl); |
678 | struct udev_ctrl_msg *ctrl_msg; | |
679 | const char *str; | |
680 | int i; | |
7b1cbec9 | 681 | |
d59f11e1 KS |
682 | ctrl_msg = udev_ctrl_receive_msg(uctrl); |
683 | if (ctrl_msg == NULL) | |
b437ec95 | 684 | return; |
4a231017 | 685 | |
d59f11e1 KS |
686 | i = udev_ctrl_get_set_log_level(ctrl_msg); |
687 | if (i >= 0) { | |
688 | info(udev, "udevd message (SET_LOG_PRIORITY) received, log_priority=%i\n", i); | |
689 | udev_set_log_priority(udev, i); | |
690 | sprintf(udev_log_env, "UDEV_LOG=%i", i); | |
691 | putenv(udev_log_env); | |
0028653c KS |
692 | } |
693 | ||
d59f11e1 | 694 | if (udev_ctrl_get_stop_exec_queue(ctrl_msg) > 0) { |
7d563a17 | 695 | info(udev, "udevd message (STOP_EXEC_QUEUE) received\n"); |
3b47c739 | 696 | stop_exec_q = 1; |
d59f11e1 KS |
697 | } |
698 | ||
699 | if (udev_ctrl_get_start_exec_queue(ctrl_msg) > 0) { | |
7d563a17 | 700 | info(udev, "udevd message (START_EXEC_QUEUE) received\n"); |
3b47c739 | 701 | stop_exec_q = 0; |
7d563a17 | 702 | msg_queue_manager(udev); |
d59f11e1 KS |
703 | } |
704 | ||
705 | if (udev_ctrl_get_reload_rules(ctrl_msg) > 0) { | |
7d563a17 | 706 | info(udev, "udevd message (RELOAD_RULES) received\n"); |
c895fd00 | 707 | reload_config = 1; |
3b47c739 | 708 | } |
d59f11e1 KS |
709 | |
710 | str = udev_ctrl_get_set_env(ctrl_msg); | |
711 | if (str != NULL) { | |
712 | char *key = strdup(str); | |
713 | char *val; | |
714 | ||
715 | val = strchr(str, '='); | |
716 | if (val != NULL) { | |
717 | val[0] = '\0'; | |
718 | val = &val[1]; | |
719 | if (val[0] == '\0') { | |
720 | info(udev, "udevd message (ENV) received, unset '%s'\n", key); | |
721 | unsetenv(str); | |
722 | } else { | |
723 | info(udev, "udevd message (ENV) received, set '%s=%s'\n", key, val); | |
724 | setenv(key, val, 1); | |
725 | } | |
726 | } else { | |
727 | err(udev, "wrong key format '%s'\n", key); | |
728 | } | |
729 | free(key); | |
730 | } | |
731 | ||
732 | i = udev_ctrl_get_set_max_childs(ctrl_msg); | |
733 | if (i >= 0) { | |
734 | info(udev, "udevd message (SET_MAX_CHILDS) received, max_childs=%i\n", i); | |
735 | max_childs = i; | |
736 | } | |
737 | ||
738 | i = udev_ctrl_get_set_max_childs_running(ctrl_msg); | |
739 | if (i > 0) { | |
740 | info(udev, "udevd message (SET_MAX_CHILDS_RUNNING) received, max_childs_running=%i\n", i); | |
741 | max_childs_running = i; | |
742 | } | |
743 | ||
744 | udev_ctrl_msg_unref(ctrl_msg); | |
88f4b648 | 745 | } |
4a231017 | 746 | |
88f4b648 | 747 | /* receive the kernel user event message and do some sanity checks */ |
7d563a17 | 748 | static struct udevd_uevent_msg *get_netlink_msg(struct udev *udev) |
88f4b648 | 749 | { |
b3518c16 | 750 | struct udevd_uevent_msg *msg; |
88f4b648 KS |
751 | int bufpos; |
752 | ssize_t size; | |
7d1e179f | 753 | static char buffer[UEVENT_BUFFER_SIZE+512]; |
88f4b648 | 754 | char *pos; |
4a231017 | 755 | |
8ab44e3f | 756 | size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); |
88f4b648 KS |
757 | if (size < 0) { |
758 | if (errno != EINTR) | |
7d563a17 | 759 | err(udev, "unable to receive kernel netlink message: %s\n", strerror(errno)); |
88f4b648 KS |
760 | return NULL; |
761 | } | |
4a231017 | 762 | |
88f4b648 KS |
763 | if ((size_t)size > sizeof(buffer)-1) |
764 | size = sizeof(buffer)-1; | |
765 | buffer[size] = '\0'; | |
7d563a17 | 766 | dbg(udev, "uevent_size=%zi\n", size); |
4a231017 | 767 | |
88f4b648 KS |
768 | /* start of event payload */ |
769 | bufpos = strlen(buffer)+1; | |
7d563a17 | 770 | msg = get_msg_from_envbuf(udev, &buffer[bufpos], size-bufpos); |
88f4b648 KS |
771 | if (msg == NULL) |
772 | return NULL; | |
4a231017 | 773 | |
88f4b648 KS |
774 | /* validate message */ |
775 | pos = strchr(buffer, '@'); | |
776 | if (pos == NULL) { | |
7d563a17 | 777 | err(udev, "invalid uevent '%s'\n", buffer); |
88f4b648 KS |
778 | free(msg); |
779 | return NULL; | |
780 | } | |
781 | pos[0] = '\0'; | |
79721e0a | 782 | |
88f4b648 | 783 | if (msg->action == NULL) { |
7d563a17 | 784 | info(udev, "no ACTION in payload found, skip event '%s'\n", buffer); |
88f4b648 KS |
785 | free(msg); |
786 | return NULL; | |
787 | } | |
7f7ae03a | 788 | |
88f4b648 | 789 | if (strcmp(msg->action, buffer) != 0) { |
7d563a17 | 790 | err(udev, "ACTION in payload does not match uevent, skip event '%s'\n", buffer); |
88f4b648 KS |
791 | free(msg); |
792 | return NULL; | |
53921bfa | 793 | } |
a695feae | 794 | |
021a294c | 795 | return msg; |
a695feae | 796 | } |
1c5c245e | 797 | |
e5a5b54a | 798 | static void asmlinkage sig_handler(int signum) |
7fafc032 | 799 | { |
53921bfa KS |
800 | switch (signum) { |
801 | case SIGINT: | |
802 | case SIGTERM: | |
63cc8f04 | 803 | udev_exit = 1; |
53921bfa | 804 | break; |
2f6cbd19 | 805 | case SIGCHLD: |
f27125f9 | 806 | /* set flag, then write to pipe if needed */ |
5cab7caa | 807 | sigchilds_waiting = 1; |
2f6cbd19 | 808 | break; |
c895fd00 KS |
809 | case SIGHUP: |
810 | reload_config = 1; | |
811 | break; | |
f27125f9 | 812 | } |
5a73b25f | 813 | |
c6303c13 KS |
814 | /* write to pipe, which will wakeup select() in our mainloop */ |
815 | write(signal_pipe[WRITE_END], "", 1); | |
33db4b8d | 816 | } |
7fafc032 | 817 | |
f4fc0136 | 818 | static void udev_done(int pid, int exitstatus) |
2f6cbd19 KS |
819 | { |
820 | /* find msg associated with pid and delete it */ | |
b3518c16 | 821 | struct udevd_uevent_msg *msg; |
2f6cbd19 | 822 | |
f8a178a3 | 823 | list_for_each_entry(msg, &running_list, node) { |
2f6cbd19 | 824 | if (msg->pid == pid) { |
7d563a17 | 825 | info(msg->udev, "seq %llu, pid [%d] exit with %i, %ld seconds old\n", msg->seqnum, msg->pid, |
f4fc0136 KS |
826 | exitstatus, time(NULL) - msg->queue_time); |
827 | msg->exitstatus = exitstatus; | |
ebfc1acd | 828 | msg_queue_delete(msg); |
3169e8d1 | 829 | |
fc465079 | 830 | /* there may be events waiting with the same devpath */ |
f27125f9 | 831 | run_exec_q = 1; |
2f6cbd19 KS |
832 | return; |
833 | } | |
834 | } | |
835 | } | |
836 | ||
5cab7caa | 837 | static void reap_sigchilds(void) |
f27125f9 | 838 | { |
40caaeec | 839 | pid_t pid; |
f4fc0136 | 840 | int status; |
ce043f85 | 841 | |
40caaeec | 842 | while (1) { |
f4fc0136 | 843 | pid = waitpid(-1, &status, WNOHANG); |
40caaeec | 844 | if (pid <= 0) |
f27125f9 | 845 | break; |
f4fc0136 KS |
846 | if (WIFEXITED(status)) |
847 | status = WEXITSTATUS(status); | |
82de5983 KS |
848 | else if (WIFSIGNALED(status)) |
849 | status = WTERMSIG(status) + 128; | |
f4fc0136 KS |
850 | else |
851 | status = 0; | |
852 | udev_done(pid, status); | |
f27125f9 | 853 | } |
854 | } | |
855 | ||
7d563a17 | 856 | static int init_uevent_netlink_sock(struct udev *udev) |
88f4b648 KS |
857 | { |
858 | struct sockaddr_nl snl; | |
a5c606f6 | 859 | const int buffersize = 16 * 1024 * 1024; |
88f4b648 KS |
860 | int retval; |
861 | ||
862 | memset(&snl, 0x00, sizeof(struct sockaddr_nl)); | |
863 | snl.nl_family = AF_NETLINK; | |
864 | snl.nl_pid = getpid(); | |
733f070d | 865 | snl.nl_groups = 1; |
88f4b648 | 866 | |
8ab44e3f KS |
867 | uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); |
868 | if (uevent_netlink_sock == -1) { | |
7d563a17 | 869 | err(udev, "error getting socket: %s\n", strerror(errno)); |
88f4b648 KS |
870 | return -1; |
871 | } | |
872 | ||
cbbde2ba | 873 | /* set receive buffersize */ |
a5c606f6 | 874 | setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); |
cbbde2ba | 875 | |
833b3c68 | 876 | retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); |
88f4b648 | 877 | if (retval < 0) { |
7d563a17 | 878 | err(udev, "bind failed: %s\n", strerror(errno)); |
8ab44e3f KS |
879 | close(uevent_netlink_sock); |
880 | uevent_netlink_sock = -1; | |
88f4b648 KS |
881 | return -1; |
882 | } | |
88f4b648 KS |
883 | return 0; |
884 | } | |
885 | ||
7d563a17 | 886 | static void export_initial_seqnum(struct udev *udev) |
90cd961e KS |
887 | { |
888 | char filename[PATH_SIZE]; | |
889 | int fd; | |
890 | char seqnum[32]; | |
891 | ssize_t len = 0; | |
892 | ||
7d563a17 | 893 | strlcpy(filename, udev_get_sys_path(udev), sizeof(filename)); |
90cd961e KS |
894 | strlcat(filename, "/kernel/uevent_seqnum", sizeof(filename)); |
895 | fd = open(filename, O_RDONLY); | |
896 | if (fd >= 0) { | |
897 | len = read(fd, seqnum, sizeof(seqnum)-1); | |
898 | close(fd); | |
899 | } | |
900 | if (len <= 0) { | |
901 | strcpy(seqnum, "0\n"); | |
902 | len = 3; | |
903 | } | |
7d563a17 | 904 | strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); |
01618658 | 905 | strlcat(filename, "/.udev/uevent_seqnum", sizeof(filename)); |
7d563a17 | 906 | create_path(udev, filename); |
90cd961e KS |
907 | fd = open(filename, O_WRONLY|O_TRUNC|O_CREAT, 0644); |
908 | if (fd >= 0) { | |
909 | write(fd, seqnum, len); | |
910 | close(fd); | |
911 | } | |
912 | } | |
913 | ||
59345311 | 914 | int main(int argc, char *argv[]) |
c2cf4012 | 915 | { |
7d563a17 | 916 | struct udev *udev; |
c2cf4012 | 917 | int retval; |
3904a758 | 918 | int fd; |
f8911dbb | 919 | struct sigaction act; |
f27125f9 | 920 | fd_set readfds; |
a15f42c4 | 921 | const char *value; |
561d4c5a | 922 | int daemonize = 0; |
b52a01ee KS |
923 | static const struct option options[] = { |
924 | { "daemon", 0, NULL, 'd' }, | |
c7c00276 | 925 | { "debug-trace", 0, NULL, 't' }, |
c70560fe | 926 | { "debug", 0, NULL, 'D' }, |
b52a01ee | 927 | { "help", 0, NULL, 'h' }, |
841e168c | 928 | { "version", 0, NULL, 'V' }, |
b52a01ee KS |
929 | {} |
930 | }; | |
e3396a2d | 931 | int rc = 1; |
0b3dfb3d | 932 | int maxfd; |
53921bfa | 933 | |
7d563a17 KS |
934 | udev = udev_new(); |
935 | if (udev == NULL) | |
936 | goto exit; | |
937 | ||
7257cb18 | 938 | logging_init("udevd"); |
7d563a17 KS |
939 | udev_set_log_fn(udev, log_fn); |
940 | ||
941 | selinux_init(udev); | |
942 | dbg(udev, "version %s\n", VERSION); | |
95a6f4c8 | 943 | |
b52a01ee | 944 | while (1) { |
7d563a17 KS |
945 | int option; |
946 | ||
c70560fe | 947 | option = getopt_long(argc, argv, "dDthV", options, NULL); |
b52a01ee KS |
948 | if (option == -1) |
949 | break; | |
950 | ||
951 | switch (option) { | |
952 | case 'd': | |
561d4c5a | 953 | daemonize = 1; |
b52a01ee | 954 | break; |
c7c00276 KS |
955 | case 't': |
956 | debug_trace = 1; | |
957 | break; | |
c70560fe KS |
958 | case 'D': |
959 | debug = 1; | |
7d563a17 KS |
960 | if (udev_get_log_priority(udev) < LOG_INFO) |
961 | udev_set_log_priority(udev, LOG_INFO); | |
9e8fe79b | 962 | break; |
b52a01ee | 963 | case 'h': |
c70560fe | 964 | printf("Usage: udevd [--help] [--daemon] [--debug-trace] [--debug] [--version]\n"); |
841e168c MS |
965 | goto exit; |
966 | case 'V': | |
01618658 | 967 | printf("%s\n", VERSION); |
e3396a2d | 968 | goto exit; |
b52a01ee KS |
969 | default: |
970 | goto exit; | |
561d4c5a | 971 | } |
561d4c5a | 972 | } |
40caaeec | 973 | |
fc89fe7e KS |
974 | if (getuid() != 0) { |
975 | fprintf(stderr, "root privileges required\n"); | |
7d563a17 | 976 | err(udev, "root privileges required\n"); |
fc89fe7e KS |
977 | goto exit; |
978 | } | |
979 | ||
5edec024 MS |
980 | /* make sure std{in,out,err} fd's are in a sane state */ |
981 | fd = open("/dev/null", O_RDWR); | |
982 | if (fd < 0) { | |
983 | fprintf(stderr, "cannot open /dev/null\n"); | |
7d563a17 | 984 | err(udev, "cannot open /dev/null\n"); |
5edec024 MS |
985 | } |
986 | if (fd > STDIN_FILENO) | |
987 | dup2(fd, STDIN_FILENO); | |
988 | if (write(STDOUT_FILENO, 0, 0) < 0) | |
989 | dup2(fd, STDOUT_FILENO); | |
990 | if (write(STDERR_FILENO, 0, 0) < 0) | |
991 | dup2(fd, STDERR_FILENO); | |
992 | ||
d59f11e1 KS |
993 | /* init control socket, bind() ensures, that only one udevd instance is running */ |
994 | udev_ctrl = udev_ctrl_new_from_socket(udev, UDEV_CTRL_SOCK_PATH); | |
995 | if (udev_ctrl == NULL) { | |
996 | fprintf(stderr, "error initializing control socket"); | |
997 | err(udev, "error initializing udevd socket"); | |
998 | rc = 1; | |
999 | goto exit; | |
1000 | } | |
1001 | ||
1002 | if (udev_ctrl_enable_receiving(udev_ctrl) < 0) { | |
1003 | fprintf(stderr, "error binding control socket, seems udevd is already running\n"); | |
1004 | err(udev, "error binding control socket, seems udevd is already running\n"); | |
1005 | rc = 1; | |
833b3c68 KS |
1006 | goto exit; |
1007 | } | |
1008 | ||
7d563a17 | 1009 | if (init_uevent_netlink_sock(udev) < 0) { |
e3396a2d | 1010 | fprintf(stderr, "error initializing netlink socket\n"); |
7d563a17 | 1011 | err(udev, "error initializing netlink socket\n"); |
833b3c68 KS |
1012 | rc = 3; |
1013 | goto exit; | |
1014 | } | |
1015 | ||
ff2eecef SV |
1016 | retval = pipe(signal_pipe); |
1017 | if (retval < 0) { | |
7d563a17 | 1018 | err(udev, "error getting pipes: %s\n", strerror(errno)); |
ff2eecef SV |
1019 | goto exit; |
1020 | } | |
1021 | ||
1022 | retval = fcntl(signal_pipe[READ_END], F_GETFL, 0); | |
1023 | if (retval < 0) { | |
7d563a17 | 1024 | err(udev, "error fcntl on read pipe: %s\n", strerror(errno)); |
ff2eecef SV |
1025 | goto exit; |
1026 | } | |
1027 | retval = fcntl(signal_pipe[READ_END], F_SETFL, retval | O_NONBLOCK); | |
1028 | if (retval < 0) { | |
7d563a17 | 1029 | err(udev, "error fcntl on read pipe: %s\n", strerror(errno)); |
ff2eecef SV |
1030 | goto exit; |
1031 | } | |
1032 | ||
1033 | retval = fcntl(signal_pipe[WRITE_END], F_GETFL, 0); | |
1034 | if (retval < 0) { | |
7d563a17 | 1035 | err(udev, "error fcntl on write pipe: %s\n", strerror(errno)); |
ff2eecef SV |
1036 | goto exit; |
1037 | } | |
1038 | retval = fcntl(signal_pipe[WRITE_END], F_SETFL, retval | O_NONBLOCK); | |
1039 | if (retval < 0) { | |
7d563a17 | 1040 | err(udev, "error fcntl on write pipe: %s\n", strerror(errno)); |
ff2eecef SV |
1041 | goto exit; |
1042 | } | |
1043 | ||
e3396a2d | 1044 | /* parse the rules and keep them in memory */ |
1aa1e248 | 1045 | sysfs_init(); |
7d563a17 | 1046 | udev_rules_init(udev, &rules, 1); |
833b3c68 | 1047 | |
7d563a17 | 1048 | export_initial_seqnum(udev); |
90cd961e | 1049 | |
561d4c5a | 1050 | if (daemonize) { |
f15515b5 KS |
1051 | pid_t pid; |
1052 | ||
1053 | pid = fork(); | |
1054 | switch (pid) { | |
1055 | case 0: | |
7d563a17 | 1056 | dbg(udev, "daemonized fork running\n"); |
f15515b5 KS |
1057 | break; |
1058 | case -1: | |
7d563a17 | 1059 | err(udev, "fork of daemon failed: %s\n", strerror(errno)); |
833b3c68 | 1060 | rc = 4; |
f15515b5 KS |
1061 | goto exit; |
1062 | default: | |
7d563a17 | 1063 | dbg(udev, "child [%u] running, parent exits\n", pid); |
2f64aa40 | 1064 | rc = 0; |
833b3c68 | 1065 | goto exit; |
f15515b5 KS |
1066 | } |
1067 | } | |
1068 | ||
d59f11e1 KS |
1069 | /* redirect std{out,err} */ |
1070 | if (!debug) { | |
5edec024 | 1071 | dup2(fd, STDOUT_FILENO); |
d59f11e1 KS |
1072 | dup2(fd, STDERR_FILENO); |
1073 | } | |
5edec024 MS |
1074 | if (fd > STDERR_FILENO) |
1075 | close(fd); | |
e3396a2d | 1076 | |
3904a758 | 1077 | /* set scheduling priority for the daemon */ |
085cce37 KS |
1078 | setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); |
1079 | ||
3904a758 | 1080 | chdir("/"); |
74adec7d | 1081 | umask(022); |
3904a758 KS |
1082 | |
1083 | /* become session leader */ | |
1084 | sid = setsid(); | |
7d563a17 | 1085 | dbg(udev, "our session is %d\n", sid); |
3904a758 KS |
1086 | |
1087 | /* OOM_DISABLE == -17 */ | |
1088 | fd = open("/proc/self/oom_adj", O_RDWR); | |
1089 | if (fd < 0) | |
7d563a17 | 1090 | err(udev, "error disabling OOM: %s\n", strerror(errno)); |
3904a758 KS |
1091 | else { |
1092 | write(fd, "-17", 3); | |
1093 | close(fd); | |
1094 | } | |
1095 | ||
798d7ab6 KS |
1096 | fd = open("/dev/kmsg", O_WRONLY); |
1097 | if (fd > 0) { | |
01618658 | 1098 | const char *str = "<6>udevd version " VERSION " started\n"; |
798d7ab6 KS |
1099 | |
1100 | write(fd, str, strlen(str)); | |
1101 | close(fd); | |
1102 | } | |
1103 | ||
f27125f9 | 1104 | /* set signal handlers */ |
0786e8e5 | 1105 | memset(&act, 0x00, sizeof(struct sigaction)); |
6b493a20 | 1106 | act.sa_handler = (void (*)(int)) sig_handler; |
f27125f9 | 1107 | sigemptyset(&act.sa_mask); |
f8911dbb KS |
1108 | act.sa_flags = SA_RESTART; |
1109 | sigaction(SIGINT, &act, NULL); | |
1110 | sigaction(SIGTERM, &act, NULL); | |
f8911dbb | 1111 | sigaction(SIGCHLD, &act, NULL); |
63cc8f04 | 1112 | sigaction(SIGHUP, &act, NULL); |
7fafc032 | 1113 | |
c895fd00 KS |
1114 | /* watch rules directory */ |
1115 | inotify_fd = inotify_init(); | |
254d6d3c | 1116 | if (inotify_fd >= 0) { |
7d563a17 KS |
1117 | if (udev_get_rules_path(udev) != NULL) { |
1118 | inotify_add_watch(inotify_fd, udev_get_rules_path(udev), | |
282988c4 KS |
1119 | IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1120 | } else { | |
1121 | char filename[PATH_MAX]; | |
1122 | ||
01618658 | 1123 | inotify_add_watch(inotify_fd, UDEV_PREFIX "/lib/udev/rules.d", |
282988c4 | 1124 | IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
01618658 | 1125 | inotify_add_watch(inotify_fd, SYSCONFDIR "/udev/rules.d", |
282988c4 KS |
1126 | IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); |
1127 | ||
1128 | /* watch dynamic rules directory */ | |
7d563a17 | 1129 | strlcpy(filename, udev_get_dev_path(udev), sizeof(filename)); |
01618658 | 1130 | strlcat(filename, "/.udev/rules.d", sizeof(filename)); |
282988c4 KS |
1131 | inotify_add_watch(inotify_fd, filename, |
1132 | IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); | |
1133 | } | |
254d6d3c | 1134 | } else if (errno == ENOSYS) |
7d563a17 | 1135 | err(udev, "the kernel does not support inotify, udevd can't monitor rules file changes\n"); |
750d10da | 1136 | else |
7d563a17 | 1137 | err(udev, "inotify_init failed: %s\n", strerror(errno)); |
0028653c | 1138 | |
a15f42c4 KS |
1139 | /* maximum limit of forked childs */ |
1140 | value = getenv("UDEVD_MAX_CHILDS"); | |
1141 | if (value) | |
1142 | max_childs = strtoul(value, NULL, 10); | |
f051e340 KS |
1143 | else { |
1144 | int memsize = mem_size_mb(); | |
1145 | if (memsize > 0) | |
1146 | max_childs = 128 + (memsize / 4); | |
1147 | else | |
1148 | max_childs = UDEVD_MAX_CHILDS; | |
1149 | } | |
7d563a17 | 1150 | info(udev, "initialize max_childs to %u\n", max_childs); |
a15f42c4 KS |
1151 | |
1152 | /* start to throttle forking if maximum number of _running_ childs is reached */ | |
1153 | value = getenv("UDEVD_MAX_CHILDS_RUNNING"); | |
1154 | if (value) | |
1155 | max_childs_running = strtoull(value, NULL, 10); | |
f051e340 KS |
1156 | else { |
1157 | int cpus = cpu_count(); | |
1158 | if (cpus > 0) | |
1159 | max_childs_running = 8 + (8 * cpus); | |
1160 | else | |
1161 | max_childs_running = UDEVD_MAX_CHILDS_RUNNING; | |
1162 | } | |
7d563a17 | 1163 | info(udev, "initialize max_childs_running to %u\n", max_childs_running); |
8b726878 | 1164 | |
c895fd00 KS |
1165 | /* clear environment for forked event processes */ |
1166 | clearenv(); | |
1167 | ||
40caaeec | 1168 | /* export log_priority , as called programs may want to follow that setting */ |
7d563a17 KS |
1169 | sprintf(udev_log_env, "UDEV_LOG=%i", udev_get_log_priority(udev)); |
1170 | putenv(udev_log_env); | |
c7c00276 KS |
1171 | if (debug_trace) |
1172 | putenv("DEBUG=1"); | |
40caaeec | 1173 | |
d59f11e1 | 1174 | maxfd = udev_ctrl_get_fd(udev_ctrl); |
0b3dfb3d KS |
1175 | maxfd = UDEV_MAX(maxfd, uevent_netlink_sock); |
1176 | maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); | |
1177 | maxfd = UDEV_MAX(maxfd, inotify_fd); | |
1178 | ||
63cc8f04 | 1179 | while (!udev_exit) { |
b3518c16 | 1180 | struct udevd_uevent_msg *msg; |
40caaeec | 1181 | int fdcount; |
021a294c | 1182 | |
40caaeec | 1183 | FD_ZERO(&readfds); |
f1ff8d7b | 1184 | FD_SET(signal_pipe[READ_END], &readfds); |
d59f11e1 | 1185 | FD_SET(udev_ctrl_get_fd(udev_ctrl), &readfds); |
833b3c68 | 1186 | FD_SET(uevent_netlink_sock, &readfds); |
90cd961e | 1187 | if (inotify_fd >= 0) |
c895fd00 | 1188 | FD_SET(inotify_fd, &readfds); |
e5a2989e | 1189 | |
0b3dfb3d | 1190 | fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); |
40caaeec | 1191 | if (fdcount < 0) { |
e5a2989e | 1192 | if (errno != EINTR) |
7d563a17 | 1193 | err(udev, "error in select: %s\n", strerror(errno)); |
f27125f9 | 1194 | continue; |
2f6cbd19 | 1195 | } |
e5a2989e | 1196 | |
b3518c16 | 1197 | /* get control message */ |
d59f11e1 KS |
1198 | if (FD_ISSET(udev_ctrl_get_fd(udev_ctrl), &readfds)) |
1199 | handle_ctrl_msg(udev_ctrl); | |
88f4b648 | 1200 | |
b3518c16 | 1201 | /* get netlink message */ |
833b3c68 | 1202 | if (FD_ISSET(uevent_netlink_sock, &readfds)) { |
7d563a17 | 1203 | msg = get_netlink_msg(udev); |
0b3dfb3d | 1204 | if (msg) |
021a294c KS |
1205 | msg_queue_insert(msg); |
1206 | } | |
e5a2989e | 1207 | |
63cc8f04 | 1208 | /* received a signal, clear our notification pipe */ |
c6303c13 KS |
1209 | if (FD_ISSET(signal_pipe[READ_END], &readfds)) { |
1210 | char buf[256]; | |
1211 | ||
1212 | read(signal_pipe[READ_END], &buf, sizeof(buf)); | |
40caaeec | 1213 | } |
e5a2989e | 1214 | |
c895fd00 | 1215 | /* rules directory inotify watch */ |
90cd961e | 1216 | if ((inotify_fd >= 0) && FD_ISSET(inotify_fd, &readfds)) { |
c895fd00 KS |
1217 | int nbytes; |
1218 | ||
1219 | /* discard all possible events, we can just reload the config */ | |
254d6d3c | 1220 | if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes > 0) { |
c895fd00 KS |
1221 | char *buf; |
1222 | ||
1223 | reload_config = 1; | |
1224 | buf = malloc(nbytes); | |
f4dce170 | 1225 | if (buf == NULL) { |
7d563a17 | 1226 | err(udev, "error getting buffer for inotify, disable watching\n"); |
c895fd00 KS |
1227 | close(inotify_fd); |
1228 | inotify_fd = -1; | |
1229 | } | |
1230 | read(inotify_fd, buf, nbytes); | |
1231 | free(buf); | |
1232 | } | |
1233 | } | |
1234 | ||
e3396a2d | 1235 | /* rules changed, set by inotify or a HUP signal */ |
c895fd00 KS |
1236 | if (reload_config) { |
1237 | reload_config = 0; | |
1aa1e248 | 1238 | udev_rules_cleanup(&rules); |
7d563a17 | 1239 | udev_rules_init(udev, &rules, 1); |
c895fd00 KS |
1240 | } |
1241 | ||
0b3dfb3d | 1242 | /* forked child has returned */ |
5cab7caa KS |
1243 | if (sigchilds_waiting) { |
1244 | sigchilds_waiting = 0; | |
1245 | reap_sigchilds(); | |
f27125f9 | 1246 | } |
e5a2989e | 1247 | |
f27125f9 | 1248 | if (run_exec_q) { |
f27125f9 | 1249 | run_exec_q = 0; |
3b47c739 | 1250 | if (!stop_exec_q) |
7d563a17 | 1251 | msg_queue_manager(udev); |
53921bfa | 1252 | } |
53921bfa | 1253 | } |
e3396a2d | 1254 | rc = 0; |
ec9cc012 | 1255 | |
53921bfa | 1256 | exit: |
1aa1e248 KS |
1257 | udev_rules_cleanup(&rules); |
1258 | sysfs_cleanup(); | |
7d563a17 | 1259 | selinux_exit(udev); |
c895fd00 | 1260 | |
90cd961e | 1261 | if (signal_pipe[READ_END] >= 0) |
f1ff8d7b | 1262 | close(signal_pipe[READ_END]); |
90cd961e | 1263 | if (signal_pipe[WRITE_END] >= 0) |
f1ff8d7b | 1264 | close(signal_pipe[WRITE_END]); |
2b996ad1 | 1265 | |
d59f11e1 | 1266 | udev_ctrl_unref(udev_ctrl); |
90cd961e | 1267 | if (inotify_fd >= 0) |
c895fd00 | 1268 | close(inotify_fd); |
90cd961e | 1269 | if (uevent_netlink_sock >= 0) |
63cc8f04 KS |
1270 | close(uevent_netlink_sock); |
1271 | ||
7257cb18 | 1272 | logging_close(); |
833b3c68 | 1273 | return rc; |
7fafc032 | 1274 | } |