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