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