devd.cc revision 155073
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: head/sbin/devd/devd.cc 155073 2006-01-30 22:50:13Z pjd $"); 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 72using namespace std; 73 74extern FILE *yyin; 75extern int lineno; 76 77static const char notify = '!'; 78static const char nomatch = '?'; 79static const char attach = '+'; 80static const char detach = '-'; 81 82static struct pidfh *pfh; 83 84int Dflag; 85int dflag; 86int nflag; 87int romeo_must_die = 0; 88 89static const char *configfile = CF; 90 91static void event_loop(void); 92static void usage(void); 93 94template <class T> void 95delete_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 104config cfg; 105 106event_proc::event_proc() : _prio(-1) 107{ 108 // nothing 109} 110 111event_proc::~event_proc() 112{ 113 delete_and_clear(_epsvec); 114} 115 116void 117event_proc::add(eps *eps) 118{ 119 _epsvec.push_back(eps); 120} 121 122bool 123event_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 133bool 134event_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 144action::action(const char *cmd) 145 : _cmd(cmd) 146{ 147 // nothing 148} 149 150action::~action() 151{ 152 // nothing 153} 154 155bool 156action::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 165match::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 175match::~match() 176{ 177 regfree(&_regex); 178} 179 180bool 181match::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 198media::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 218media::~media() 219{ 220} 221 222bool 223media::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 267const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 268const string var_list::nothing = ""; 269 270const string & 271var_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 281bool 282var_list::is_set(const string &var) const 283{ 284 return (_vars.find(var) != _vars.end()); 285} 286 287void 288var_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 295void 296config::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 306void 307config::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 if (yyparse() != 0) 315 errx(1, "Cannot parse %s at line %d", fn, lineno); 316 fclose(yyin); 317} 318 319void 320config::parse_files_in_dir(const char *dirname) 321{ 322 DIR *dirp; 323 struct dirent *dp; 324 char path[PATH_MAX]; 325 326 if (Dflag) 327 printf("Parsing files in %s\n", dirname); 328 dirp = opendir(dirname); 329 if (dirp == NULL) 330 return; 331 readdir(dirp); /* Skip . */ 332 readdir(dirp); /* Skip .. */ 333 while ((dp = readdir(dirp)) != NULL) { 334 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 335 snprintf(path, sizeof(path), "%s/%s", 336 dirname, dp->d_name); 337 parse_one_file(path); 338 } 339 } 340} 341 342class epv_greater { 343public: 344 int operator()(event_proc *const&l1, event_proc *const&l2) 345 { 346 return (l1->get_priority() > l2->get_priority()); 347 } 348}; 349 350void 351config::sort_vector(vector<event_proc *> &v) 352{ 353 sort(v.begin(), v.end(), epv_greater()); 354} 355 356void 357config::parse(void) 358{ 359 vector<string>::const_iterator i; 360 361 parse_one_file(configfile); 362 for (i = _dir_list.begin(); i != _dir_list.end(); i++) 363 parse_files_in_dir((*i).c_str()); 364 sort_vector(_attach_list); 365 sort_vector(_detach_list); 366 sort_vector(_nomatch_list); 367 sort_vector(_notify_list); 368} 369 370void 371config::open_pidfile() 372{ 373 pid_t otherpid; 374 375 if (_pidfile == "") 376 return; 377 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 378 if (pfh == NULL) { 379 if (errno == EEXIST) 380 errx(1, "devd already running, pid: %d", (int)otherpid); 381 warn("cannot open pid file"); 382 } 383} 384 385void 386config::write_pidfile() 387{ 388 389 pidfile_write(pfh); 390} 391 392void 393config::remove_pidfile() 394{ 395 396 pidfile_remove(pfh); 397} 398 399void 400config::add_attach(int prio, event_proc *p) 401{ 402 p->set_priority(prio); 403 _attach_list.push_back(p); 404} 405 406void 407config::add_detach(int prio, event_proc *p) 408{ 409 p->set_priority(prio); 410 _detach_list.push_back(p); 411} 412 413void 414config::add_directory(const char *dir) 415{ 416 _dir_list.push_back(string(dir)); 417} 418 419void 420config::add_nomatch(int prio, event_proc *p) 421{ 422 p->set_priority(prio); 423 _nomatch_list.push_back(p); 424} 425 426void 427config::add_notify(int prio, event_proc *p) 428{ 429 p->set_priority(prio); 430 _notify_list.push_back(p); 431} 432 433void 434config::set_pidfile(const char *fn) 435{ 436 _pidfile = string(fn); 437} 438 439void 440config::push_var_table() 441{ 442 var_list *vl; 443 444 vl = new var_list(); 445 _var_list_table.push_back(vl); 446 if (Dflag) 447 fprintf(stderr, "Pushing table\n"); 448} 449 450void 451config::pop_var_table() 452{ 453 delete _var_list_table.back(); 454 _var_list_table.pop_back(); 455 if (Dflag) 456 fprintf(stderr, "Popping table\n"); 457} 458 459void 460config::set_variable(const char *var, const char *val) 461{ 462 _var_list_table.back()->set_variable(var, val); 463} 464 465const string & 466config::get_variable(const string &var) 467{ 468 vector<var_list *>::reverse_iterator i; 469 470 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 471 if ((*i)->is_set(var)) 472 return ((*i)->get_variable(var)); 473 } 474 return (var_list::nothing); 475} 476 477bool 478config::is_id_char(char ch) 479{ 480 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 481 ch == '-')); 482} 483 484void 485config::expand_one(const char *&src, string &dst) 486{ 487 int count; 488 string buffer, varstr; 489 490 src++; 491 // $$ -> $ 492 if (*src == '$') { 493 dst.append(src++, 1); 494 return; 495 } 496 497 // $(foo) -> $(foo) 498 // Not sure if I want to support this or not, so for now we just pass 499 // it through. 500 if (*src == '(') { 501 dst.append("$"); 502 count = 1; 503 /* If the string ends before ) is matched , return. */ 504 while (count > 0 && *src) { 505 if (*src == ')') 506 count--; 507 else if (*src == '(') 508 count++; 509 dst.append(src++, 1); 510 } 511 return; 512 } 513 514 // ${^A-Za-z] -> $\1 515 if (!isalpha(*src)) { 516 dst.append("$"); 517 dst.append(src++, 1); 518 return; 519 } 520 521 // $var -> replace with value 522 do { 523 buffer.append(src++, 1); 524 } while (is_id_char(*src)); 525 buffer.append("", 1); 526 varstr = get_variable(buffer.c_str()); 527 dst.append(varstr); 528} 529 530const string 531config::expand_string(const string &s) 532{ 533 const char *src; 534 string dst; 535 536 src = s.c_str(); 537 while (*src) { 538 if (*src == '$') 539 expand_one(src, dst); 540 else 541 dst.append(src++, 1); 542 } 543 dst.append("", 1); 544 545 return (dst); 546} 547 548bool 549config::chop_var(char *&buffer, char *&lhs, char *&rhs) 550{ 551 char *walker; 552 553 if (*buffer == '\0') 554 return (false); 555 walker = lhs = buffer; 556 while (is_id_char(*walker)) 557 walker++; 558 if (*walker != '=') 559 return (false); 560 walker++; // skip = 561 if (*walker == '"') { 562 walker++; // skip " 563 rhs = walker; 564 while (*walker && *walker != '"') 565 walker++; 566 if (*walker != '"') 567 return (false); 568 rhs[-2] = '\0'; 569 *walker++ = '\0'; 570 } else { 571 rhs = walker; 572 while (*walker && !isspace(*walker)) 573 walker++; 574 if (*walker != '\0') 575 *walker++ = '\0'; 576 rhs[-1] = '\0'; 577 } 578 while (isspace(*walker)) 579 walker++; 580 buffer = walker; 581 return (true); 582} 583 584 585char * 586config::set_vars(char *buffer) 587{ 588 char *lhs; 589 char *rhs; 590 591 while (1) { 592 if (!chop_var(buffer, lhs, rhs)) 593 break; 594 set_variable(lhs, rhs); 595 } 596 return (buffer); 597} 598 599void 600config::find_and_execute(char type) 601{ 602 vector<event_proc *> *l; 603 vector<event_proc *>::const_iterator i; 604 const char *s; 605 606 switch (type) { 607 default: 608 return; 609 case notify: 610 l = &_notify_list; 611 s = "notify"; 612 break; 613 case nomatch: 614 l = &_nomatch_list; 615 s = "nomatch"; 616 break; 617 case attach: 618 l = &_attach_list; 619 s = "attach"; 620 break; 621 case detach: 622 l = &_detach_list; 623 s = "detach"; 624 break; 625 } 626 if (Dflag) 627 fprintf(stderr, "Processing %s event\n", s); 628 for (i = l->begin(); i != l->end(); i++) { 629 if ((*i)->matches(*this)) { 630 (*i)->run(*this); 631 break; 632 } 633 } 634 635} 636 637 638static void 639process_event(char *buffer) 640{ 641 char type; 642 char *sp; 643 644 sp = buffer + 1; 645 if (Dflag) 646 fprintf(stderr, "Processing event '%s'\n", buffer); 647 type = *buffer++; 648 cfg.push_var_table(); 649 // No match doesn't have a device, and the format is a little 650 // different, so handle it separately. 651 switch (type) { 652 case notify: 653 sp = cfg.set_vars(sp); 654 break; 655 case nomatch: 656 //? at location pnp-info on bus 657 sp = strchr(sp, ' '); 658 if (sp == NULL) 659 return; /* Can't happen? */ 660 *sp++ = '\0'; 661 if (strncmp(sp, "at ", 3) == 0) 662 sp += 3; 663 sp = cfg.set_vars(sp); 664 if (strncmp(sp, "on ", 3) == 0) 665 cfg.set_variable("bus", sp + 3); 666 break; 667 case attach: /*FALLTHROUGH*/ 668 case detach: 669 sp = strchr(sp, ' '); 670 if (sp == NULL) 671 return; /* Can't happen? */ 672 *sp++ = '\0'; 673 cfg.set_variable("device-name", buffer); 674 if (strncmp(sp, "at ", 3) == 0) 675 sp += 3; 676 sp = cfg.set_vars(sp); 677 if (strncmp(sp, "on ", 3) == 0) 678 cfg.set_variable("bus", sp + 3); 679 break; 680 } 681 682 cfg.find_and_execute(type); 683 cfg.pop_var_table(); 684} 685 686int 687create_socket(const char *name) 688{ 689 int fd, slen; 690 struct sockaddr_un sun; 691 692 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 693 err(1, "socket"); 694 bzero(&sun, sizeof(sun)); 695 sun.sun_family = AF_UNIX; 696 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 697 slen = SUN_LEN(&sun); 698 unlink(name); 699 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 700 err(1, "fcntl"); 701 if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 702 err(1, "bind"); 703 listen(fd, 4); 704 chown(name, 0, 0); /* XXX - root.wheel */ 705 chmod(name, 0666); 706 return (fd); 707} 708 709list<int> clients; 710 711void 712notify_clients(const char *data, int len) 713{ 714 list<int> bad; 715 list<int>::const_iterator i; 716 717 for (i = clients.begin(); i != clients.end(); i++) { 718 if (write(*i, data, len) <= 0) { 719 bad.push_back(*i); 720 close(*i); 721 } 722 } 723 724 for (i = bad.begin(); i != bad.end(); i++) 725 clients.erase(find(clients.begin(), clients.end(), *i)); 726} 727 728void 729new_client(int fd) 730{ 731 int s; 732 733 s = accept(fd, NULL, NULL); 734 if (s != -1) 735 clients.push_back(s); 736} 737 738static void 739event_loop(void) 740{ 741 int rv; 742 int fd; 743 char buffer[DEVCTL_MAXBUF]; 744 int once = 0; 745 int server_fd, max_fd; 746 timeval tv; 747 fd_set fds; 748 749 fd = open(PATH_DEVCTL, O_RDONLY); 750 if (fd == -1) 751 err(1, "Can't open devctl device %s", PATH_DEVCTL); 752 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 753 err(1, "Can't set close-on-exec flag on devctl"); 754 server_fd = create_socket(PIPE); 755 max_fd = max(fd, server_fd) + 1; 756 while (1) { 757 if (romeo_must_die) 758 break; 759 if (!once && !dflag && !nflag) { 760 // Check to see if we have any events pending. 761 tv.tv_sec = 0; 762 tv.tv_usec = 0; 763 FD_ZERO(&fds); 764 FD_SET(fd, &fds); 765 rv = select(fd + 1, &fds, &fds, &fds, &tv); 766 // No events -> we've processed all pending events 767 if (rv == 0) { 768 if (Dflag) 769 fprintf(stderr, "Calling daemon\n"); 770 cfg.remove_pidfile(); 771 cfg.open_pidfile(); 772 daemon(0, 0); 773 cfg.write_pidfile(); 774 once++; 775 } 776 } 777 FD_ZERO(&fds); 778 FD_SET(fd, &fds); 779 FD_SET(server_fd, &fds); 780 rv = select(max_fd, &fds, NULL, NULL, NULL); 781 if (rv == -1) { 782 if (errno == EINTR) 783 continue; 784 err(1, "select"); 785 } 786 if (FD_ISSET(fd, &fds)) { 787 rv = read(fd, buffer, sizeof(buffer) - 1); 788 if (rv > 0) { 789 notify_clients(buffer, rv); 790 buffer[rv] = '\0'; 791 while (buffer[--rv] == '\n') 792 buffer[rv] = '\0'; 793 process_event(buffer); 794 } else if (rv < 0) { 795 if (errno != EINTR) 796 break; 797 } else { 798 /* EOF */ 799 break; 800 } 801 } 802 if (FD_ISSET(server_fd, &fds)) 803 new_client(server_fd); 804 } 805 close(fd); 806} 807 808/* 809 * functions that the parser uses. 810 */ 811void 812add_attach(int prio, event_proc *p) 813{ 814 cfg.add_attach(prio, p); 815} 816 817void 818add_detach(int prio, event_proc *p) 819{ 820 cfg.add_detach(prio, p); 821} 822 823void 824add_directory(const char *dir) 825{ 826 cfg.add_directory(dir); 827 free(const_cast<char *>(dir)); 828} 829 830void 831add_nomatch(int prio, event_proc *p) 832{ 833 cfg.add_nomatch(prio, p); 834} 835 836void 837add_notify(int prio, event_proc *p) 838{ 839 cfg.add_notify(prio, p); 840} 841 842event_proc * 843add_to_event_proc(event_proc *ep, eps *eps) 844{ 845 if (ep == NULL) 846 ep = new event_proc(); 847 ep->add(eps); 848 return (ep); 849} 850 851eps * 852new_action(const char *cmd) 853{ 854 eps *e = new action(cmd); 855 free(const_cast<char *>(cmd)); 856 return (e); 857} 858 859eps * 860new_match(const char *var, const char *re) 861{ 862 eps *e = new match(cfg, var, re); 863 free(const_cast<char *>(var)); 864 free(const_cast<char *>(re)); 865 return (e); 866} 867 868eps * 869new_media(const char *var, const char *re) 870{ 871 eps *e = new media(cfg, var, re); 872 free(const_cast<char *>(var)); 873 free(const_cast<char *>(re)); 874 return (e); 875} 876 877void 878set_pidfile(const char *name) 879{ 880 cfg.set_pidfile(name); 881 free(const_cast<char *>(name)); 882} 883 884void 885set_variable(const char *var, const char *val) 886{ 887 cfg.set_variable(var, val); 888 free(const_cast<char *>(var)); 889 free(const_cast<char *>(val)); 890} 891 892 893 894static void 895gensighand(int) 896{ 897 romeo_must_die++; 898 _exit(0); 899} 900 901static void 902usage() 903{ 904 fprintf(stderr, "usage: %s [-Ddn]\n", getprogname()); 905 exit(1); 906} 907 908static void 909check_devd_enabled() 910{ 911 int val = 0; 912 size_t len; 913 914 len = sizeof(val); 915 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 916 errx(1, "devctl sysctl missing from kernel!"); 917 if (val) { 918 warnx("Setting " SYSCTL " to 0"); 919 val = 0; 920 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 921 } 922} 923 924/* 925 * main 926 */ 927int 928main(int argc, char **argv) 929{ 930 int ch; 931 932 check_devd_enabled(); 933 while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 934 switch (ch) { 935 case 'D': 936 Dflag++; 937 break; 938 case 'd': 939 dflag++; 940 break; 941 case 'f': 942 configfile = optarg; 943 break; 944 case 'n': 945 nflag++; 946 break; 947 default: 948 usage(); 949 } 950 } 951 952 cfg.parse(); 953 if (!dflag && nflag) { 954 cfg.open_pidfile(); 955 daemon(0, 0); 956 cfg.write_pidfile(); 957 } 958 signal(SIGPIPE, SIG_IGN); 959 signal(SIGHUP, gensighand); 960 signal(SIGINT, gensighand); 961 signal(SIGTERM, gensighand); 962 event_loop(); 963 return (0); 964} 965