devd.cc revision 250186
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 250186 2013-05-02 17:02:50Z 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 <cctype> 77#include <cerrno> 78#include <cstdlib> 79#include <cstdio> 80#include <csignal> 81#include <cstring> 82 83#include <dirent.h> 84#include <err.h> 85#include <fcntl.h> 86#include <libutil.h> 87#include <paths.h> 88#include <poll.h> 89#include <regex.h> 90#include <unistd.h> 91 92#include <algorithm> 93#include <map> 94#include <string> 95#include <list> 96#include <vector> 97 98#include "devd.h" /* C compatible definitions */ 99#include "devd.hh" /* C++ class definitions */ 100 101#define PIPE "/var/run/devd.pipe" 102#define CF "/etc/devd.conf" 103#define SYSCTL "hw.bus.devctl_disable" 104 105using namespace std; 106 107extern FILE *yyin; 108extern int lineno; 109 110static const char notify = '!'; 111static const char nomatch = '?'; 112static const char attach = '+'; 113static const char detach = '-'; 114 115static struct pidfh *pfh; 116 117int Dflag; 118int dflag; 119int nflag; 120static volatile sig_atomic_t romeo_must_die = 0; 121 122static const char *configfile = CF; 123 124static void event_loop(void); 125static void usage(void); 126 127template <class T> void 128delete_and_clear(vector<T *> &v) 129{ 130 typename vector<T *>::const_iterator i; 131 132 for (i = v.begin(); i != v.end(); ++i) 133 delete *i; 134 v.clear(); 135} 136 137config cfg; 138 139event_proc::event_proc() : _prio(-1) 140{ 141 _epsvec.reserve(4); 142} 143 144event_proc::~event_proc() 145{ 146 delete_and_clear(_epsvec); 147} 148 149void 150event_proc::add(eps *eps) 151{ 152 _epsvec.push_back(eps); 153} 154 155bool 156event_proc::matches(config &c) const 157{ 158 vector<eps *>::const_iterator i; 159 160 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 161 if (!(*i)->do_match(c)) 162 return (false); 163 return (true); 164} 165 166bool 167event_proc::run(config &c) const 168{ 169 vector<eps *>::const_iterator i; 170 171 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 172 if (!(*i)->do_action(c)) 173 return (false); 174 return (true); 175} 176 177action::action(const char *cmd) 178 : _cmd(cmd) 179{ 180 // nothing 181} 182 183action::~action() 184{ 185 // nothing 186} 187 188static int 189my_system(const char *command) 190{ 191 pid_t pid, savedpid; 192 int pstat; 193 struct sigaction ign, intact, quitact; 194 sigset_t newsigblock, oldsigblock; 195 196 if (!command) /* just checking... */ 197 return(1); 198 199 /* 200 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 201 * existing signal dispositions. 202 */ 203 ign.sa_handler = SIG_IGN; 204 ::sigemptyset(&ign.sa_mask); 205 ign.sa_flags = 0; 206 ::sigaction(SIGINT, &ign, &intact); 207 ::sigaction(SIGQUIT, &ign, &quitact); 208 ::sigemptyset(&newsigblock); 209 ::sigaddset(&newsigblock, SIGCHLD); 210 ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 211 switch (pid = ::fork()) { 212 case -1: /* error */ 213 break; 214 case 0: /* child */ 215 /* 216 * Restore original signal dispositions and exec the command. 217 */ 218 ::sigaction(SIGINT, &intact, NULL); 219 ::sigaction(SIGQUIT, &quitact, NULL); 220 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 221 /* 222 * Close the PID file, and all other open descriptors. 223 * Inherit std{in,out,err} only. 224 */ 225 cfg.close_pidfile(); 226 ::closefrom(3); 227 ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 228 ::_exit(127); 229 default: /* parent */ 230 savedpid = pid; 231 do { 232 pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 233 } while (pid == -1 && errno == EINTR); 234 break; 235 } 236 ::sigaction(SIGINT, &intact, NULL); 237 ::sigaction(SIGQUIT, &quitact, NULL); 238 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 239 return (pid == -1 ? -1 : pstat); 240} 241 242bool 243action::do_action(config &c) 244{ 245 string s = c.expand_string(_cmd.c_str()); 246 if (Dflag) 247 fprintf(stderr, "Executing '%s'\n", s.c_str()); 248 my_system(s.c_str()); 249 return (true); 250} 251 252match::match(config &c, const char *var, const char *re) : 253 _inv(re[0] == '!'), 254 _var(var), 255 _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 256{ 257 regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 258} 259 260match::~match() 261{ 262 regfree(&_regex); 263} 264 265bool 266match::do_match(config &c) 267{ 268 const string &value = c.get_variable(_var); 269 bool retval; 270 271 if (Dflag) 272 fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 273 _var.c_str(), value.c_str(), _re.c_str(), _inv); 274 275 retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 276 if (_inv == 1) 277 retval = (retval == 0) ? 1 : 0; 278 279 return retval; 280} 281 282#include <sys/sockio.h> 283#include <net/if.h> 284#include <net/if_media.h> 285 286media::media(config &, const char *var, const char *type) 287 : _var(var), _type(-1) 288{ 289 static struct ifmedia_description media_types[] = { 290 { IFM_ETHER, "Ethernet" }, 291 { IFM_TOKEN, "Tokenring" }, 292 { IFM_FDDI, "FDDI" }, 293 { IFM_IEEE80211, "802.11" }, 294 { IFM_ATM, "ATM" }, 295 { -1, "unknown" }, 296 { 0, NULL }, 297 }; 298 for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 299 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 300 _type = media_types[i].ifmt_word; 301 break; 302 } 303} 304 305media::~media() 306{ 307} 308 309bool 310media::do_match(config &c) 311{ 312 string value; 313 struct ifmediareq ifmr; 314 bool retval; 315 int s; 316 317 // Since we can be called from both a device attach/detach 318 // context where device-name is defined and what we want, 319 // as well as from a link status context, where subsystem is 320 // the name of interest, first try device-name and fall back 321 // to subsystem if none exists. 322 value = c.get_variable("device-name"); 323 if (value.empty()) 324 value = c.get_variable("subsystem"); 325 if (Dflag) 326 fprintf(stderr, "Testing media type of %s against 0x%x\n", 327 value.c_str(), _type); 328 329 retval = false; 330 331 s = socket(PF_INET, SOCK_DGRAM, 0); 332 if (s >= 0) { 333 memset(&ifmr, 0, sizeof(ifmr)); 334 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 335 336 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 337 ifmr.ifm_status & IFM_AVALID) { 338 if (Dflag) 339 fprintf(stderr, "%s has media type 0x%x\n", 340 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 341 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 342 } else if (_type == -1) { 343 if (Dflag) 344 fprintf(stderr, "%s has unknown media type\n", 345 value.c_str()); 346 retval = true; 347 } 348 close(s); 349 } 350 351 return retval; 352} 353 354const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 355const string var_list::nothing = ""; 356 357const string & 358var_list::get_variable(const string &var) const 359{ 360 map<string, string>::const_iterator i; 361 362 i = _vars.find(var); 363 if (i == _vars.end()) 364 return (var_list::bogus); 365 return (i->second); 366} 367 368bool 369var_list::is_set(const string &var) const 370{ 371 return (_vars.find(var) != _vars.end()); 372} 373 374void 375var_list::set_variable(const string &var, const string &val) 376{ 377 if (Dflag) 378 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 379 _vars[var] = val; 380} 381 382void 383config::reset(void) 384{ 385 _dir_list.clear(); 386 delete_and_clear(_var_list_table); 387 delete_and_clear(_attach_list); 388 delete_and_clear(_detach_list); 389 delete_and_clear(_nomatch_list); 390 delete_and_clear(_notify_list); 391} 392 393void 394config::parse_one_file(const char *fn) 395{ 396 if (Dflag) 397 fprintf(stderr, "Parsing %s\n", fn); 398 yyin = fopen(fn, "r"); 399 if (yyin == NULL) 400 err(1, "Cannot open config file %s", fn); 401 lineno = 1; 402 if (yyparse() != 0) 403 errx(1, "Cannot parse %s at line %d", fn, lineno); 404 fclose(yyin); 405} 406 407void 408config::parse_files_in_dir(const char *dirname) 409{ 410 DIR *dirp; 411 struct dirent *dp; 412 char path[PATH_MAX]; 413 414 if (Dflag) 415 fprintf(stderr, "Parsing files in %s\n", dirname); 416 dirp = opendir(dirname); 417 if (dirp == NULL) 418 return; 419 readdir(dirp); /* Skip . */ 420 readdir(dirp); /* Skip .. */ 421 while ((dp = readdir(dirp)) != NULL) { 422 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 423 snprintf(path, sizeof(path), "%s/%s", 424 dirname, dp->d_name); 425 parse_one_file(path); 426 } 427 } 428 closedir(dirp); 429} 430 431class epv_greater { 432public: 433 int operator()(event_proc *const&l1, event_proc *const&l2) const 434 { 435 return (l1->get_priority() > l2->get_priority()); 436 } 437}; 438 439void 440config::sort_vector(vector<event_proc *> &v) 441{ 442 stable_sort(v.begin(), v.end(), epv_greater()); 443} 444 445void 446config::parse(void) 447{ 448 vector<string>::const_iterator i; 449 450 parse_one_file(configfile); 451 for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 452 parse_files_in_dir((*i).c_str()); 453 sort_vector(_attach_list); 454 sort_vector(_detach_list); 455 sort_vector(_nomatch_list); 456 sort_vector(_notify_list); 457} 458 459void 460config::open_pidfile() 461{ 462 pid_t otherpid; 463 464 if (_pidfile.empty()) 465 return; 466 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 467 if (pfh == NULL) { 468 if (errno == EEXIST) 469 errx(1, "devd already running, pid: %d", (int)otherpid); 470 warn("cannot open pid file"); 471 } 472} 473 474void 475config::write_pidfile() 476{ 477 478 pidfile_write(pfh); 479} 480 481void 482config::close_pidfile() 483{ 484 485 pidfile_close(pfh); 486} 487 488void 489config::remove_pidfile() 490{ 491 492 pidfile_remove(pfh); 493} 494 495void 496config::add_attach(int prio, event_proc *p) 497{ 498 p->set_priority(prio); 499 _attach_list.push_back(p); 500} 501 502void 503config::add_detach(int prio, event_proc *p) 504{ 505 p->set_priority(prio); 506 _detach_list.push_back(p); 507} 508 509void 510config::add_directory(const char *dir) 511{ 512 _dir_list.push_back(string(dir)); 513} 514 515void 516config::add_nomatch(int prio, event_proc *p) 517{ 518 p->set_priority(prio); 519 _nomatch_list.push_back(p); 520} 521 522void 523config::add_notify(int prio, event_proc *p) 524{ 525 p->set_priority(prio); 526 _notify_list.push_back(p); 527} 528 529void 530config::set_pidfile(const char *fn) 531{ 532 _pidfile = fn; 533} 534 535void 536config::push_var_table() 537{ 538 var_list *vl; 539 540 vl = new var_list(); 541 _var_list_table.push_back(vl); 542 if (Dflag) 543 fprintf(stderr, "Pushing table\n"); 544} 545 546void 547config::pop_var_table() 548{ 549 delete _var_list_table.back(); 550 _var_list_table.pop_back(); 551 if (Dflag) 552 fprintf(stderr, "Popping table\n"); 553} 554 555void 556config::set_variable(const char *var, const char *val) 557{ 558 _var_list_table.back()->set_variable(var, val); 559} 560 561const string & 562config::get_variable(const string &var) 563{ 564 vector<var_list *>::reverse_iterator i; 565 566 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 567 if ((*i)->is_set(var)) 568 return ((*i)->get_variable(var)); 569 } 570 return (var_list::nothing); 571} 572 573bool 574config::is_id_char(char ch) const 575{ 576 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 577 ch == '-')); 578} 579 580void 581config::expand_one(const char *&src, string &dst) 582{ 583 int count; 584 string buffer; 585 586 src++; 587 // $$ -> $ 588 if (*src == '$') { 589 dst += *src++; 590 return; 591 } 592 593 // $(foo) -> $(foo) 594 // Not sure if I want to support this or not, so for now we just pass 595 // it through. 596 if (*src == '(') { 597 dst += '$'; 598 count = 1; 599 /* If the string ends before ) is matched , return. */ 600 while (count > 0 && *src) { 601 if (*src == ')') 602 count--; 603 else if (*src == '(') 604 count++; 605 dst += *src++; 606 } 607 return; 608 } 609 610 // $[^A-Za-z] -> $\1 611 if (!isalpha(*src)) { 612 dst += '$'; 613 dst += *src++; 614 return; 615 } 616 617 // $var -> replace with value 618 do { 619 buffer += *src++; 620 } while (is_id_char(*src)); 621 dst.append(get_variable(buffer)); 622} 623 624const string 625config::expand_string(const char *src, const char *prepend, const char *append) 626{ 627 const char *var_at; 628 string dst; 629 630 /* 631 * 128 bytes is enough for 2427 of 2438 expansions that happen 632 * while parsing config files, as tested on 2013-01-30. 633 */ 634 dst.reserve(128); 635 636 if (prepend != NULL) 637 dst = prepend; 638 639 for (;;) { 640 var_at = strchr(src, '$'); 641 if (var_at == NULL) { 642 dst.append(src); 643 break; 644 } 645 dst.append(src, var_at - src); 646 src = var_at; 647 expand_one(src, dst); 648 } 649 650 if (append != NULL) 651 dst.append(append); 652 653 return (dst); 654} 655 656bool 657config::chop_var(char *&buffer, char *&lhs, char *&rhs) const 658{ 659 char *walker; 660 661 if (*buffer == '\0') 662 return (false); 663 walker = lhs = buffer; 664 while (is_id_char(*walker)) 665 walker++; 666 if (*walker != '=') 667 return (false); 668 walker++; // skip = 669 if (*walker == '"') { 670 walker++; // skip " 671 rhs = walker; 672 while (*walker && *walker != '"') 673 walker++; 674 if (*walker != '"') 675 return (false); 676 rhs[-2] = '\0'; 677 *walker++ = '\0'; 678 } else { 679 rhs = walker; 680 while (*walker && !isspace(*walker)) 681 walker++; 682 if (*walker != '\0') 683 *walker++ = '\0'; 684 rhs[-1] = '\0'; 685 } 686 while (isspace(*walker)) 687 walker++; 688 buffer = walker; 689 return (true); 690} 691 692 693char * 694config::set_vars(char *buffer) 695{ 696 char *lhs; 697 char *rhs; 698 699 while (1) { 700 if (!chop_var(buffer, lhs, rhs)) 701 break; 702 set_variable(lhs, rhs); 703 } 704 return (buffer); 705} 706 707void 708config::find_and_execute(char type) 709{ 710 vector<event_proc *> *l; 711 vector<event_proc *>::const_iterator i; 712 const char *s; 713 714 switch (type) { 715 default: 716 return; 717 case notify: 718 l = &_notify_list; 719 s = "notify"; 720 break; 721 case nomatch: 722 l = &_nomatch_list; 723 s = "nomatch"; 724 break; 725 case attach: 726 l = &_attach_list; 727 s = "attach"; 728 break; 729 case detach: 730 l = &_detach_list; 731 s = "detach"; 732 break; 733 } 734 if (Dflag) 735 fprintf(stderr, "Processing %s event\n", s); 736 for (i = l->begin(); i != l->end(); ++i) { 737 if ((*i)->matches(*this)) { 738 (*i)->run(*this); 739 break; 740 } 741 } 742 743} 744 745 746static void 747process_event(char *buffer) 748{ 749 char type; 750 char *sp; 751 752 sp = buffer + 1; 753 if (Dflag) 754 fprintf(stderr, "Processing event '%s'\n", buffer); 755 type = *buffer++; 756 cfg.push_var_table(); 757 // No match doesn't have a device, and the format is a little 758 // different, so handle it separately. 759 switch (type) { 760 case notify: 761 sp = cfg.set_vars(sp); 762 break; 763 case nomatch: 764 //? at location pnp-info on bus 765 sp = strchr(sp, ' '); 766 if (sp == NULL) 767 return; /* Can't happen? */ 768 *sp++ = '\0'; 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 case attach: /*FALLTHROUGH*/ 780 case detach: 781 sp = strchr(sp, ' '); 782 if (sp == NULL) 783 return; /* Can't happen? */ 784 *sp++ = '\0'; 785 cfg.set_variable("device-name", buffer); 786 while (isspace(*sp)) 787 sp++; 788 if (strncmp(sp, "at ", 3) == 0) 789 sp += 3; 790 sp = cfg.set_vars(sp); 791 while (isspace(*sp)) 792 sp++; 793 if (strncmp(sp, "on ", 3) == 0) 794 cfg.set_variable("bus", sp + 3); 795 break; 796 } 797 798 cfg.find_and_execute(type); 799 cfg.pop_var_table(); 800} 801 802int 803create_socket(const char *name) 804{ 805 int fd, slen; 806 struct sockaddr_un sun; 807 808 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 809 err(1, "socket"); 810 bzero(&sun, sizeof(sun)); 811 sun.sun_family = AF_UNIX; 812 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 813 slen = SUN_LEN(&sun); 814 unlink(name); 815 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 816 err(1, "fcntl"); 817 if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 818 err(1, "bind"); 819 listen(fd, 4); 820 chown(name, 0, 0); /* XXX - root.wheel */ 821 chmod(name, 0666); 822 return (fd); 823} 824 825unsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 826unsigned int num_clients; 827list<int> clients; 828 829void 830notify_clients(const char *data, int len) 831{ 832 list<int>::iterator i; 833 834 /* 835 * Deliver the data to all clients. Throw clients overboard at the 836 * first sign of trouble. This reaps clients who've died or closed 837 * their sockets, and also clients who are alive but failing to keep up 838 * (or who are maliciously not reading, to consume buffer space in 839 * kernel memory or tie up the limited number of available connections). 840 */ 841 for (i = clients.begin(); i != clients.end(); ) { 842 if (write(*i, data, len) != len) { 843 --num_clients; 844 close(*i); 845 i = clients.erase(i); 846 } else 847 ++i; 848 } 849} 850 851void 852check_clients(void) 853{ 854 int s; 855 struct pollfd pfd; 856 list<int>::iterator i; 857 858 /* 859 * Check all existing clients to see if any of them have disappeared. 860 * Normally we reap clients when we get an error trying to send them an 861 * event. This check eliminates the problem of an ever-growing list of 862 * zombie clients because we're never writing to them on a system 863 * without frequent device-change activity. 864 */ 865 pfd.events = 0; 866 for (i = clients.begin(); i != clients.end(); ) { 867 pfd.fd = *i; 868 s = poll(&pfd, 1, 0); 869 if ((s < 0 && s != EINTR ) || 870 (s > 0 && (pfd.revents & POLLHUP))) { 871 --num_clients; 872 close(*i); 873 i = clients.erase(i); 874 } else 875 ++i; 876 } 877} 878 879void 880new_client(int fd) 881{ 882 int s; 883 884 /* 885 * First go reap any zombie clients, then accept the connection, and 886 * shut down the read side to stop clients from consuming kernel memory 887 * by sending large buffers full of data we'll never read. 888 */ 889 check_clients(); 890 s = accept(fd, NULL, NULL); 891 if (s != -1) { 892 shutdown(s, SHUT_RD); 893 clients.push_back(s); 894 ++num_clients; 895 } 896} 897 898static void 899event_loop(void) 900{ 901 int rv; 902 int fd; 903 char buffer[DEVCTL_MAXBUF]; 904 int once = 0; 905 int server_fd, max_fd; 906 int accepting; 907 timeval tv; 908 fd_set fds; 909 910 fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 911 if (fd == -1) 912 err(1, "Can't open devctl device %s", PATH_DEVCTL); 913 server_fd = create_socket(PIPE); 914 accepting = 1; 915 max_fd = max(fd, server_fd) + 1; 916 while (!romeo_must_die) { 917 if (!once && !dflag && !nflag) { 918 // Check to see if we have any events pending. 919 tv.tv_sec = 0; 920 tv.tv_usec = 0; 921 FD_ZERO(&fds); 922 FD_SET(fd, &fds); 923 rv = select(fd + 1, &fds, &fds, &fds, &tv); 924 // No events -> we've processed all pending events 925 if (rv == 0) { 926 if (Dflag) 927 fprintf(stderr, "Calling daemon\n"); 928 cfg.remove_pidfile(); 929 cfg.open_pidfile(); 930 daemon(0, 0); 931 cfg.write_pidfile(); 932 once++; 933 } 934 } 935 /* 936 * When we've already got the max number of clients, stop 937 * accepting new connections (don't put server_fd in the set), 938 * shrink the accept() queue to reject connections quickly, and 939 * poll the existing clients more often, so that we notice more 940 * quickly when any of them disappear to free up client slots. 941 */ 942 FD_ZERO(&fds); 943 FD_SET(fd, &fds); 944 if (num_clients < max_clients) { 945 if (!accepting) { 946 listen(server_fd, max_clients); 947 accepting = 1; 948 } 949 FD_SET(server_fd, &fds); 950 tv.tv_sec = 60; 951 tv.tv_usec = 0; 952 } else { 953 if (accepting) { 954 listen(server_fd, 0); 955 accepting = 0; 956 } 957 tv.tv_sec = 2; 958 tv.tv_usec = 0; 959 } 960 rv = select(max_fd, &fds, NULL, NULL, &tv); 961 if (rv == -1) { 962 if (errno == EINTR) 963 continue; 964 err(1, "select"); 965 } else if (rv == 0) 966 check_clients(); 967 if (FD_ISSET(fd, &fds)) { 968 rv = read(fd, buffer, sizeof(buffer) - 1); 969 if (rv > 0) { 970 notify_clients(buffer, rv); 971 buffer[rv] = '\0'; 972 while (buffer[--rv] == '\n') 973 buffer[rv] = '\0'; 974 process_event(buffer); 975 } else if (rv < 0) { 976 if (errno != EINTR) 977 break; 978 } else { 979 /* EOF */ 980 break; 981 } 982 } 983 if (FD_ISSET(server_fd, &fds)) 984 new_client(server_fd); 985 } 986 close(fd); 987} 988 989/* 990 * functions that the parser uses. 991 */ 992void 993add_attach(int prio, event_proc *p) 994{ 995 cfg.add_attach(prio, p); 996} 997 998void 999add_detach(int prio, event_proc *p) 1000{ 1001 cfg.add_detach(prio, p); 1002} 1003 1004void 1005add_directory(const char *dir) 1006{ 1007 cfg.add_directory(dir); 1008 free(const_cast<char *>(dir)); 1009} 1010 1011void 1012add_nomatch(int prio, event_proc *p) 1013{ 1014 cfg.add_nomatch(prio, p); 1015} 1016 1017void 1018add_notify(int prio, event_proc *p) 1019{ 1020 cfg.add_notify(prio, p); 1021} 1022 1023event_proc * 1024add_to_event_proc(event_proc *ep, eps *eps) 1025{ 1026 if (ep == NULL) 1027 ep = new event_proc(); 1028 ep->add(eps); 1029 return (ep); 1030} 1031 1032eps * 1033new_action(const char *cmd) 1034{ 1035 eps *e = new action(cmd); 1036 free(const_cast<char *>(cmd)); 1037 return (e); 1038} 1039 1040eps * 1041new_match(const char *var, const char *re) 1042{ 1043 eps *e = new match(cfg, var, re); 1044 free(const_cast<char *>(var)); 1045 free(const_cast<char *>(re)); 1046 return (e); 1047} 1048 1049eps * 1050new_media(const char *var, const char *re) 1051{ 1052 eps *e = new media(cfg, var, re); 1053 free(const_cast<char *>(var)); 1054 free(const_cast<char *>(re)); 1055 return (e); 1056} 1057 1058void 1059set_pidfile(const char *name) 1060{ 1061 cfg.set_pidfile(name); 1062 free(const_cast<char *>(name)); 1063} 1064 1065void 1066set_variable(const char *var, const char *val) 1067{ 1068 cfg.set_variable(var, val); 1069 free(const_cast<char *>(var)); 1070 free(const_cast<char *>(val)); 1071} 1072 1073 1074 1075static void 1076gensighand(int) 1077{ 1078 romeo_must_die = 1; 1079} 1080 1081static void 1082usage() 1083{ 1084 fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n", 1085 getprogname()); 1086 exit(1); 1087} 1088 1089static void 1090check_devd_enabled() 1091{ 1092 int val = 0; 1093 size_t len; 1094 1095 len = sizeof(val); 1096 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1097 errx(1, "devctl sysctl missing from kernel!"); 1098 if (val) { 1099 warnx("Setting " SYSCTL " to 0"); 1100 val = 0; 1101 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1102 } 1103} 1104 1105/* 1106 * main 1107 */ 1108int 1109main(int argc, char **argv) 1110{ 1111 int ch; 1112 1113 check_devd_enabled(); 1114 while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) { 1115 switch (ch) { 1116 case 'D': 1117 Dflag++; 1118 break; 1119 case 'd': 1120 dflag++; 1121 break; 1122 case 'f': 1123 configfile = optarg; 1124 break; 1125 case 'l': 1126 max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1127 break; 1128 case 'n': 1129 nflag++; 1130 break; 1131 default: 1132 usage(); 1133 } 1134 } 1135 1136 cfg.parse(); 1137 if (!dflag && nflag) { 1138 cfg.open_pidfile(); 1139 daemon(0, 0); 1140 cfg.write_pidfile(); 1141 } 1142 signal(SIGPIPE, SIG_IGN); 1143 signal(SIGHUP, gensighand); 1144 signal(SIGINT, gensighand); 1145 signal(SIGTERM, gensighand); 1146 event_loop(); 1147 return (0); 1148} 1149