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