1 /*- 2 * Copyright (c) 2002-2010 M. Warner Losh. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 * 26 * my_system is a variation on lib/libc/stdlib/system.c: 27 * 28 * Copyright (c) 1988, 1993 29 * The Regents of the University of California. All rights reserved. 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions 33 * are met: 34 * 1. Redistributions of source code must retain the above copyright 35 * notice, this list of conditions and the following disclaimer. 36 * 2. Redistributions in binary form must reproduce the above copyright 37 * notice, this list of conditions and the following disclaimer in the 38 * documentation and/or other materials provided with the distribution. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53 * SUCH DAMAGE. 54 */ 55 56 /* 57 * DEVD control daemon. 58 */ 59 60 // TODO list: 61 // o devd.conf and devd man pages need a lot of help: 62 // - devd needs to document the unix domain socket 63 // - devd.conf needs more details on the supported statements. 64 65 #include <sys/cdefs.h> 66 __FBSDID("$FreeBSD$"); 67 68 #include <sys/param.h> 69 #include <sys/socket.h> 70 #include <sys/stat.h> 71 #include <sys/sysctl.h> 72 #include <sys/types.h> 73 #include <sys/wait.h> 74 #include <sys/un.h> 75 76 #include <ctype.h> 77 #include <dirent.h> 78 #include <errno.h> 79 #include <err.h> 80 #include <fcntl.h> 81 #include <libutil.h> 82 #include <paths.h> 83 #include <poll.h> 84 #include <regex.h> 85 #include <signal.h> 86 #include <stdlib.h> 87 #include <stdio.h> 88 #include <string.h> 89 #include <unistd.h> 90 91 #include <algorithm> 92 #include <map> 93 #include <string> 94 #include <list> 95 #include <vector> 96 97 #include "devd.h" /* C compatible definitions */ 98 #include "devd.hh" /* C++ class definitions */ 99 100 #define PIPE "/var/run/devd.pipe" 101 #define CF "/etc/devd.conf" 102 #define SYSCTL "hw.bus.devctl_disable" 103 104 using namespace std; 105 106 extern FILE *yyin; 107 extern int lineno; 108 109 static const char notify = '!'; 110 static const char nomatch = '?'; 111 static const char attach = '+'; 112 static const char detach = '-'; 113 114 static struct pidfh *pfh; 115 116 int Dflag; 117 int dflag; 118 int nflag; 119 int romeo_must_die = 0; 120 121 static const char *configfile = CF; 122 123 static void event_loop(void); 124 static void usage(void); 125 126 template <class T> void 127 delete_and_clear(vector<T *> &v) 128 { 129 typename vector<T *>::const_iterator i; 130 131 for (i = v.begin(); i != v.end(); ++i) 132 delete *i; 133 v.clear(); 134 } 135 136 config cfg; 137 138 event_proc::event_proc() : _prio(-1) 139 { 140 _epsvec.reserve(4); 141 } 142 143 event_proc::~event_proc() 144 { 145 delete_and_clear(_epsvec); 146 } 147 148 void 149 event_proc::add(eps *eps) 150 { 151 _epsvec.push_back(eps); 152 } 153 154 bool 155 event_proc::matches(config &c) const 156 { 157 vector<eps *>::const_iterator i; 158 159 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 160 if (!(*i)->do_match(c)) 161 return (false); 162 return (true); 163 } 164 165 bool 166 event_proc::run(config &c) const 167 { 168 vector<eps *>::const_iterator i; 169 170 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 171 if (!(*i)->do_action(c)) 172 return (false); 173 return (true); 174 } 175 176 action::action(const char *cmd) 177 : _cmd(cmd) 178 { 179 // nothing 180 } 181 182 action::~action() 183 { 184 // nothing 185 } 186 187 static int 188 my_system(const char *command) 189 { 190 pid_t pid, savedpid; 191 int pstat; 192 struct sigaction ign, intact, quitact; 193 sigset_t newsigblock, oldsigblock; 194 195 if (!command) /* just checking... */ 196 return(1); 197 198 /* 199 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 200 * existing signal dispositions. 201 */ 202 ign.sa_handler = SIG_IGN; 203 ::sigemptyset(&ign.sa_mask); 204 ign.sa_flags = 0; 205 ::sigaction(SIGINT, &ign, &intact); 206 ::sigaction(SIGQUIT, &ign, &quitact); 207 ::sigemptyset(&newsigblock); 208 ::sigaddset(&newsigblock, SIGCHLD); 209 ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 210 switch (pid = ::fork()) { 211 case -1: /* error */ 212 break; 213 case 0: /* child */ 214 /* 215 * Restore original signal dispositions and exec the command. 216 */ 217 ::sigaction(SIGINT, &intact, NULL); 218 ::sigaction(SIGQUIT, &quitact, NULL); 219 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 220 /* 221 * Close the PID file, and all other open descriptors. 222 * Inherit std{in,out,err} only. 223 */ 224 cfg.close_pidfile(); 225 ::closefrom(3); 226 ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 227 ::_exit(127); 228 default: /* parent */ 229 savedpid = pid; 230 do { 231 pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 232 } while (pid == -1 && errno == EINTR); 233 break; 234 } 235 ::sigaction(SIGINT, &intact, NULL); 236 ::sigaction(SIGQUIT, &quitact, NULL); 237 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 238 return (pid == -1 ? -1 : pstat); 239 } 240 241 bool 242 action::do_action(config &c) 243 { 244 string s = c.expand_string(_cmd.c_str()); 245 if (Dflag) 246 fprintf(stderr, "Executing '%s'\n", s.c_str()); 247 my_system(s.c_str()); 248 return (true); 249 } 250 251 match::match(config &c, const char *var, const char *re) : 252 _inv(re[0] == '!'), 253 _var(var), 254 _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 255 { 256 regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 257 } 258 259 match::~match() 260 { 261 regfree(&_regex); 262 } 263 264 bool 265 match::do_match(config &c) 266 { 267 const string &value = c.get_variable(_var); 268 bool retval; 269 270 if (Dflag) 271 fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 272 _var.c_str(), value.c_str(), _re.c_str(), _inv); 273 274 retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 275 if (_inv == 1) 276 retval = (retval == 0) ? 1 : 0; 277 278 return retval; 279 } 280 281 #include <sys/sockio.h> 282 #include <net/if.h> 283 #include <net/if_media.h> 284 285 media::media(config &, const char *var, const char *type) 286 : _var(var), _type(-1) 287 { 288 static struct ifmedia_description media_types[] = { 289 { IFM_ETHER, "Ethernet" }, 290 { IFM_TOKEN, "Tokenring" }, 291 { IFM_FDDI, "FDDI" }, 292 { IFM_IEEE80211, "802.11" }, 293 { IFM_ATM, "ATM" }, 294 { -1, "unknown" }, 295 { 0, NULL }, 296 }; 297 for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 298 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 299 _type = media_types[i].ifmt_word; 300 break; 301 } 302 } 303 304 media::~media() 305 { 306 } 307 308 bool 309 media::do_match(config &c) 310 { 311 string value; 312 struct ifmediareq ifmr; 313 bool retval; 314 int s; 315 316 // Since we can be called from both a device attach/detach 317 // context where device-name is defined and what we want, 318 // as well as from a link status context, where subsystem is 319 // the name of interest, first try device-name and fall back 320 // to subsystem if none exists. 321 value = c.get_variable("device-name"); 322 if (value.length() == 0) 323 value = c.get_variable("subsystem"); 324 if (Dflag) 325 fprintf(stderr, "Testing media type of %s against 0x%x\n", 326 value.c_str(), _type); 327 328 retval = false; 329 330 s = socket(PF_INET, SOCK_DGRAM, 0); 331 if (s >= 0) { 332 memset(&ifmr, 0, sizeof(ifmr)); 333 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 334 335 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 336 ifmr.ifm_status & IFM_AVALID) { 337 if (Dflag) 338 fprintf(stderr, "%s has media type 0x%x\n", 339 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 340 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 341 } else if (_type == -1) { 342 if (Dflag) 343 fprintf(stderr, "%s has unknown media type\n", 344 value.c_str()); 345 retval = true; 346 } 347 close(s); 348 } 349 350 return retval; 351 } 352 353 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 354 const string var_list::nothing = ""; 355 356 const string & 357 var_list::get_variable(const string &var) const 358 { 359 map<string, string>::const_iterator i; 360 361 i = _vars.find(var); 362 if (i == _vars.end()) 363 return (var_list::bogus); 364 return (i->second); 365 } 366 367 bool 368 var_list::is_set(const string &var) const 369 { 370 return (_vars.find(var) != _vars.end()); 371 } 372 373 void 374 var_list::set_variable(const string &var, const string &val) 375 { 376 if (Dflag) 377 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 378 _vars[var] = val; 379 } 380 381 void 382 config::reset(void) 383 { 384 _dir_list.clear(); 385 delete_and_clear(_var_list_table); 386 delete_and_clear(_attach_list); 387 delete_and_clear(_detach_list); 388 delete_and_clear(_nomatch_list); 389 delete_and_clear(_notify_list); 390 } 391 392 void 393 config::parse_one_file(const char *fn) 394 { 395 if (Dflag) 396 fprintf(stderr, "Parsing %s\n", fn); 397 yyin = fopen(fn, "r"); 398 if (yyin == NULL) 399 err(1, "Cannot open config file %s", fn); 400 lineno = 1; 401 if (yyparse() != 0) 402 errx(1, "Cannot parse %s at line %d", fn, lineno); 403 fclose(yyin); 404 } 405 406 void 407 config::parse_files_in_dir(const char *dirname) 408 { 409 DIR *dirp; 410 struct dirent *dp; 411 char path[PATH_MAX]; 412 413 if (Dflag) 414 fprintf(stderr, "Parsing files in %s\n", dirname); 415 dirp = opendir(dirname); 416 if (dirp == NULL) 417 return; 418 readdir(dirp); /* Skip . */ 419 readdir(dirp); /* Skip .. */ 420 while ((dp = readdir(dirp)) != NULL) { 421 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 422 snprintf(path, sizeof(path), "%s/%s", 423 dirname, dp->d_name); 424 parse_one_file(path); 425 } 426 } 427 closedir(dirp); 428 } 429 430 class epv_greater { 431 public: 432 int operator()(event_proc *const&l1, event_proc *const&l2) const 433 { 434 return (l1->get_priority() > l2->get_priority()); 435 } 436 }; 437 438 void 439 config::sort_vector(vector<event_proc *> &v) 440 { 441 stable_sort(v.begin(), v.end(), epv_greater()); 442 } 443 444 void 445 config::parse(void) 446 { 447 vector<string>::const_iterator i; 448 449 parse_one_file(configfile); 450 for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 451 parse_files_in_dir((*i).c_str()); 452 sort_vector(_attach_list); 453 sort_vector(_detach_list); 454 sort_vector(_nomatch_list); 455 sort_vector(_notify_list); 456 } 457 458 void 459 config::open_pidfile() 460 { 461 pid_t otherpid; 462 463 if (_pidfile == "") 464 return; 465 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 466 if (pfh == NULL) { 467 if (errno == EEXIST) 468 errx(1, "devd already running, pid: %d", (int)otherpid); 469 warn("cannot open pid file"); 470 } 471 } 472 473 void 474 config::write_pidfile() 475 { 476 477 pidfile_write(pfh); 478 } 479 480 void 481 config::close_pidfile() 482 { 483 484 pidfile_close(pfh); 485 } 486 487 void 488 config::remove_pidfile() 489 { 490 491 pidfile_remove(pfh); 492 } 493 494 void 495 config::add_attach(int prio, event_proc *p) 496 { 497 p->set_priority(prio); 498 _attach_list.push_back(p); 499 } 500 501 void 502 config::add_detach(int prio, event_proc *p) 503 { 504 p->set_priority(prio); 505 _detach_list.push_back(p); 506 } 507 508 void 509 config::add_directory(const char *dir) 510 { 511 _dir_list.push_back(string(dir)); 512 } 513 514 void 515 config::add_nomatch(int prio, event_proc *p) 516 { 517 p->set_priority(prio); 518 _nomatch_list.push_back(p); 519 } 520 521 void 522 config::add_notify(int prio, event_proc *p) 523 { 524 p->set_priority(prio); 525 _notify_list.push_back(p); 526 } 527 528 void 529 config::set_pidfile(const char *fn) 530 { 531 _pidfile = string(fn); 532 } 533 534 void 535 config::push_var_table() 536 { 537 var_list *vl; 538 539 vl = new var_list(); 540 _var_list_table.push_back(vl); 541 if (Dflag) 542 fprintf(stderr, "Pushing table\n"); 543 } 544 545 void 546 config::pop_var_table() 547 { 548 delete _var_list_table.back(); 549 _var_list_table.pop_back(); 550 if (Dflag) 551 fprintf(stderr, "Popping table\n"); 552 } 553 554 void 555 config::set_variable(const char *var, const char *val) 556 { 557 _var_list_table.back()->set_variable(var, val); 558 } 559 560 const string & 561 config::get_variable(const string &var) 562 { 563 vector<var_list *>::reverse_iterator i; 564 565 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 566 if ((*i)->is_set(var)) 567 return ((*i)->get_variable(var)); 568 } 569 return (var_list::nothing); 570 } 571 572 bool 573 config::is_id_char(char ch) const 574 { 575 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 576 ch == '-')); 577 } 578 579 void 580 config::expand_one(const char *&src, string &dst) 581 { 582 int count; 583 string buffer; 584 585 src++; 586 // $$ -> $ 587 if (*src == '$') { 588 dst.append(src++, 1); 589 return; 590 } 591 592 // $(foo) -> $(foo) 593 // Not sure if I want to support this or not, so for now we just pass 594 // it through. 595 if (*src == '(') { 596 dst.append("$"); 597 count = 1; 598 /* If the string ends before ) is matched , return. */ 599 while (count > 0 && *src) { 600 if (*src == ')') 601 count--; 602 else if (*src == '(') 603 count++; 604 dst.append(src++, 1); 605 } 606 return; 607 } 608 609 // ${^A-Za-z] -> $\1 610 if (!isalpha(*src)) { 611 dst.append("$"); 612 dst.append(src++, 1); 613 return; 614 } 615 616 // $var -> replace with value 617 do { 618 buffer.append(src++, 1); 619 } while (is_id_char(*src)); 620 dst.append(get_variable(buffer.c_str())); 621 } 622 623 const string 624 config::expand_string(const char *src, const char *prepend, const char *append) 625 { 626 const char *var_at; 627 string dst; 628 629 /* 630 * 128 bytes is enough for 2427 of 2438 expansions that happen 631 * while parsing config files, as tested on 2013-01-30. 632 */ 633 dst.reserve(128); 634 635 if (prepend != NULL) 636 dst = prepend; 637 638 for (;;) { 639 var_at = strchr(src, '$'); 640 if (var_at == NULL) { 641 dst.append(src); 642 break; 643 } 644 dst.append(src, var_at - src); 645 src = var_at; 646 expand_one(src, dst); 647 } 648 649 if (append != NULL) 650 dst.append(append); 651 652 return (dst); 653 } 654 655 bool 656 config::chop_var(char *&buffer, char *&lhs, char *&rhs) 657 { 658 char *walker; 659 660 if (*buffer == '\0') 661 return (false); 662 walker = lhs = buffer; 663 while (is_id_char(*walker)) 664 walker++; 665 if (*walker != '=') 666 return (false); 667 walker++; // skip = 668 if (*walker == '"') { 669 walker++; // skip " 670 rhs = walker; 671 while (*walker && *walker != '"') 672 walker++; 673 if (*walker != '"') 674 return (false); 675 rhs[-2] = '\0'; 676 *walker++ = '\0'; 677 } else { 678 rhs = walker; 679 while (*walker && !isspace(*walker)) 680 walker++; 681 if (*walker != '\0') 682 *walker++ = '\0'; 683 rhs[-1] = '\0'; 684 } 685 while (isspace(*walker)) 686 walker++; 687 buffer = walker; 688 return (true); 689 } 690 691 692 char * 693 config::set_vars(char *buffer) 694 { 695 char *lhs; 696 char *rhs; 697 698 while (1) { 699 if (!chop_var(buffer, lhs, rhs)) 700 break; 701 set_variable(lhs, rhs); 702 } 703 return (buffer); 704 } 705 706 void 707 config::find_and_execute(char type) 708 { 709 vector<event_proc *> *l; 710 vector<event_proc *>::const_iterator i; 711 const char *s; 712 713 switch (type) { 714 default: 715 return; 716 case notify: 717 l = &_notify_list; 718 s = "notify"; 719 break; 720 case nomatch: 721 l = &_nomatch_list; 722 s = "nomatch"; 723 break; 724 case attach: 725 l = &_attach_list; 726 s = "attach"; 727 break; 728 case detach: 729 l = &_detach_list; 730 s = "detach"; 731 break; 732 } 733 if (Dflag) 734 fprintf(stderr, "Processing %s event\n", s); 735 for (i = l->begin(); i != l->end(); ++i) { 736 if ((*i)->matches(*this)) { 737 (*i)->run(*this); 738 break; 739 } 740 } 741 742 } 743 744 745 static void 746 process_event(char *buffer) 747 { 748 char type; 749 char *sp; 750 751 sp = buffer + 1; 752 if (Dflag) 753 fprintf(stderr, "Processing event '%s'\n", buffer); 754 type = *buffer++; 755 cfg.push_var_table(); 756 // No match doesn't have a device, and the format is a little 757 // different, so handle it separately. 758 switch (type) { 759 case notify: 760 sp = cfg.set_vars(sp); 761 break; 762 case nomatch: 763 //? at location pnp-info on bus 764 sp = strchr(sp, ' '); 765 if (sp == NULL) 766 return; /* Can't happen? */ 767 *sp++ = '\0'; 768 while (isspace(*sp)) 769 sp++; 770 if (strncmp(sp, "at ", 3) == 0) 771 sp += 3; 772 sp = cfg.set_vars(sp); 773 while (isspace(*sp)) 774 sp++; 775 if (strncmp(sp, "on ", 3) == 0) 776 cfg.set_variable("bus", sp + 3); 777 break; 778 case attach: /*FALLTHROUGH*/ 779 case detach: 780 sp = strchr(sp, ' '); 781 if (sp == NULL) 782 return; /* Can't happen? */ 783 *sp++ = '\0'; 784 cfg.set_variable("device-name", buffer); 785 while (isspace(*sp)) 786 sp++; 787 if (strncmp(sp, "at ", 3) == 0) 788 sp += 3; 789 sp = cfg.set_vars(sp); 790 while (isspace(*sp)) 791 sp++; 792 if (strncmp(sp, "on ", 3) == 0) 793 cfg.set_variable("bus", sp + 3); 794 break; 795 } 796 797 cfg.find_and_execute(type); 798 cfg.pop_var_table(); 799 } 800 801 int 802 create_socket(const char *name) 803 { 804 int fd, slen; 805 struct sockaddr_un sun; 806 807 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 808 err(1, "socket"); 809 bzero(&sun, sizeof(sun)); 810 sun.sun_family = AF_UNIX; 811 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 812 slen = SUN_LEN(&sun); 813 unlink(name); 814 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 815 err(1, "fcntl"); 816 if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 817 err(1, "bind"); 818 listen(fd, 4); 819 chown(name, 0, 0); /* XXX - root.wheel */ 820 chmod(name, 0666); 821 return (fd); 822 } 823 824 unsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 825 unsigned int num_clients; 826 list<int> clients; 827 828 void 829 notify_clients(const char *data, int len) 830 { 831 list<int>::iterator i; 832 833 /* 834 * Deliver the data to all clients. Throw clients overboard at the 835 * first sign of trouble. This reaps clients who've died or closed 836 * their sockets, and also clients who are alive but failing to keep up 837 * (or who are maliciously not reading, to consume buffer space in 838 * kernel memory or tie up the limited number of available connections). 839 */ 840 for (i = clients.begin(); i != clients.end(); ) { 841 if (write(*i, data, len) != len) { 842 --num_clients; 843 close(*i); 844 i = clients.erase(i); 845 } else 846 ++i; 847 } 848 } 849 850 void 851 check_clients(void) 852 { 853 int s; 854 struct pollfd pfd; 855 list<int>::iterator i; 856 857 /* 858 * Check all existing clients to see if any of them have disappeared. 859 * Normally we reap clients when we get an error trying to send them an 860 * event. This check eliminates the problem of an ever-growing list of 861 * zombie clients because we're never writing to them on a system 862 * without frequent device-change activity. 863 */ 864 pfd.events = 0; 865 for (i = clients.begin(); i != clients.end(); ) { 866 pfd.fd = *i; 867 s = poll(&pfd, 1, 0); 868 if ((s < 0 && s != EINTR ) || 869 (s > 0 && (pfd.revents & POLLHUP))) { 870 --num_clients; 871 close(*i); 872 i = clients.erase(i); 873 } else 874 ++i; 875 } 876 } 877 878 void 879 new_client(int fd) 880 { 881 int s; 882 883 /* 884 * First go reap any zombie clients, then accept the connection, and 885 * shut down the read side to stop clients from consuming kernel memory 886 * by sending large buffers full of data we'll never read. 887 */ 888 check_clients(); 889 s = accept(fd, NULL, NULL); 890 if (s != -1) { 891 shutdown(s, SHUT_RD); 892 clients.push_back(s); 893 ++num_clients; 894 } 895 } 896 897 static void 898 event_loop(void) 899 { 900 int rv; 901 int fd; 902 char buffer[DEVCTL_MAXBUF]; 903 int once = 0; 904 int server_fd, max_fd; 905 int accepting; 906 timeval tv; 907 fd_set fds; 908 909 fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 910 if (fd == -1) 911 err(1, "Can't open devctl device %s", PATH_DEVCTL); 912 server_fd = create_socket(PIPE); 913 accepting = 1; 914 max_fd = max(fd, server_fd) + 1; 915 while (1) { 916 if (romeo_must_die) 917 break; 918 if (!once && !dflag && !nflag) { 919 // Check to see if we have any events pending. 920 tv.tv_sec = 0; 921 tv.tv_usec = 0; 922 FD_ZERO(&fds); 923 FD_SET(fd, &fds); 924 rv = select(fd + 1, &fds, &fds, &fds, &tv); 925 // No events -> we've processed all pending events 926 if (rv == 0) { 927 if (Dflag) 928 fprintf(stderr, "Calling daemon\n"); 929 cfg.remove_pidfile(); 930 cfg.open_pidfile(); 931 daemon(0, 0); 932 cfg.write_pidfile(); 933 once++; 934 } 935 } 936 /* 937 * When we've already got the max number of clients, stop 938 * accepting new connections (don't put server_fd in the set), 939 * shrink the accept() queue to reject connections quickly, and 940 * poll the existing clients more often, so that we notice more 941 * quickly when any of them disappear to free up client slots. 942 */ 943 FD_ZERO(&fds); 944 FD_SET(fd, &fds); 945 if (num_clients < max_clients) { 946 if (!accepting) { 947 listen(server_fd, max_clients); 948 accepting = 1; 949 } 950 FD_SET(server_fd, &fds); 951 tv.tv_sec = 60; 952 tv.tv_usec = 0; 953 } else { 954 if (accepting) { 955 listen(server_fd, 0); 956 accepting = 0; 957 } 958 tv.tv_sec = 2; 959 tv.tv_usec = 0; 960 } 961 rv = select(max_fd, &fds, NULL, NULL, &tv); 962 if (rv == -1) { 963 if (errno == EINTR) 964 continue; 965 err(1, "select"); 966 } else if (rv == 0) 967 check_clients(); 968 if (FD_ISSET(fd, &fds)) { 969 rv = read(fd, buffer, sizeof(buffer) - 1); 970 if (rv > 0) { 971 notify_clients(buffer, rv); 972 buffer[rv] = '\0'; 973 while (buffer[--rv] == '\n') 974 buffer[rv] = '\0'; 975 process_event(buffer); 976 } else if (rv < 0) { 977 if (errno != EINTR) 978 break; 979 } else { 980 /* EOF */ 981 break; 982 } 983 } 984 if (FD_ISSET(server_fd, &fds)) 985 new_client(server_fd); 986 } 987 close(fd); 988 } 989 990 /* 991 * functions that the parser uses. 992 */ 993 void 994 add_attach(int prio, event_proc *p) 995 { 996 cfg.add_attach(prio, p); 997 } 998 999 void 1000 add_detach(int prio, event_proc *p) 1001 { 1002 cfg.add_detach(prio, p); 1003 } 1004 1005 void 1006 add_directory(const char *dir) 1007 { 1008 cfg.add_directory(dir); 1009 free(const_cast<char *>(dir)); 1010 } 1011 1012 void 1013 add_nomatch(int prio, event_proc *p) 1014 { 1015 cfg.add_nomatch(prio, p); 1016 } 1017 1018 void 1019 add_notify(int prio, event_proc *p) 1020 { 1021 cfg.add_notify(prio, p); 1022 } 1023 1024 event_proc * 1025 add_to_event_proc(event_proc *ep, eps *eps) 1026 { 1027 if (ep == NULL) 1028 ep = new event_proc(); 1029 ep->add(eps); 1030 return (ep); 1031 } 1032 1033 eps * 1034 new_action(const char *cmd) 1035 { 1036 eps *e = new action(cmd); 1037 free(const_cast<char *>(cmd)); 1038 return (e); 1039 } 1040 1041 eps * 1042 new_match(const char *var, const char *re) 1043 { 1044 eps *e = new match(cfg, var, re); 1045 free(const_cast<char *>(var)); 1046 free(const_cast<char *>(re)); 1047 return (e); 1048 } 1049 1050 eps * 1051 new_media(const char *var, const char *re) 1052 { 1053 eps *e = new media(cfg, var, re); 1054 free(const_cast<char *>(var)); 1055 free(const_cast<char *>(re)); 1056 return (e); 1057 } 1058 1059 void 1060 set_pidfile(const char *name) 1061 { 1062 cfg.set_pidfile(name); 1063 free(const_cast<char *>(name)); 1064 } 1065 1066 void 1067 set_variable(const char *var, const char *val) 1068 { 1069 cfg.set_variable(var, val); 1070 free(const_cast<char *>(var)); 1071 free(const_cast<char *>(val)); 1072 } 1073 1074 1075 1076 static void 1077 gensighand(int) 1078 { 1079 romeo_must_die++; 1080 _exit(0); 1081 } 1082 1083 static void 1084 usage() 1085 { 1086 fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n", 1087 getprogname()); 1088 exit(1); 1089 } 1090 1091 static void 1092 check_devd_enabled() 1093 { 1094 int val = 0; 1095 size_t len; 1096 1097 len = sizeof(val); 1098 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1099 errx(1, "devctl sysctl missing from kernel!"); 1100 if (val) { 1101 warnx("Setting " SYSCTL " to 0"); 1102 val = 0; 1103 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1104 } 1105 } 1106 1107 /* 1108 * main 1109 */ 1110 int 1111 main(int argc, char **argv) 1112 { 1113 int ch; 1114 1115 check_devd_enabled(); 1116 while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) { 1117 switch (ch) { 1118 case 'D': 1119 Dflag++; 1120 break; 1121 case 'd': 1122 dflag++; 1123 break; 1124 case 'f': 1125 configfile = optarg; 1126 break; 1127 case 'l': 1128 max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1129 break; 1130 case 'n': 1131 nflag++; 1132 break; 1133 default: 1134 usage(); 1135 } 1136 } 1137 1138 cfg.parse(); 1139 if (!dflag && nflag) { 1140 cfg.open_pidfile(); 1141 daemon(0, 0); 1142 cfg.write_pidfile(); 1143 } 1144 signal(SIGPIPE, SIG_IGN); 1145 signal(SIGHUP, gensighand); 1146 signal(SIGINT, gensighand); 1147 signal(SIGTERM, gensighand); 1148 event_loop(); 1149 return (0); 1150 } 1151