]> git.ipfire.org Git - thirdparty/systemd.git/blame - udevd.c
[PATCH] $local user
[thirdparty/systemd.git] / udevd.c
CommitLineData
7fafc032 1/*
53921bfa 2 * udevd.c - hotplug event serializer
7fafc032 3 *
7fafc032 4 * Copyright (C) 2004 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>
90c210eb 24#include <sys/wait.h>
7fafc032
KS
25#include <signal.h>
26#include <unistd.h>
27#include <errno.h>
28#include <stdio.h>
29#include <stdlib.h>
30#include <string.h>
e5a2989e 31#include <sys/time.h>
53921bfa
KS
32#include <sys/types.h>
33#include <sys/socket.h>
34#include <sys/un.h>
f27125f9 35#include <fcntl.h>
e5a2989e 36#include "klibc_fixups.h"
e5a2989e 37#include <sys/sysinfo.h>
dc117daa 38#include <sys/stat.h>
7fafc032 39
a695feae 40#include "list.h"
7fafc032 41#include "udev.h"
c81b35c0 42#include "udev_lib.h"
35b7d88c 43#include "udev_version.h"
7fafc032
KS
44#include "udevd.h"
45#include "logging.h"
46
f27125f9 47static int pipefds[2];
cdc60e8a 48static unsigned long long expected_seqnum = 0;
c8fa2d8b
HH
49static volatile int children_waiting;
50static volatile int run_msg_q;
51static volatile int sig_flag;
f27125f9 52static int run_exec_q;
7fafc032 53
4a539daf
KS
54static LIST_HEAD(msg_list);
55static LIST_HEAD(exec_list);
56static LIST_HEAD(running_list);
7fafc032 57
2f6cbd19
KS
58static void exec_queue_manager(void);
59static void msg_queue_manager(void);
f27125f9 60static void user_sighandler(void);
61static void reap_kids(void);
7d855c31 62char *udev_bin;
7fafc032 63
d026a35d 64#ifdef LOG
d00bd172 65unsigned char logname[LOGNAME_SIZE];
d026a35d 66void log_message (int level, const char *format, ...)
51a8bb2f 67{
d026a35d
GKH
68 va_list args;
69
70 va_start(args, format);
71 vsyslog(level, format, args);
72 va_end(args);
51a8bb2f 73}
d026a35d 74#endif
51a8bb2f 75
b6bf0b12 76#define msg_dump(msg) \
cdc60e8a 77 dbg("msg_dump: sequence %llu, '%s', '%s', '%s'", \
b6bf0b12
KS
78 msg->seqnum, msg->action, msg->devpath, msg->subsystem);
79
53921bfa 80static void msg_dump_queue(void)
7fafc032 81{
f27125f9 82#ifdef DEBUG
53921bfa 83 struct hotplug_msg *msg;
7fafc032 84
53921bfa 85 list_for_each_entry(msg, &msg_list, list)
cdc60e8a 86 dbg("sequence %llu in queue", msg->seqnum);
f27125f9 87#endif
35b7d88c
KS
88}
89
53921bfa 90static struct hotplug_msg *msg_create(void)
35b7d88c 91{
53921bfa 92 struct hotplug_msg *new_msg;
35b7d88c 93
53921bfa 94 new_msg = malloc(sizeof(struct hotplug_msg));
2f6cbd19 95 if (new_msg == NULL)
53921bfa 96 dbg("error malloc");
53921bfa 97 return new_msg;
35b7d88c
KS
98}
99
2f6cbd19 100static void run_queue_delete(struct hotplug_msg *msg)
8e2229c4 101{
2f6cbd19
KS
102 list_del(&msg->list);
103 free(msg);
8e2229c4
KS
104}
105
53921bfa
KS
106/* orders the message in the queue by sequence number */
107static void msg_queue_insert(struct hotplug_msg *msg)
35b7d88c 108{
53921bfa 109 struct hotplug_msg *loop_msg;
e5a2989e 110 struct sysinfo info;
35b7d88c 111
f27125f9 112 /* sort message by sequence number into list. events
113 * will tend to come in order, so scan the list backwards
114 */
115 list_for_each_entry_reverse(loop_msg, &msg_list, list)
116 if (loop_msg->seqnum < msg->seqnum)
53921bfa 117 break;
35b7d88c 118
53921bfa 119 /* store timestamp of queuing */
e5a2989e
KS
120 sysinfo(&info);
121 msg->queue_time = info.uptime;
122
123 list_add(&msg->list, &loop_msg->list);
cdc60e8a 124 dbg("queued message seq %llu", msg->seqnum);
35b7d88c 125
2f6cbd19 126 /* run msg queue manager */
f27125f9 127 run_msg_q = 1;
7fafc032 128
53921bfa 129 return ;
7fafc032
KS
130}
131
53921bfa 132/* forks event and removes event from run queue when finished */
2f6cbd19 133static void udev_run(struct hotplug_msg *msg)
7fafc032 134{
90c210eb 135 pid_t pid;
e964c2c0
KS
136 char action[ACTION_SIZE];
137 char devpath[DEVPATH_SIZE];
3169e8d1
KS
138 char seqnum[SEQNUM_SIZE];
139 char *env[] = { action, devpath, seqnum, NULL };
f8911dbb 140
cdc60e8a
KS
141 snprintf(action, ACTION_SIZE-1, "ACTION=%s", msg->action);
142 action[ACTION_SIZE-1] = '\0';
143 snprintf(devpath, DEVPATH_SIZE-1, "DEVPATH=%s", msg->devpath);
144 devpath[DEVPATH_SIZE-1] = '\0';
145 sprintf(seqnum, "SEQNUM=%llu", msg->seqnum);
90c210eb
KS
146
147 pid = fork();
148 switch (pid) {
149 case 0:
33db4b8d 150 /* child */
7d855c31 151 execle(udev_bin, "udev", msg->subsystem, NULL, env);
33db4b8d 152 dbg("exec of child failed");
7257cb18 153 _exit(1);
90c210eb
KS
154 break;
155 case -1:
33db4b8d 156 dbg("fork of child failed");
2f6cbd19 157 run_queue_delete(msg);
f27125f9 158 /* note: we never managed to run, so we had no impact on
159 * running_with_devpath(), so don't bother setting run_exec_q
160 */
2f6cbd19 161 break;
90c210eb 162 default:
2f6cbd19 163 /* get SIGCHLD in main loop */
cdc60e8a 164 dbg("==> exec seq %llu [%d] working at '%s'", msg->seqnum, pid, msg->devpath);
2f6cbd19 165 msg->pid = pid;
90c210eb 166 }
7fafc032
KS
167}
168
53921bfa
KS
169/* returns already running task with devpath */
170static struct hotplug_msg *running_with_devpath(struct hotplug_msg *msg)
7fafc032 171{
53921bfa 172 struct hotplug_msg *loop_msg;
2f6cbd19 173 list_for_each_entry(loop_msg, &running_list, list)
53921bfa
KS
174 if (strncmp(loop_msg->devpath, msg->devpath, sizeof(loop_msg->devpath)) == 0)
175 return loop_msg;
176 return NULL;
7fafc032
KS
177}
178
2f6cbd19
KS
179/* exec queue management routine executes the events and delays events for the same devpath */
180static void exec_queue_manager()
7fafc032 181{
53921bfa
KS
182 struct hotplug_msg *loop_msg;
183 struct hotplug_msg *tmp_msg;
a695feae 184 struct hotplug_msg *msg;
53921bfa 185
2f6cbd19
KS
186 list_for_each_entry_safe(loop_msg, tmp_msg, &exec_list, list) {
187 msg = running_with_devpath(loop_msg);
188 if (!msg) {
189 /* move event to run list */
190 list_move_tail(&loop_msg->list, &running_list);
191 udev_run(loop_msg);
cdc60e8a 192 dbg("moved seq %llu to running list", loop_msg->seqnum);
2f6cbd19 193 } else {
cdc60e8a 194 dbg("delay seq %llu, cause seq %llu already working on '%s'",
2f6cbd19 195 loop_msg->seqnum, msg->seqnum, msg->devpath);
53921bfa 196 }
53921bfa
KS
197 }
198}
199
2f6cbd19 200static void msg_move_exec(struct hotplug_msg *msg)
86590cd5 201{
2f6cbd19 202 list_move_tail(&msg->list, &exec_list);
f27125f9 203 run_exec_q = 1;
2f6cbd19 204 expected_seqnum = msg->seqnum+1;
cdc60e8a 205 dbg("moved seq %llu to exec, next expected is %llu",
2f6cbd19 206 msg->seqnum, expected_seqnum);
86590cd5
KS
207}
208
2f6cbd19
KS
209/* msg queue management routine handles the timeouts and dispatches the events */
210static void msg_queue_manager()
53921bfa
KS
211{
212 struct hotplug_msg *loop_msg;
a695feae 213 struct hotplug_msg *tmp_msg;
e5a2989e
KS
214 struct sysinfo info;
215 long msg_age = 0;
a695feae 216
cdc60e8a 217 dbg("msg queue manager, next expected is %llu", expected_seqnum);
a695feae 218recheck:
2f6cbd19
KS
219 list_for_each_entry_safe(loop_msg, tmp_msg, &msg_list, list) {
220 /* move event with expected sequence to the exec list */
221 if (loop_msg->seqnum == expected_seqnum) {
222 msg_move_exec(loop_msg);
223 continue;
a695feae 224 }
35b7d88c 225
2f6cbd19 226 /* move event with expired timeout to the exec list */
e5a2989e
KS
227 sysinfo(&info);
228 msg_age = info.uptime - loop_msg->queue_time;
cdc60e8a 229 dbg("seq %llu is %li seconds old", loop_msg->seqnum, msg_age);
2f6cbd19
KS
230 if (msg_age > EVENT_TIMEOUT_SEC-1) {
231 msg_move_exec(loop_msg);
232 goto recheck;
53921bfa 233 } else {
2f6cbd19 234 break;
53921bfa 235 }
2f6cbd19
KS
236 }
237
238 msg_dump_queue();
239
e5a2989e 240 /* set timeout for remaining queued events */
2f6cbd19 241 if (list_empty(&msg_list) == 0) {
2f6cbd19 242 struct itimerval itv = {{0, 0}, {EVENT_TIMEOUT_SEC - msg_age, 0}};
e5a2989e 243 dbg("next event expires in %li seconds", EVENT_TIMEOUT_SEC - msg_age);
2f6cbd19 244 setitimer(ITIMER_REAL, &itv, 0);
7fafc032 245 }
7fafc032
KS
246}
247
2f6cbd19
KS
248/* receive the msg, do some basic sanity checks, and queue it */
249static void handle_msg(int sock)
7fafc032 250{
53921bfa
KS
251 struct hotplug_msg *msg;
252 int retval;
0028653c
KS
253 struct msghdr smsg;
254 struct cmsghdr *cmsg;
255 struct iovec iov;
256 struct ucred *cred;
257 char cred_msg[CMSG_SPACE(sizeof(struct ucred))];
a695feae 258
53921bfa
KS
259 msg = msg_create();
260 if (msg == NULL) {
261 dbg("unable to store message");
2f6cbd19 262 return;
7fafc032 263 }
7fafc032 264
0028653c
KS
265 iov.iov_base = msg;
266 iov.iov_len = sizeof(struct hotplug_msg);
267
268 memset(&smsg, 0x00, sizeof(struct msghdr));
269 smsg.msg_iov = &iov;
270 smsg.msg_iovlen = 1;
271 smsg.msg_control = cred_msg;
272 smsg.msg_controllen = sizeof(cred_msg);
273
274 retval = recvmsg(sock, &smsg, 0);
53921bfa 275 if (retval < 0) {
2f6cbd19
KS
276 if (errno != EINTR)
277 dbg("unable to receive message");
278 return;
53921bfa 279 }
0028653c
KS
280 cmsg = CMSG_FIRSTHDR(&smsg);
281 cred = (struct ucred *) CMSG_DATA(cmsg);
282
7b1cbec9
KS
283 if (cmsg == NULL || cmsg->cmsg_type != SCM_CREDENTIALS) {
284 dbg("no sender credentials received, message ignored");
285 goto skip;
286 }
287
0028653c
KS
288 if (cred->uid != 0) {
289 dbg("sender uid=%i, message ignored", cred->uid);
7b1cbec9 290 goto skip;
0028653c
KS
291 }
292
53921bfa
KS
293 if (strncmp(msg->magic, UDEV_MAGIC, sizeof(UDEV_MAGIC)) != 0 ) {
294 dbg("message magic '%s' doesn't match, ignore it", msg->magic);
7b1cbec9 295 goto skip;
53921bfa 296 }
a695feae 297
86590cd5 298 /* if no seqnum is given, we move straight to exec queue */
cdc60e8a 299 if (msg->seqnum == 0) {
86590cd5 300 list_add(&msg->list, &exec_list);
f27125f9 301 run_exec_q = 1;
86590cd5 302 } else {
86590cd5 303 msg_queue_insert(msg);
86590cd5 304 }
7b1cbec9
KS
305 return;
306
307skip:
308 free(msg);
309 return;
a695feae 310}
1c5c245e 311
e5a5b54a 312static void asmlinkage sig_handler(int signum)
7fafc032 313{
f27125f9 314 int rc;
47bf9196 315
53921bfa
KS
316 switch (signum) {
317 case SIGINT:
318 case SIGTERM:
53921bfa
KS
319 exit(20 + signum);
320 break;
2f6cbd19 321 case SIGALRM:
f27125f9 322 /* set flag, then write to pipe if needed */
323 run_msg_q = 1;
324 goto do_write;
2f6cbd19
KS
325 break;
326 case SIGCHLD:
f27125f9 327 /* set flag, then write to pipe if needed */
2f6cbd19 328 children_waiting = 1;
f27125f9 329 goto do_write;
2f6cbd19 330 break;
53921bfa 331 default:
47bf9196 332 dbg("unhandled signal %d", signum);
f27125f9 333 return;
334 }
335
336do_write:
337 /* if pipe is empty, write to pipe to force select to return
338 * immediately when it gets called
339 */
340 if (!sig_flag) {
341 rc = write(pipefds[1],&signum,sizeof(signum));
342 if (rc < 0)
343 dbg("unable to write to pipe");
344 else
345 sig_flag = 1;
7fafc032 346 }
33db4b8d 347}
7fafc032 348
2f6cbd19
KS
349static void udev_done(int pid)
350{
351 /* find msg associated with pid and delete it */
352 struct hotplug_msg *msg;
353
354 list_for_each_entry(msg, &running_list, list) {
355 if (msg->pid == pid) {
cdc60e8a 356 dbg("<== exec seq %llu came back", msg->seqnum);
2f6cbd19 357 run_queue_delete(msg);
3169e8d1 358
f27125f9 359 /* we want to run the exec queue manager since there may
360 * be events waiting with the devpath of the one that
361 * just finished
362 */
363 run_exec_q = 1;
2f6cbd19
KS
364 return;
365 }
366 }
367}
368
f27125f9 369static void reap_kids()
370{
371 /* reap all dead children */
372 while(1) {
373 int pid = waitpid(-1, 0, WNOHANG);
374 if ((pid == -1) || (pid == 0))
375 break;
376 udev_done(pid);
377 }
378}
379
380/* just read everything from the pipe and clear the flag,
381 * the useful flags were set in the signal handler
382 */
383static void user_sighandler()
384{
385 int sig;
386 while(1) {
387 int rc = read(pipefds[0],&sig,sizeof(sig));
388 if (rc < 0)
389 break;
390
391 sig_flag = 0;
392 }
393}
394
395
33db4b8d
KS
396int main(int argc, char *argv[])
397{
f27125f9 398 int ssock, maxsockplus;
53921bfa 399 struct sockaddr_un saddr;
1dadabd7 400 socklen_t addrlen;
c8fa2d8b 401 int retval, fd;
0028653c 402 const int on = 1;
f8911dbb 403 struct sigaction act;
f27125f9 404 fd_set readfds;
53921bfa 405
7257cb18 406 logging_init("udevd");
896e5aa9 407 dbg("version %s", UDEV_VERSION);
95a6f4c8 408
7b1cbec9
KS
409 if (getuid() != 0) {
410 dbg("need to be root, exit");
411 exit(1);
412 }
c8fa2d8b
HH
413 /* make sure we are at top of dir */
414 chdir("/");
415 umask( umask( 077 ) | 022 );
416 /* Set fds to dev/null */
417 fd = open( "/dev/null", O_RDWR );
418 if ( fd < 0 ) {
419 dbg("error opening /dev/null %s", strerror(errno));
420 exit(1);
421 }
422 dup2(fd, 0);
423 dup2(fd, 1);
424 dup2(fd, 2);
425 if (fd > 2)
426 close(fd);
427 /* Get new session id so stray signals don't come our way. */
428 setsid();
e5a2989e 429
f27125f9 430 /* setup signal handler pipe */
e5a2989e
KS
431 retval = pipe(pipefds);
432 if (retval < 0) {
433 dbg("error getting pipes: %s", strerror(errno));
434 exit(1);
435 }
436
437 retval = fcntl(pipefds[0], F_SETFL, O_NONBLOCK);
c8fa2d8b
HH
438 if (retval < 0) {
439 dbg("error fcntl on read pipe: %s", strerror(errno));
440 exit(1);
441 }
442 retval = fcntl(pipefds[0], F_SETFD, FD_CLOEXEC);
443 if (retval < 0) {
e5a2989e
KS
444 dbg("error fcntl on read pipe: %s", strerror(errno));
445 exit(1);
446 }
447
448 retval = fcntl(pipefds[1], F_SETFL, O_NONBLOCK);
449 if (retval < 0) {
450 dbg("error fcntl on write pipe: %s", strerror(errno));
451 exit(1);
452 }
c8fa2d8b
HH
453 retval = fcntl(pipefds[1], F_SETFD, FD_CLOEXEC);
454 if (retval < 0) {
455 dbg("error fcntl on write pipe: %s", strerror(errno));
456 exit(1);
457 }
f27125f9 458
c8fa2d8b 459
f27125f9 460 /* set signal handlers */
dc117daa 461 act.sa_handler = (void (*) (int))sig_handler;
f27125f9 462 sigemptyset(&act.sa_mask);
f8911dbb
KS
463 act.sa_flags = SA_RESTART;
464 sigaction(SIGINT, &act, NULL);
465 sigaction(SIGTERM, &act, NULL);
f8911dbb
KS
466 sigaction(SIGALRM, &act, NULL);
467 sigaction(SIGCHLD, &act, NULL);
7fafc032 468
53921bfa
KS
469 memset(&saddr, 0x00, sizeof(saddr));
470 saddr.sun_family = AF_LOCAL;
872344c4
KS
471 /* use abstract namespace for socket path */
472 strcpy(&saddr.sun_path[1], UDEVD_SOCK_PATH);
1dadabd7 473 addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(saddr.sun_path+1) + 1;
53921bfa 474
2f6cbd19 475 ssock = socket(AF_LOCAL, SOCK_DGRAM, 0);
53921bfa 476 if (ssock == -1) {
7b1cbec9 477 dbg("error getting socket, exit");
53921bfa
KS
478 exit(1);
479 }
480
6e3e3c34
HH
481 set_cloexec_flag(ssock, 1);
482
2f6cbd19 483 /* the bind takes care of ensuring only one copy running */
f8911dbb 484 retval = bind(ssock, (struct sockaddr *) &saddr, addrlen);
53921bfa 485 if (retval < 0) {
7b1cbec9 486 dbg("bind failed, exit");
53921bfa
KS
487 goto exit;
488 }
c8fa2d8b
HH
489 retval = fcntl(ssock, F_SETFD, FD_CLOEXEC);
490 if (retval < 0) {
491 dbg("error fcntl on ssock: %s", strerror(errno));
492 exit(1);
493 }
53921bfa 494
0028653c
KS
495 /* enable receiving of the sender credentials */
496 setsockopt(ssock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on));
7d855c31
KS
497
498 /* possible override of udev binary, used for testing */
499 udev_bin = getenv("UDEV_BIN");
500 if (udev_bin != NULL)
501 dbg("udev binary is set to '%s'", udev_bin);
502 else
503 udev_bin = UDEV_BIN;
0028653c 504
e5a2989e
KS
505 FD_ZERO(&readfds);
506 FD_SET(ssock, &readfds);
507 FD_SET(pipefds[0], &readfds);
f27125f9 508 maxsockplus = ssock+1;
2f6cbd19 509 while (1) {
f27125f9 510 fd_set workreadfds = readfds;
511 retval = select(maxsockplus, &workreadfds, NULL, NULL, NULL);
e5a2989e 512
f27125f9 513 if (retval < 0) {
e5a2989e
KS
514 if (errno != EINTR)
515 dbg("error in select: %s", strerror(errno));
f27125f9 516 continue;
2f6cbd19 517 }
e5a2989e 518
f27125f9 519 if (FD_ISSET(ssock, &workreadfds))
520 handle_msg(ssock);
e5a2989e 521
f27125f9 522 if (FD_ISSET(pipefds[0], &workreadfds))
523 user_sighandler();
e5a2989e 524
f27125f9 525 if (children_waiting) {
2f6cbd19 526 children_waiting = 0;
f27125f9 527 reap_kids();
528 }
e5a2989e 529
f27125f9 530 if (run_msg_q) {
531 run_msg_q = 0;
532 msg_queue_manager();
533 }
e5a2989e 534
f27125f9 535 if (run_exec_q) {
f27125f9 536 /* this is tricky. exec_queue_manager() loops over exec_list, and
537 * calls running_with_devpath(), which loops over running_list. This gives
538 * O(N*M), which can get *nasty*. Clean up running_list before
539 * calling exec_queue_manager().
540 */
f27125f9 541 if (children_waiting) {
542 children_waiting = 0;
543 reap_kids();
2f6cbd19 544 }
f27125f9 545
546 run_exec_q = 0;
547 exec_queue_manager();
53921bfa 548 }
53921bfa
KS
549 }
550exit:
551 close(ssock);
7257cb18 552 logging_close();
53921bfa 553 exit(1);
7fafc032 554}