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