]>
Commit | Line | Data |
---|---|---|
7fafc032 | 1 | /* |
34c00c91 | 2 | * udevd.c - event listener and serializer |
7fafc032 | 3 | * |
6c18b1fb | 4 | * Copyright (C) 2004-2005 Kay Sievers <kay.sievers@vrfy.org> |
2f6cbd19 | 5 | * Copyright (C) 2004 Chris Friesen <chris_friesen@sympatico.ca> |
7fafc032 KS |
6 | * |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation version 2 of the License. | |
11 | * | |
12 | * This program is distributed in the hope that it will be useful, but | |
13 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | * General Public License for more details. | |
16 | * | |
17 | * You should have received a copy of the GNU General Public License along | |
18 | * with this program; if not, write to the Free Software Foundation, Inc., | |
19 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
20 | * | |
21 | */ | |
22 | ||
a695feae | 23 | #include <stddef.h> |
7fafc032 KS |
24 | #include <signal.h> |
25 | #include <unistd.h> | |
26 | #include <errno.h> | |
27 | #include <stdio.h> | |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
085cce37 KS |
30 | #include <ctype.h> |
31 | #include <dirent.h> | |
32 | #include <fcntl.h> | |
40caaeec | 33 | #include <syslog.h> |
0b3dfb3d | 34 | #include <time.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> |
88f4b648 | 42 | #include <linux/netlink.h> |
7fafc032 | 43 | |
a695feae | 44 | #include "list.h" |
63f61c5c | 45 | #include "udev_libc_wrapper.h" |
7fafc032 | 46 | #include "udev.h" |
35b7d88c | 47 | #include "udev_version.h" |
c895fd00 | 48 | #include "udev_rules.h" |
9af5bb2f | 49 | #include "udev_utils.h" |
7fafc032 KS |
50 | #include "udevd.h" |
51 | #include "logging.h" | |
52 | ||
c895fd00 | 53 | struct udev_rules rules; |
3b47c739 | 54 | static int udevd_sock; |
8ab44e3f | 55 | static int uevent_netlink_sock; |
c895fd00 | 56 | static int inotify_fd; |
085cce37 | 57 | static pid_t sid; |
13f24d59 | 58 | |
f1ff8d7b | 59 | static int signal_pipe[2] = {-1, -1}; |
5cab7caa | 60 | static volatile int sigchilds_waiting; |
63cc8f04 | 61 | static volatile int udev_exit; |
c895fd00 | 62 | static volatile int reload_config; |
f27125f9 | 63 | static int run_exec_q; |
3b47c739 | 64 | static int stop_exec_q; |
a15f42c4 KS |
65 | static int max_childs; |
66 | static int max_childs_running; | |
916c5e47 | 67 | static char udev_log[32]; |
40caaeec | 68 | |
40caaeec KS |
69 | static LIST_HEAD(exec_list); |
70 | static LIST_HEAD(running_list); | |
a15f42c4 | 71 | |
7fafc032 | 72 | |
6c18b1fb | 73 | #ifdef USE_LOG |
c895fd00 | 74 | void log_message(int priority, const char *format, ...) |
51a8bb2f | 75 | { |
6b493a20 KS |
76 | va_list args; |
77 | ||
78 | if (priority > udev_log_priority) | |
79 | return; | |
d026a35d GKH |
80 | |
81 | va_start(args, format); | |
6b493a20 | 82 | vsyslog(priority, format, args); |
d026a35d | 83 | va_end(args); |
51a8bb2f | 84 | } |
d026a35d | 85 | #endif |
51a8bb2f | 86 | |
c895fd00 KS |
87 | static void asmlinkage udev_event_sig_handler(int signum) |
88 | { | |
89 | if (signum == SIGALRM) | |
90 | exit(1); | |
91 | } | |
92 | ||
93 | static int udev_event_process(struct uevent_msg *msg) | |
94 | { | |
95 | struct sigaction act; | |
96 | struct udevice udev; | |
97 | struct name_entry *name_loop; | |
98 | int i; | |
99 | int retval; | |
100 | ||
101 | /* set signal handlers */ | |
102 | memset(&act, 0x00, sizeof(act)); | |
103 | act.sa_handler = (void (*)(int)) udev_event_sig_handler; | |
104 | sigemptyset (&act.sa_mask); | |
105 | act.sa_flags = 0; | |
106 | sigaction(SIGALRM, &act, NULL); | |
107 | ||
108 | /* trigger timeout to prevent hanging processes */ | |
109 | alarm(UDEV_ALARM_TIMEOUT); | |
110 | ||
111 | /* reconstruct env from message */ | |
112 | for (i = 0; msg->envp[i]; i++) | |
113 | putenv(msg->envp[i]); | |
114 | ||
115 | udev_init_device(&udev, msg->devpath, msg->subsystem, msg->action); | |
116 | retval = udev_process_event(&rules, &udev); | |
117 | ||
118 | /* run programs collected by RUN-key*/ | |
119 | if (!retval) { | |
120 | list_for_each_entry(name_loop, &udev.run_list, node) { | |
121 | if (strncmp(name_loop->name, "socket:", strlen("socket:")) == 0) | |
122 | pass_env_to_socket(&name_loop->name[strlen("socket:")], msg->devpath, msg->action); | |
123 | else | |
124 | run_program(name_loop->name, udev.subsystem, NULL, 0, NULL, (udev_log_priority >= LOG_INFO)); | |
125 | } | |
126 | } | |
127 | ||
128 | udev_cleanup_device(&udev); | |
129 | ||
130 | return 0; | |
131 | } | |
132 | ||
fc465079 KS |
133 | static void msg_queue_delete(struct uevent_msg *msg) |
134 | { | |
135 | list_del(&msg->node); | |
136 | free(msg); | |
137 | } | |
138 | ||
40caaeec | 139 | static void udev_event_run(struct uevent_msg *msg) |
7fafc032 | 140 | { |
90c210eb | 141 | pid_t pid; |
90c210eb KS |
142 | |
143 | pid = fork(); | |
144 | switch (pid) { | |
145 | case 0: | |
33db4b8d | 146 | /* child */ |
833b3c68 KS |
147 | close(uevent_netlink_sock); |
148 | close(udevd_sock); | |
c895fd00 KS |
149 | if (inotify_fd > 0) |
150 | close(inotify_fd); | |
c895fd00 KS |
151 | close(signal_pipe[READ_END]); |
152 | close(signal_pipe[WRITE_END]); | |
f602ccf0 | 153 | logging_close(); |
c895fd00 KS |
154 | |
155 | logging_init("udevd-event"); | |
085cce37 | 156 | setpriority(PRIO_PROCESS, 0, UDEV_PRIORITY); |
c895fd00 KS |
157 | udev_event_process(msg); |
158 | info("seq %llu finished", msg->seqnum); | |
159 | ||
160 | logging_close(); | |
161 | exit(0); | |
90c210eb | 162 | case -1: |
ff3e4bed | 163 | err("fork of child failed: %s", strerror(errno)); |
ebfc1acd | 164 | msg_queue_delete(msg); |
2f6cbd19 | 165 | break; |
90c210eb | 166 | default: |
2f6cbd19 | 167 | /* get SIGCHLD in main loop */ |
40caaeec | 168 | info("seq %llu forked, pid [%d], '%s' '%s', %ld seconds old", |
0b3dfb3d | 169 | msg->seqnum, pid, msg->action, msg->subsystem, time(NULL) - msg->queue_time); |
2f6cbd19 | 170 | msg->pid = pid; |
90c210eb | 171 | } |
7fafc032 KS |
172 | } |
173 | ||
fc465079 KS |
174 | static void msg_queue_insert(struct uevent_msg *msg) |
175 | { | |
176 | msg->queue_time = time(NULL); | |
177 | ||
178 | /* run all events with a timeout set immediately */ | |
179 | if (msg->timeout != 0) { | |
180 | list_add_tail(&msg->node, &running_list); | |
181 | udev_event_run(msg); | |
182 | return; | |
183 | } | |
184 | ||
185 | list_add_tail(&msg->node, &exec_list); | |
186 | run_exec_q = 1; | |
187 | } | |
188 | ||
189 | /* runs event and removes event from run queue when finished */ | |
085cce37 KS |
190 | static int running_processes(void) |
191 | { | |
192 | int f; | |
193 | static char buf[4096]; | |
194 | int len; | |
195 | int running; | |
196 | const char *pos; | |
197 | ||
198 | f = open("/proc/stat", O_RDONLY); | |
199 | if (f == -1) | |
200 | return -1; | |
201 | ||
b4f192f0 | 202 | len = read(f, buf, sizeof(buf)-1); |
085cce37 KS |
203 | close(f); |
204 | ||
205 | if (len <= 0) | |
206 | return -1; | |
207 | else | |
208 | buf[len] = '\0'; | |
209 | ||
210 | pos = strstr(buf, "procs_running "); | |
211 | if (pos == NULL) | |
212 | return -1; | |
213 | ||
214 | if (sscanf(pos, "procs_running %u", &running) != 1) | |
215 | return -1; | |
216 | ||
217 | return running; | |
218 | } | |
219 | ||
220 | /* return the number of process es in our session, count only until limit */ | |
221 | static int running_processes_in_session(pid_t session, int limit) | |
222 | { | |
223 | DIR *dir; | |
224 | struct dirent *dent; | |
225 | int running = 0; | |
226 | ||
227 | dir = opendir("/proc"); | |
228 | if (!dir) | |
229 | return -1; | |
230 | ||
231 | /* read process info from /proc */ | |
232 | for (dent = readdir(dir); dent != NULL; dent = readdir(dir)) { | |
233 | int f; | |
234 | char procdir[64]; | |
235 | char line[256]; | |
236 | const char *pos; | |
237 | char state; | |
238 | pid_t ppid, pgrp, sess; | |
239 | int len; | |
240 | ||
241 | if (!isdigit(dent->d_name[0])) | |
242 | continue; | |
243 | ||
244 | snprintf(procdir, sizeof(procdir), "/proc/%s/stat", dent->d_name); | |
245 | procdir[sizeof(procdir)-1] = '\0'; | |
246 | ||
247 | f = open(procdir, O_RDONLY); | |
248 | if (f == -1) | |
249 | continue; | |
250 | ||
b4f192f0 | 251 | len = read(f, line, sizeof(line)-1); |
085cce37 KS |
252 | close(f); |
253 | ||
254 | if (len <= 0) | |
255 | continue; | |
256 | else | |
257 | line[len] = '\0'; | |
258 | ||
259 | /* skip ugly program name */ | |
260 | pos = strrchr(line, ')') + 2; | |
261 | if (pos == NULL) | |
262 | continue; | |
263 | ||
264 | if (sscanf(pos, "%c %d %d %d ", &state, &ppid, &pgrp, &sess) != 4) | |
265 | continue; | |
266 | ||
267 | /* count only processes in our session */ | |
268 | if (sess != session) | |
269 | continue; | |
270 | ||
271 | /* count only running, no sleeping processes */ | |
272 | if (state != 'R') | |
273 | continue; | |
274 | ||
275 | running++; | |
276 | if (limit > 0 && running >= limit) | |
277 | break; | |
278 | } | |
279 | closedir(dir); | |
280 | ||
281 | return running; | |
282 | } | |
283 | ||
7b6571a9 KS |
284 | static int compare_devpath(const char *running, const char *waiting) |
285 | { | |
286 | int i; | |
287 | ||
63f61c5c | 288 | for (i = 0; i < PATH_SIZE; i++) { |
7b6571a9 KS |
289 | /* identical device event found */ |
290 | if (running[i] == '\0' && waiting[i] == '\0') | |
291 | return 1; | |
292 | ||
293 | /* parent device event found */ | |
294 | if (running[i] == '\0' && waiting[i] == '/') | |
295 | return 2; | |
296 | ||
297 | /* child device event found */ | |
298 | if (running[i] == '/' && waiting[i] == '\0') | |
299 | return 3; | |
300 | ||
301 | /* no matching event */ | |
302 | if (running[i] != waiting[i]) | |
303 | break; | |
304 | } | |
305 | ||
306 | return 0; | |
307 | } | |
308 | ||
309 | /* returns still running task for the same device, its parent or its physical device */ | |
a15f42c4 | 310 | static int running_with_devpath(struct uevent_msg *msg, int limit) |
7fafc032 | 311 | { |
3b47c739 | 312 | struct uevent_msg *loop_msg; |
a15f42c4 | 313 | int childs_count = 0; |
7b6571a9 KS |
314 | |
315 | if (msg->devpath == NULL) | |
a15f42c4 | 316 | return 0; |
79721e0a | 317 | |
f8a178a3 | 318 | list_for_each_entry(loop_msg, &running_list, node) { |
a15f42c4 KS |
319 | if (limit && childs_count++ > limit) { |
320 | dbg("%llu, maximum number (%i) of child reached", msg->seqnum, childs_count); | |
321 | return 1; | |
322 | } | |
7b6571a9 | 323 | if (loop_msg->devpath == NULL) |
80513ea3 KS |
324 | continue; |
325 | ||
7b6571a9 | 326 | /* return running parent/child device event */ |
a15f42c4 KS |
327 | if (compare_devpath(loop_msg->devpath, msg->devpath) != 0) { |
328 | dbg("%llu, child device event still running %llu (%s)", | |
329 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); | |
330 | return 2; | |
331 | } | |
79721e0a | 332 | |
7b6571a9 | 333 | /* return running physical device event */ |
79721e0a | 334 | if (msg->physdevpath && msg->action && strcmp(msg->action, "add") == 0) |
a15f42c4 KS |
335 | if (compare_devpath(loop_msg->devpath, msg->physdevpath) != 0) { |
336 | dbg("%llu, physical device event still running %llu (%s)", | |
337 | msg->seqnum, loop_msg->seqnum, loop_msg->devpath); | |
338 | return 3; | |
339 | } | |
80513ea3 KS |
340 | } |
341 | ||
a15f42c4 | 342 | return 0; |
7fafc032 KS |
343 | } |
344 | ||
79721e0a | 345 | /* exec queue management routine executes the events and serializes events in the same sequence */ |
fc465079 | 346 | static void msg_queue_manager(void) |
7fafc032 | 347 | { |
3b47c739 KS |
348 | struct uevent_msg *loop_msg; |
349 | struct uevent_msg *tmp_msg; | |
085cce37 KS |
350 | int running; |
351 | ||
8ab44e3f KS |
352 | if (list_empty(&exec_list)) |
353 | return; | |
354 | ||
085cce37 KS |
355 | running = running_processes(); |
356 | dbg("%d processes runnning on system", running); | |
357 | if (running < 0) | |
a15f42c4 | 358 | running = max_childs_running; |
53921bfa | 359 | |
f8a178a3 | 360 | list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, node) { |
085cce37 | 361 | /* check running processes in our session and possibly throttle */ |
a15f42c4 KS |
362 | if (running >= max_childs_running) { |
363 | running = running_processes_in_session(sid, max_childs_running+10); | |
364 | dbg("at least %d processes running in session", running); | |
365 | if (running >= max_childs_running) { | |
fc465079 | 366 | dbg("delay seq %llu, too many processes already running", loop_msg->seqnum); |
085cce37 KS |
367 | return; |
368 | } | |
369 | } | |
370 | ||
fc465079 KS |
371 | /* don't run two processes for the same devpath and wait for the parent*/ |
372 | if (running_with_devpath(loop_msg, max_childs)) { | |
a15f42c4 | 373 | dbg("delay seq %llu (%s)", loop_msg->seqnum, loop_msg->devpath); |
fc465079 KS |
374 | continue; |
375 | } | |
376 | ||
377 | /* move event to run list */ | |
378 | list_move_tail(&loop_msg->node, &running_list); | |
379 | udev_event_run(loop_msg); | |
380 | running++; | |
381 | dbg("moved seq %llu to running list", loop_msg->seqnum); | |
53921bfa KS |
382 | } |
383 | } | |
384 | ||
3b47c739 | 385 | static struct uevent_msg *get_msg_from_envbuf(const char *buf, int buf_size) |
88f4b648 KS |
386 | { |
387 | int bufpos; | |
388 | int i; | |
3b47c739 | 389 | struct uevent_msg *msg; |
ebfc1acd KS |
390 | int major = 0; |
391 | int minor = 0; | |
88f4b648 | 392 | |
3b47c739 | 393 | msg = malloc(sizeof(struct uevent_msg) + buf_size); |
88f4b648 KS |
394 | if (msg == NULL) |
395 | return NULL; | |
3b47c739 | 396 | memset(msg, 0x00, sizeof(struct uevent_msg) + buf_size); |
88f4b648 KS |
397 | |
398 | /* copy environment buffer and reconstruct envp */ | |
399 | memcpy(msg->envbuf, buf, buf_size); | |
400 | bufpos = 0; | |
3b47c739 | 401 | for (i = 0; (bufpos < buf_size) && (i < UEVENT_NUM_ENVP-2); i++) { |
88f4b648 KS |
402 | int keylen; |
403 | char *key; | |
404 | ||
405 | key = &msg->envbuf[bufpos]; | |
406 | keylen = strlen(key); | |
407 | msg->envp[i] = key; | |
408 | bufpos += keylen + 1; | |
409 | dbg("add '%s' to msg.envp[%i]", msg->envp[i], i); | |
410 | ||
411 | /* remember some keys for further processing */ | |
412 | if (strncmp(key, "ACTION=", 7) == 0) | |
413 | msg->action = &key[7]; | |
ebfc1acd | 414 | else if (strncmp(key, "DEVPATH=", 8) == 0) |
88f4b648 | 415 | msg->devpath = &key[8]; |
ebfc1acd | 416 | else if (strncmp(key, "SUBSYSTEM=", 10) == 0) |
88f4b648 | 417 | msg->subsystem = &key[10]; |
ebfc1acd | 418 | else if (strncmp(key, "SEQNUM=", 7) == 0) |
88f4b648 | 419 | msg->seqnum = strtoull(&key[7], NULL, 10); |
ebfc1acd | 420 | else if (strncmp(key, "PHYSDEVPATH=", 12) == 0) |
88f4b648 | 421 | msg->physdevpath = &key[12]; |
ebfc1acd KS |
422 | else if (strncmp(key, "MAJOR=", 6) == 0) |
423 | major = strtoull(&key[6], NULL, 10); | |
424 | else if (strncmp(key, "MINOR=", 6) == 0) | |
425 | minor = strtoull(&key[6], NULL, 10); | |
426 | else if (strncmp(key, "TIMEOUT=", 8) == 0) | |
88f4b648 KS |
427 | msg->timeout = strtoull(&key[8], NULL, 10); |
428 | } | |
ebfc1acd | 429 | msg->devt = makedev(major, minor); |
88f4b648 KS |
430 | msg->envp[i++] = "UDEVD_EVENT=1"; |
431 | msg->envp[i] = NULL; | |
432 | ||
433 | return msg; | |
434 | } | |
435 | ||
3b47c739 KS |
436 | /* receive the udevd message from userspace */ |
437 | static struct uevent_msg *get_udevd_msg(void) | |
7fafc032 | 438 | { |
3b47c739 KS |
439 | static struct udevd_msg usend_msg; |
440 | struct uevent_msg *msg; | |
4a231017 | 441 | ssize_t size; |
0028653c KS |
442 | struct msghdr smsg; |
443 | struct cmsghdr *cmsg; | |
444 | struct iovec iov; | |
445 | struct ucred *cred; | |
446 | char cred_msg[CMSG_SPACE(sizeof(struct ucred))]; | |
4a231017 | 447 | int envbuf_size; |
8ab44e3f | 448 | int *intval; |
a695feae | 449 | |
3b47c739 | 450 | memset(&usend_msg, 0x00, sizeof(struct udevd_msg)); |
4a231017 | 451 | iov.iov_base = &usend_msg; |
3b47c739 | 452 | iov.iov_len = sizeof(struct udevd_msg); |
0028653c KS |
453 | |
454 | memset(&smsg, 0x00, sizeof(struct msghdr)); | |
455 | smsg.msg_iov = &iov; | |
456 | smsg.msg_iovlen = 1; | |
457 | smsg.msg_control = cred_msg; | |
458 | smsg.msg_controllen = sizeof(cred_msg); | |
459 | ||
3b47c739 | 460 | size = recvmsg(udevd_sock, &smsg, 0); |
4a231017 | 461 | if (size < 0) { |
2f6cbd19 | 462 | if (errno != EINTR) |
ff3e4bed | 463 | err("unable to receive udevd message: %s", strerror(errno)); |
021a294c | 464 | return NULL; |
53921bfa | 465 | } |
0028653c KS |
466 | cmsg = CMSG_FIRSTHDR(&smsg); |
467 | cred = (struct ucred *) CMSG_DATA(cmsg); | |
468 | ||
7b1cbec9 | 469 | if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) { |
199cdd86 | 470 | err("no sender credentials received, message ignored"); |
021a294c | 471 | return NULL; |
7b1cbec9 KS |
472 | } |
473 | ||
0028653c | 474 | if (cred->uid != 0) { |
199cdd86 | 475 | err("sender uid=%i, message ignored", cred->uid); |
021a294c | 476 | return NULL; |
4a231017 KS |
477 | } |
478 | ||
479 | if (strncmp(usend_msg.magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) { | |
199cdd86 | 480 | err("message magic '%s' doesn't match, ignore it", usend_msg.magic); |
021a294c | 481 | return NULL; |
0028653c KS |
482 | } |
483 | ||
8ab44e3f KS |
484 | switch (usend_msg.type) { |
485 | case UDEVD_UEVENT_UDEVSEND: | |
486 | case UDEVD_UEVENT_INITSEND: | |
199cdd86 | 487 | info("udevd event message received"); |
3b47c739 KS |
488 | envbuf_size = size - offsetof(struct udevd_msg, envbuf); |
489 | dbg("envbuf_size=%i", envbuf_size); | |
490 | msg = get_msg_from_envbuf(usend_msg.envbuf, envbuf_size); | |
491 | if (msg == NULL) | |
492 | return NULL; | |
510de9c9 | 493 | msg->type = usend_msg.type; |
3b47c739 KS |
494 | return msg; |
495 | case UDEVD_STOP_EXEC_QUEUE: | |
8ab44e3f | 496 | info("udevd message (STOP_EXEC_QUEUE) received"); |
3b47c739 KS |
497 | stop_exec_q = 1; |
498 | break; | |
499 | case UDEVD_START_EXEC_QUEUE: | |
8ab44e3f | 500 | info("udevd message (START_EXEC_QUEUE) received"); |
3b47c739 | 501 | stop_exec_q = 0; |
fc465079 | 502 | msg_queue_manager(); |
3b47c739 | 503 | break; |
8ab44e3f KS |
504 | case UDEVD_SET_LOG_LEVEL: |
505 | intval = (int *) usend_msg.envbuf; | |
506 | info("udevd message (SET_LOG_PRIORITY) received, udev_log_priority=%i", *intval); | |
507 | udev_log_priority = *intval; | |
916c5e47 KS |
508 | sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); |
509 | putenv(udev_log); | |
8ab44e3f KS |
510 | break; |
511 | case UDEVD_SET_MAX_CHILDS: | |
512 | intval = (int *) usend_msg.envbuf; | |
513 | info("udevd message (UDEVD_SET_MAX_CHILDS) received, max_childs=%i", *intval); | |
514 | max_childs = *intval; | |
515 | break; | |
c895fd00 KS |
516 | case UDEVD_RELOAD_RULES: |
517 | info("udevd message (RELOAD_RULES) received"); | |
518 | reload_config = 1; | |
519 | break; | |
3b47c739 KS |
520 | default: |
521 | dbg("unknown message type"); | |
522 | } | |
523 | return NULL; | |
88f4b648 | 524 | } |
4a231017 | 525 | |
88f4b648 | 526 | /* receive the kernel user event message and do some sanity checks */ |
8ab44e3f | 527 | static struct uevent_msg *get_netlink_msg(void) |
88f4b648 | 528 | { |
3b47c739 | 529 | struct uevent_msg *msg; |
88f4b648 KS |
530 | int bufpos; |
531 | ssize_t size; | |
3b47c739 | 532 | static char buffer[UEVENT_BUFFER_SIZE + 512]; |
88f4b648 | 533 | char *pos; |
4a231017 | 534 | |
8ab44e3f | 535 | size = recv(uevent_netlink_sock, &buffer, sizeof(buffer), 0); |
88f4b648 KS |
536 | if (size < 0) { |
537 | if (errno != EINTR) | |
ff3e4bed | 538 | err("unable to receive udevd message: %s", strerror(errno)); |
88f4b648 KS |
539 | return NULL; |
540 | } | |
4a231017 | 541 | |
88f4b648 KS |
542 | if ((size_t)size > sizeof(buffer)-1) |
543 | size = sizeof(buffer)-1; | |
544 | buffer[size] = '\0'; | |
ebfc1acd | 545 | dbg("uevent_size=%zi", size); |
4a231017 | 546 | |
88f4b648 KS |
547 | /* start of event payload */ |
548 | bufpos = strlen(buffer)+1; | |
549 | msg = get_msg_from_envbuf(&buffer[bufpos], size-bufpos); | |
550 | if (msg == NULL) | |
551 | return NULL; | |
8ab44e3f | 552 | msg->type = UDEVD_UEVENT_NETLINK; |
4a231017 | 553 | |
88f4b648 KS |
554 | /* validate message */ |
555 | pos = strchr(buffer, '@'); | |
556 | if (pos == NULL) { | |
199cdd86 | 557 | err("invalid uevent '%s'", buffer); |
88f4b648 KS |
558 | free(msg); |
559 | return NULL; | |
560 | } | |
561 | pos[0] = '\0'; | |
79721e0a | 562 | |
88f4b648 | 563 | if (msg->action == NULL) { |
dfedc446 | 564 | info("no ACTION in payload found, skip event '%s'", buffer); |
88f4b648 KS |
565 | free(msg); |
566 | return NULL; | |
567 | } | |
7f7ae03a | 568 | |
88f4b648 | 569 | if (strcmp(msg->action, buffer) != 0) { |
199cdd86 | 570 | err("ACTION in payload does not match uevent, skip event '%s'", buffer); |
88f4b648 KS |
571 | free(msg); |
572 | return NULL; | |
53921bfa | 573 | } |
a695feae | 574 | |
021a294c | 575 | return msg; |
a695feae | 576 | } |
1c5c245e | 577 | |
e5a5b54a | 578 | static void asmlinkage sig_handler(int signum) |
7fafc032 | 579 | { |
53921bfa KS |
580 | switch (signum) { |
581 | case SIGINT: | |
582 | case SIGTERM: | |
63cc8f04 | 583 | udev_exit = 1; |
53921bfa | 584 | break; |
2f6cbd19 | 585 | case SIGCHLD: |
f27125f9 | 586 | /* set flag, then write to pipe if needed */ |
5cab7caa | 587 | sigchilds_waiting = 1; |
2f6cbd19 | 588 | break; |
c895fd00 KS |
589 | case SIGHUP: |
590 | reload_config = 1; | |
591 | break; | |
f27125f9 | 592 | } |
5a73b25f | 593 | |
c6303c13 KS |
594 | /* write to pipe, which will wakeup select() in our mainloop */ |
595 | write(signal_pipe[WRITE_END], "", 1); | |
33db4b8d | 596 | } |
7fafc032 | 597 | |
2f6cbd19 KS |
598 | static void udev_done(int pid) |
599 | { | |
600 | /* find msg associated with pid and delete it */ | |
3b47c739 | 601 | struct uevent_msg *msg; |
2f6cbd19 | 602 | |
f8a178a3 | 603 | list_for_each_entry(msg, &running_list, node) { |
2f6cbd19 | 604 | if (msg->pid == pid) { |
fc465079 | 605 | info("seq %llu, pid [%d] exit, %ld seconds old", msg->seqnum, msg->pid, time(NULL) - msg->queue_time); |
ebfc1acd | 606 | msg_queue_delete(msg); |
3169e8d1 | 607 | |
fc465079 | 608 | /* there may be events waiting with the same devpath */ |
f27125f9 | 609 | run_exec_q = 1; |
2f6cbd19 KS |
610 | return; |
611 | } | |
612 | } | |
613 | } | |
614 | ||
5cab7caa | 615 | static void reap_sigchilds(void) |
f27125f9 | 616 | { |
40caaeec | 617 | pid_t pid; |
ce043f85 | 618 | |
40caaeec KS |
619 | while (1) { |
620 | pid = waitpid(-1, NULL, WNOHANG); | |
621 | if (pid <= 0) | |
f27125f9 | 622 | break; |
40caaeec | 623 | udev_done(pid); |
f27125f9 | 624 | } |
625 | } | |
626 | ||
3b47c739 | 627 | static int init_udevd_socket(void) |
33db4b8d | 628 | { |
53921bfa | 629 | struct sockaddr_un saddr; |
a5c606f6 | 630 | const int buffersize = 16 * 1024 * 1024; |
1dadabd7 | 631 | socklen_t addrlen; |
5cab7caa | 632 | const int feature_on = 1; |
c2cf4012 KS |
633 | int retval; |
634 | ||
635 | memset(&saddr, 0x00, sizeof(saddr)); | |
636 | saddr.sun_family = AF_LOCAL; | |
637 | /* use abstract namespace for socket path */ | |
638 | strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH); | |
639 | addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1; | |
640 | ||
3b47c739 KS |
641 | udevd_sock = socket(AF_LOCAL, SOCK_DGRAM, 0); |
642 | if (udevd_sock == -1) { | |
ff3e4bed | 643 | err("error getting socket: %s", strerror(errno)); |
c2cf4012 KS |
644 | return -1; |
645 | } | |
646 | ||
cbbde2ba | 647 | /* set receive buffersize */ |
a5c606f6 | 648 | setsockopt(udevd_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); |
cbbde2ba | 649 | |
c2cf4012 | 650 | /* the bind takes care of ensuring only one copy running */ |
3b47c739 | 651 | retval = bind(udevd_sock, (struct sockaddr *) &saddr, addrlen); |
c2cf4012 | 652 | if (retval < 0) { |
ff3e4bed | 653 | err("bind failed: %s", strerror(errno)); |
c2cf4012 KS |
654 | return -1; |
655 | } | |
656 | ||
657 | /* enable receiving of the sender credentials */ | |
3b47c739 | 658 | setsockopt(udevd_sock, SOL_SOCKET, SO_PASSCRED, &feature_on, sizeof(feature_on)); |
c2cf4012 KS |
659 | |
660 | return 0; | |
661 | } | |
662 | ||
8ab44e3f | 663 | static int init_uevent_netlink_sock(void) |
88f4b648 KS |
664 | { |
665 | struct sockaddr_nl snl; | |
a5c606f6 | 666 | const int buffersize = 16 * 1024 * 1024; |
88f4b648 KS |
667 | int retval; |
668 | ||
669 | memset(&snl, 0x00, sizeof(struct sockaddr_nl)); | |
670 | snl.nl_family = AF_NETLINK; | |
671 | snl.nl_pid = getpid(); | |
672 | snl.nl_groups = 0xffffffff; | |
673 | ||
8ab44e3f KS |
674 | uevent_netlink_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); |
675 | if (uevent_netlink_sock == -1) { | |
ff3e4bed | 676 | err("error getting socket: %s", strerror(errno)); |
88f4b648 KS |
677 | return -1; |
678 | } | |
679 | ||
cbbde2ba | 680 | /* set receive buffersize */ |
a5c606f6 | 681 | setsockopt(uevent_netlink_sock, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize)); |
cbbde2ba | 682 | |
833b3c68 | 683 | retval = bind(uevent_netlink_sock, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl)); |
88f4b648 | 684 | if (retval < 0) { |
ff3e4bed | 685 | err("bind failed: %s", strerror(errno)); |
8ab44e3f KS |
686 | close(uevent_netlink_sock); |
687 | uevent_netlink_sock = -1; | |
88f4b648 KS |
688 | return -1; |
689 | } | |
88f4b648 KS |
690 | return 0; |
691 | } | |
692 | ||
c2cf4012 KS |
693 | int main(int argc, char *argv[], char *envp[]) |
694 | { | |
c2cf4012 | 695 | int retval; |
3904a758 | 696 | int fd; |
f8911dbb | 697 | struct sigaction act; |
f27125f9 | 698 | fd_set readfds; |
a15f42c4 | 699 | const char *value; |
561d4c5a KS |
700 | int daemonize = 0; |
701 | int i; | |
833b3c68 | 702 | int rc = 0; |
0b3dfb3d | 703 | int maxfd; |
53921bfa | 704 | |
833b3c68 | 705 | /* redirect std fd's, if the kernel forks us, we don't have them at all */ |
3904a758 KS |
706 | fd = open("/dev/null", O_RDWR); |
707 | if (fd >= 0) { | |
708 | if (fd != STDIN_FILENO) | |
709 | dup2(fd, STDIN_FILENO); | |
710 | if (fd != STDOUT_FILENO) | |
711 | dup2(fd, STDOUT_FILENO); | |
712 | if (fd != STDERR_FILENO) | |
713 | dup2(fd, STDERR_FILENO); | |
714 | if (fd > STDERR_FILENO) | |
715 | close(fd); | |
3f9f8de4 KS |
716 | } |
717 | ||
7257cb18 | 718 | logging_init("udevd"); |
3904a758 | 719 | if (fd < 0) |
ff3e4bed | 720 | err("fatal, could not open /dev/null: %s", strerror(errno)); |
3f9f8de4 | 721 | |
6b493a20 | 722 | udev_init_config(); |
896e5aa9 | 723 | dbg("version %s", UDEV_VERSION); |
95a6f4c8 | 724 | |
7b1cbec9 | 725 | if (getuid() != 0) { |
6b493a20 | 726 | err("need to be root, exit"); |
ec9cc012 | 727 | goto exit; |
7b1cbec9 | 728 | } |
5cab7caa | 729 | |
833b3c68 | 730 | /* parse commandline options */ |
561d4c5a KS |
731 | for (i = 1 ; i < argc; i++) { |
732 | char *arg = argv[i]; | |
733 | if (strcmp(arg, "--daemon") == 0 || strcmp(arg, "-d") == 0) { | |
734 | info("will daemonize"); | |
735 | daemonize = 1; | |
736 | } | |
737 | if (strcmp(arg, "--stop-exec-queue") == 0) { | |
8ab44e3f | 738 | info("will not execute events until START_EXEC_QUEUE is received"); |
561d4c5a KS |
739 | stop_exec_q = 1; |
740 | } | |
741 | } | |
40caaeec | 742 | |
833b3c68 KS |
743 | /* init sockets to receive events */ |
744 | if (init_udevd_socket() < 0) { | |
745 | if (errno == EADDRINUSE) { | |
746 | dbg("another udevd running, exit"); | |
747 | rc = 1; | |
748 | } else { | |
749 | dbg("error initializing udevd socket: %s", strerror(errno)); | |
750 | rc = 2; | |
751 | } | |
752 | goto exit; | |
753 | } | |
754 | ||
755 | if (init_uevent_netlink_sock() < 0) { | |
756 | err("uevent socket not available"); | |
757 | rc = 3; | |
758 | goto exit; | |
759 | } | |
760 | ||
761 | /* parse the rules and keep it in memory */ | |
287814b2 | 762 | udev_rules_init(&rules, 1); |
833b3c68 | 763 | |
561d4c5a | 764 | if (daemonize) { |
f15515b5 KS |
765 | pid_t pid; |
766 | ||
767 | pid = fork(); | |
768 | switch (pid) { | |
769 | case 0: | |
833b3c68 | 770 | dbg("daemonized fork running"); |
f15515b5 KS |
771 | break; |
772 | case -1: | |
ff3e4bed | 773 | err("fork of daemon failed: %s", strerror(errno)); |
833b3c68 | 774 | rc = 4; |
f15515b5 KS |
775 | goto exit; |
776 | default: | |
833b3c68 KS |
777 | dbg("child [%u] running, parent exits", pid); |
778 | goto exit; | |
f15515b5 KS |
779 | } |
780 | } | |
781 | ||
3904a758 | 782 | /* set scheduling priority for the daemon */ |
085cce37 KS |
783 | setpriority(PRIO_PROCESS, 0, UDEVD_PRIORITY); |
784 | ||
3904a758 | 785 | chdir("/"); |
74adec7d | 786 | umask(022); |
3904a758 KS |
787 | |
788 | /* become session leader */ | |
789 | sid = setsid(); | |
790 | dbg("our session is %d", sid); | |
791 | ||
792 | /* OOM_DISABLE == -17 */ | |
793 | fd = open("/proc/self/oom_adj", O_RDWR); | |
794 | if (fd < 0) | |
ff3e4bed | 795 | err("error disabling OOM: %s", strerror(errno)); |
3904a758 KS |
796 | else { |
797 | write(fd, "-17", 3); | |
798 | close(fd); | |
799 | } | |
800 | ||
f27125f9 | 801 | /* setup signal handler pipe */ |
f1ff8d7b | 802 | retval = pipe(signal_pipe); |
e5a2989e | 803 | if (retval < 0) { |
6b493a20 | 804 | err("error getting pipes: %s", strerror(errno)); |
ec9cc012 | 805 | goto exit; |
e5a2989e | 806 | } |
f1ff8d7b | 807 | retval = fcntl(signal_pipe[READ_END], F_SETFL, O_NONBLOCK); |
c8fa2d8b | 808 | if (retval < 0) { |
6b493a20 | 809 | err("error fcntl on read pipe: %s", strerror(errno)); |
ec9cc012 | 810 | goto exit; |
c8fa2d8b | 811 | } |
f1ff8d7b | 812 | retval = fcntl(signal_pipe[WRITE_END], F_SETFL, O_NONBLOCK); |
e5a2989e | 813 | if (retval < 0) { |
6b493a20 | 814 | err("error fcntl on write pipe: %s", strerror(errno)); |
ec9cc012 | 815 | goto exit; |
e5a2989e | 816 | } |
f27125f9 | 817 | |
818 | /* set signal handlers */ | |
0786e8e5 | 819 | memset(&act, 0x00, sizeof(struct sigaction)); |
6b493a20 | 820 | act.sa_handler = (void (*)(int)) sig_handler; |
f27125f9 | 821 | sigemptyset(&act.sa_mask); |
f8911dbb KS |
822 | act.sa_flags = SA_RESTART; |
823 | sigaction(SIGINT, &act, NULL); | |
824 | sigaction(SIGTERM, &act, NULL); | |
f8911dbb | 825 | sigaction(SIGCHLD, &act, NULL); |
63cc8f04 | 826 | sigaction(SIGHUP, &act, NULL); |
7fafc032 | 827 | |
c895fd00 KS |
828 | /* watch rules directory */ |
829 | inotify_fd = inotify_init(); | |
830 | if (inotify_fd > 0) | |
831 | inotify_add_watch(inotify_fd, udev_rules_filename, IN_CREATE | IN_DELETE | IN_MOVE | IN_CLOSE_WRITE); | |
0028653c | 832 | |
a15f42c4 KS |
833 | /* maximum limit of forked childs */ |
834 | value = getenv("UDEVD_MAX_CHILDS"); | |
835 | if (value) | |
836 | max_childs = strtoul(value, NULL, 10); | |
837 | else | |
838 | max_childs = UDEVD_MAX_CHILDS; | |
839 | info("initialize max_childs to %u", max_childs); | |
840 | ||
841 | /* start to throttle forking if maximum number of _running_ childs is reached */ | |
842 | value = getenv("UDEVD_MAX_CHILDS_RUNNING"); | |
843 | if (value) | |
844 | max_childs_running = strtoull(value, NULL, 10); | |
845 | else | |
846 | max_childs_running = UDEVD_MAX_CHILDS_RUNNING; | |
847 | info("initialize max_childs_running to %u", max_childs_running); | |
8b726878 | 848 | |
c895fd00 KS |
849 | /* clear environment for forked event processes */ |
850 | clearenv(); | |
851 | ||
40caaeec | 852 | /* export log_priority , as called programs may want to follow that setting */ |
916c5e47 KS |
853 | sprintf(udev_log, "UDEV_LOG=%i", udev_log_priority); |
854 | putenv(udev_log); | |
40caaeec | 855 | |
0b3dfb3d KS |
856 | maxfd = udevd_sock; |
857 | maxfd = UDEV_MAX(maxfd, uevent_netlink_sock); | |
858 | maxfd = UDEV_MAX(maxfd, signal_pipe[READ_END]); | |
859 | maxfd = UDEV_MAX(maxfd, inotify_fd); | |
860 | ||
63cc8f04 | 861 | while (!udev_exit) { |
3b47c739 | 862 | struct uevent_msg *msg; |
40caaeec | 863 | int fdcount; |
021a294c | 864 | |
40caaeec | 865 | FD_ZERO(&readfds); |
f1ff8d7b | 866 | FD_SET(signal_pipe[READ_END], &readfds); |
40caaeec | 867 | FD_SET(udevd_sock, &readfds); |
833b3c68 | 868 | FD_SET(uevent_netlink_sock, &readfds); |
c895fd00 KS |
869 | if (inotify_fd > 0) |
870 | FD_SET(inotify_fd, &readfds); | |
e5a2989e | 871 | |
0b3dfb3d | 872 | fdcount = select(maxfd+1, &readfds, NULL, NULL, NULL); |
40caaeec | 873 | if (fdcount < 0) { |
e5a2989e KS |
874 | if (errno != EINTR) |
875 | dbg("error in select: %s", strerror(errno)); | |
f27125f9 | 876 | continue; |
2f6cbd19 | 877 | } |
e5a2989e | 878 | |
63cc8f04 | 879 | /* get user socket message */ |
40caaeec | 880 | if (FD_ISSET(udevd_sock, &readfds)) { |
3b47c739 | 881 | msg = get_udevd_msg(); |
88f4b648 | 882 | if (msg) { |
0b3dfb3d KS |
883 | if (msg->type == UDEVD_UEVENT_UDEVSEND && msg->seqnum != 0) { |
884 | info("skip non-kernel message with SEQNUM"); | |
88f4b648 | 885 | free(msg); |
d2ecd6a1 KS |
886 | } else |
887 | msg_queue_insert(msg); | |
88f4b648 KS |
888 | } |
889 | } | |
890 | ||
63cc8f04 | 891 | /* get kernel netlink message */ |
833b3c68 | 892 | if (FD_ISSET(uevent_netlink_sock, &readfds)) { |
8ab44e3f | 893 | msg = get_netlink_msg(); |
0b3dfb3d | 894 | if (msg) |
021a294c KS |
895 | msg_queue_insert(msg); |
896 | } | |
e5a2989e | 897 | |
63cc8f04 | 898 | /* received a signal, clear our notification pipe */ |
c6303c13 KS |
899 | if (FD_ISSET(signal_pipe[READ_END], &readfds)) { |
900 | char buf[256]; | |
901 | ||
902 | read(signal_pipe[READ_END], &buf, sizeof(buf)); | |
40caaeec | 903 | } |
e5a2989e | 904 | |
c895fd00 KS |
905 | /* rules directory inotify watch */ |
906 | if ((inotify_fd > 0) && FD_ISSET(inotify_fd, &readfds)) { | |
907 | int nbytes; | |
908 | ||
909 | /* discard all possible events, we can just reload the config */ | |
910 | if ((ioctl(inotify_fd, FIONREAD, &nbytes) == 0) && nbytes) { | |
911 | char *buf; | |
912 | ||
913 | reload_config = 1; | |
914 | buf = malloc(nbytes); | |
915 | if (!buf) { | |
916 | err("error getting buffer for inotify, disable watching"); | |
917 | close(inotify_fd); | |
918 | inotify_fd = -1; | |
919 | } | |
920 | read(inotify_fd, buf, nbytes); | |
921 | free(buf); | |
922 | } | |
923 | } | |
924 | ||
925 | /* rules changed, set by inotify or a signal*/ | |
926 | if (reload_config) { | |
927 | reload_config = 0; | |
928 | udev_rules_close(&rules); | |
287814b2 | 929 | udev_rules_init(&rules, 1); |
c895fd00 KS |
930 | } |
931 | ||
0b3dfb3d | 932 | /* forked child has returned */ |
5cab7caa KS |
933 | if (sigchilds_waiting) { |
934 | sigchilds_waiting = 0; | |
935 | reap_sigchilds(); | |
f27125f9 | 936 | } |
e5a2989e | 937 | |
f27125f9 | 938 | if (run_exec_q) { |
f27125f9 | 939 | run_exec_q = 0; |
3b47c739 | 940 | if (!stop_exec_q) |
fc465079 | 941 | msg_queue_manager(); |
53921bfa | 942 | } |
53921bfa | 943 | } |
ec9cc012 | 944 | |
53921bfa | 945 | exit: |
c895fd00 KS |
946 | udev_rules_close(&rules); |
947 | ||
f1ff8d7b KS |
948 | if (signal_pipe[READ_END] > 0) |
949 | close(signal_pipe[READ_END]); | |
950 | if (signal_pipe[WRITE_END] > 0) | |
951 | close(signal_pipe[WRITE_END]); | |
2b996ad1 | 952 | |
63cc8f04 KS |
953 | if (udevd_sock > 0) |
954 | close(udevd_sock); | |
c895fd00 KS |
955 | if (inotify_fd > 0) |
956 | close(inotify_fd); | |
63cc8f04 KS |
957 | if (uevent_netlink_sock > 0) |
958 | close(uevent_netlink_sock); | |
959 | ||
7257cb18 | 960 | logging_close(); |
63cc8f04 | 961 | |
833b3c68 | 962 | return rc; |
7fafc032 | 963 | } |