1/*****************************************************************************\ 2* _ _ _ _ ___ * 3* | || | ___ | |_ _ __ | | _ _ __ _ |_ ) * 4* | __ |/ _ \| _|| '_ \| || || |/ _` | / / * 5* |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| * 6* |_| |___/ * 7\*****************************************************************************/ 8 9#define _GNU_SOURCE 10 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 25#include "mem_utils.h" 26#include "hotplug2.h" 27#include "rules.h" 28#include "childlist.h" 29 30#define TERMCONDITION (persistent == 0 && \ 31 coldplug_p == FORK_FINISHED && \ 32 child == NULL && \ 33 highest_seqnum == get_kernel_seqnum()) 34 35event_seqnum_t highest_seqnum = 0; 36pid_t coldplug_p; 37int coldplug = 1; 38int persistent = 0; 39int max_child_c = 20; 40int dumb = 0; 41int terminate = 0; 42int child_c; 43struct hotplug2_child_t *child; 44int netlink_socket; 45 46char *modprobe_command = NULL; 47 48inline void free_hotplug2_event(struct hotplug2_event_t *event) { 49 int i; 50 51 for (i = 0; i < event->env_vars_c; i++) { 52 free(event->env_vars[i].key); 53 free(event->env_vars[i].value); 54 } 55 free(event->env_vars); 56 free(event->plain); 57 free(event); 58} 59 60inline int get_hotplug2_event_action(char *action) { 61 if (!strcmp(action, "add")) 62 return ACTION_ADD; 63 64 if (!strcmp(action, "remove")) 65 return ACTION_REMOVE; 66 67 return ACTION_UNKNOWN; 68} 69 70char *get_hotplug2_value_by_key(struct hotplug2_event_t *event, char *key) { 71 int i; 72 73 for (i = 0; i < event->env_vars_c; i++) { 74 if (!strcmp(event->env_vars[i].key, key)) 75 return event->env_vars[i].value; 76 } 77 78 return NULL; 79} 80 81inline int add_hotplug2_event_env(struct hotplug2_event_t *event, char *item) { 82 char *ptr, *tmp; 83 84 ptr = strchr(item, '='); 85 if (ptr == NULL) 86 return -1; 87 88 *ptr='\0'; 89 90 event->env_vars_c++; 91 event->env_vars = xrealloc(event->env_vars, sizeof(struct env_var_t) * event->env_vars_c); 92 event->env_vars[event->env_vars_c - 1].key = strdup(item); 93 event->env_vars[event->env_vars_c - 1].value = strdup(ptr + 1); 94 95 /* 96 * Variables not generated by kernel but demanded nonetheless... 97 */ 98 if (!strcmp(item, "DEVPATH")) { 99 event->env_vars_c++; 100 event->env_vars = xrealloc(event->env_vars, sizeof(struct env_var_t) * event->env_vars_c); 101 event->env_vars[event->env_vars_c - 1].key = strdup("DEVICENAME"); 102 tmp = strdup(ptr + 1); 103 event->env_vars[event->env_vars_c - 1].value = strdup(basename(tmp)); 104 free(tmp); 105 } 106 107 *ptr='='; 108 109 return 0; 110} 111 112inline struct hotplug2_event_t *dup_hotplug2_event(struct hotplug2_event_t *src) { 113 struct hotplug2_event_t *dest; 114 int i; 115 116 dest = xmalloc(sizeof(struct hotplug2_event_t)); 117 dest->action = src->action; 118 dest->env_vars_c = src->env_vars_c; 119 dest->env_vars = xmalloc(sizeof(struct env_var_t) * dest->env_vars_c); 120 dest->plain_s = src->plain_s; 121 dest->plain = xmalloc(dest->plain_s); 122 memcpy(dest->plain, src->plain, dest->plain_s); 123 124 for (i = 0; i < src->env_vars_c; i++) { 125 dest->env_vars[i].key = strdup(src->env_vars[i].key); 126 dest->env_vars[i].value = strdup(src->env_vars[i].value); 127 } 128 129 return dest; 130} 131 132inline struct hotplug2_event_t *get_hotplug2_event(char *event_str, int size) { 133 char *ptr; 134 struct hotplug2_event_t *event; 135 int skip; 136 137 ptr = strchr(event_str, '@'); 138 if (ptr == NULL) { 139 return NULL; 140 } 141 *ptr='\0'; 142 143 event = xmalloc(sizeof(struct hotplug2_event_t)); 144 event->action = get_hotplug2_event_action(event_str); 145 event->env_vars_c = 0; 146 event->env_vars = NULL; 147 event->plain_s = size; 148 event->plain = xmalloc(size); 149 memcpy(event->plain, event_str, size); 150 151 skip = ++ptr - event_str; 152 size -= skip; 153 154 while (size > 0) { 155 add_hotplug2_event_env(event, ptr); 156 skip = strlen(ptr); 157 ptr += skip + 1; 158 size -= skip + 1; 159 } 160 161 return event; 162} 163 164inline event_seqnum_t get_kernel_seqnum() { 165 FILE *fp; 166 167 char filename[64]; 168 char seqnum[64]; 169 170 strcpy(filename, sysfs_seqnum_path); 171 172 fp = fopen(filename, "r"); 173 if (fp == NULL) 174 return 0; 175 176 fread(seqnum, 1, 64, fp); 177 fclose(fp); 178 179 return strtoull(seqnum, NULL, 0); 180} 181 182inline int init_netlink_socket() { 183 int netlink_socket; 184 struct sockaddr_nl snl; 185 int buffersize = 16 * 1024 * 1024; 186 187 memset(&snl, 0x00, sizeof(struct sockaddr_nl)); 188 snl.nl_family = AF_NETLINK; 189 snl.nl_pid = getpid(); 190 snl.nl_groups = 1; 191 netlink_socket = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_KOBJECT_UEVENT); 192 if (netlink_socket == -1) { 193 ERROR("opening netlink","Failed socket: %s.", strerror(errno)); 194 return -1; 195 } 196 197 if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUFFORCE, &buffersize, sizeof(buffersize))) { 198 ERROR("opening netlink","Failed setsockopt: %s. (non-critical)", strerror(errno)); 199 200 /* Somewhat safe default. */ 201 buffersize = 106496; 202 203 if (setsockopt(netlink_socket, SOL_SOCKET, SO_RCVBUF, &buffersize, sizeof(buffersize))) { 204 ERROR("opening netlink","Failed setsockopt: %s. (critical)", strerror(errno)); 205 } 206 } 207 208 if (bind(netlink_socket, (struct sockaddr *) &snl, sizeof(struct sockaddr_nl))) { 209 ERROR("opening netlink","Failed bind: %s.", strerror(errno)); 210 close(netlink_socket); 211 return -1; 212 } 213 214 return netlink_socket; 215} 216 217int get_bool_opt(char *argv, char *name, int *value) { 218 int rv = -1; 219 220 if (!strncmp(argv, "--no-", 5)) { 221 rv = 0; 222 argv+=5; 223 } 224 225 if (!strncmp(argv, "--", 2)) { 226 rv = 1; 227 argv+=2; 228 } 229 230 if (rv == -1) 231 return -1; 232 233 if (!strcmp(argv, name)) { 234 *value = rv; 235 return 0; 236 } else { 237 return -1; 238 } 239} 240 241void cleanup(void) { 242 pid_t p; 243 244 close(netlink_socket); 245 246 signal(SIGUSR1, SIG_DFL); 247 signal(SIGINT, SIG_DFL); 248 signal(SIGCHLD, SIG_DFL); 249 250 INFO("cleanup", "Waiting for children."); 251 /* Wait for our possible children... */ 252 while ((p = wait(NULL)) != -1) 253 DBG("cleanup", "pid: %d.", p); 254 INFO("cleanup", "All children terminated."); 255} 256 257void sighandler(int sig) { 258 pid_t p; 259 260 switch (sig) { 261 /* 262 * SIGINT simply tells that yes, we caught the signal, and 263 * exits. 264 */ 265 case SIGINT: 266 INFO("sighandler", "Obtained SIGINT, quitting."); 267 cleanup(); 268 exit(0); 269 break; 270 271 /* 272 * SIGUSR1 is handled so that if we have turned off persistency 273 * and have processed all events, we quit. 274 */ 275 case SIGUSR1: 276 persistent = !persistent; 277 INFO("sighandler", "Changed persistency to: %s", persistent ? "yes" : "no"); 278 if (TERMCONDITION) { 279 INFO("sighandler", "No more events to be processed, quitting."); 280 cleanup(); 281 exit(0); 282 } 283 break; 284 285 /* 286 * SIGCHLD helps us to figure out when a child died and 287 * what kind of child it was. It may also invoke termination. 288 */ 289 case SIGCHLD: 290 while (1) { 291 p = waitpid (WAIT_ANY, NULL, WNOHANG); 292 if (p <= 0) 293 break; 294 295 DBG("sighandler", "caught pid: %d.", p); 296 if (p == coldplug_p) { 297 DBG("sighandler", "coldplug_p: %d.", coldplug_p); 298 coldplug_p = FORK_FINISHED; 299 } else { 300 child = remove_child_by_pid(child, p, NULL, &child_c); 301 } 302 303 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); 304 } 305 306 if (TERMCONDITION) { 307 INFO("sighandler", "No more events to be processed, quitting."); 308 cleanup(); 309 exit(0); 310 } 311 break; 312 } 313} 314 315#ifdef HAVE_RULES 316void perform_action(struct hotplug2_event_t *event, struct rules_t *rules) { 317 int i, rv; 318 319 for (i = 0; i < rules->rules_c; i++) { 320 rv = rule_execute(event, &rules->rules[i]); 321 if (rv == -1) 322 break; 323 } 324 325 free_hotplug2_event(event); 326} 327#endif 328 329void perform_dumb_action(struct hotplug2_event_t *event, char *modalias) { 330 free_hotplug2_event(event); 331 execl(modprobe_command, modprobe_command, "-q", modalias, NULL); 332} 333 334int get_modprobe_command() { 335 pid_t p; 336 int fds[2]; 337 char buf[18]; 338 FILE *fp; 339 340 pipe(fds); 341 342 p = fork(); 343 344 switch (p) { 345 case -1: 346 ERROR("modprobe_command","Unable to fork."); 347 return -1; 348 break; 349 350 case 0: 351 close(fds[0]); 352 close(2); 353 dup2(fds[1], 1); 354 355 execlp("/sbin/modprobe", "/sbin/modprobe", "--version", NULL); 356 exit(1); 357 break; 358 359 default: 360 close(fds[1]); 361 fp = fdopen(fds[0], "r"); 362 fread(buf, 1, 17, fp); 363 buf[17]='\0'; 364 365 /* 366 * module-init-tools can handle aliases. 367 * If we do not have a match, we use hotplug2-depwrap, 368 * even though our modprobe can do fnmatch aliases, 369 * which is the case of eg. busybox. 370 */ 371 if (!strcmp(buf, "module-init-tools")) { 372 modprobe_command = "/sbin/modprobe"; 373 } else { 374 modprobe_command = "/sbin/hotplug2-depwrap"; 375 } 376 fclose(fp); 377 waitpid(p, NULL, 0); 378 break; 379 } 380 return 0; 381} 382 383int main(int argc, char *argv[]) { 384 static char buffer[UEVENT_BUFFER_SIZE+512]; 385 struct hotplug2_event_t *tmpevent; 386 char *modalias, *seqnum; 387 event_seqnum_t cur_seqnum; 388 pid_t p; 389 int recv_errno; 390 int size; 391 int rv = 0; 392 int i; 393 char *coldplug_command = NULL; 394 sigset_t block_mask; 395 396 struct rules_t *rules = NULL; 397 struct stat statbuf; 398 void *filemap; 399 int rule_fd; 400 401 struct options_t bool_options[] = { 402 {"persistent", &persistent}, 403 {"coldplug", &coldplug}, 404 {"udevtrigger", &coldplug}, /* compatibility */ 405#ifdef HAVE_RULES 406 {"dumb", &dumb}, 407#endif 408 {NULL, NULL} 409 }; 410 411 for (argc--; argc > 0; argc--) { 412 argv++; 413 for (i = 0; bool_options[i].name != NULL; i++) { 414 if (!get_bool_opt(*argv, bool_options[i].name, bool_options[i].value)) { 415 break; 416 } else { 417 if (!strcmp(*argv, "--max-children")) { 418 argv++; 419 argc--; 420 if (argc <= 0) 421 break; 422 423 max_child_c = strtol(*argv, NULL, 0); 424 } else if (!strcmp(*argv, "--set-coldplug-cmd")) { 425 argv++; 426 argc--; 427 if (argc <= 0) 428 break; 429 430 coldplug_command = *argv; 431 } else if (!strcmp(*argv, "--set-modprobe-cmd")) { 432 argv++; 433 argc--; 434 if (argc <= 0) 435 break; 436 437 modprobe_command = *argv; 438 } 439 } 440 } 441 } 442 443#ifdef HAVE_RULES 444 if (!dumb) { 445 filemap = MAP_FAILED; 446 rule_fd = open(HOTPLUG2_RULE_PATH, O_RDONLY | O_NOATIME); 447 if (rule_fd == -1) { 448 dumb = 1; 449 ERROR("rules parse","Unable to open rules file: %s.", strerror(errno)); 450 goto end_rules; 451 } 452 453 if (fstat(rule_fd, &statbuf)) { 454 dumb = 1; 455 ERROR("rules parse","Unable to stat rules file: %s.", strerror(errno)); 456 goto end_rules; 457 } 458 459 filemap = mmap(0, statbuf.st_size, PROT_READ, MAP_SHARED, rule_fd, 0); 460 if (filemap == MAP_FAILED) { 461 dumb = 1; 462 ERROR("rules parse","Unable to mmap rules file: %s.", strerror(errno)); 463 goto end_rules; 464 } 465 466 rules = rules_from_config((char*)filemap); 467 if (rules == NULL) { 468 ERROR("rules parse","Unable to parse rules file."); 469 dumb = 1; 470 } 471 472end_rules: 473 if (filemap != MAP_FAILED) 474 munmap(filemap, statbuf.st_size); 475 if (rule_fd != -1) 476 close(rule_fd); 477 478 if (dumb == 1) 479 ERROR("rules parse","Parsing rules failed, switching to dumb mode."); 480 } else if (!modprobe_command) 481#else 482 if (dumb && !modprobe_command) 483#endif 484 { 485 if (get_modprobe_command()) { 486 ERROR("modprobe_command","Unable to autodetect modprobe command."); 487 goto exit; 488 } 489 DBG("modprobe_command", "Using modprobe: `%s'.", modprobe_command); 490 } 491 492 netlink_socket = init_netlink_socket(); 493 494 if (netlink_socket == -1) { 495 ERROR("netlink init","Unable to open netlink socket."); 496 goto exit; 497 } 498 499 child = NULL; 500 child_c = 0; 501 502 signal(SIGUSR1, sighandler); 503 signal(SIGINT, sighandler); 504 signal(SIGCHLD, sighandler); 505 506 if (coldplug) { 507 if (coldplug_command == NULL) 508 coldplug_command = UDEVTRIGGER_COMMAND; 509 coldplug_p = fork(); 510 switch (coldplug_p) { 511 case FORK_ERROR: 512 ERROR("coldplug","Coldplug fork failed: %s.", strerror(errno)); 513 perror("coldplug fork failed"); 514 goto exit; 515 break; 516 case 0: 517 execlp(coldplug_command, coldplug_command, NULL); 518 ERROR("coldplug","Coldplug exec ('%s') failed: %s.", coldplug_command, strerror(errno)); 519 goto exit; 520 break; 521 } 522 } else { 523 coldplug_p = FORK_FINISHED; 524 } 525 526 while (!terminate) { 527 size = recv(netlink_socket, &buffer, sizeof(buffer), 0); 528 recv_errno = errno; 529 530 tmpevent = get_hotplug2_event(buffer, size); 531 532 if (tmpevent == NULL) { 533 ERROR("reading events", "Malformed event read (missing action prefix)."); 534 continue; 535 } 536 537 modalias = get_hotplug2_value_by_key(tmpevent, "MODALIAS"); 538 seqnum = get_hotplug2_value_by_key(tmpevent, "SEQNUM"); 539 540 if (seqnum == NULL) { 541 free_hotplug2_event(tmpevent); 542 ERROR("reading events", "Malformed event read (missing SEQNUM)."); 543 continue; 544 } 545 546 cur_seqnum = strtoull(seqnum, NULL, 0); 547 if (cur_seqnum > highest_seqnum) 548 highest_seqnum = cur_seqnum; 549 550 if (tmpevent->action == ACTION_ADD && (!dumb || modalias != NULL)) { 551 /* 552 * We have more children than we want. Wait until SIGCHLD handler reduces 553 * their numbers. 554 */ 555 while (child_c >= max_child_c) { 556 usleep(HOTPLUG2_THROTTLE_INTERVAL); 557 } 558 559 sigemptyset(&block_mask); 560 sigaddset(&block_mask, SIGCHLD); 561 sigprocmask(SIG_BLOCK, &block_mask, 0); 562 p = fork(); 563 switch (p) { 564 case -1: 565 ERROR("event","fork failed: %s.", strerror(errno)); 566 break; 567 case 0: 568 sigprocmask(SIG_UNBLOCK, &block_mask, 0); 569 signal(SIGCHLD, SIG_DFL); 570 signal(SIGUSR1, SIG_DFL); 571#ifdef HAVE_RULES 572 if (!dumb) 573 perform_action(dup_hotplug2_event(tmpevent), rules); 574 else 575#endif 576 perform_dumb_action(dup_hotplug2_event(tmpevent), modalias); 577 exit(0); 578 break; 579 default: 580 DBG("spawn", "spawning: %d.", p); 581 child = add_child(child, p, cur_seqnum); 582 child_c++; 583 break; 584 } 585 sigprocmask(SIG_UNBLOCK, &block_mask, 0); 586 } 587 588 free_hotplug2_event(tmpevent); 589 } 590 591exit: 592 signal(SIGUSR1, SIG_DFL); 593 signal(SIGINT, SIG_DFL); 594 signal(SIGCHLD, SIG_DFL); 595 596#ifdef HAVE_RULES 597 if (!dumb) { 598 rules_free(rules); 599 free(rules); 600 } 601#endif 602 603 cleanup(); 604 605 return rv; 606} 607