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