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 * $FreeBSD: src/sbin/devd/devd.cc,v 1.33 2006/09/17 22:49:26 ru Exp $ 27 * $DragonFly: src/sbin/devd/devd.cc,v 1.1 2008/10/03 00:26:21 hasso Exp $ 28 */ 29 30 /* 31 * DEVD control daemon. 32 */ 33 34 // TODO list: 35 // o devd.conf and devd man pages need a lot of help: 36 // - devd needs to document the unix domain socket 37 // - devd.conf needs more details on the supported statements. 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_IEEE80211, "802.11" }, 204 { IFM_ATM, "ATM" }, 205 { IFM_CARP, "CARP" }, 206 { -1, "unknown" }, 207 { 0, NULL }, 208 }; 209 for (int i = 0; media_types[i].ifmt_string != NULL; i++) 210 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 211 _type = media_types[i].ifmt_word; 212 break; 213 } 214 } 215 216 media::~media() 217 { 218 } 219 220 bool 221 media::do_match(config &c) 222 { 223 string value; 224 struct ifmediareq ifmr; 225 bool retval; 226 int s; 227 228 // Since we can be called from both a device attach/detach 229 // context where device-name is defined and what we want, 230 // as well as from a link status context, where subsystem is 231 // the name of interest, first try device-name and fall back 232 // to subsystem if none exists. 233 value = c.get_variable("device-name"); 234 if (value.length() == 0) 235 value = c.get_variable("subsystem"); 236 if (Dflag) 237 fprintf(stderr, "Testing media type of %s against 0x%x\n", 238 value.c_str(), _type); 239 240 retval = false; 241 242 s = socket(PF_INET, SOCK_DGRAM, 0); 243 if (s >= 0) { 244 memset(&ifmr, 0, sizeof(ifmr)); 245 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 246 247 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 248 ifmr.ifm_status & IFM_AVALID) { 249 if (Dflag) 250 fprintf(stderr, "%s has media type 0x%x\n", 251 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 252 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 253 } else if (_type == -1) { 254 if (Dflag) 255 fprintf(stderr, "%s has unknown media type\n", 256 value.c_str()); 257 retval = true; 258 } 259 close(s); 260 } 261 262 return retval; 263 } 264 265 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 266 const string var_list::nothing = ""; 267 268 const string & 269 var_list::get_variable(const string &var) const 270 { 271 map<string, string>::const_iterator i; 272 273 i = _vars.find(var); 274 if (i == _vars.end()) 275 return (var_list::bogus); 276 return (i->second); 277 } 278 279 bool 280 var_list::is_set(const string &var) const 281 { 282 return (_vars.find(var) != _vars.end()); 283 } 284 285 void 286 var_list::set_variable(const string &var, const string &val) 287 { 288 if (Dflag) 289 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 290 _vars[var] = val; 291 } 292 293 void 294 config::reset(void) 295 { 296 _dir_list.clear(); 297 delete_and_clear(_var_list_table); 298 delete_and_clear(_attach_list); 299 delete_and_clear(_detach_list); 300 delete_and_clear(_nomatch_list); 301 delete_and_clear(_notify_list); 302 } 303 304 void 305 config::parse_one_file(const char *fn) 306 { 307 if (Dflag) 308 printf("Parsing %s\n", fn); 309 yyin = fopen(fn, "r"); 310 if (yyin == NULL) 311 err(1, "Cannot open config file %s", fn); 312 lineno = 1; 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(configfile); 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::open_pidfile() 371 { 372 if (pidfile(NULL)) 373 errx(1, "devd already running"); 374 } 375 376 void 377 config::add_attach(int prio, event_proc *p) 378 { 379 p->set_priority(prio); 380 _attach_list.push_back(p); 381 } 382 383 void 384 config::add_detach(int prio, event_proc *p) 385 { 386 p->set_priority(prio); 387 _detach_list.push_back(p); 388 } 389 390 void 391 config::add_directory(const char *dir) 392 { 393 _dir_list.push_back(string(dir)); 394 } 395 396 void 397 config::add_nomatch(int prio, event_proc *p) 398 { 399 p->set_priority(prio); 400 _nomatch_list.push_back(p); 401 } 402 403 void 404 config::add_notify(int prio, event_proc *p) 405 { 406 p->set_priority(prio); 407 _notify_list.push_back(p); 408 } 409 410 void 411 config::set_pidfile(const char *fn) 412 { 413 _pidfile = string(fn); 414 } 415 416 void 417 config::push_var_table() 418 { 419 var_list *vl; 420 421 vl = new var_list(); 422 _var_list_table.push_back(vl); 423 if (Dflag) 424 fprintf(stderr, "Pushing table\n"); 425 } 426 427 void 428 config::pop_var_table() 429 { 430 delete _var_list_table.back(); 431 _var_list_table.pop_back(); 432 if (Dflag) 433 fprintf(stderr, "Popping table\n"); 434 } 435 436 void 437 config::set_variable(const char *var, const char *val) 438 { 439 _var_list_table.back()->set_variable(var, val); 440 } 441 442 const string & 443 config::get_variable(const string &var) 444 { 445 vector<var_list *>::reverse_iterator i; 446 447 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 448 if ((*i)->is_set(var)) 449 return ((*i)->get_variable(var)); 450 } 451 return (var_list::nothing); 452 } 453 454 bool 455 config::is_id_char(char ch) 456 { 457 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 458 ch == '-')); 459 } 460 461 void 462 config::expand_one(const char *&src, string &dst) 463 { 464 int count; 465 string buffer, varstr; 466 467 src++; 468 // $$ -> $ 469 if (*src == '$') { 470 dst.append(src++, 1); 471 return; 472 } 473 474 // $(foo) -> $(foo) 475 // Not sure if I want to support this or not, so for now we just pass 476 // it through. 477 if (*src == '(') { 478 dst.append("$"); 479 count = 1; 480 /* If the string ends before ) is matched , return. */ 481 while (count > 0 && *src) { 482 if (*src == ')') 483 count--; 484 else if (*src == '(') 485 count++; 486 dst.append(src++, 1); 487 } 488 return; 489 } 490 491 // ${^A-Za-z] -> $\1 492 if (!isalpha(*src)) { 493 dst.append("$"); 494 dst.append(src++, 1); 495 return; 496 } 497 498 // $var -> replace with value 499 do { 500 buffer.append(src++, 1); 501 } while (is_id_char(*src)); 502 buffer.append("", 1); 503 varstr = get_variable(buffer.c_str()); 504 dst.append(varstr); 505 } 506 507 const string 508 config::expand_string(const string &s) 509 { 510 const char *src; 511 string dst; 512 513 src = s.c_str(); 514 while (*src) { 515 if (*src == '$') 516 expand_one(src, dst); 517 else 518 dst.append(src++, 1); 519 } 520 dst.append("", 1); 521 522 return (dst); 523 } 524 525 bool 526 config::chop_var(char *&buffer, char *&lhs, char *&rhs) 527 { 528 char *walker; 529 530 if (*buffer == '\0') 531 return (false); 532 walker = lhs = buffer; 533 while (is_id_char(*walker)) 534 walker++; 535 if (*walker != '=') 536 return (false); 537 walker++; // skip = 538 if (*walker == '"') { 539 walker++; // skip " 540 rhs = walker; 541 while (*walker && *walker != '"') 542 walker++; 543 if (*walker != '"') 544 return (false); 545 rhs[-2] = '\0'; 546 *walker++ = '\0'; 547 } else { 548 rhs = walker; 549 while (*walker && !isspace(*walker)) 550 walker++; 551 if (*walker != '\0') 552 *walker++ = '\0'; 553 rhs[-1] = '\0'; 554 } 555 while (isspace(*walker)) 556 walker++; 557 buffer = walker; 558 return (true); 559 } 560 561 562 char * 563 config::set_vars(char *buffer) 564 { 565 char *lhs; 566 char *rhs; 567 568 while (1) { 569 if (!chop_var(buffer, lhs, rhs)) 570 break; 571 set_variable(lhs, rhs); 572 } 573 return (buffer); 574 } 575 576 void 577 config::find_and_execute(char type) 578 { 579 vector<event_proc *> *l; 580 vector<event_proc *>::const_iterator i; 581 const char *s; 582 583 switch (type) { 584 default: 585 return; 586 case notify: 587 l = &_notify_list; 588 s = "notify"; 589 break; 590 case nomatch: 591 l = &_nomatch_list; 592 s = "nomatch"; 593 break; 594 case attach: 595 l = &_attach_list; 596 s = "attach"; 597 break; 598 case detach: 599 l = &_detach_list; 600 s = "detach"; 601 break; 602 } 603 if (Dflag) 604 fprintf(stderr, "Processing %s event\n", s); 605 for (i = l->begin(); i != l->end(); i++) { 606 if ((*i)->matches(*this)) { 607 (*i)->run(*this); 608 break; 609 } 610 } 611 612 } 613 614 615 static void 616 process_event(char *buffer) 617 { 618 char type; 619 char *sp; 620 621 sp = buffer + 1; 622 if (Dflag) 623 fprintf(stderr, "Processing event '%s'\n", buffer); 624 type = *buffer++; 625 cfg.push_var_table(); 626 // No match doesn't have a device, and the format is a little 627 // different, so handle it separately. 628 switch (type) { 629 case notify: 630 sp = cfg.set_vars(sp); 631 break; 632 case nomatch: 633 //? at location pnp-info on bus 634 sp = strchr(sp, ' '); 635 if (sp == NULL) 636 return; /* Can't happen? */ 637 *sp++ = '\0'; 638 if (strncmp(sp, "at ", 3) == 0) 639 sp += 3; 640 sp = cfg.set_vars(sp); 641 if (strncmp(sp, "on ", 3) == 0) 642 cfg.set_variable("bus", sp + 3); 643 break; 644 case attach: /*FALLTHROUGH*/ 645 case detach: 646 sp = strchr(sp, ' '); 647 if (sp == NULL) 648 return; /* Can't happen? */ 649 *sp++ = '\0'; 650 cfg.set_variable("device-name", buffer); 651 if (strncmp(sp, "at ", 3) == 0) 652 sp += 3; 653 sp = cfg.set_vars(sp); 654 if (strncmp(sp, "on ", 3) == 0) 655 cfg.set_variable("bus", sp + 3); 656 break; 657 } 658 659 cfg.find_and_execute(type); 660 cfg.pop_var_table(); 661 } 662 663 int 664 create_socket(const char *name) 665 { 666 int fd, slen; 667 struct sockaddr_un sun; 668 669 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 670 err(1, "socket"); 671 bzero(&sun, sizeof(sun)); 672 sun.sun_family = AF_UNIX; 673 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 674 slen = SUN_LEN(&sun); 675 unlink(name); 676 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 677 err(1, "fcntl"); 678 if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 679 err(1, "bind"); 680 listen(fd, 4); 681 chown(name, 0, 0); /* XXX - root.wheel */ 682 chmod(name, 0666); 683 return (fd); 684 } 685 686 list<int> clients; 687 688 void 689 notify_clients(const char *data, int len) 690 { 691 list<int> bad; 692 list<int>::const_iterator i; 693 694 for (i = clients.begin(); i != clients.end(); i++) { 695 if (write(*i, data, len) <= 0) { 696 bad.push_back(*i); 697 close(*i); 698 } 699 } 700 701 for (i = bad.begin(); i != bad.end(); i++) 702 clients.erase(find(clients.begin(), clients.end(), *i)); 703 } 704 705 void 706 new_client(int fd) 707 { 708 int s; 709 710 s = accept(fd, NULL, NULL); 711 if (s != -1) 712 clients.push_back(s); 713 } 714 715 static void 716 event_loop(void) 717 { 718 int rv; 719 int fd; 720 char buffer[DEVCTL_MAXBUF]; 721 int once = 0; 722 int server_fd, max_fd; 723 timeval tv; 724 fd_set fds; 725 726 fd = open(PATH_DEVCTL, O_RDONLY); 727 if (fd == -1) 728 err(1, "Can't open devctl device %s", PATH_DEVCTL); 729 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 730 err(1, "Can't set close-on-exec flag on devctl"); 731 server_fd = create_socket(PIPE); 732 max_fd = max(fd, server_fd) + 1; 733 while (1) { 734 if (romeo_must_die) 735 break; 736 if (!once && !dflag && !nflag) { 737 // Check to see if we have any events pending. 738 tv.tv_sec = 0; 739 tv.tv_usec = 0; 740 FD_ZERO(&fds); 741 FD_SET(fd, &fds); 742 rv = select(fd + 1, &fds, NULL, NULL, &tv); 743 // No events -> we've processed all pending events 744 if (rv == 0) { 745 if (Dflag) 746 fprintf(stderr, "Calling daemon\n"); 747 daemon(0, 0); 748 cfg.open_pidfile(); 749 once++; 750 } 751 } 752 FD_ZERO(&fds); 753 FD_SET(fd, &fds); 754 FD_SET(server_fd, &fds); 755 rv = select(max_fd, &fds, NULL, NULL, NULL); 756 if (rv == -1) { 757 if (errno == EINTR) 758 continue; 759 err(1, "select"); 760 } 761 if (FD_ISSET(fd, &fds)) { 762 rv = read(fd, buffer, sizeof(buffer) - 1); 763 if (rv > 0) { 764 notify_clients(buffer, rv); 765 buffer[rv] = '\0'; 766 while (buffer[--rv] == '\n') 767 buffer[rv] = '\0'; 768 process_event(buffer); 769 } else if (rv < 0) { 770 if (errno != EINTR) 771 break; 772 } else { 773 /* EOF */ 774 break; 775 } 776 } 777 if (FD_ISSET(server_fd, &fds)) 778 new_client(server_fd); 779 } 780 close(fd); 781 } 782 783 /* 784 * functions that the parser uses. 785 */ 786 void 787 add_attach(int prio, event_proc *p) 788 { 789 cfg.add_attach(prio, p); 790 } 791 792 void 793 add_detach(int prio, event_proc *p) 794 { 795 cfg.add_detach(prio, p); 796 } 797 798 void 799 add_directory(const char *dir) 800 { 801 cfg.add_directory(dir); 802 free(const_cast<char *>(dir)); 803 } 804 805 void 806 add_nomatch(int prio, event_proc *p) 807 { 808 cfg.add_nomatch(prio, p); 809 } 810 811 void 812 add_notify(int prio, event_proc *p) 813 { 814 cfg.add_notify(prio, p); 815 } 816 817 event_proc * 818 add_to_event_proc(event_proc *ep, eps *eps) 819 { 820 if (ep == NULL) 821 ep = new event_proc(); 822 ep->add(eps); 823 return (ep); 824 } 825 826 eps * 827 new_action(const char *cmd) 828 { 829 eps *e = new action(cmd); 830 free(const_cast<char *>(cmd)); 831 return (e); 832 } 833 834 eps * 835 new_match(const char *var, const char *re) 836 { 837 eps *e = new match(cfg, var, re); 838 free(const_cast<char *>(var)); 839 free(const_cast<char *>(re)); 840 return (e); 841 } 842 843 eps * 844 new_media(const char *var, const char *re) 845 { 846 eps *e = new media(cfg, var, re); 847 free(const_cast<char *>(var)); 848 free(const_cast<char *>(re)); 849 return (e); 850 } 851 852 void 853 set_pidfile(const char *name) 854 { 855 cfg.set_pidfile(name); 856 free(const_cast<char *>(name)); 857 } 858 859 void 860 set_variable(const char *var, const char *val) 861 { 862 cfg.set_variable(var, val); 863 free(const_cast<char *>(var)); 864 free(const_cast<char *>(val)); 865 } 866 867 868 869 static void 870 gensighand(int) 871 { 872 romeo_must_die++; 873 unlink("/var/run/devd.pid"); /* XXX */ 874 _exit(0); 875 } 876 877 static void 878 usage() 879 { 880 fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 881 exit(1); 882 } 883 884 static void 885 check_devd_enabled() 886 { 887 int val = 0; 888 size_t len; 889 890 len = sizeof(val); 891 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 892 errx(1, "devctl sysctl missing from kernel!"); 893 if (val) { 894 warnx("Setting " SYSCTL " to 0"); 895 val = 0; 896 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 897 } 898 } 899 900 /* 901 * main 902 */ 903 int 904 main(int argc, char **argv) 905 { 906 int ch; 907 908 check_devd_enabled(); 909 while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 910 switch (ch) { 911 case 'D': 912 Dflag++; 913 break; 914 case 'd': 915 dflag++; 916 break; 917 case 'f': 918 configfile = optarg; 919 break; 920 case 'n': 921 nflag++; 922 break; 923 default: 924 usage(); 925 } 926 } 927 928 cfg.parse(); 929 if (!dflag && nflag) { 930 if (Dflag) 931 fprintf(stderr, "Calling daemon\n"); 932 daemon(0, 0); 933 cfg.open_pidfile(); 934 } 935 signal(SIGPIPE, SIG_IGN); 936 signal(SIGHUP, gensighand); 937 signal(SIGINT, gensighand); 938 signal(SIGTERM, gensighand); 939 event_loop(); 940 return (0); 941 } 942