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