devd.cc revision 253046
1107665Simp/*- 2209583Simp * Copyright (c) 2002-2010 M. Warner Losh. 3107665Simp * All rights reserved. 4107665Simp * 5107665Simp * Redistribution and use in source and binary forms, with or without 6107665Simp * modification, are permitted provided that the following conditions 7107665Simp * are met: 8107665Simp * 1. Redistributions of source code must retain the above copyright 9107665Simp * notice, this list of conditions and the following disclaimer. 10107665Simp * 2. Redistributions in binary form must reproduce the above copyright 11107665Simp * notice, this list of conditions and the following disclaimer in the 12107665Simp * documentation and/or other materials provided with the distribution. 13107665Simp * 14107665Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15107665Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16107665Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17107665Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18107665Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19107665Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20107665Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21107665Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22107665Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23107665Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24107665Simp * SUCH DAMAGE. 25209583Simp * 26209583Simp * my_system is a variation on lib/libc/stdlib/system.c: 27209583Simp * 28209583Simp * Copyright (c) 1988, 1993 29209583Simp * The Regents of the University of California. All rights reserved. 30209583Simp * 31209583Simp * Redistribution and use in source and binary forms, with or without 32209583Simp * modification, are permitted provided that the following conditions 33209583Simp * are met: 34209583Simp * 1. Redistributions of source code must retain the above copyright 35209583Simp * notice, this list of conditions and the following disclaimer. 36209583Simp * 2. Redistributions in binary form must reproduce the above copyright 37209583Simp * notice, this list of conditions and the following disclaimer in the 38209583Simp * documentation and/or other materials provided with the distribution. 39209583Simp * 4. Neither the name of the University nor the names of its contributors 40209583Simp * may be used to endorse or promote products derived from this software 41209583Simp * without specific prior written permission. 42209583Simp * 43209583Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44209583Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45209583Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46209583Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47209583Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48209583Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49209583Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50209583Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51209583Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52209583Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53209583Simp * SUCH DAMAGE. 54107665Simp */ 55107665Simp 56107665Simp/* 57107665Simp * DEVD control daemon. 58107665Simp */ 59107665Simp 60107665Simp// TODO list: 61107665Simp// o devd.conf and devd man pages need a lot of help: 62131397Simp// - devd needs to document the unix domain socket 63107665Simp// - devd.conf needs more details on the supported statements. 64107665Simp 65107665Simp#include <sys/cdefs.h> 66107665Simp__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 253046 2013-07-08 21:10:30Z asomers $"); 67107665Simp 68107665Simp#include <sys/param.h> 69131397Simp#include <sys/socket.h> 70131397Simp#include <sys/stat.h> 71131397Simp#include <sys/sysctl.h> 72107665Simp#include <sys/types.h> 73209583Simp#include <sys/wait.h> 74131397Simp#include <sys/un.h> 75107665Simp 76250186Seadler#include <cctype> 77250186Seadler#include <cerrno> 78250186Seadler#include <cstdlib> 79250186Seadler#include <cstdio> 80250186Seadler#include <csignal> 81250186Seadler#include <cstring> 82252508Sasomers#include <cstdarg> 83250186Seadler 84107665Simp#include <dirent.h> 85107665Simp#include <err.h> 86107665Simp#include <fcntl.h> 87155073Spjd#include <libutil.h> 88209583Simp#include <paths.h> 89246121Sian#include <poll.h> 90108014Simp#include <regex.h> 91252481Sasomers#include <syslog.h> 92107665Simp#include <unistd.h> 93107665Simp 94108783Simp#include <algorithm> 95107665Simp#include <map> 96107665Simp#include <string> 97131397Simp#include <list> 98107665Simp#include <vector> 99107665Simp 100114086Simp#include "devd.h" /* C compatible definitions */ 101114086Simp#include "devd.hh" /* C++ class definitions */ 102107665Simp 103131397Simp#define PIPE "/var/run/devd.pipe" 104107665Simp#define CF "/etc/devd.conf" 105113787Simp#define SYSCTL "hw.bus.devctl_disable" 106107665Simp 107107665Simpusing namespace std; 108107665Simp 109107665Simpextern FILE *yyin; 110107665Simpextern int lineno; 111107665Simp 112121487Simpstatic const char notify = '!'; 113108783Simpstatic const char nomatch = '?'; 114108783Simpstatic const char attach = '+'; 115108783Simpstatic const char detach = '-'; 116108783Simp 117155073Spjdstatic struct pidfh *pfh; 118155073Spjd 119107665Simpint dflag; 120114000Simpint nflag; 121252482Sasomersstatic unsigned total_events = 0; 122252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0; 123247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 124107665Simp 125152770Sjkoshystatic const char *configfile = CF; 126152770Sjkoshy 127253046Sasomersstatic void devdlog(int priority, const char* message, ...) 128253046Sasomers __printflike(2, 3); 129107665Simpstatic void event_loop(void); 130107665Simpstatic void usage(void); 131107665Simp 132108783Simptemplate <class T> void 133108783Simpdelete_and_clear(vector<T *> &v) 134108783Simp{ 135108783Simp typename vector<T *>::const_iterator i; 136108783Simp 137243931Seadler for (i = v.begin(); i != v.end(); ++i) 138108783Simp delete *i; 139108783Simp v.clear(); 140108783Simp} 141108783Simp 142107665Simpconfig cfg; 143107665Simp 144107665Simpevent_proc::event_proc() : _prio(-1) 145107665Simp{ 146246134Sian _epsvec.reserve(4); 147107665Simp} 148107665Simp 149107665Simpevent_proc::~event_proc() 150107665Simp{ 151152406Sbland delete_and_clear(_epsvec); 152107665Simp} 153107665Simp 154107665Simpvoid 155107665Simpevent_proc::add(eps *eps) 156107665Simp{ 157107665Simp _epsvec.push_back(eps); 158107665Simp} 159107665Simp 160107665Simpbool 161243930Seadlerevent_proc::matches(config &c) const 162107665Simp{ 163107665Simp vector<eps *>::const_iterator i; 164107665Simp 165243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 166107665Simp if (!(*i)->do_match(c)) 167107665Simp return (false); 168107665Simp return (true); 169107665Simp} 170107665Simp 171107665Simpbool 172243930Seadlerevent_proc::run(config &c) const 173107665Simp{ 174107665Simp vector<eps *>::const_iterator i; 175252485Sasomers 176243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 177107665Simp if (!(*i)->do_action(c)) 178107665Simp return (false); 179107665Simp return (true); 180107665Simp} 181107665Simp 182107665Simpaction::action(const char *cmd) 183252485Sasomers : _cmd(cmd) 184107665Simp{ 185107665Simp // nothing 186107665Simp} 187107665Simp 188107665Simpaction::~action() 189107665Simp{ 190107665Simp // nothing 191107665Simp} 192107665Simp 193209583Simpstatic int 194209583Simpmy_system(const char *command) 195209583Simp{ 196209583Simp pid_t pid, savedpid; 197209583Simp int pstat; 198209583Simp struct sigaction ign, intact, quitact; 199209583Simp sigset_t newsigblock, oldsigblock; 200209583Simp 201209583Simp if (!command) /* just checking... */ 202252485Sasomers return (1); 203209583Simp 204209583Simp /* 205209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 206209583Simp * existing signal dispositions. 207209583Simp */ 208209583Simp ign.sa_handler = SIG_IGN; 209209583Simp ::sigemptyset(&ign.sa_mask); 210209583Simp ign.sa_flags = 0; 211209583Simp ::sigaction(SIGINT, &ign, &intact); 212209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 213209583Simp ::sigemptyset(&newsigblock); 214209583Simp ::sigaddset(&newsigblock, SIGCHLD); 215209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 216209583Simp switch (pid = ::fork()) { 217209583Simp case -1: /* error */ 218209583Simp break; 219209583Simp case 0: /* child */ 220209583Simp /* 221209583Simp * Restore original signal dispositions and exec the command. 222209583Simp */ 223209583Simp ::sigaction(SIGINT, &intact, NULL); 224209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 225209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 226209583Simp /* 227209583Simp * Close the PID file, and all other open descriptors. 228209583Simp * Inherit std{in,out,err} only. 229209583Simp */ 230209583Simp cfg.close_pidfile(); 231209583Simp ::closefrom(3); 232209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 233209583Simp ::_exit(127); 234209583Simp default: /* parent */ 235209583Simp savedpid = pid; 236209583Simp do { 237209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 238209583Simp } while (pid == -1 && errno == EINTR); 239209583Simp break; 240209583Simp } 241209583Simp ::sigaction(SIGINT, &intact, NULL); 242209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 243209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 244209583Simp return (pid == -1 ? -1 : pstat); 245209583Simp} 246209583Simp 247107665Simpbool 248108014Simpaction::do_action(config &c) 249107665Simp{ 250246134Sian string s = c.expand_string(_cmd.c_str()); 251252481Sasomers devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str()); 252209583Simp my_system(s.c_str()); 253107665Simp return (true); 254107665Simp} 255107665Simp 256246134Sianmatch::match(config &c, const char *var, const char *re) : 257246134Sian _inv(re[0] == '!'), 258246134Sian _var(var), 259246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 260107665Simp{ 261154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 262107665Simp} 263107665Simp 264107665Simpmatch::~match() 265107665Simp{ 266108014Simp regfree(&_regex); 267107665Simp} 268107665Simp 269107665Simpbool 270108014Simpmatch::do_match(config &c) 271107665Simp{ 272210610Slulf const string &value = c.get_variable(_var); 273108014Simp bool retval; 274108014Simp 275252485Sasomers /* 276252481Sasomers * This function gets called WAY too often to justify calling syslog() 277252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 278252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 279252481Sasomers * log when we're in -d mode. 280252481Sasomers */ 281252481Sasomers if (dflag) { 282252481Sasomers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 283226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 284252481Sasomers } 285108783Simp 286108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 287226775Shrs if (_inv == 1) 288226775Shrs retval = (retval == 0) ? 1 : 0; 289226775Shrs 290252485Sasomers return (retval); 291107665Simp} 292107665Simp 293147874Simp#include <sys/sockio.h> 294147874Simp#include <net/if.h> 295147874Simp#include <net/if_media.h> 296147874Simp 297151486Sbrooksmedia::media(config &, const char *var, const char *type) 298147874Simp : _var(var), _type(-1) 299147874Simp{ 300147874Simp static struct ifmedia_description media_types[] = { 301147874Simp { IFM_ETHER, "Ethernet" }, 302147874Simp { IFM_TOKEN, "Tokenring" }, 303147874Simp { IFM_FDDI, "FDDI" }, 304147874Simp { IFM_IEEE80211, "802.11" }, 305147874Simp { IFM_ATM, "ATM" }, 306147874Simp { -1, "unknown" }, 307147874Simp { 0, NULL }, 308147874Simp }; 309243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 310147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 311147874Simp _type = media_types[i].ifmt_word; 312147874Simp break; 313147874Simp } 314147874Simp} 315147874Simp 316147874Simpmedia::~media() 317147874Simp{ 318147874Simp} 319147874Simp 320147874Simpbool 321147874Simpmedia::do_match(config &c) 322147874Simp{ 323150949Simp string value; 324147874Simp struct ifmediareq ifmr; 325147874Simp bool retval; 326147874Simp int s; 327147874Simp 328150949Simp // Since we can be called from both a device attach/detach 329150949Simp // context where device-name is defined and what we want, 330150949Simp // as well as from a link status context, where subsystem is 331150949Simp // the name of interest, first try device-name and fall back 332150949Simp // to subsystem if none exists. 333150949Simp value = c.get_variable("device-name"); 334247761Seadler if (value.empty()) 335151480Simp value = c.get_variable("subsystem"); 336252481Sasomers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 337147874Simp value.c_str(), _type); 338147874Simp 339147874Simp retval = false; 340147874Simp 341147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 342147874Simp if (s >= 0) { 343147874Simp memset(&ifmr, 0, sizeof(ifmr)); 344147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 345147874Simp 346147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 347147874Simp ifmr.ifm_status & IFM_AVALID) { 348252481Sasomers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 349147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 350147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 351147874Simp } else if (_type == -1) { 352252481Sasomers devdlog(LOG_DEBUG, "%s has unknown media type\n", 353147874Simp value.c_str()); 354147874Simp retval = true; 355147874Simp } 356147874Simp close(s); 357147874Simp } 358147874Simp 359252485Sasomers return (retval); 360147874Simp} 361147874Simp 362107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 363107665Simpconst string var_list::nothing = ""; 364107665Simp 365107665Simpconst string & 366107665Simpvar_list::get_variable(const string &var) const 367107665Simp{ 368107665Simp map<string, string>::const_iterator i; 369107665Simp 370107665Simp i = _vars.find(var); 371107665Simp if (i == _vars.end()) 372108783Simp return (var_list::bogus); 373107665Simp return (i->second); 374107665Simp} 375107665Simp 376107665Simpbool 377107665Simpvar_list::is_set(const string &var) const 378107665Simp{ 379107665Simp return (_vars.find(var) != _vars.end()); 380107665Simp} 381107665Simp 382107665Simpvoid 383107665Simpvar_list::set_variable(const string &var, const string &val) 384107665Simp{ 385252481Sasomers /* 386252481Sasomers * This function gets called WAY too often to justify calling syslog() 387252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 388252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 389252481Sasomers * log when we're in -d mode. 390252481Sasomers */ 391252481Sasomers if (dflag) 392252481Sasomers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 393107665Simp _vars[var] = val; 394107665Simp} 395107665Simp 396107665Simpvoid 397107665Simpconfig::reset(void) 398107665Simp{ 399107665Simp _dir_list.clear(); 400108783Simp delete_and_clear(_var_list_table); 401108783Simp delete_and_clear(_attach_list); 402108783Simp delete_and_clear(_detach_list); 403108783Simp delete_and_clear(_nomatch_list); 404121487Simp delete_and_clear(_notify_list); 405107665Simp} 406107665Simp 407107665Simpvoid 408107665Simpconfig::parse_one_file(const char *fn) 409107665Simp{ 410252481Sasomers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 411107665Simp yyin = fopen(fn, "r"); 412107665Simp if (yyin == NULL) 413107665Simp err(1, "Cannot open config file %s", fn); 414157746Smaxim lineno = 1; 415107665Simp if (yyparse() != 0) 416107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 417107665Simp fclose(yyin); 418107665Simp} 419107665Simp 420107665Simpvoid 421107665Simpconfig::parse_files_in_dir(const char *dirname) 422107665Simp{ 423107665Simp DIR *dirp; 424107665Simp struct dirent *dp; 425107665Simp char path[PATH_MAX]; 426107665Simp 427252481Sasomers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 428107665Simp dirp = opendir(dirname); 429107665Simp if (dirp == NULL) 430107665Simp return; 431107665Simp readdir(dirp); /* Skip . */ 432107665Simp readdir(dirp); /* Skip .. */ 433107665Simp while ((dp = readdir(dirp)) != NULL) { 434107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 435107665Simp snprintf(path, sizeof(path), "%s/%s", 436107665Simp dirname, dp->d_name); 437107665Simp parse_one_file(path); 438107665Simp } 439107665Simp } 440215607Skevlo closedir(dirp); 441107665Simp} 442107665Simp 443108783Simpclass epv_greater { 444108783Simppublic: 445243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 446108783Simp { 447108783Simp return (l1->get_priority() > l2->get_priority()); 448108783Simp } 449108783Simp}; 450108783Simp 451107665Simpvoid 452108783Simpconfig::sort_vector(vector<event_proc *> &v) 453108783Simp{ 454243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 455108783Simp} 456108783Simp 457108783Simpvoid 458107665Simpconfig::parse(void) 459107665Simp{ 460107665Simp vector<string>::const_iterator i; 461107665Simp 462152770Sjkoshy parse_one_file(configfile); 463243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 464107665Simp parse_files_in_dir((*i).c_str()); 465108783Simp sort_vector(_attach_list); 466108783Simp sort_vector(_detach_list); 467108783Simp sort_vector(_nomatch_list); 468121487Simp sort_vector(_notify_list); 469107665Simp} 470107665Simp 471107665Simpvoid 472155073Spjdconfig::open_pidfile() 473107665Simp{ 474155073Spjd pid_t otherpid; 475252485Sasomers 476247758Seadler if (_pidfile.empty()) 477107665Simp return; 478155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 479155073Spjd if (pfh == NULL) { 480155073Spjd if (errno == EEXIST) 481155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 482155073Spjd warn("cannot open pid file"); 483155073Spjd } 484107665Simp} 485107665Simp 486107665Simpvoid 487155073Spjdconfig::write_pidfile() 488155073Spjd{ 489252485Sasomers 490155073Spjd pidfile_write(pfh); 491155073Spjd} 492155073Spjd 493155073Spjdvoid 494209583Simpconfig::close_pidfile() 495209583Simp{ 496252485Sasomers 497209583Simp pidfile_close(pfh); 498209583Simp} 499209583Simp 500209583Simpvoid 501155073Spjdconfig::remove_pidfile() 502155073Spjd{ 503252485Sasomers 504155073Spjd pidfile_remove(pfh); 505155073Spjd} 506155073Spjd 507155073Spjdvoid 508107665Simpconfig::add_attach(int prio, event_proc *p) 509107665Simp{ 510107665Simp p->set_priority(prio); 511107665Simp _attach_list.push_back(p); 512107665Simp} 513107665Simp 514107665Simpvoid 515107665Simpconfig::add_detach(int prio, event_proc *p) 516107665Simp{ 517107665Simp p->set_priority(prio); 518107665Simp _detach_list.push_back(p); 519107665Simp} 520107665Simp 521107665Simpvoid 522107665Simpconfig::add_directory(const char *dir) 523107665Simp{ 524107665Simp _dir_list.push_back(string(dir)); 525107665Simp} 526107665Simp 527107665Simpvoid 528107665Simpconfig::add_nomatch(int prio, event_proc *p) 529107665Simp{ 530107665Simp p->set_priority(prio); 531107665Simp _nomatch_list.push_back(p); 532107665Simp} 533107665Simp 534107665Simpvoid 535121487Simpconfig::add_notify(int prio, event_proc *p) 536121487Simp{ 537121487Simp p->set_priority(prio); 538121487Simp _notify_list.push_back(p); 539121487Simp} 540121487Simp 541121487Simpvoid 542107665Simpconfig::set_pidfile(const char *fn) 543107665Simp{ 544247758Seadler _pidfile = fn; 545107665Simp} 546107665Simp 547107665Simpvoid 548107665Simpconfig::push_var_table() 549107665Simp{ 550107665Simp var_list *vl; 551252485Sasomers 552107665Simp vl = new var_list(); 553107665Simp _var_list_table.push_back(vl); 554252481Sasomers devdlog(LOG_DEBUG, "Pushing table\n"); 555107665Simp} 556107665Simp 557107665Simpvoid 558107665Simpconfig::pop_var_table() 559107665Simp{ 560107665Simp delete _var_list_table.back(); 561107665Simp _var_list_table.pop_back(); 562252481Sasomers devdlog(LOG_DEBUG, "Popping table\n"); 563107665Simp} 564107665Simp 565107665Simpvoid 566107665Simpconfig::set_variable(const char *var, const char *val) 567107665Simp{ 568107665Simp _var_list_table.back()->set_variable(var, val); 569107665Simp} 570107665Simp 571107665Simpconst string & 572107665Simpconfig::get_variable(const string &var) 573107665Simp{ 574107665Simp vector<var_list *>::reverse_iterator i; 575107665Simp 576243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 577107665Simp if ((*i)->is_set(var)) 578108783Simp return ((*i)->get_variable(var)); 579107665Simp } 580107665Simp return (var_list::nothing); 581107665Simp} 582107665Simp 583108783Simpbool 584243930Seadlerconfig::is_id_char(char ch) const 585108783Simp{ 586252485Sasomers return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 587108783Simp ch == '-')); 588108783Simp} 589108783Simp 590108014Simpvoid 591114081Simpconfig::expand_one(const char *&src, string &dst) 592107665Simp{ 593108014Simp int count; 594210610Slulf string buffer; 595108014Simp 596108783Simp src++; 597108014Simp // $$ -> $ 598108014Simp if (*src == '$') { 599247762Seadler dst += *src++; 600108014Simp return; 601108014Simp } 602252485Sasomers 603108014Simp // $(foo) -> $(foo) 604108783Simp // Not sure if I want to support this or not, so for now we just pass 605108783Simp // it through. 606108014Simp if (*src == '(') { 607247762Seadler dst += '$'; 608108014Simp count = 1; 609114081Simp /* If the string ends before ) is matched , return. */ 610114081Simp while (count > 0 && *src) { 611108014Simp if (*src == ')') 612108014Simp count--; 613108014Simp else if (*src == '(') 614108014Simp count++; 615247762Seadler dst += *src++; 616108014Simp } 617108014Simp return; 618108014Simp } 619252485Sasomers 620247763Seadler // $[^A-Za-z] -> $\1 621108014Simp if (!isalpha(*src)) { 622247762Seadler dst += '$'; 623247762Seadler dst += *src++; 624108014Simp return; 625108014Simp } 626108014Simp 627108014Simp // $var -> replace with value 628114081Simp do { 629247762Seadler buffer += *src++; 630114084Simp } while (is_id_char(*src)); 631247758Seadler dst.append(get_variable(buffer)); 632107665Simp} 633107665Simp 634108014Simpconst string 635246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 636108014Simp{ 637246134Sian const char *var_at; 638114081Simp string dst; 639108014Simp 640246134Sian /* 641246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 642246134Sian * while parsing config files, as tested on 2013-01-30. 643246134Sian */ 644246134Sian dst.reserve(128); 645246134Sian 646246134Sian if (prepend != NULL) 647246134Sian dst = prepend; 648246134Sian 649246134Sian for (;;) { 650246134Sian var_at = strchr(src, '$'); 651246134Sian if (var_at == NULL) { 652246134Sian dst.append(src); 653246134Sian break; 654246134Sian } 655246134Sian dst.append(src, var_at - src); 656246134Sian src = var_at; 657246134Sian expand_one(src, dst); 658108014Simp } 659108014Simp 660246134Sian if (append != NULL) 661246134Sian dst.append(append); 662246134Sian 663114081Simp return (dst); 664108014Simp} 665108014Simp 666108783Simpbool 667247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 668108783Simp{ 669108783Simp char *walker; 670252485Sasomers 671108783Simp if (*buffer == '\0') 672108783Simp return (false); 673108783Simp walker = lhs = buffer; 674108783Simp while (is_id_char(*walker)) 675108783Simp walker++; 676108783Simp if (*walker != '=') 677108783Simp return (false); 678108783Simp walker++; // skip = 679108783Simp if (*walker == '"') { 680108783Simp walker++; // skip " 681108783Simp rhs = walker; 682108783Simp while (*walker && *walker != '"') 683108783Simp walker++; 684108783Simp if (*walker != '"') 685108783Simp return (false); 686108783Simp rhs[-2] = '\0'; 687108783Simp *walker++ = '\0'; 688108783Simp } else { 689108783Simp rhs = walker; 690108783Simp while (*walker && !isspace(*walker)) 691108783Simp walker++; 692108783Simp if (*walker != '\0') 693108783Simp *walker++ = '\0'; 694108783Simp rhs[-1] = '\0'; 695108783Simp } 696113785Simp while (isspace(*walker)) 697113785Simp walker++; 698108783Simp buffer = walker; 699108783Simp return (true); 700108783Simp} 701108783Simp 702108783Simp 703108783Simpchar * 704108783Simpconfig::set_vars(char *buffer) 705108783Simp{ 706108783Simp char *lhs; 707108783Simp char *rhs; 708108783Simp 709108783Simp while (1) { 710108783Simp if (!chop_var(buffer, lhs, rhs)) 711108783Simp break; 712108783Simp set_variable(lhs, rhs); 713108783Simp } 714108783Simp return (buffer); 715108783Simp} 716108783Simp 717108783Simpvoid 718108783Simpconfig::find_and_execute(char type) 719108783Simp{ 720108783Simp vector<event_proc *> *l; 721108783Simp vector<event_proc *>::const_iterator i; 722151486Sbrooks const char *s; 723108783Simp 724108783Simp switch (type) { 725108783Simp default: 726108783Simp return; 727121487Simp case notify: 728121487Simp l = &_notify_list; 729121487Simp s = "notify"; 730121487Simp break; 731108783Simp case nomatch: 732108783Simp l = &_nomatch_list; 733108783Simp s = "nomatch"; 734108783Simp break; 735108783Simp case attach: 736108783Simp l = &_attach_list; 737108783Simp s = "attach"; 738108783Simp break; 739108783Simp case detach: 740108783Simp l = &_detach_list; 741108783Simp s = "detach"; 742108783Simp break; 743108783Simp } 744252481Sasomers devdlog(LOG_DEBUG, "Processing %s event\n", s); 745243931Seadler for (i = l->begin(); i != l->end(); ++i) { 746108783Simp if ((*i)->matches(*this)) { 747108783Simp (*i)->run(*this); 748108783Simp break; 749108783Simp } 750108783Simp } 751108783Simp 752108783Simp} 753108783Simp 754252485Sasomers 755107665Simpstatic void 756108783Simpprocess_event(char *buffer) 757107665Simp{ 758107665Simp char type; 759107665Simp char *sp; 760107665Simp 761108783Simp sp = buffer + 1; 762252481Sasomers devdlog(LOG_DEBUG, "Processing event '%s'\n", buffer); 763107665Simp type = *buffer++; 764108783Simp cfg.push_var_table(); 765108783Simp // No match doesn't have a device, and the format is a little 766108783Simp // different, so handle it separately. 767121487Simp switch (type) { 768121487Simp case notify: 769121487Simp sp = cfg.set_vars(sp); 770121487Simp break; 771121487Simp case nomatch: 772145218Simp //? at location pnp-info on bus 773145218Simp sp = strchr(sp, ' '); 774145218Simp if (sp == NULL) 775145218Simp return; /* Can't happen? */ 776145218Simp *sp++ = '\0'; 777213646Simp while (isspace(*sp)) 778213646Simp sp++; 779121487Simp if (strncmp(sp, "at ", 3) == 0) 780121487Simp sp += 3; 781121487Simp sp = cfg.set_vars(sp); 782213646Simp while (isspace(*sp)) 783213646Simp sp++; 784121487Simp if (strncmp(sp, "on ", 3) == 0) 785121487Simp cfg.set_variable("bus", sp + 3); 786121487Simp break; 787121487Simp case attach: /*FALLTHROUGH*/ 788121487Simp case detach: 789108783Simp sp = strchr(sp, ' '); 790108783Simp if (sp == NULL) 791108783Simp return; /* Can't happen? */ 792108783Simp *sp++ = '\0'; 793108783Simp cfg.set_variable("device-name", buffer); 794213646Simp while (isspace(*sp)) 795213646Simp sp++; 796113785Simp if (strncmp(sp, "at ", 3) == 0) 797113785Simp sp += 3; 798113785Simp sp = cfg.set_vars(sp); 799213646Simp while (isspace(*sp)) 800213646Simp sp++; 801113785Simp if (strncmp(sp, "on ", 3) == 0) 802113785Simp cfg.set_variable("bus", sp + 3); 803121487Simp break; 804108783Simp } 805252485Sasomers 806108783Simp cfg.find_and_execute(type); 807108783Simp cfg.pop_var_table(); 808107665Simp} 809107665Simp 810131397Simpint 811131397Simpcreate_socket(const char *name) 812131397Simp{ 813131397Simp int fd, slen; 814131397Simp struct sockaddr_un sun; 815131397Simp 816131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 817131397Simp err(1, "socket"); 818131397Simp bzero(&sun, sizeof(sun)); 819131397Simp sun.sun_family = AF_UNIX; 820131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 821131397Simp slen = SUN_LEN(&sun); 822131397Simp unlink(name); 823147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 824147973Smarcus err(1, "fcntl"); 825236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 826131397Simp err(1, "bind"); 827131397Simp listen(fd, 4); 828147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 829147973Smarcus chmod(name, 0666); 830131397Simp return (fd); 831131397Simp} 832131397Simp 833246121Sianunsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 834246121Sianunsigned int num_clients; 835131397Simplist<int> clients; 836131397Simp 837131397Simpvoid 838131397Simpnotify_clients(const char *data, int len) 839131397Simp{ 840246121Sian list<int>::iterator i; 841131397Simp 842246121Sian /* 843246121Sian * Deliver the data to all clients. Throw clients overboard at the 844246121Sian * first sign of trouble. This reaps clients who've died or closed 845246121Sian * their sockets, and also clients who are alive but failing to keep up 846246121Sian * (or who are maliciously not reading, to consume buffer space in 847246121Sian * kernel memory or tie up the limited number of available connections). 848246121Sian */ 849246121Sian for (i = clients.begin(); i != clients.end(); ) { 850246121Sian if (write(*i, data, len) != len) { 851246121Sian --num_clients; 852131397Simp close(*i); 853246121Sian i = clients.erase(i); 854252481Sasomers devdlog(LOG_WARNING, "notify_clients: write() failed; " 855252481Sasomers "dropping unresponsive client\n"); 856246121Sian } else 857246121Sian ++i; 858131397Simp } 859246121Sian} 860131397Simp 861246121Sianvoid 862246121Siancheck_clients(void) 863246121Sian{ 864246121Sian int s; 865246121Sian struct pollfd pfd; 866246121Sian list<int>::iterator i; 867246121Sian 868246121Sian /* 869246121Sian * Check all existing clients to see if any of them have disappeared. 870246121Sian * Normally we reap clients when we get an error trying to send them an 871246121Sian * event. This check eliminates the problem of an ever-growing list of 872246121Sian * zombie clients because we're never writing to them on a system 873246121Sian * without frequent device-change activity. 874246121Sian */ 875246121Sian pfd.events = 0; 876246121Sian for (i = clients.begin(); i != clients.end(); ) { 877246121Sian pfd.fd = *i; 878246121Sian s = poll(&pfd, 1, 0); 879246121Sian if ((s < 0 && s != EINTR ) || 880246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 881246121Sian --num_clients; 882246121Sian close(*i); 883246121Sian i = clients.erase(i); 884252481Sasomers devdlog(LOG_NOTICE, "check_clients: " 885252481Sasomers "dropping disconnected client\n"); 886246121Sian } else 887246121Sian ++i; 888246121Sian } 889131397Simp} 890131397Simp 891131397Simpvoid 892131397Simpnew_client(int fd) 893131397Simp{ 894131397Simp int s; 895131397Simp 896246121Sian /* 897246121Sian * First go reap any zombie clients, then accept the connection, and 898246121Sian * shut down the read side to stop clients from consuming kernel memory 899246121Sian * by sending large buffers full of data we'll never read. 900246121Sian */ 901246121Sian check_clients(); 902131397Simp s = accept(fd, NULL, NULL); 903246121Sian if (s != -1) { 904246121Sian shutdown(s, SHUT_RD); 905131397Simp clients.push_back(s); 906246121Sian ++num_clients; 907246121Sian } 908131397Simp} 909131397Simp 910107665Simpstatic void 911107665Simpevent_loop(void) 912107665Simp{ 913107665Simp int rv; 914107665Simp int fd; 915107665Simp char buffer[DEVCTL_MAXBUF]; 916113790Simp int once = 0; 917131397Simp int server_fd, max_fd; 918246121Sian int accepting; 919113790Simp timeval tv; 920113790Simp fd_set fds; 921107665Simp 922240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 923107665Simp if (fd == -1) 924131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 925131397Simp server_fd = create_socket(PIPE); 926246121Sian accepting = 1; 927131397Simp max_fd = max(fd, server_fd) + 1; 928247756Seadler while (!romeo_must_die) { 929113790Simp if (!once && !dflag && !nflag) { 930113790Simp // Check to see if we have any events pending. 931113790Simp tv.tv_sec = 0; 932113790Simp tv.tv_usec = 0; 933113790Simp FD_ZERO(&fds); 934113790Simp FD_SET(fd, &fds); 935113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 936113790Simp // No events -> we've processed all pending events 937117944Simp if (rv == 0) { 938252481Sasomers devdlog(LOG_DEBUG, "Calling daemon\n"); 939155073Spjd cfg.remove_pidfile(); 940155073Spjd cfg.open_pidfile(); 941113790Simp daemon(0, 0); 942155073Spjd cfg.write_pidfile(); 943113790Simp once++; 944113790Simp } 945113790Simp } 946246121Sian /* 947246121Sian * When we've already got the max number of clients, stop 948246121Sian * accepting new connections (don't put server_fd in the set), 949246121Sian * shrink the accept() queue to reject connections quickly, and 950246121Sian * poll the existing clients more often, so that we notice more 951246121Sian * quickly when any of them disappear to free up client slots. 952246121Sian */ 953131397Simp FD_ZERO(&fds); 954131397Simp FD_SET(fd, &fds); 955246121Sian if (num_clients < max_clients) { 956246121Sian if (!accepting) { 957246121Sian listen(server_fd, max_clients); 958246121Sian accepting = 1; 959246121Sian } 960246121Sian FD_SET(server_fd, &fds); 961246121Sian tv.tv_sec = 60; 962246121Sian tv.tv_usec = 0; 963246121Sian } else { 964246121Sian if (accepting) { 965246121Sian listen(server_fd, 0); 966246121Sian accepting = 0; 967246121Sian } 968246121Sian tv.tv_sec = 2; 969246121Sian tv.tv_usec = 0; 970246121Sian } 971246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 972252482Sasomers if (got_siginfo) { 973253046Sasomers devdlog(LOG_INFO, "Events received so far=%u\n", 974252482Sasomers total_events); 975252482Sasomers got_siginfo = 0; 976252482Sasomers } 977131397Simp if (rv == -1) { 978131397Simp if (errno == EINTR) 979131397Simp continue; 980131397Simp err(1, "select"); 981246121Sian } else if (rv == 0) 982246121Sian check_clients(); 983131397Simp if (FD_ISSET(fd, &fds)) { 984131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 985131397Simp if (rv > 0) { 986252482Sasomers total_events++; 987252481Sasomers if (rv == sizeof(buffer) - 1) { 988252481Sasomers devdlog(LOG_WARNING, "Warning: " 989252481Sasomers "available event data exceeded " 990252481Sasomers "buffer space\n"); 991252481Sasomers } 992131397Simp notify_clients(buffer, rv); 993107665Simp buffer[rv] = '\0'; 994131397Simp while (buffer[--rv] == '\n') 995131397Simp buffer[rv] = '\0'; 996131397Simp process_event(buffer); 997131397Simp } else if (rv < 0) { 998131397Simp if (errno != EINTR) 999131397Simp break; 1000131397Simp } else { 1001131397Simp /* EOF */ 1002107665Simp break; 1003131397Simp } 1004107665Simp } 1005131397Simp if (FD_ISSET(server_fd, &fds)) 1006131397Simp new_client(server_fd); 1007107665Simp } 1008107665Simp close(fd); 1009107665Simp} 1010252485Sasomers 1011107665Simp/* 1012107665Simp * functions that the parser uses. 1013107665Simp */ 1014107665Simpvoid 1015107665Simpadd_attach(int prio, event_proc *p) 1016107665Simp{ 1017107665Simp cfg.add_attach(prio, p); 1018107665Simp} 1019107665Simp 1020107665Simpvoid 1021107665Simpadd_detach(int prio, event_proc *p) 1022107665Simp{ 1023107665Simp cfg.add_detach(prio, p); 1024107665Simp} 1025107665Simp 1026107665Simpvoid 1027107665Simpadd_directory(const char *dir) 1028107665Simp{ 1029107665Simp cfg.add_directory(dir); 1030107665Simp free(const_cast<char *>(dir)); 1031107665Simp} 1032107665Simp 1033107665Simpvoid 1034107665Simpadd_nomatch(int prio, event_proc *p) 1035107665Simp{ 1036107665Simp cfg.add_nomatch(prio, p); 1037107665Simp} 1038107665Simp 1039121487Simpvoid 1040121487Simpadd_notify(int prio, event_proc *p) 1041121487Simp{ 1042121487Simp cfg.add_notify(prio, p); 1043121487Simp} 1044121487Simp 1045107665Simpevent_proc * 1046107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1047107665Simp{ 1048107665Simp if (ep == NULL) 1049107665Simp ep = new event_proc(); 1050107665Simp ep->add(eps); 1051107665Simp return (ep); 1052107665Simp} 1053107665Simp 1054107665Simpeps * 1055107665Simpnew_action(const char *cmd) 1056107665Simp{ 1057107665Simp eps *e = new action(cmd); 1058107665Simp free(const_cast<char *>(cmd)); 1059107665Simp return (e); 1060107665Simp} 1061107665Simp 1062107665Simpeps * 1063107665Simpnew_match(const char *var, const char *re) 1064107665Simp{ 1065108014Simp eps *e = new match(cfg, var, re); 1066107665Simp free(const_cast<char *>(var)); 1067107665Simp free(const_cast<char *>(re)); 1068107665Simp return (e); 1069107665Simp} 1070107665Simp 1071147874Simpeps * 1072147874Simpnew_media(const char *var, const char *re) 1073147874Simp{ 1074147874Simp eps *e = new media(cfg, var, re); 1075147874Simp free(const_cast<char *>(var)); 1076147874Simp free(const_cast<char *>(re)); 1077147874Simp return (e); 1078147874Simp} 1079147874Simp 1080107665Simpvoid 1081107665Simpset_pidfile(const char *name) 1082107665Simp{ 1083107665Simp cfg.set_pidfile(name); 1084107665Simp free(const_cast<char *>(name)); 1085107665Simp} 1086107665Simp 1087107665Simpvoid 1088107665Simpset_variable(const char *var, const char *val) 1089107665Simp{ 1090107665Simp cfg.set_variable(var, val); 1091107665Simp free(const_cast<char *>(var)); 1092107665Simp free(const_cast<char *>(val)); 1093107665Simp} 1094107665Simp 1095107665Simp 1096252485Sasomers 1097107665Simpstatic void 1098107665Simpgensighand(int) 1099107665Simp{ 1100247754Seadler romeo_must_die = 1; 1101107665Simp} 1102107665Simp 1103252481Sasomers/* 1104252482Sasomers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1105252482Sasomers * as appropriate 1106252482Sasomers */ 1107252482Sasomersstatic void 1108252482Sasomerssiginfohand(int) 1109252482Sasomers{ 1110252482Sasomers got_siginfo = 1; 1111252482Sasomers} 1112252482Sasomers 1113252482Sasomers/* 1114252481Sasomers * Local logging function. Prints to syslog if we're daemonized; syslog 1115252481Sasomers * otherwise. 1116252481Sasomers */ 1117107665Simpstatic void 1118252481Sasomersdevdlog(int priority, const char* fmt, ...) 1119252481Sasomers{ 1120252481Sasomers va_list argp; 1121252481Sasomers 1122252481Sasomers va_start(argp, fmt); 1123252481Sasomers if (dflag) 1124252481Sasomers vfprintf(stderr, fmt, argp); 1125252481Sasomers else 1126252481Sasomers vsyslog(priority, fmt, argp); 1127252481Sasomers va_end(argp); 1128252481Sasomers} 1129252481Sasomers 1130252481Sasomersstatic void 1131107665Simpusage() 1132107665Simp{ 1133252481Sasomers fprintf(stderr, "usage: %s [-dn] [-l connlimit] [-f file]\n", 1134246121Sian getprogname()); 1135107665Simp exit(1); 1136107665Simp} 1137107665Simp 1138113787Simpstatic void 1139113787Simpcheck_devd_enabled() 1140113787Simp{ 1141113787Simp int val = 0; 1142113787Simp size_t len; 1143113787Simp 1144113787Simp len = sizeof(val); 1145114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1146113787Simp errx(1, "devctl sysctl missing from kernel!"); 1147113787Simp if (val) { 1148113787Simp warnx("Setting " SYSCTL " to 0"); 1149113787Simp val = 0; 1150113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1151113787Simp } 1152113787Simp} 1153113787Simp 1154107665Simp/* 1155107665Simp * main 1156107665Simp */ 1157107665Simpint 1158107665Simpmain(int argc, char **argv) 1159107665Simp{ 1160107665Simp int ch; 1161107665Simp 1162113787Simp check_devd_enabled(); 1163252481Sasomers while ((ch = getopt(argc, argv, "df:l:n")) != -1) { 1164107665Simp switch (ch) { 1165107665Simp case 'd': 1166107665Simp dflag++; 1167107665Simp break; 1168152770Sjkoshy case 'f': 1169152770Sjkoshy configfile = optarg; 1170152770Sjkoshy break; 1171246121Sian case 'l': 1172246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1173246121Sian break; 1174113790Simp case 'n': 1175113790Simp nflag++; 1176113790Simp break; 1177107665Simp default: 1178107665Simp usage(); 1179107665Simp } 1180107665Simp } 1181107665Simp 1182107665Simp cfg.parse(); 1183117246Simp if (!dflag && nflag) { 1184155073Spjd cfg.open_pidfile(); 1185107665Simp daemon(0, 0); 1186155073Spjd cfg.write_pidfile(); 1187117246Simp } 1188146306Simp signal(SIGPIPE, SIG_IGN); 1189107665Simp signal(SIGHUP, gensighand); 1190107665Simp signal(SIGINT, gensighand); 1191107665Simp signal(SIGTERM, gensighand); 1192252482Sasomers signal(SIGINFO, siginfohand); 1193107665Simp event_loop(); 1194107665Simp return (0); 1195107665Simp} 1196