devd.cc revision 226775
1219820Sjeff/*- 2219820Sjeff * Copyright (c) 2002-2010 M. Warner Losh. 3219820Sjeff * All rights reserved. 4219820Sjeff * 5219820Sjeff * Redistribution and use in source and binary forms, with or without 6219820Sjeff * modification, are permitted provided that the following conditions 7219820Sjeff * are met: 8219820Sjeff * 1. Redistributions of source code must retain the above copyright 9219820Sjeff * notice, this list of conditions and the following disclaimer. 10219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 11219820Sjeff * notice, this list of conditions and the following disclaimer in the 12219820Sjeff * documentation and/or other materials provided with the distribution. 13219820Sjeff * 14219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17219820Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24219820Sjeff * SUCH DAMAGE. 25219820Sjeff * 26219820Sjeff * my_system is a variation on lib/libc/stdlib/system.c: 27219820Sjeff * 28219820Sjeff * Copyright (c) 1988, 1993 29219820Sjeff * The Regents of the University of California. All rights reserved. 30219820Sjeff * 31219820Sjeff * Redistribution and use in source and binary forms, with or without 32219820Sjeff * modification, are permitted provided that the following conditions 33219820Sjeff * are met: 34219820Sjeff * 1. Redistributions of source code must retain the above copyright 35219820Sjeff * notice, this list of conditions and the following disclaimer. 36219820Sjeff * 2. Redistributions in binary form must reproduce the above copyright 37219820Sjeff * notice, this list of conditions and the following disclaimer in the 38219820Sjeff * documentation and/or other materials provided with the distribution. 39219820Sjeff * 4. Neither the name of the University nor the names of its contributors 40219820Sjeff * may be used to endorse or promote products derived from this software 41219820Sjeff * without specific prior written permission. 42219820Sjeff * 43219820Sjeff * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44219820Sjeff * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45219820Sjeff * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46219820Sjeff * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47219820Sjeff * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48219820Sjeff * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49219820Sjeff * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50219820Sjeff * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51219820Sjeff * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52219820Sjeff * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53219820Sjeff * SUCH DAMAGE. 54219820Sjeff */ 55219820Sjeff 56219820Sjeff/* 57219820Sjeff * DEVD control daemon. 58219820Sjeff */ 59219820Sjeff 60219820Sjeff// TODO list: 61219820Sjeff// o devd.conf and devd man pages need a lot of help: 62219820Sjeff// - devd needs to document the unix domain socket 63219820Sjeff// - devd.conf needs more details on the supported statements. 64219820Sjeff 65219820Sjeff#include <sys/cdefs.h> 66219820Sjeff__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 226775 2011-10-26 02:11:28Z hrs $"); 67219820Sjeff 68219820Sjeff#include <sys/param.h> 69219820Sjeff#include <sys/socket.h> 70219820Sjeff#include <sys/stat.h> 71219820Sjeff#include <sys/sysctl.h> 72219820Sjeff#include <sys/types.h> 73219820Sjeff#include <sys/wait.h> 74219820Sjeff#include <sys/un.h> 75219820Sjeff 76219820Sjeff#include <ctype.h> 77219820Sjeff#include <dirent.h> 78219820Sjeff#include <errno.h> 79219820Sjeff#include <err.h> 80219820Sjeff#include <fcntl.h> 81219820Sjeff#include <libutil.h> 82219820Sjeff#include <paths.h> 83219820Sjeff#include <regex.h> 84219820Sjeff#include <signal.h> 85219820Sjeff#include <stdlib.h> 86219820Sjeff#include <stdio.h> 87219820Sjeff#include <string.h> 88219820Sjeff#include <unistd.h> 89219820Sjeff 90219820Sjeff#include <algorithm> 91219820Sjeff#include <map> 92219820Sjeff#include <string> 93219820Sjeff#include <list> 94219820Sjeff#include <vector> 95219820Sjeff 96219820Sjeff#include "devd.h" /* C compatible definitions */ 97219820Sjeff#include "devd.hh" /* C++ class definitions */ 98219820Sjeff 99219820Sjeff#define PIPE "/var/run/devd.pipe" 100219820Sjeff#define CF "/etc/devd.conf" 101219820Sjeff#define SYSCTL "hw.bus.devctl_disable" 102219820Sjeff 103219820Sjeffusing namespace std; 104219820Sjeff 105219820Sjeffextern FILE *yyin; 106219820Sjeffextern int lineno; 107219820Sjeff 108219820Sjeffstatic const char notify = '!'; 109219820Sjeffstatic const char nomatch = '?'; 110219820Sjeffstatic const char attach = '+'; 111219820Sjeffstatic const char detach = '-'; 112219820Sjeff 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 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 { IFM_CARP, "CARP" }, 302 { -1, "unknown" }, 303 { 0, NULL }, 304 }; 305 for (int i = 0; media_types[i].ifmt_string != NULL; i++) 306 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 307 _type = media_types[i].ifmt_word; 308 break; 309 } 310} 311 312media::~media() 313{ 314} 315 316bool 317media::do_match(config &c) 318{ 319 string value; 320 struct ifmediareq ifmr; 321 bool retval; 322 int s; 323 324 // Since we can be called from both a device attach/detach 325 // context where device-name is defined and what we want, 326 // as well as from a link status context, where subsystem is 327 // the name of interest, first try device-name and fall back 328 // to subsystem if none exists. 329 value = c.get_variable("device-name"); 330 if (value.length() == 0) 331 value = c.get_variable("subsystem"); 332 if (Dflag) 333 fprintf(stderr, "Testing media type of %s against 0x%x\n", 334 value.c_str(), _type); 335 336 retval = false; 337 338 s = socket(PF_INET, SOCK_DGRAM, 0); 339 if (s >= 0) { 340 memset(&ifmr, 0, sizeof(ifmr)); 341 strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 342 343 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 344 ifmr.ifm_status & IFM_AVALID) { 345 if (Dflag) 346 fprintf(stderr, "%s has media type 0x%x\n", 347 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 348 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 349 } else if (_type == -1) { 350 if (Dflag) 351 fprintf(stderr, "%s has unknown media type\n", 352 value.c_str()); 353 retval = true; 354 } 355 close(s); 356 } 357 358 return retval; 359} 360 361const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 362const string var_list::nothing = ""; 363 364const string & 365var_list::get_variable(const string &var) const 366{ 367 map<string, string>::const_iterator i; 368 369 i = _vars.find(var); 370 if (i == _vars.end()) 371 return (var_list::bogus); 372 return (i->second); 373} 374 375bool 376var_list::is_set(const string &var) const 377{ 378 return (_vars.find(var) != _vars.end()); 379} 380 381void 382var_list::set_variable(const string &var, const string &val) 383{ 384 if (Dflag) 385 fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 386 _vars[var] = val; 387} 388 389void 390config::reset(void) 391{ 392 _dir_list.clear(); 393 delete_and_clear(_var_list_table); 394 delete_and_clear(_attach_list); 395 delete_and_clear(_detach_list); 396 delete_and_clear(_nomatch_list); 397 delete_and_clear(_notify_list); 398} 399 400void 401config::parse_one_file(const char *fn) 402{ 403 if (Dflag) 404 fprintf(stderr, "Parsing %s\n", fn); 405 yyin = fopen(fn, "r"); 406 if (yyin == NULL) 407 err(1, "Cannot open config file %s", fn); 408 lineno = 1; 409 if (yyparse() != 0) 410 errx(1, "Cannot parse %s at line %d", fn, lineno); 411 fclose(yyin); 412} 413 414void 415config::parse_files_in_dir(const char *dirname) 416{ 417 DIR *dirp; 418 struct dirent *dp; 419 char path[PATH_MAX]; 420 421 if (Dflag) 422 fprintf(stderr, "Parsing files in %s\n", dirname); 423 dirp = opendir(dirname); 424 if (dirp == NULL) 425 return; 426 readdir(dirp); /* Skip . */ 427 readdir(dirp); /* Skip .. */ 428 while ((dp = readdir(dirp)) != NULL) { 429 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 430 snprintf(path, sizeof(path), "%s/%s", 431 dirname, dp->d_name); 432 parse_one_file(path); 433 } 434 } 435 closedir(dirp); 436} 437 438class epv_greater { 439public: 440 int operator()(event_proc *const&l1, event_proc *const&l2) 441 { 442 return (l1->get_priority() > l2->get_priority()); 443 } 444}; 445 446void 447config::sort_vector(vector<event_proc *> &v) 448{ 449 sort(v.begin(), v.end(), epv_greater()); 450} 451 452void 453config::parse(void) 454{ 455 vector<string>::const_iterator i; 456 457 parse_one_file(configfile); 458 for (i = _dir_list.begin(); i != _dir_list.end(); i++) 459 parse_files_in_dir((*i).c_str()); 460 sort_vector(_attach_list); 461 sort_vector(_detach_list); 462 sort_vector(_nomatch_list); 463 sort_vector(_notify_list); 464} 465 466void 467config::open_pidfile() 468{ 469 pid_t otherpid; 470 471 if (_pidfile == "") 472 return; 473 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 474 if (pfh == NULL) { 475 if (errno == EEXIST) 476 errx(1, "devd already running, pid: %d", (int)otherpid); 477 warn("cannot open pid file"); 478 } 479} 480 481void 482config::write_pidfile() 483{ 484 485 pidfile_write(pfh); 486} 487 488void 489config::close_pidfile() 490{ 491 492 pidfile_close(pfh); 493} 494 495void 496config::remove_pidfile() 497{ 498 499 pidfile_remove(pfh); 500} 501 502void 503config::add_attach(int prio, event_proc *p) 504{ 505 p->set_priority(prio); 506 _attach_list.push_back(p); 507} 508 509void 510config::add_detach(int prio, event_proc *p) 511{ 512 p->set_priority(prio); 513 _detach_list.push_back(p); 514} 515 516void 517config::add_directory(const char *dir) 518{ 519 _dir_list.push_back(string(dir)); 520} 521 522void 523config::add_nomatch(int prio, event_proc *p) 524{ 525 p->set_priority(prio); 526 _nomatch_list.push_back(p); 527} 528 529void 530config::add_notify(int prio, event_proc *p) 531{ 532 p->set_priority(prio); 533 _notify_list.push_back(p); 534} 535 536void 537config::set_pidfile(const char *fn) 538{ 539 _pidfile = string(fn); 540} 541 542void 543config::push_var_table() 544{ 545 var_list *vl; 546 547 vl = new var_list(); 548 _var_list_table.push_back(vl); 549 if (Dflag) 550 fprintf(stderr, "Pushing table\n"); 551} 552 553void 554config::pop_var_table() 555{ 556 delete _var_list_table.back(); 557 _var_list_table.pop_back(); 558 if (Dflag) 559 fprintf(stderr, "Popping table\n"); 560} 561 562void 563config::set_variable(const char *var, const char *val) 564{ 565 _var_list_table.back()->set_variable(var, val); 566} 567 568const string & 569config::get_variable(const string &var) 570{ 571 vector<var_list *>::reverse_iterator i; 572 573 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 574 if ((*i)->is_set(var)) 575 return ((*i)->get_variable(var)); 576 } 577 return (var_list::nothing); 578} 579 580bool 581config::is_id_char(char ch) 582{ 583 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 584 ch == '-')); 585} 586 587void 588config::expand_one(const char *&src, string &dst) 589{ 590 int count; 591 string buffer; 592 593 src++; 594 // $$ -> $ 595 if (*src == '$') { 596 dst.append(src++, 1); 597 return; 598 } 599 600 // $(foo) -> $(foo) 601 // Not sure if I want to support this or not, so for now we just pass 602 // it through. 603 if (*src == '(') { 604 dst.append("$"); 605 count = 1; 606 /* If the string ends before ) is matched , return. */ 607 while (count > 0 && *src) { 608 if (*src == ')') 609 count--; 610 else if (*src == '(') 611 count++; 612 dst.append(src++, 1); 613 } 614 return; 615 } 616 617 // ${^A-Za-z] -> $\1 618 if (!isalpha(*src)) { 619 dst.append("$"); 620 dst.append(src++, 1); 621 return; 622 } 623 624 // $var -> replace with value 625 do { 626 buffer.append(src++, 1); 627 } while (is_id_char(*src)); 628 buffer.append("", 1); 629 dst.append(get_variable(buffer.c_str())); 630} 631 632const string 633config::expand_string(const string &s) 634{ 635 const char *src; 636 string dst; 637 638 src = s.c_str(); 639 while (*src) { 640 if (*src == '$') 641 expand_one(src, dst); 642 else 643 dst.append(src++, 1); 644 } 645 dst.append("", 1); 646 647 return (dst); 648} 649 650bool 651config::chop_var(char *&buffer, char *&lhs, char *&rhs) 652{ 653 char *walker; 654 655 if (*buffer == '\0') 656 return (false); 657 walker = lhs = buffer; 658 while (is_id_char(*walker)) 659 walker++; 660 if (*walker != '=') 661 return (false); 662 walker++; // skip = 663 if (*walker == '"') { 664 walker++; // skip " 665 rhs = walker; 666 while (*walker && *walker != '"') 667 walker++; 668 if (*walker != '"') 669 return (false); 670 rhs[-2] = '\0'; 671 *walker++ = '\0'; 672 } else { 673 rhs = walker; 674 while (*walker && !isspace(*walker)) 675 walker++; 676 if (*walker != '\0') 677 *walker++ = '\0'; 678 rhs[-1] = '\0'; 679 } 680 while (isspace(*walker)) 681 walker++; 682 buffer = walker; 683 return (true); 684} 685 686 687char * 688config::set_vars(char *buffer) 689{ 690 char *lhs; 691 char *rhs; 692 693 while (1) { 694 if (!chop_var(buffer, lhs, rhs)) 695 break; 696 set_variable(lhs, rhs); 697 } 698 return (buffer); 699} 700 701void 702config::find_and_execute(char type) 703{ 704 vector<event_proc *> *l; 705 vector<event_proc *>::const_iterator i; 706 const char *s; 707 708 switch (type) { 709 default: 710 return; 711 case notify: 712 l = &_notify_list; 713 s = "notify"; 714 break; 715 case nomatch: 716 l = &_nomatch_list; 717 s = "nomatch"; 718 break; 719 case attach: 720 l = &_attach_list; 721 s = "attach"; 722 break; 723 case detach: 724 l = &_detach_list; 725 s = "detach"; 726 break; 727 } 728 if (Dflag) 729 fprintf(stderr, "Processing %s event\n", s); 730 for (i = l->begin(); i != l->end(); i++) { 731 if ((*i)->matches(*this)) { 732 (*i)->run(*this); 733 break; 734 } 735 } 736 737} 738 739 740static void 741process_event(char *buffer) 742{ 743 char type; 744 char *sp; 745 746 sp = buffer + 1; 747 if (Dflag) 748 fprintf(stderr, "Processing event '%s'\n", buffer); 749 type = *buffer++; 750 cfg.push_var_table(); 751 // No match doesn't have a device, and the format is a little 752 // different, so handle it separately. 753 switch (type) { 754 case notify: 755 sp = cfg.set_vars(sp); 756 break; 757 case nomatch: 758 //? at location pnp-info on bus 759 sp = strchr(sp, ' '); 760 if (sp == NULL) 761 return; /* Can't happen? */ 762 *sp++ = '\0'; 763 while (isspace(*sp)) 764 sp++; 765 if (strncmp(sp, "at ", 3) == 0) 766 sp += 3; 767 sp = cfg.set_vars(sp); 768 while (isspace(*sp)) 769 sp++; 770 if (strncmp(sp, "on ", 3) == 0) 771 cfg.set_variable("bus", sp + 3); 772 break; 773 case attach: /*FALLTHROUGH*/ 774 case detach: 775 sp = strchr(sp, ' '); 776 if (sp == NULL) 777 return; /* Can't happen? */ 778 *sp++ = '\0'; 779 cfg.set_variable("device-name", buffer); 780 while (isspace(*sp)) 781 sp++; 782 if (strncmp(sp, "at ", 3) == 0) 783 sp += 3; 784 sp = cfg.set_vars(sp); 785 while (isspace(*sp)) 786 sp++; 787 if (strncmp(sp, "on ", 3) == 0) 788 cfg.set_variable("bus", sp + 3); 789 break; 790 } 791 792 cfg.find_and_execute(type); 793 cfg.pop_var_table(); 794} 795 796int 797create_socket(const char *name) 798{ 799 int fd, slen; 800 struct sockaddr_un sun; 801 802 if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 803 err(1, "socket"); 804 bzero(&sun, sizeof(sun)); 805 sun.sun_family = AF_UNIX; 806 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 807 slen = SUN_LEN(&sun); 808 unlink(name); 809 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 810 err(1, "fcntl"); 811 if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 812 err(1, "bind"); 813 listen(fd, 4); 814 chown(name, 0, 0); /* XXX - root.wheel */ 815 chmod(name, 0666); 816 return (fd); 817} 818 819list<int> clients; 820 821void 822notify_clients(const char *data, int len) 823{ 824 list<int> bad; 825 list<int>::const_iterator i; 826 827 for (i = clients.begin(); i != clients.end(); i++) { 828 if (write(*i, data, len) <= 0) { 829 bad.push_back(*i); 830 close(*i); 831 } 832 } 833 834 for (i = bad.begin(); i != bad.end(); i++) 835 clients.erase(find(clients.begin(), clients.end(), *i)); 836} 837 838void 839new_client(int fd) 840{ 841 int s; 842 843 s = accept(fd, NULL, NULL); 844 if (s != -1) 845 clients.push_back(s); 846} 847 848static void 849event_loop(void) 850{ 851 int rv; 852 int fd; 853 char buffer[DEVCTL_MAXBUF]; 854 int once = 0; 855 int server_fd, max_fd; 856 timeval tv; 857 fd_set fds; 858 859 fd = open(PATH_DEVCTL, O_RDONLY); 860 if (fd == -1) 861 err(1, "Can't open devctl device %s", PATH_DEVCTL); 862 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 863 err(1, "Can't set close-on-exec flag on devctl"); 864 server_fd = create_socket(PIPE); 865 max_fd = max(fd, server_fd) + 1; 866 while (1) { 867 if (romeo_must_die) 868 break; 869 if (!once && !dflag && !nflag) { 870 // Check to see if we have any events pending. 871 tv.tv_sec = 0; 872 tv.tv_usec = 0; 873 FD_ZERO(&fds); 874 FD_SET(fd, &fds); 875 rv = select(fd + 1, &fds, &fds, &fds, &tv); 876 // No events -> we've processed all pending events 877 if (rv == 0) { 878 if (Dflag) 879 fprintf(stderr, "Calling daemon\n"); 880 cfg.remove_pidfile(); 881 cfg.open_pidfile(); 882 daemon(0, 0); 883 cfg.write_pidfile(); 884 once++; 885 } 886 } 887 FD_ZERO(&fds); 888 FD_SET(fd, &fds); 889 FD_SET(server_fd, &fds); 890 rv = select(max_fd, &fds, NULL, NULL, NULL); 891 if (rv == -1) { 892 if (errno == EINTR) 893 continue; 894 err(1, "select"); 895 } 896 if (FD_ISSET(fd, &fds)) { 897 rv = read(fd, buffer, sizeof(buffer) - 1); 898 if (rv > 0) { 899 notify_clients(buffer, rv); 900 buffer[rv] = '\0'; 901 while (buffer[--rv] == '\n') 902 buffer[rv] = '\0'; 903 process_event(buffer); 904 } else if (rv < 0) { 905 if (errno != EINTR) 906 break; 907 } else { 908 /* EOF */ 909 break; 910 } 911 } 912 if (FD_ISSET(server_fd, &fds)) 913 new_client(server_fd); 914 } 915 close(fd); 916} 917 918/* 919 * functions that the parser uses. 920 */ 921void 922add_attach(int prio, event_proc *p) 923{ 924 cfg.add_attach(prio, p); 925} 926 927void 928add_detach(int prio, event_proc *p) 929{ 930 cfg.add_detach(prio, p); 931} 932 933void 934add_directory(const char *dir) 935{ 936 cfg.add_directory(dir); 937 free(const_cast<char *>(dir)); 938} 939 940void 941add_nomatch(int prio, event_proc *p) 942{ 943 cfg.add_nomatch(prio, p); 944} 945 946void 947add_notify(int prio, event_proc *p) 948{ 949 cfg.add_notify(prio, p); 950} 951 952event_proc * 953add_to_event_proc(event_proc *ep, eps *eps) 954{ 955 if (ep == NULL) 956 ep = new event_proc(); 957 ep->add(eps); 958 return (ep); 959} 960 961eps * 962new_action(const char *cmd) 963{ 964 eps *e = new action(cmd); 965 free(const_cast<char *>(cmd)); 966 return (e); 967} 968 969eps * 970new_match(const char *var, const char *re) 971{ 972 eps *e = new match(cfg, var, re); 973 free(const_cast<char *>(var)); 974 free(const_cast<char *>(re)); 975 return (e); 976} 977 978eps * 979new_media(const char *var, const char *re) 980{ 981 eps *e = new media(cfg, var, re); 982 free(const_cast<char *>(var)); 983 free(const_cast<char *>(re)); 984 return (e); 985} 986 987void 988set_pidfile(const char *name) 989{ 990 cfg.set_pidfile(name); 991 free(const_cast<char *>(name)); 992} 993 994void 995set_variable(const char *var, const char *val) 996{ 997 cfg.set_variable(var, val); 998 free(const_cast<char *>(var)); 999 free(const_cast<char *>(val)); 1000} 1001 1002 1003 1004static void 1005gensighand(int) 1006{ 1007 romeo_must_die++; 1008 _exit(0); 1009} 1010 1011static void 1012usage() 1013{ 1014 fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 1015 exit(1); 1016} 1017 1018static void 1019check_devd_enabled() 1020{ 1021 int val = 0; 1022 size_t len; 1023 1024 len = sizeof(val); 1025 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1026 errx(1, "devctl sysctl missing from kernel!"); 1027 if (val) { 1028 warnx("Setting " SYSCTL " to 0"); 1029 val = 0; 1030 sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1031 } 1032} 1033 1034/* 1035 * main 1036 */ 1037int 1038main(int argc, char **argv) 1039{ 1040 int ch; 1041 1042 check_devd_enabled(); 1043 while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 1044 switch (ch) { 1045 case 'D': 1046 Dflag++; 1047 break; 1048 case 'd': 1049 dflag++; 1050 break; 1051 case 'f': 1052 configfile = optarg; 1053 break; 1054 case 'n': 1055 nflag++; 1056 break; 1057 default: 1058 usage(); 1059 } 1060 } 1061 1062 cfg.parse(); 1063 if (!dflag && nflag) { 1064 cfg.open_pidfile(); 1065 daemon(0, 0); 1066 cfg.write_pidfile(); 1067 } 1068 signal(SIGPIPE, SIG_IGN); 1069 signal(SIGHUP, gensighand); 1070 signal(SIGINT, gensighand); 1071 signal(SIGTERM, gensighand); 1072 event_loop(); 1073 return (0); 1074} 1075