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 <regex.h> 84 #include <signal.h> 85 #include <stdlib.h> 86 #include <stdio.h> 87 #include <string.h> 88 #include <unistd.h> 89 90 #include <algorithm> 91 #include <map> 92 #include <string> 93 #include <list> 94 #include <vector> 95 96 #include "devd.h" /* C compatible definitions */ 97 #include "devd.hh" /* C++ class definitions */ 98 99 #define PIPE "/var/run/devd.pipe" 100 #define CF "/etc/devd.conf" 101 #define SYSCTL "hw.bus.devctl_disable" 102 103 using namespace std; 104 105 extern FILE *yyin; 106 extern int lineno; 107 108 static const char notify = '!'; 109 static const char nomatch = '?'; 110 static const char attach = '+'; 111 static const char detach = '-'; 112 113 static struct pidfh *pfh; 114 115 int Dflag; 116 int dflag; 117 int nflag; 118 int romeo_must_die = 0; 119 120 static const char *configfile = CF; 121 122 static void event_loop(void); 123 static void usage(void); 124 125 template <class T> void 126 delete_and_clear(vector<T *> &v) 127 { 128 typename vector<T *>::const_iterator i; 129 130 for (i = v.begin(); i != v.end(); ++i) 131 delete *i; 132 v.clear(); 133 } 134 135 config cfg; 136 137 event_proc::event_proc() : _prio(-1) 138 { 139 // nothing 140 } 141 142 event_proc::~event_proc() 143 { 144 delete_and_clear(_epsvec); 145 } 146 147 void 148 event_proc::add(eps *eps) 149 { 150 _epsvec.push_back(eps); 151 } 152 153 bool 154 event_proc::matches(config &c) const 155 { 156 vector<eps *>::const_iterator i; 157 158 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 159 if (!(*i)->do_match(c)) 160 return (false); 161 return (true); 162 } 163 164 bool 165 event_proc::run(config &c) const 166 { 167 vector<eps *>::const_iterator i; 168 169 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 170 if (!(*i)->do_action(c)) 171 return (false); 172 return (true); 173 } 174 175 action::action(const char *cmd) 176 : _cmd(cmd) 177 { 178 // nothing 179 } 180 181 action::~action() 182 { 183 // nothing 184 } 185 186 static int 187 my_system(const char *command) 188 { 189 pid_t pid, savedpid; 190 int pstat; 191 struct sigaction ign, intact, quitact; 192 sigset_t newsigblock, oldsigblock; 193 194 if (!command) /* just checking... */ 195 return(1); 196 197 /* 198 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 199 * existing signal dispositions. 200 */ 201 ign.sa_handler = SIG_IGN; 202 ::sigemptyset(&ign.sa_mask); 203 ign.sa_flags = 0; 204 ::sigaction(SIGINT, &ign, &intact); 205 ::sigaction(SIGQUIT, &ign, &quitact); 206 ::sigemptyset(&newsigblock); 207 ::sigaddset(&newsigblock, SIGCHLD); 208 ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 209 switch (pid = ::fork()) { 210 case -1: /* error */ 211 break; 212 case 0: /* child */ 213 /* 214 * Restore original signal dispositions and exec the command. 215 */ 216 ::sigaction(SIGINT, &intact, NULL); 217 ::sigaction(SIGQUIT, &quitact, NULL); 218 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 219 /* 220 * Close the PID file, and all other open descriptors. 221 * Inherit std{in,out,err} only. 222 */ 223 cfg.close_pidfile(); 224 ::closefrom(3); 225 ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 226 ::_exit(127); 227 default: /* parent */ 228 savedpid = pid; 229 do { 230 pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 231 } while (pid == -1 && errno == EINTR); 232 break; 233 } 234 ::sigaction(SIGINT, &intact, NULL); 235 ::sigaction(SIGQUIT, &quitact, NULL); 236 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 237 return (pid == -1 ? -1 : pstat); 238 } 239 240 bool 241 action::do_action(config &c) 242 { 243 string s = c.expand_string(_cmd); 244 if (Dflag) 245 fprintf(stderr, "Executing '%s'\n", s.c_str()); 246 my_system(s.c_str()); 247 return (true); 248 } 249 250 match::match(config &c, const char *var, const char *re) 251 : _var(var), _re("^") 252 { 253 if (!c.expand_string(string(re)).empty() && 254 c.expand_string(string(re)).at(0) == '!') { 255 _re.append(c.expand_string(string(re)).substr(1)); 256 _inv = 1; 257 } else { 258 _re.append(c.expand_string(string(re))); 259 _inv = 0; 260 } 261 _re.append("$"); 262 regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 263 } 264 265 match::~match() 266 { 267 regfree(&_regex); 268 } 269 270 bool 271 match::do_match(config &c) 272 { 273 const string &value = c.get_variable(_var); 274 bool retval; 275 276 if (Dflag) 277 fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 278 _var.c_str(), value.c_str(), _re.c_str(), _inv); 279 280 retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 281 if (_inv == 1) 282 retval = (retval == 0) ? 1 : 0; 283 284 return retval; 285 } 286 287 #include <sys/sockio.h> 288 #include <net/if.h> 289 #include <net/if_media.h> 290 291 media::media(config &, const char *var, const char *type) 292 : _var(var), _type(-1) 293 { 294 static struct ifmedia_description media_types[] = { 295 { IFM_ETHER, "Ethernet" }, 296 { IFM_TOKEN, "Tokenring" }, 297 { IFM_FDDI, "FDDI" }, 298 { IFM_IEEE80211, "802.11" }, 299 { IFM_ATM, "ATM" }, 300 { -1, "unknown" }, 301 { 0, NULL }, 302 }; 303 for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 304 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 305 _type = media_types[i].ifmt_word; 306 break; 307 } 308 } 309 310 media::~media() 311 { 312 } 313 314 bool 315 media::do_match(config &c) 316 { 317 string value; 318 struct ifmediareq ifmr; 319 bool retval; 320 int s; 321 322 // Since we can be called from both a device attach/detach 323 // context where device-name is defined and what we want, 324 // as well as from a link status context, where subsystem is 325 // the name of interest, first try device-name and fall back 326 // to subsystem if none exists. 327 value = c.get_variable("device-name"); 328 if (value.length() == 0) 329 value = c.get_variable("subsystem"); 330 if (Dflag) 331 fprintf(stderr, "Testing media type of %s against 0x%x\n", 332 value.c_str(), _type); 333 334 retval = false; 335 336 s = socket(PF_INET, SOCK_DGRAM, 0); 337 if (s >= 0) { 338 memset(&ifmr, 0, sizeof(ifmr)); 339 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 340 341 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 342 ifmr.ifm_status & IFM_AVALID) { 343 if (Dflag) 344 fprintf(stderr, "%s has media type 0x%x\n", 345 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 346 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 347 } else if (_type == -1) { 348 if (Dflag) 349 fprintf(stderr, "%s has unknown media type\n", 350 value.c_str()); 351 retval = true; 352 } 353 close(s); 354 } 355 356 return retval; 357 } 358 359 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 360 const string var_list::nothing = ""; 361 362 const string & 363 var_list::get_variable(const string &var) const 364 { 365 map<string, string>::const_iterator i; 366 367 i = _vars.find(var); 368 if (i == _vars.end()) 369 return (var_list::bogus); 370 return (i->second); 371 } 372 373 bool 374 var_list::is_set(const string &var) const 375 { 376 return (_vars.find(var) != _vars.end()); 377 } 378 379 void 380 var_list::set_variable(const string &var, const string &val) 381 { 382 if (Dflag) 383 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 384 _vars[var] = val; 385 } 386 387 void 388 config::reset(void) 389 { 390 _dir_list.clear(); 391 delete_and_clear(_var_list_table); 392 delete_and_clear(_attach_list); 393 delete_and_clear(_detach_list); 394 delete_and_clear(_nomatch_list); 395 delete_and_clear(_notify_list); 396 } 397 398 void 399 config::parse_one_file(const char *fn) 400 { 401 if (Dflag) 402 fprintf(stderr, "Parsing %s\n", fn); 403 yyin = fopen(fn, "r"); 404 if (yyin == NULL) 405 err(1, "Cannot open config file %s", fn); 406 lineno = 1; 407 if (yyparse() != 0) 408 errx(1, "Cannot parse %s at line %d", fn, lineno); 409 fclose(yyin); 410 } 411 412 void 413 config::parse_files_in_dir(const char *dirname) 414 { 415 DIR *dirp; 416 struct dirent *dp; 417 char path[PATH_MAX]; 418 419 if (Dflag) 420 fprintf(stderr, "Parsing files in %s\n", dirname); 421 dirp = opendir(dirname); 422 if (dirp == NULL) 423 return; 424 readdir(dirp); /* Skip . */ 425 readdir(dirp); /* Skip .. */ 426 while ((dp = readdir(dirp)) != NULL) { 427 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 428 snprintf(path, sizeof(path), "%s/%s", 429 dirname, dp->d_name); 430 parse_one_file(path); 431 } 432 } 433 closedir(dirp); 434 } 435 436 class epv_greater { 437 public: 438 int operator()(event_proc *const&l1, event_proc *const&l2) const 439 { 440 return (l1->get_priority() > l2->get_priority()); 441 } 442 }; 443 444 void 445 config::sort_vector(vector<event_proc *> &v) 446 { 447 stable_sort(v.begin(), v.end(), epv_greater()); 448 } 449 450 void 451 config::parse(void) 452 { 453 vector<string>::const_iterator i; 454 455 parse_one_file(configfile); 456 for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 457 parse_files_in_dir((*i).c_str()); 458 sort_vector(_attach_list); 459 sort_vector(_detach_list); 460 sort_vector(_nomatch_list); 461 sort_vector(_notify_list); 462 } 463 464 void 465 config::open_pidfile() 466 { 467 pid_t otherpid; 468 469 if (_pidfile == "") 470 return; 471 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 472 if (pfh == NULL) { 473 if (errno == EEXIST) 474 errx(1, "devd already running, pid: %d", (int)otherpid); 475 warn("cannot open pid file"); 476 } 477 } 478 479 void 480 config::write_pidfile() 481 { 482 483 pidfile_write(pfh); 484 } 485 486 void 487 config::close_pidfile() 488 { 489 490 pidfile_close(pfh); 491 } 492 493 void 494 config::remove_pidfile() 495 { 496 497 pidfile_remove(pfh); 498 } 499 500 void 501 config::add_attach(int prio, event_proc *p) 502 { 503 p->set_priority(prio); 504 _attach_list.push_back(p); 505 } 506 507 void 508 config::add_detach(int prio, event_proc *p) 509 { 510 p->set_priority(prio); 511 _detach_list.push_back(p); 512 } 513 514 void 515 config::add_directory(const char *dir) 516 { 517 _dir_list.push_back(string(dir)); 518 } 519 520 void 521 config::add_nomatch(int prio, event_proc *p) 522 { 523 p->set_priority(prio); 524 _nomatch_list.push_back(p); 525 } 526 527 void 528 config::add_notify(int prio, event_proc *p) 529 { 530 p->set_priority(prio); 531 _notify_list.push_back(p); 532 } 533 534 void 535 config::set_pidfile(const char *fn) 536 { 537 _pidfile = string(fn); 538 } 539 540 void 541 config::push_var_table() 542 { 543 var_list *vl; 544 545 vl = new var_list(); 546 _var_list_table.push_back(vl); 547 if (Dflag) 548 fprintf(stderr, "Pushing table\n"); 549 } 550 551 void 552 config::pop_var_table() 553 { 554 delete _var_list_table.back(); 555 _var_list_table.pop_back(); 556 if (Dflag) 557 fprintf(stderr, "Popping table\n"); 558 } 559 560 void 561 config::set_variable(const char *var, const char *val) 562 { 563 _var_list_table.back()->set_variable(var, val); 564 } 565 566 const string & 567 config::get_variable(const string &var) 568 { 569 vector<var_list *>::reverse_iterator i; 570 571 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 572 if ((*i)->is_set(var)) 573 return ((*i)->get_variable(var)); 574 } 575 return (var_list::nothing); 576 } 577 578 bool 579 config::is_id_char(char ch) const 580 { 581 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 582 ch == '-')); 583 } 584 585 void 586 config::expand_one(const char *&src, string &dst) 587 { 588 int count; 589 string buffer; 590 591 src++; 592 // $$ -> $ 593 if (*src == '$') { 594 dst.append(src++, 1); 595 return; 596 } 597 598 // $(foo) -> $(foo) 599 // Not sure if I want to support this or not, so for now we just pass 600 // it through. 601 if (*src == '(') { 602 dst.append("$"); 603 count = 1; 604 /* If the string ends before ) is matched , return. */ 605 while (count > 0 && *src) { 606 if (*src == ')') 607 count--; 608 else if (*src == '(') 609 count++; 610 dst.append(src++, 1); 611 } 612 return; 613 } 614 615 // ${^A-Za-z] -> $\1 616 if (!isalpha(*src)) { 617 dst.append("$"); 618 dst.append(src++, 1); 619 return; 620 } 621 622 // $var -> replace with value 623 do { 624 buffer.append(src++, 1); 625 } while (is_id_char(*src)); 626 buffer.append("", 1); 627 dst.append(get_variable(buffer.c_str())); 628 } 629 630 const string 631 config::expand_string(const string &s) 632 { 633 const char *src; 634 string dst; 635 636 src = s.c_str(); 637 while (*src) { 638 if (*src == '$') 639 expand_one(src, dst); 640 else 641 dst.append(src++, 1); 642 } 643 dst.append("", 1); 644 645 return (dst); 646 } 647 648 bool 649 config::chop_var(char *&buffer, char *&lhs, char *&rhs) 650 { 651 char *walker; 652 653 if (*buffer == '\0') 654 return (false); 655 walker = lhs = buffer; 656 while (is_id_char(*walker)) 657 walker++; 658 if (*walker != '=') 659 return (false); 660 walker++; // skip = 661 if (*walker == '"') { 662 walker++; // skip " 663 rhs = walker; 664 while (*walker && *walker != '"') 665 walker++; 666 if (*walker != '"') 667 return (false); 668 rhs[-2] = '\0'; 669 *walker++ = '\0'; 670 } else { 671 rhs = walker; 672 while (*walker && !isspace(*walker)) 673 walker++; 674 if (*walker != '\0') 675 *walker++ = '\0'; 676 rhs[-1] = '\0'; 677 } 678 while (isspace(*walker)) 679 walker++; 680 buffer = walker; 681 return (true); 682 } 683 684 685 char * 686 config::set_vars(char *buffer) 687 { 688 char *lhs; 689 char *rhs; 690 691 while (1) { 692 if (!chop_var(buffer, lhs, rhs)) 693 break; 694 set_variable(lhs, rhs); 695 } 696 return (buffer); 697 } 698 699 void 700 config::find_and_execute(char type) 701 { 702 vector<event_proc *> *l; 703 vector<event_proc *>::const_iterator i; 704 const char *s; 705 706 switch (type) { 707 default: 708 return; 709 case notify: 710 l = &_notify_list; 711 s = "notify"; 712 break; 713 case nomatch: 714 l = &_nomatch_list; 715 s = "nomatch"; 716 break; 717 case attach: 718 l = &_attach_list; 719 s = "attach"; 720 break; 721 case detach: 722 l = &_detach_list; 723 s = "detach"; 724 break; 725 } 726 if (Dflag) 727 fprintf(stderr, "Processing %s event\n", s); 728 for (i = l->begin(); i != l->end(); ++i) { 729 if ((*i)->matches(*this)) { 730 (*i)->run(*this); 731 break; 732 } 733 } 734 735 } 736 737 738 static void 739 process_event(char *buffer) 740 { 741 char type; 742 char *sp; 743 744 sp = buffer + 1; 745 if (Dflag) 746 fprintf(stderr, "Processing event '%s'\n", buffer); 747 type = *buffer++; 748 cfg.push_var_table(); 749 // No match doesn't have a device, and the format is a little 750 // different, so handle it separately. 751 switch (type) { 752 case notify: 753 sp = cfg.set_vars(sp); 754 break; 755 case nomatch: 756 //? at location pnp-info on bus 757 sp = strchr(sp, ' '); 758 if (sp == NULL) 759 return; /* Can't happen? */ 760 *sp++ = '\0'; 761 while (isspace(*sp)) 762 sp++; 763 if (strncmp(sp, "at ", 3) == 0) 764 sp += 3; 765 sp = cfg.set_vars(sp); 766 while (isspace(*sp)) 767 sp++; 768 if (strncmp(sp, "on ", 3) == 0) 769 cfg.set_variable("bus", sp + 3); 770 break; 771 case attach: /*FALLTHROUGH*/ 772 case detach: 773 sp = strchr(sp, ' '); 774 if (sp == NULL) 775 return; /* Can't happen? */ 776 *sp++ = '\0'; 777 cfg.set_variable("device-name", buffer); 778 while (isspace(*sp)) 779 sp++; 780 if (strncmp(sp, "at ", 3) == 0) 781 sp += 3; 782 sp = cfg.set_vars(sp); 783 while (isspace(*sp)) 784 sp++; 785 if (strncmp(sp, "on ", 3) == 0) 786 cfg.set_variable("bus", sp + 3); 787 break; 788 } 789 790 cfg.find_and_execute(type); 791 cfg.pop_var_table(); 792 } 793 794 int 795 create_socket(const char *name) 796 { 797 int fd, slen; 798 struct sockaddr_un sun; 799 800 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 801 err(1, "socket"); 802 bzero(&sun, sizeof(sun)); 803 sun.sun_family = AF_UNIX; 804 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 805 slen = SUN_LEN(&sun); 806 unlink(name); 807 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 808 err(1, "fcntl"); 809 if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 810 err(1, "bind"); 811 listen(fd, 4); 812 chown(name, 0, 0); /* XXX - root.wheel */ 813 chmod(name, 0666); 814 return (fd); 815 } 816 817 list<int> clients; 818 819 void 820 notify_clients(const char *data, int len) 821 { 822 list<int> bad; 823 list<int>::const_iterator i; 824 825 for (i = clients.begin(); i != clients.end(); ++i) { 826 if (write(*i, data, len) <= 0) { 827 bad.push_back(*i); 828 close(*i); 829 } 830 } 831 832 for (i = bad.begin(); i != bad.end(); ++i) 833 clients.erase(find(clients.begin(), clients.end(), *i)); 834 } 835 836 void 837 new_client(int fd) 838 { 839 int s; 840 841 s = accept(fd, NULL, NULL); 842 if (s != -1) 843 clients.push_back(s); 844 } 845 846 static void 847 event_loop(void) 848 { 849 int rv; 850 int fd; 851 char buffer[DEVCTL_MAXBUF]; 852 int once = 0; 853 int server_fd, max_fd; 854 timeval tv; 855 fd_set fds; 856 857 fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 858 if (fd == -1) 859 err(1, "Can't open devctl device %s", PATH_DEVCTL); 860 server_fd = create_socket(PIPE); 861 max_fd = max(fd, server_fd) + 1; 862 while (1) { 863 if (romeo_must_die) 864 break; 865 if (!once && !dflag && !nflag) { 866 // Check to see if we have any events pending. 867 tv.tv_sec = 0; 868 tv.tv_usec = 0; 869 FD_ZERO(&fds); 870 FD_SET(fd, &fds); 871 rv = select(fd + 1, &fds, &fds, &fds, &tv); 872 // No events -> we've processed all pending events 873 if (rv == 0) { 874 if (Dflag) 875 fprintf(stderr, "Calling daemon\n"); 876 cfg.remove_pidfile(); 877 cfg.open_pidfile(); 878 daemon(0, 0); 879 cfg.write_pidfile(); 880 once++; 881 } 882 } 883 FD_ZERO(&fds); 884 FD_SET(fd, &fds); 885 FD_SET(server_fd, &fds); 886 rv = select(max_fd, &fds, NULL, NULL, NULL); 887 if (rv == -1) { 888 if (errno == EINTR) 889 continue; 890 err(1, "select"); 891 } 892 if (FD_ISSET(fd, &fds)) { 893 rv = read(fd, buffer, sizeof(buffer) - 1); 894 if (rv > 0) { 895 notify_clients(buffer, rv); 896 buffer[rv] = '\0'; 897 while (buffer[--rv] == '\n') 898 buffer[rv] = '\0'; 899 process_event(buffer); 900 } else if (rv < 0) { 901 if (errno != EINTR) 902 break; 903 } else { 904 /* EOF */ 905 break; 906 } 907 } 908 if (FD_ISSET(server_fd, &fds)) 909 new_client(server_fd); 910 } 911 close(fd); 912 } 913 914 /* 915 * functions that the parser uses. 916 */ 917 void 918 add_attach(int prio, event_proc *p) 919 { 920 cfg.add_attach(prio, p); 921 } 922 923 void 924 add_detach(int prio, event_proc *p) 925 { 926 cfg.add_detach(prio, p); 927 } 928 929 void 930 add_directory(const char *dir) 931 { 932 cfg.add_directory(dir); 933 free(const_cast<char *>(dir)); 934 } 935 936 void 937 add_nomatch(int prio, event_proc *p) 938 { 939 cfg.add_nomatch(prio, p); 940 } 941 942 void 943 add_notify(int prio, event_proc *p) 944 { 945 cfg.add_notify(prio, p); 946 } 947 948 event_proc * 949 add_to_event_proc(event_proc *ep, eps *eps) 950 { 951 if (ep == NULL) 952 ep = new event_proc(); 953 ep->add(eps); 954 return (ep); 955 } 956 957 eps * 958 new_action(const char *cmd) 959 { 960 eps *e = new action(cmd); 961 free(const_cast<char *>(cmd)); 962 return (e); 963 } 964 965 eps * 966 new_match(const char *var, const char *re) 967 { 968 eps *e = new match(cfg, var, re); 969 free(const_cast<char *>(var)); 970 free(const_cast<char *>(re)); 971 return (e); 972 } 973 974 eps * 975 new_media(const char *var, const char *re) 976 { 977 eps *e = new media(cfg, var, re); 978 free(const_cast<char *>(var)); 979 free(const_cast<char *>(re)); 980 return (e); 981 } 982 983 void 984 set_pidfile(const char *name) 985 { 986 cfg.set_pidfile(name); 987 free(const_cast<char *>(name)); 988 } 989 990 void 991 set_variable(const char *var, const char *val) 992 { 993 cfg.set_variable(var, val); 994 free(const_cast<char *>(var)); 995 free(const_cast<char *>(val)); 996 } 997 998 999 1000 static void 1001 gensighand(int) 1002 { 1003 romeo_must_die++; 1004 _exit(0); 1005 } 1006 1007 static void 1008 usage() 1009 { 1010 fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 1011 exit(1); 1012 } 1013 1014 static void 1015 check_devd_enabled() 1016 { 1017 int val = 0; 1018 size_t len; 1019 1020 len = sizeof(val); 1021 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1022 errx(1, "devctl sysctl missing from kernel!"); 1023 if (val) { 1024 warnx("Setting " SYSCTL " to 0"); 1025 val = 0; 1026 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1027 } 1028 } 1029 1030 /* 1031 * main 1032 */ 1033 int 1034 main(int argc, char **argv) 1035 { 1036 int ch; 1037 1038 check_devd_enabled(); 1039 while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 1040 switch (ch) { 1041 case 'D': 1042 Dflag++; 1043 break; 1044 case 'd': 1045 dflag++; 1046 break; 1047 case 'f': 1048 configfile = optarg; 1049 break; 1050 case 'n': 1051 nflag++; 1052 break; 1053 default: 1054 usage(); 1055 } 1056 } 1057 1058 cfg.parse(); 1059 if (!dflag && nflag) { 1060 cfg.open_pidfile(); 1061 daemon(0, 0); 1062 cfg.write_pidfile(); 1063 } 1064 signal(SIGPIPE, SIG_IGN); 1065 signal(SIGHUP, gensighand); 1066 signal(SIGINT, gensighand); 1067 signal(SIGTERM, gensighand); 1068 event_loop(); 1069 return (0); 1070 } 1071