hotplug2: patches from OpenWRT/svn
[tomato.git] / release / src / router / hotplug2 / hotplug2.c
blob362b1dd43d65454ea25f69bbaf3a94c46db7f09c
1 /*****************************************************************************\
2 * _ _ _ _ ___ *
3 * | || | ___ | |_ _ __ | | _ _ __ _ |_ ) *
4 * | __ |/ _ \| _|| '_ \| || || |/ _` | / / *
5 * |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| *
6 * |_| |___/ *
7 \*****************************************************************************/
9 #define _GNU_SOURCE
11 #include <fcntl.h>
12 #include <stdio.h>
13 #include <unistd.h>
14 #include <stdlib.h>
15 #include <errno.h>
16 #include <sys/socket.h>
17 #include <sys/types.h>
18 #include <sys/un.h>
19 #include <sys/wait.h>
20 #include <sys/stat.h>
21 #include <sys/mman.h>
22 #include <linux/types.h>
23 #include <linux/netlink.h>
24 #include <poll.h>
26 #include "mem_utils.h"
27 #include "filemap_utils.h"
28 #include "hotplug2.h"
29 #include "hotplug2_utils.h"
30 #include "rules.h"
31 #include "childlist.h"
33 #define TERMCONDITION (persistent == 0 && \
34 coldplug_p == FORK_FINISHED && \
35 child == NULL && \
36 highest_seqnum == get_kernel_seqnum())
39 * These variables are accessed from throughout the code.
41 * TODO: Move this into a hotplug2_t-like variable.
43 event_seqnum_t highest_seqnum = 0;
44 pid_t coldplug_p;
45 int coldplug = 1;
46 int persistent = 0;
47 int override = 0;
48 int max_child_c = 20;
49 int dumb = 0;
50 int terminate = 0;
51 int child_c;
52 struct hotplug2_child_t *child;
53 int netlink_socket;
55 char *modprobe_command = NULL;
57 /**
58 * Release all memory associated with an uevent read from kernel. The given
59 * pointer is no longer valid, as it gets freed as well.
61 * @1 The event that is to be freed.
63 * Returns: void
65 inline void free_hotplug2_event(struct hotplug2_event_t *event) {
66 int i;
68 for (i = 0; i < event->env_vars_c; i++) {
69 free(event->env_vars[i].key);
70 free(event->env_vars[i].value);
72 free(event->env_vars);
73 free(event->plain);
74 free(event);
77 /**
78 * A trivial function determining the action that the uevent.
80 * @1 String containing the action name (null-terminated).
82 * Returns: Macro of the given action
84 inline int get_hotplug2_event_action(char *action) {
85 if (!strcmp(action, "add"))
86 return ACTION_ADD;
88 if (!strcmp(action, "remove"))
89 return ACTION_REMOVE;
91 return ACTION_UNKNOWN;
94 /**
95 * Looks up a value according to the given key.
97 * @1 A hotplug event structure
98 * @2 Key for lookup
100 * Returns: The value of the key or NULL if no such key found
102 char *get_hotplug2_value_by_key(struct hotplug2_event_t *event, char *key) {
103 int i;
105 for (i = 0; i < event->env_vars_c; i++) {
106 if (!strcmp(event->env_vars[i].key, key))
107 return event->env_vars[i].value;
110 return NULL;
114 * Appends a key-value pair described by the second argument to the
115 * hotplug event.
117 * @1 A hotplug event structure
118 * @1 An item in format "key=value" to be appended
120 * Returns: 0 if success, -1 if the string is malformed
122 int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) {
123 char *ptr, *tmp;
125 ptr = strchr(item, '=');
126 if (ptr == NULL)
127 return -1;
129 *ptr='\0';
131 event->env_vars_c++;
132 event->env_vars = xrealloc(event->env_vars, sizeof(struct env_var_t) * event->env_vars_c);
133 event->env_vars[event->env_vars_c - 1].key = strdup(item);
134 event->env_vars[event->env_vars_c - 1].value = strdup(ptr + 1);
137 * Variables not generated by kernel but demanded nonetheless...
139 * TODO: Split this to a different function
141 if (!strcmp(item, "DEVPATH")) {
142 event->env_vars_c++;
143 event->env_vars = xrealloc(event->env_vars, sizeof(struct env_var_t) * event->env_vars_c);
144 event->env_vars[event->env_vars_c - 1].key = strdup("DEVICENAME");
145 tmp = strdup(ptr + 1);
146 event->env_vars[event->env_vars_c - 1].value = strdup(basename(tmp));
147 free(tmp);
150 *ptr='=';
152 return 0;
156 * Duplicates all allocated memory of a source hotplug event
157 * and returns a new hotplug event, an identical copy of the
158 * source event.
160 * @1 Source hotplug event structure
162 * Returns: A copy of the source event structure
164 inline struct hotplug2_event_t *dup_hotplug2_event(struct hotplug2_event_t *src) {
165 struct hotplug2_event_t *dest;
166 int i;
168 dest = xmalloc(sizeof(struct hotplug2_event_t));
169 dest->action = src->action;
170 dest->env_vars_c = src->env_vars_c;
171 dest->env_vars = xmalloc(sizeof(struct env_var_t) * dest->env_vars_c);
172 dest->plain_s = src->plain_s;
173 dest->plain = xmalloc(dest->plain_s);
174 memcpy(dest->plain, src->plain, dest->plain_s);
176 for (i = 0; i < src->env_vars_c; i++) {
177 dest->env_vars[i].key = strdup(src->env_vars[i].key);
178 dest->env_vars[i].value = strdup(src->env_vars[i].value);
181 return dest;
185 * Parses a string into a hotplug event structurs.
187 * @1 The event string (not null terminated)
188 * @2 The size of the event string
190 * Returns: A new event structure
192 inline struct hotplug2_event_t *get_hotplug2_event(char *event_str, int size) {
193 char *ptr;
194 struct hotplug2_event_t *event;
195 int skip;
197 ptr = strchr(event_str, '@');
198 if (ptr == NULL) {
199 return NULL;
201 *ptr='\0';
203 event = xmalloc(sizeof(struct hotplug2_event_t));
204 event->action = get_hotplug2_event_action(event_str);
205 event->env_vars_c = 0;
206 event->env_vars = NULL;
207 event->plain_s = size;
208 event->plain = xmalloc(size);
209 memcpy(event->plain, event_str, size);
211 skip = ++ptr - event_str;
212 size -= skip;
214 while (size > 0) {
215 add_hotplug2_event_env(event, ptr);
216 skip = strlen(ptr);
217 ptr += skip + 1;
218 size -= skip + 1;
221 return event;
225 * Evaluates an argument into a true/false value.
227 * @1 argument
228 * @2 argument flag
229 * @3 pointer to output value
231 * Returns: 0 if success, -1 otherwise
233 int get_bool_opt(char *argv, char *name, int *value) {
234 int rv = -1;
236 if (!strncmp(argv, "--no-", 5)) {
237 rv = 0;
238 argv+=5;
241 if (!strncmp(argv, "--", 2)) {
242 rv = 1;
243 argv+=2;
246 if (rv == -1)
247 return -1;
249 if (!strcmp(argv, name)) {
250 *value = rv;
251 return 0;
252 } else {
253 return -1;
258 * Performs a cleanup; closes uevent socket, resets signal
259 * handlers, waits for all the children.
261 * Returns: void
263 void cleanup() {
264 pid_t p;
266 close(netlink_socket);
268 signal(SIGUSR1, SIG_DFL);
269 signal(SIGINT, SIG_DFL);
270 signal(SIGCHLD, SIG_DFL);
272 INFO("cleanup", "Waiting for children.");
273 /* Wait for our possible children... */
274 while ((p = wait(NULL)) != -1)
275 DBG("cleanup", "pid: %d.", p);
276 INFO("cleanup", "All children terminated.");
280 * Handles all signals.
282 * @1 Signal identifier
284 * Returns: void
286 void sighandler(int sig) {
287 pid_t p;
289 switch (sig) {
291 * SIGINT simply tells that yes, we caught the signal, and
292 * exits.
294 case SIGINT:
295 INFO("sighandler", "Obtained SIGINT, quitting.");
296 cleanup();
297 exit(0);
298 break;
301 * SIGUSR1 is handled so that if we have turned off persistency
302 * and have processed all events, we quit.
304 case SIGUSR1:
305 persistent = !persistent;
306 INFO("sighandler", "Changed persistency to: %s", persistent ? "yes" : "no");
307 if (TERMCONDITION) {
308 INFO("sighandler", "No more events to be processed, quitting.");
309 cleanup();
310 exit(0);
312 break;
315 * SIGCHLD helps us to figure out when a child died and
316 * what kind of child it was. It may also invoke termination.
318 case SIGCHLD:
319 while (1) {
320 p = waitpid (WAIT_ANY, NULL, WNOHANG);
321 if (p <= 0)
322 break;
324 DBG("sighandler", "caught pid: %d.", p);
325 if (p == coldplug_p) {
326 DBG("sighandler", "coldplug_p: %d.", coldplug_p);
327 coldplug_p = FORK_FINISHED;
328 } else {
329 child = remove_child_by_pid(child, p, NULL, &child_c);
332 DBG("sighandler", "child_c: %d, child: %p, highest_seqnum: %lld, cur_seqnum: %lld, coldplug_p: %d.\n", child_c, child, highest_seqnum, get_kernel_seqnum(), coldplug_p);
335 if (TERMCONDITION) {
336 INFO("sighandler", "No more events to be processed, quitting.");
337 cleanup();
338 exit(0);
340 break;
344 #ifdef HAVE_RULES
346 * Execute all rules for this particular event.
348 * @1 Hotplug event structure
349 * @2 Rules structure, containing array of rules
351 * Returns: void
353 void perform_action(struct hotplug2_event_t *event, struct rules_t *rules) {
354 int i, rv;
356 for (i = 0; i < rules->rules_c; i++) {
357 rv = rule_execute(event, &rules->rules[i]);
358 if (rv == -1)
359 break;
362 free_hotplug2_event(event);
366 * Iterates through all rules, and performs an AND between all flags that
367 * would apply during execution (ie. all rules that have conditions matching
368 * the hotplug event).
370 * @1 Hotplug event structure
371 * @2 Rules structure, containing array of rules
373 * Returns: Flags that apply to all matching rules
375 int flags_eval(struct hotplug2_event_t *event, struct rules_t *rules) {
376 int flags = FLAG_ALL;
377 int match = 0;
378 int i, j;
380 for (i = 0; i < rules->rules_c; i++) {
381 match = 1;
383 for (j = 0; j < rules->rules[i].conditions_c; j++) {
384 if (rule_condition_eval(event, &rules->rules[i].conditions[j]) != EVAL_MATCH) {
385 match = 0;
386 break;
391 * Logical AND between flags we've got already and
392 * those we're adding.
394 if (match) {
395 rule_flags(&rules->rules[i]);
396 flags &= rules->rules[i].flags;
401 * A little trick; if no rule matched, we return FLAG_ALL
402 * and have it skipped completely.
405 return flags;
407 #else
408 #define perform_action(event, rules)
409 #endif
412 * Blindly modprobe the modalias, nothing more.
414 * @1 Hotplug event structure
415 * @2 Modalias to be loaded
417 * Returns: void
419 void perform_dumb_action(struct hotplug2_event_t *event, char *modalias) {
420 free_hotplug2_event(event);
421 execl(modprobe_command, modprobe_command, "-q", modalias, NULL);
425 * Attempt to figure out whether our modprobe command can handle modalias.
426 * If not, use our own wrapper.
428 * Returns: 0 if success, -1 otherwise
430 int get_modprobe_command() {
431 pid_t p;
432 int fds[2];
433 char buf[18];
434 FILE *fp;
436 pipe(fds);
438 p = fork();
440 switch (p) {
441 case -1:
442 ERROR("modprobe_command","Unable to fork.");
443 return -1;
444 break;
446 case 0:
447 close(fds[0]);
448 close(2);
449 dup2(fds[1], 1);
451 execlp("/sbin/modprobe", "/sbin/modprobe", "--version", NULL);
452 exit(1);
453 break;
455 default:
456 close(fds[1]);
457 fp = fdopen(fds[0], "r");
458 fread(buf, 1, 17, fp);
459 buf[17]='\0';
462 * module-init-tools can handle aliases.
463 * If we do not have a match, we use hotplug2-depwrap,
464 * even though our modprobe can do fnmatch aliases,
465 * which is the case of eg. busybox.
467 if (!strcmp(buf, "module-init-tools")) {
468 modprobe_command = "/sbin/modprobe";
469 } else {
470 modprobe_command = "/sbin/hotplug2-depwrap";
472 fclose(fp);
473 waitpid(p, NULL, 0);
474 break;
476 return 0;
479 int main(int argc, char *argv[]) {
481 * TODO, cleanup
483 static char buffer[UEVENT_BUFFER_SIZE+512];
484 struct hotplug2_event_t *tmpevent;
485 char *modalias, *seqnum;
486 event_seqnum_t cur_seqnum;
487 pid_t p;
488 int recv_errno;
489 int size;
490 int rv = 0;
491 int i;
492 unsigned int flags;
493 char *coldplug_command = NULL;
494 char *rules_file = HOTPLUG2_RULE_PATH;
495 sigset_t block_mask;
496 struct pollfd msg_poll;
498 struct hotplug2_event_t *backlog = NULL;
499 struct hotplug2_event_t *backlog_tail = NULL;
500 int n_backlog = 0;
502 struct rules_t *rules = NULL;
503 struct filemap_t filemap;
505 struct options_t bool_options[] = {
506 {"persistent", &persistent},
507 {"coldplug", &coldplug},
508 {"udevtrigger", &coldplug}, /* compatibility */
509 {"override", &override},
510 #ifdef HAVE_RULES
511 {"dumb", &dumb},
512 #endif
513 {NULL, NULL}
517 * We parse all the options...
519 for (argc--; argc > 0; argc--) {
520 argv++;
522 * TODO, cleanup
524 for (i = 0; bool_options[i].name != NULL; i++) {
525 if (!get_bool_opt(*argv, bool_options[i].name, bool_options[i].value)) {
527 * Bool options are --option or --no-options. If we handled
528 * it, quit iterating.
530 break;
531 } else {
532 if (!strcmp(*argv, "--max-children")) {
533 argv++;
534 argc--;
535 if (argc <= 0)
536 break;
538 max_child_c = strtol(*argv, NULL, 0);
539 } else if (!strcmp(*argv, "--set-coldplug-cmd")) {
540 argv++;
541 argc--;
542 if (argc <= 0)
543 break;
545 coldplug_command = *argv;
546 } else if (!strcmp(*argv, "--set-modprobe-cmd")) {
547 argv++;
548 argc--;
549 if (argc <= 0)
550 break;
552 modprobe_command = *argv;
553 } else if (!strcmp(*argv, "--set-rules-file")) {
554 argv++;
555 argc--;
556 if (argc <= 0)
557 break;
559 rules_file = *argv;
565 #ifndef HAVE_RULES
567 * We don't use rules, so we use dumb mode only.
569 dumb = 1;
570 #else
572 * We're not in dumb mode, parse the rules. If we fail,
573 * faillback to dumb mode.
575 if (!dumb) {
576 if (map_file(rules_file, &filemap)) {
577 ERROR("rules parse","Unable to open/mmap rules file.");
578 dumb = 1;
579 goto end_rules;
582 rules = rules_from_config((char*)(filemap.map), NULL);
583 if (rules == NULL) {
584 ERROR("rules parse","Unable to parse rules file.");
585 dumb = 1;
588 unmap_file(&filemap);
590 end_rules:
591 if (dumb == 1)
592 ERROR("rules parse","Parsing rules failed, switching to dumb mode.");
593 } else
594 #endif
596 * No modprobe command specified, let's autodetect it.
598 if (!modprobe_command)
600 if (get_modprobe_command()) {
601 ERROR("modprobe_command","Unable to autodetect modprobe command.");
602 goto exit;
604 DBG("modprobe_command", "Using modprobe: `%s'.", modprobe_command);
608 * Open netlink socket to read the uevents
610 netlink_socket = init_netlink_socket(NETLINK_BIND);
611 msg_poll.fd = netlink_socket;
612 msg_poll.events = POLLIN;
614 if (netlink_socket == -1) {
615 ERROR("netlink init","Unable to open netlink socket.");
616 goto exit;
619 child = NULL;
620 child_c = 0;
622 signal(SIGUSR1, sighandler);
623 signal(SIGINT, sighandler);
624 signal(SIGCHLD, sighandler);
627 * If we desire coldplugging, we initiate it right now.
629 if (coldplug) {
630 if (coldplug_command == NULL)
631 coldplug_command = UDEVTRIGGER_COMMAND;
632 coldplug_p = fork();
633 switch (coldplug_p) {
634 case FORK_ERROR:
635 ERROR("coldplug","Coldplug fork failed: %s.", strerror(errno));
636 perror("coldplug fork failed");
637 goto exit;
638 break;
639 case 0:
640 execlp(coldplug_command, coldplug_command, NULL);
641 ERROR("coldplug","Coldplug exec ('%s') failed: %s.", coldplug_command, strerror(errno));
642 goto exit;
643 break;
645 } else {
646 coldplug_p = FORK_FINISHED;
650 * Main loop reading uevents
652 while (!terminate) {
653 if ((n_backlog > 0) && (child_c < max_child_c)) {
654 /* dequeue backlog message */
655 tmpevent = backlog;
656 backlog = backlog->next;
657 n_backlog--;
658 if (backlog_tail == tmpevent)
659 backlog_tail = NULL;
660 } else {
662 * Read the uevent packet
664 if (n_backlog >= HOTPLUG2_MSG_BACKLOG) {
665 usleep(HOTPLUG2_THROTTLE_INTERVAL * 1000);
666 continue;
669 if ((n_backlog > 0) && (child_c >= max_child_c)) {
670 int fds;
671 msg_poll.revents = 0;
672 fds = poll(&msg_poll, 1, HOTPLUG2_THROTTLE_INTERVAL);
673 if (fds < 0) {
674 continue;
676 if (fds == 0)
677 continue;
679 size = recv(netlink_socket, &buffer, sizeof(buffer), 0);
680 recv_errno = errno;
681 if (size < 0)
682 continue;
685 * Parse the event into an event structure
687 tmpevent = get_hotplug2_event(buffer, size);
689 if (tmpevent == NULL) {
690 ERROR("reading events", "Malformed event read (missing action prefix).");
691 continue;
696 * Look up two important items of the event
698 modalias = get_hotplug2_value_by_key(tmpevent, "MODALIAS");
699 seqnum = get_hotplug2_value_by_key(tmpevent, "SEQNUM");
702 * Seqnum is necessary not to end up in a race with the kernel.
704 if (seqnum == NULL) {
705 free_hotplug2_event(tmpevent);
706 ERROR("reading events", "Malformed event read (missing SEQNUM).");
707 continue;
711 * Maintain seqnum continuity
713 cur_seqnum = strtoull(seqnum, NULL, 0);
714 if (cur_seqnum > highest_seqnum)
715 highest_seqnum = cur_seqnum;
718 * If we are in smart mode, we'll always pass. If we're in dumb mode,
719 * we only pass events that have 'add' action and have modalias set.
721 if ((dumb && tmpevent->action == ACTION_ADD && modalias != NULL) || (!dumb)) {
723 * Pre-evaluation of the flags
725 if (!dumb && override) {
726 flags = flags_eval(tmpevent, rules);
728 DBG("flags", "flag returned: %8x", flags);
730 if (flags == FLAG_ALL)
731 continue;
732 } else {
733 flags = FLAG_UNSET;
737 * We have more children than we want. Wait until SIGCHLD handler reduces
738 * their numbers.
740 * Unless, of course, we've specified otherwise and no rules that match
741 * need throttling.
743 if (!(flags & FLAG_NOTHROTTLE) && (child_c >= max_child_c)) {
744 /* log the packet and process it later */
745 if (backlog_tail)
746 backlog_tail->next = tmpevent;
747 else
748 backlog = tmpevent;
749 tmpevent->next = NULL;
750 backlog_tail = tmpevent;
751 n_backlog++;
752 continue;
755 sigemptyset(&block_mask);
756 sigaddset(&block_mask, SIGCHLD);
757 sigprocmask(SIG_BLOCK, &block_mask, 0);
758 p = fork();
759 switch (p) {
760 case -1:
761 ERROR("event", "fork failed: %s.", strerror(errno));
762 break;
763 case 0:
765 * TODO: We do not have to dup here, or do we?
767 sigprocmask(SIG_UNBLOCK, &block_mask, 0);
768 signal(SIGCHLD, SIG_DFL);
769 signal(SIGUSR1, SIG_DFL);
770 if (!dumb)
771 perform_action(dup_hotplug2_event(tmpevent), rules);
772 else
773 perform_dumb_action(dup_hotplug2_event(tmpevent), modalias);
774 exit(0);
775 break;
776 default:
777 DBG("spawn", "spawning: %d.", p);
778 child = add_child(child, p, cur_seqnum);
779 child_c++;
780 break;
782 sigprocmask(SIG_UNBLOCK, &block_mask, 0);
785 free_hotplug2_event(tmpevent);
788 exit:
789 signal(SIGUSR1, SIG_DFL);
790 signal(SIGINT, SIG_DFL);
791 signal(SIGCHLD, SIG_DFL);
793 if (!dumb) {
794 rules_free(rules);
795 free(rules);
798 cleanup();
800 return rv;