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