devd.cc revision 213646
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 * my_system is a variation on lib/libc/stdlib/system.c: 27 * 28 * Copyright (c) 1988, 1993 29 * The Regents of the University of California. All rights reserved. 30 * 31 * Redistribution and use in source and binary forms, with or without 32 * modification, are permitted provided that the following conditions 33 * are met: 34 * 1. Redistributions of source code must retain the above copyright 35 * notice, this list of conditions and the following disclaimer. 36 * 2. Redistributions in binary form must reproduce the above copyright 37 * notice, this list of conditions and the following disclaimer in the 38 * documentation and/or other materials provided with the distribution. 39 * 4. Neither the name of the University nor the names of its contributors 40 * may be used to endorse or promote products derived from this software 41 * without specific prior written permission. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53 * SUCH DAMAGE. 54 */ 55 56/* 57 * DEVD control daemon. 58 */ 59 60// TODO list: 61// o devd.conf and devd man pages need a lot of help: 62// - devd needs to document the unix domain socket 63// - devd.conf needs more details on the supported statements. 64 65#include <sys/cdefs.h> 66__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 213646 2010-10-09 07:29:13Z imp $"); 67 68#include <sys/param.h> 69#include <sys/socket.h> 70#include <sys/stat.h> 71#include <sys/sysctl.h> 72#include <sys/types.h> 73#include <sys/wait.h> 74#include <sys/un.h> 75 76#include <ctype.h> 77#include <dirent.h> 78#include <errno.h> 79#include <err.h> 80#include <fcntl.h> 81#include <libutil.h> 82#include <paths.h> 83#include <regex.h> 84#include <signal.h> 85#include <stdlib.h> 86#include <stdio.h> 87#include <string.h> 88#include <unistd.h> 89 90#include <algorithm> 91#include <map> 92#include <string> 93#include <list> 94#include <vector> 95 96#include "devd.h" /* C compatible definitions */ 97#include "devd.hh" /* C++ class definitions */ 98 99#define PIPE "/var/run/devd.pipe" 100#define CF "/etc/devd.conf" 101#define SYSCTL "hw.bus.devctl_disable" 102 103using namespace std; 104 105extern FILE *yyin; 106extern int lineno; 107 108static const char notify = '!'; 109static const char nomatch = '?'; 110static const char attach = '+'; 111static const char detach = '-'; 112 113static struct pidfh *pfh; 114 115int Dflag; 116int dflag; 117int nflag; 118int romeo_must_die = 0; 119 120static const char *configfile = CF; 121 122static void event_loop(void); 123static void usage(void); 124 125template <class T> void 126delete_and_clear(vector<T *> &v) 127{ 128 typename vector<T *>::const_iterator i; 129 130 for (i = v.begin(); i != v.end(); i++) 131 delete *i; 132 v.clear(); 133} 134 135config cfg; 136 137event_proc::event_proc() : _prio(-1) 138{ 139 // nothing 140} 141 142event_proc::~event_proc() 143{ 144 delete_and_clear(_epsvec); 145} 146 147void 148event_proc::add(eps *eps) 149{ 150 _epsvec.push_back(eps); 151} 152 153bool 154event_proc::matches(config &c) 155{ 156 vector<eps *>::const_iterator i; 157 158 for (i = _epsvec.begin(); i != _epsvec.end(); i++) 159 if (!(*i)->do_match(c)) 160 return (false); 161 return (true); 162} 163 164bool 165event_proc::run(config &c) 166{ 167 vector<eps *>::const_iterator i; 168 169 for (i = _epsvec.begin(); i != _epsvec.end(); i++) 170 if (!(*i)->do_action(c)) 171 return (false); 172 return (true); 173} 174 175action::action(const char *cmd) 176 : _cmd(cmd) 177{ 178 // nothing 179} 180 181action::~action() 182{ 183 // nothing 184} 185 186static int 187my_system(const char *command) 188{ 189 pid_t pid, savedpid; 190 int pstat; 191 struct sigaction ign, intact, quitact; 192 sigset_t newsigblock, oldsigblock; 193 194 if (!command) /* just checking... */ 195 return(1); 196 197 /* 198 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 199 * existing signal dispositions. 200 */ 201 ign.sa_handler = SIG_IGN; 202 ::sigemptyset(&ign.sa_mask); 203 ign.sa_flags = 0; 204 ::sigaction(SIGINT, &ign, &intact); 205 ::sigaction(SIGQUIT, &ign, &quitact); 206 ::sigemptyset(&newsigblock); 207 ::sigaddset(&newsigblock, SIGCHLD); 208 ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 209 switch (pid = ::fork()) { 210 case -1: /* error */ 211 break; 212 case 0: /* child */ 213 /* 214 * Restore original signal dispositions and exec the command. 215 */ 216 ::sigaction(SIGINT, &intact, NULL); 217 ::sigaction(SIGQUIT, &quitact, NULL); 218 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 219 /* 220 * Close the PID file, and all other open descriptors. 221 * Inherit std{in,out,err} only. 222 */ 223 cfg.close_pidfile(); 224 ::closefrom(3); 225 ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 226 ::_exit(127); 227 default: /* parent */ 228 savedpid = pid; 229 do { 230 pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 231 } while (pid == -1 && errno == EINTR); 232 break; 233 } 234 ::sigaction(SIGINT, &intact, NULL); 235 ::sigaction(SIGQUIT, &quitact, NULL); 236 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 237 return (pid == -1 ? -1 : pstat); 238} 239 240bool 241action::do_action(config &c) 242{ 243 string s = c.expand_string(_cmd); 244 if (Dflag) 245 fprintf(stderr, "Executing '%s'\n", s.c_str()); 246 my_system(s.c_str()); 247 return (true); 248} 249 250match::match(config &c, const char *var, const char *re) 251 : _var(var) 252{ 253 _re = "^"; 254 _re.append(c.expand_string(string(re))); 255 _re.append("$"); 256 regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 257} 258 259match::~match() 260{ 261 regfree(&_regex); 262} 263 264bool 265match::do_match(config &c) 266{ 267 const string &value = c.get_variable(_var); 268 bool retval; 269 270 if (Dflag) 271 fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(), 272 value.c_str(), _re.c_str()); 273 274 retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 275 return retval; 276} 277 278#include <sys/sockio.h> 279#include <net/if.h> 280#include <net/if_media.h> 281 282media::media(config &, const char *var, const char *type) 283 : _var(var), _type(-1) 284{ 285 static struct ifmedia_description media_types[] = { 286 { IFM_ETHER, "Ethernet" }, 287 { IFM_TOKEN, "Tokenring" }, 288 { IFM_FDDI, "FDDI" }, 289 { IFM_IEEE80211, "802.11" }, 290 { IFM_ATM, "ATM" }, 291 { IFM_CARP, "CARP" }, 292 { -1, "unknown" }, 293 { 0, NULL }, 294 }; 295 for (int i = 0; media_types[i].ifmt_string != NULL; i++) 296 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 297 _type = media_types[i].ifmt_word; 298 break; 299 } 300} 301 302media::~media() 303{ 304} 305 306bool 307media::do_match(config &c) 308{ 309 string value; 310 struct ifmediareq ifmr; 311 bool retval; 312 int s; 313 314 // Since we can be called from both a device attach/detach 315 // context where device-name is defined and what we want, 316 // as well as from a link status context, where subsystem is 317 // the name of interest, first try device-name and fall back 318 // to subsystem if none exists. 319 value = c.get_variable("device-name"); 320 if (value.length() == 0) 321 value = c.get_variable("subsystem"); 322 if (Dflag) 323 fprintf(stderr, "Testing media type of %s against 0x%x\n", 324 value.c_str(), _type); 325 326 retval = false; 327 328 s = socket(PF_INET, SOCK_DGRAM, 0); 329 if (s >= 0) { 330 memset(&ifmr, 0, sizeof(ifmr)); 331 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 332 333 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 334 ifmr.ifm_status & IFM_AVALID) { 335 if (Dflag) 336 fprintf(stderr, "%s has media type 0x%x\n", 337 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 338 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 339 } else if (_type == -1) { 340 if (Dflag) 341 fprintf(stderr, "%s has unknown media type\n", 342 value.c_str()); 343 retval = true; 344 } 345 close(s); 346 } 347 348 return retval; 349} 350 351const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 352const string var_list::nothing = ""; 353 354const string & 355var_list::get_variable(const string &var) const 356{ 357 map<string, string>::const_iterator i; 358 359 i = _vars.find(var); 360 if (i == _vars.end()) 361 return (var_list::bogus); 362 return (i->second); 363} 364 365bool 366var_list::is_set(const string &var) const 367{ 368 return (_vars.find(var) != _vars.end()); 369} 370 371void 372var_list::set_variable(const string &var, const string &val) 373{ 374 if (Dflag) 375 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 376 _vars[var] = val; 377} 378 379void 380config::reset(void) 381{ 382 _dir_list.clear(); 383 delete_and_clear(_var_list_table); 384 delete_and_clear(_attach_list); 385 delete_and_clear(_detach_list); 386 delete_and_clear(_nomatch_list); 387 delete_and_clear(_notify_list); 388} 389 390void 391config::parse_one_file(const char *fn) 392{ 393 if (Dflag) 394 fprintf(stderr, "Parsing %s\n", fn); 395 yyin = fopen(fn, "r"); 396 if (yyin == NULL) 397 err(1, "Cannot open config file %s", fn); 398 lineno = 1; 399 if (yyparse() != 0) 400 errx(1, "Cannot parse %s at line %d", fn, lineno); 401 fclose(yyin); 402} 403 404void 405config::parse_files_in_dir(const char *dirname) 406{ 407 DIR *dirp; 408 struct dirent *dp; 409 char path[PATH_MAX]; 410 411 if (Dflag) 412 fprintf(stderr, "Parsing files in %s\n", dirname); 413 dirp = opendir(dirname); 414 if (dirp == NULL) 415 return; 416 readdir(dirp); /* Skip . */ 417 readdir(dirp); /* Skip .. */ 418 while ((dp = readdir(dirp)) != NULL) { 419 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 420 snprintf(path, sizeof(path), "%s/%s", 421 dirname, dp->d_name); 422 parse_one_file(path); 423 } 424 } 425} 426 427class epv_greater { 428public: 429 int operator()(event_proc *const&l1, event_proc *const&l2) 430 { 431 return (l1->get_priority() > l2->get_priority()); 432 } 433}; 434 435void 436config::sort_vector(vector<event_proc *> &v) 437{ 438 sort(v.begin(), v.end(), epv_greater()); 439} 440 441void 442config::parse(void) 443{ 444 vector<string>::const_iterator i; 445 446 parse_one_file(configfile); 447 for (i = _dir_list.begin(); i != _dir_list.end(); i++) 448 parse_files_in_dir((*i).c_str()); 449 sort_vector(_attach_list); 450 sort_vector(_detach_list); 451 sort_vector(_nomatch_list); 452 sort_vector(_notify_list); 453} 454 455void 456config::open_pidfile() 457{ 458 pid_t otherpid; 459 460 if (_pidfile == "") 461 return; 462 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 463 if (pfh == NULL) { 464 if (errno == EEXIST) 465 errx(1, "devd already running, pid: %d", (int)otherpid); 466 warn("cannot open pid file"); 467 } 468} 469 470void 471config::write_pidfile() 472{ 473 474 pidfile_write(pfh); 475} 476 477void 478config::close_pidfile() 479{ 480 481 pidfile_close(pfh); 482} 483 484void 485config::remove_pidfile() 486{ 487 488 pidfile_remove(pfh); 489} 490 491void 492config::add_attach(int prio, event_proc *p) 493{ 494 p->set_priority(prio); 495 _attach_list.push_back(p); 496} 497 498void 499config::add_detach(int prio, event_proc *p) 500{ 501 p->set_priority(prio); 502 _detach_list.push_back(p); 503} 504 505void 506config::add_directory(const char *dir) 507{ 508 _dir_list.push_back(string(dir)); 509} 510 511void 512config::add_nomatch(int prio, event_proc *p) 513{ 514 p->set_priority(prio); 515 _nomatch_list.push_back(p); 516} 517 518void 519config::add_notify(int prio, event_proc *p) 520{ 521 p->set_priority(prio); 522 _notify_list.push_back(p); 523} 524 525void 526config::set_pidfile(const char *fn) 527{ 528 _pidfile = string(fn); 529} 530 531void 532config::push_var_table() 533{ 534 var_list *vl; 535 536 vl = new var_list(); 537 _var_list_table.push_back(vl); 538 if (Dflag) 539 fprintf(stderr, "Pushing table\n"); 540} 541 542void 543config::pop_var_table() 544{ 545 delete _var_list_table.back(); 546 _var_list_table.pop_back(); 547 if (Dflag) 548 fprintf(stderr, "Popping table\n"); 549} 550 551void 552config::set_variable(const char *var, const char *val) 553{ 554 _var_list_table.back()->set_variable(var, val); 555} 556 557const string & 558config::get_variable(const string &var) 559{ 560 vector<var_list *>::reverse_iterator i; 561 562 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 563 if ((*i)->is_set(var)) 564 return ((*i)->get_variable(var)); 565 } 566 return (var_list::nothing); 567} 568 569bool 570config::is_id_char(char ch) 571{ 572 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 573 ch == '-')); 574} 575 576void 577config::expand_one(const char *&src, string &dst) 578{ 579 int count; 580 string buffer; 581 582 src++; 583 // $$ -> $ 584 if (*src == '$') { 585 dst.append(src++, 1); 586 return; 587 } 588 589 // $(foo) -> $(foo) 590 // Not sure if I want to support this or not, so for now we just pass 591 // it through. 592 if (*src == '(') { 593 dst.append("$"); 594 count = 1; 595 /* If the string ends before ) is matched , return. */ 596 while (count > 0 && *src) { 597 if (*src == ')') 598 count--; 599 else if (*src == '(') 600 count++; 601 dst.append(src++, 1); 602 } 603 return; 604 } 605 606 // ${^A-Za-z] -> $\1 607 if (!isalpha(*src)) { 608 dst.append("$"); 609 dst.append(src++, 1); 610 return; 611 } 612 613 // $var -> replace with value 614 do { 615 buffer.append(src++, 1); 616 } while (is_id_char(*src)); 617 buffer.append("", 1); 618 dst.append(get_variable(buffer.c_str())); 619} 620 621const string 622config::expand_string(const string &s) 623{ 624 const char *src; 625 string dst; 626 627 src = s.c_str(); 628 while (*src) { 629 if (*src == '$') 630 expand_one(src, dst); 631 else 632 dst.append(src++, 1); 633 } 634 dst.append("", 1); 635 636 return (dst); 637} 638 639bool 640config::chop_var(char *&buffer, char *&lhs, char *&rhs) 641{ 642 char *walker; 643 644 if (*buffer == '\0') 645 return (false); 646 walker = lhs = buffer; 647 while (is_id_char(*walker)) 648 walker++; 649 if (*walker != '=') 650 return (false); 651 walker++; // skip = 652 if (*walker == '"') { 653 walker++; // skip " 654 rhs = walker; 655 while (*walker && *walker != '"') 656 walker++; 657 if (*walker != '"') 658 return (false); 659 rhs[-2] = '\0'; 660 *walker++ = '\0'; 661 } else { 662 rhs = walker; 663 while (*walker && !isspace(*walker)) 664 walker++; 665 if (*walker != '\0') 666 *walker++ = '\0'; 667 rhs[-1] = '\0'; 668 } 669 while (isspace(*walker)) 670 walker++; 671 buffer = walker; 672 return (true); 673} 674 675 676char * 677config::set_vars(char *buffer) 678{ 679 char *lhs; 680 char *rhs; 681 682 while (1) { 683 if (!chop_var(buffer, lhs, rhs)) 684 break; 685 set_variable(lhs, rhs); 686 } 687 return (buffer); 688} 689 690void 691config::find_and_execute(char type) 692{ 693 vector<event_proc *> *l; 694 vector<event_proc *>::const_iterator i; 695 const char *s; 696 697 switch (type) { 698 default: 699 return; 700 case notify: 701 l = &_notify_list; 702 s = "notify"; 703 break; 704 case nomatch: 705 l = &_nomatch_list; 706 s = "nomatch"; 707 break; 708 case attach: 709 l = &_attach_list; 710 s = "attach"; 711 break; 712 case detach: 713 l = &_detach_list; 714 s = "detach"; 715 break; 716 } 717 if (Dflag) 718 fprintf(stderr, "Processing %s event\n", s); 719 for (i = l->begin(); i != l->end(); i++) { 720 if ((*i)->matches(*this)) { 721 (*i)->run(*this); 722 break; 723 } 724 } 725 726} 727 728 729static void 730process_event(char *buffer) 731{ 732 char type; 733 char *sp; 734 735 sp = buffer + 1; 736 if (Dflag) 737 fprintf(stderr, "Processing event '%s'\n", buffer); 738 type = *buffer++; 739 cfg.push_var_table(); 740 // No match doesn't have a device, and the format is a little 741 // different, so handle it separately. 742 switch (type) { 743 case notify: 744 sp = cfg.set_vars(sp); 745 break; 746 case nomatch: 747 //? at location pnp-info on bus 748 sp = strchr(sp, ' '); 749 if (sp == NULL) 750 return; /* Can't happen? */ 751 *sp++ = '\0'; 752 while (isspace(*sp)) 753 sp++; 754 if (strncmp(sp, "at ", 3) == 0) 755 sp += 3; 756 sp = cfg.set_vars(sp); 757 while (isspace(*sp)) 758 sp++; 759 if (strncmp(sp, "on ", 3) == 0) 760 cfg.set_variable("bus", sp + 3); 761 break; 762 case attach: /*FALLTHROUGH*/ 763 case detach: 764 sp = strchr(sp, ' '); 765 if (sp == NULL) 766 return; /* Can't happen? */ 767 *sp++ = '\0'; 768 cfg.set_variable("device-name", buffer); 769 while (isspace(*sp)) 770 sp++; 771 if (strncmp(sp, "at ", 3) == 0) 772 sp += 3; 773 sp = cfg.set_vars(sp); 774 while (isspace(*sp)) 775 sp++; 776 if (strncmp(sp, "on ", 3) == 0) 777 cfg.set_variable("bus", sp + 3); 778 break; 779 } 780 781 cfg.find_and_execute(type); 782 cfg.pop_var_table(); 783} 784 785int 786create_socket(const char *name) 787{ 788 int fd, slen; 789 struct sockaddr_un sun; 790 791 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 792 err(1, "socket"); 793 bzero(&sun, sizeof(sun)); 794 sun.sun_family = AF_UNIX; 795 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 796 slen = SUN_LEN(&sun); 797 unlink(name); 798 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 799 err(1, "fcntl"); 800 if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 801 err(1, "bind"); 802 listen(fd, 4); 803 chown(name, 0, 0); /* XXX - root.wheel */ 804 chmod(name, 0666); 805 return (fd); 806} 807 808list<int> clients; 809 810void 811notify_clients(const char *data, int len) 812{ 813 list<int> bad; 814 list<int>::const_iterator i; 815 816 for (i = clients.begin(); i != clients.end(); i++) { 817 if (write(*i, data, len) <= 0) { 818 bad.push_back(*i); 819 close(*i); 820 } 821 } 822 823 for (i = bad.begin(); i != bad.end(); i++) 824 clients.erase(find(clients.begin(), clients.end(), *i)); 825} 826 827void 828new_client(int fd) 829{ 830 int s; 831 832 s = accept(fd, NULL, NULL); 833 if (s != -1) 834 clients.push_back(s); 835} 836 837static void 838event_loop(void) 839{ 840 int rv; 841 int fd; 842 char buffer[DEVCTL_MAXBUF]; 843 int once = 0; 844 int server_fd, max_fd; 845 timeval tv; 846 fd_set fds; 847 848 fd = open(PATH_DEVCTL, O_RDONLY); 849 if (fd == -1) 850 err(1, "Can't open devctl device %s", PATH_DEVCTL); 851 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 852 err(1, "Can't set close-on-exec flag on devctl"); 853 server_fd = create_socket(PIPE); 854 max_fd = max(fd, server_fd) + 1; 855 while (1) { 856 if (romeo_must_die) 857 break; 858 if (!once && !dflag && !nflag) { 859 // Check to see if we have any events pending. 860 tv.tv_sec = 0; 861 tv.tv_usec = 0; 862 FD_ZERO(&fds); 863 FD_SET(fd, &fds); 864 rv = select(fd + 1, &fds, &fds, &fds, &tv); 865 // No events -> we've processed all pending events 866 if (rv == 0) { 867 if (Dflag) 868 fprintf(stderr, "Calling daemon\n"); 869 cfg.remove_pidfile(); 870 cfg.open_pidfile(); 871 daemon(0, 0); 872 cfg.write_pidfile(); 873 once++; 874 } 875 } 876 FD_ZERO(&fds); 877 FD_SET(fd, &fds); 878 FD_SET(server_fd, &fds); 879 rv = select(max_fd, &fds, NULL, NULL, NULL); 880 if (rv == -1) { 881 if (errno == EINTR) 882 continue; 883 err(1, "select"); 884 } 885 if (FD_ISSET(fd, &fds)) { 886 rv = read(fd, buffer, sizeof(buffer) - 1); 887 if (rv > 0) { 888 notify_clients(buffer, rv); 889 buffer[rv] = '\0'; 890 while (buffer[--rv] == '\n') 891 buffer[rv] = '\0'; 892 process_event(buffer); 893 } else if (rv < 0) { 894 if (errno != EINTR) 895 break; 896 } else { 897 /* EOF */ 898 break; 899 } 900 } 901 if (FD_ISSET(server_fd, &fds)) 902 new_client(server_fd); 903 } 904 close(fd); 905} 906 907/* 908 * functions that the parser uses. 909 */ 910void 911add_attach(int prio, event_proc *p) 912{ 913 cfg.add_attach(prio, p); 914} 915 916void 917add_detach(int prio, event_proc *p) 918{ 919 cfg.add_detach(prio, p); 920} 921 922void 923add_directory(const char *dir) 924{ 925 cfg.add_directory(dir); 926 free(const_cast<char *>(dir)); 927} 928 929void 930add_nomatch(int prio, event_proc *p) 931{ 932 cfg.add_nomatch(prio, p); 933} 934 935void 936add_notify(int prio, event_proc *p) 937{ 938 cfg.add_notify(prio, p); 939} 940 941event_proc * 942add_to_event_proc(event_proc *ep, eps *eps) 943{ 944 if (ep == NULL) 945 ep = new event_proc(); 946 ep->add(eps); 947 return (ep); 948} 949 950eps * 951new_action(const char *cmd) 952{ 953 eps *e = new action(cmd); 954 free(const_cast<char *>(cmd)); 955 return (e); 956} 957 958eps * 959new_match(const char *var, const char *re) 960{ 961 eps *e = new match(cfg, var, re); 962 free(const_cast<char *>(var)); 963 free(const_cast<char *>(re)); 964 return (e); 965} 966 967eps * 968new_media(const char *var, const char *re) 969{ 970 eps *e = new media(cfg, var, re); 971 free(const_cast<char *>(var)); 972 free(const_cast<char *>(re)); 973 return (e); 974} 975 976void 977set_pidfile(const char *name) 978{ 979 cfg.set_pidfile(name); 980 free(const_cast<char *>(name)); 981} 982 983void 984set_variable(const char *var, const char *val) 985{ 986 cfg.set_variable(var, val); 987 free(const_cast<char *>(var)); 988 free(const_cast<char *>(val)); 989} 990 991 992 993static void 994gensighand(int) 995{ 996 romeo_must_die++; 997 _exit(0); 998} 999 1000static void 1001usage() 1002{ 1003 fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 1004 exit(1); 1005} 1006 1007static void 1008check_devd_enabled() 1009{ 1010 int val = 0; 1011 size_t len; 1012 1013 len = sizeof(val); 1014 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1015 errx(1, "devctl sysctl missing from kernel!"); 1016 if (val) { 1017 warnx("Setting " SYSCTL " to 0"); 1018 val = 0; 1019 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1020 } 1021} 1022 1023/* 1024 * main 1025 */ 1026int 1027main(int argc, char **argv) 1028{ 1029 int ch; 1030 1031 check_devd_enabled(); 1032 while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 1033 switch (ch) { 1034 case 'D': 1035 Dflag++; 1036 break; 1037 case 'd': 1038 dflag++; 1039 break; 1040 case 'f': 1041 configfile = optarg; 1042 break; 1043 case 'n': 1044 nflag++; 1045 break; 1046 default: 1047 usage(); 1048 } 1049 } 1050 1051 cfg.parse(); 1052 if (!dflag && nflag) { 1053 cfg.open_pidfile(); 1054 daemon(0, 0); 1055 cfg.write_pidfile(); 1056 } 1057 signal(SIGPIPE, SIG_IGN); 1058 signal(SIGHUP, gensighand); 1059 signal(SIGINT, gensighand); 1060 signal(SIGTERM, gensighand); 1061 event_loop(); 1062 return (0); 1063} 1064