devd.cc revision 247754
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 247754 2013-03-04 02:21:08Z eadler $"); 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 76108014Simp#include <ctype.h> 77107665Simp#include <dirent.h> 78107665Simp#include <errno.h> 79107665Simp#include <err.h> 80107665Simp#include <fcntl.h> 81155073Spjd#include <libutil.h> 82209583Simp#include <paths.h> 83246121Sian#include <poll.h> 84108014Simp#include <regex.h> 85146306Simp#include <signal.h> 86107665Simp#include <stdlib.h> 87107665Simp#include <stdio.h> 88107665Simp#include <string.h> 89107665Simp#include <unistd.h> 90107665Simp 91108783Simp#include <algorithm> 92107665Simp#include <map> 93107665Simp#include <string> 94131397Simp#include <list> 95107665Simp#include <vector> 96107665Simp 97114086Simp#include "devd.h" /* C compatible definitions */ 98114086Simp#include "devd.hh" /* C++ class definitions */ 99107665Simp 100131397Simp#define PIPE "/var/run/devd.pipe" 101107665Simp#define CF "/etc/devd.conf" 102113787Simp#define SYSCTL "hw.bus.devctl_disable" 103107665Simp 104107665Simpusing namespace std; 105107665Simp 106107665Simpextern FILE *yyin; 107107665Simpextern int lineno; 108107665Simp 109121487Simpstatic const char notify = '!'; 110108783Simpstatic const char nomatch = '?'; 111108783Simpstatic const char attach = '+'; 112108783Simpstatic const char detach = '-'; 113108783Simp 114155073Spjdstatic struct pidfh *pfh; 115155073Spjd 116113790Simpint Dflag; 117107665Simpint dflag; 118114000Simpint nflag; 119247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 120107665Simp 121152770Sjkoshystatic const char *configfile = CF; 122152770Sjkoshy 123107665Simpstatic void event_loop(void); 124107665Simpstatic void usage(void); 125107665Simp 126108783Simptemplate <class T> void 127108783Simpdelete_and_clear(vector<T *> &v) 128108783Simp{ 129108783Simp typename vector<T *>::const_iterator i; 130108783Simp 131243931Seadler for (i = v.begin(); i != v.end(); ++i) 132108783Simp delete *i; 133108783Simp v.clear(); 134108783Simp} 135108783Simp 136107665Simpconfig cfg; 137107665Simp 138107665Simpevent_proc::event_proc() : _prio(-1) 139107665Simp{ 140246134Sian _epsvec.reserve(4); 141107665Simp} 142107665Simp 143107665Simpevent_proc::~event_proc() 144107665Simp{ 145152406Sbland delete_and_clear(_epsvec); 146107665Simp} 147107665Simp 148107665Simpvoid 149107665Simpevent_proc::add(eps *eps) 150107665Simp{ 151107665Simp _epsvec.push_back(eps); 152107665Simp} 153107665Simp 154107665Simpbool 155243930Seadlerevent_proc::matches(config &c) const 156107665Simp{ 157107665Simp vector<eps *>::const_iterator i; 158107665Simp 159243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 160107665Simp if (!(*i)->do_match(c)) 161107665Simp return (false); 162107665Simp return (true); 163107665Simp} 164107665Simp 165107665Simpbool 166243930Seadlerevent_proc::run(config &c) const 167107665Simp{ 168107665Simp vector<eps *>::const_iterator i; 169107665Simp 170243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 171107665Simp if (!(*i)->do_action(c)) 172107665Simp return (false); 173107665Simp return (true); 174107665Simp} 175107665Simp 176107665Simpaction::action(const char *cmd) 177107665Simp : _cmd(cmd) 178107665Simp{ 179107665Simp // nothing 180107665Simp} 181107665Simp 182107665Simpaction::~action() 183107665Simp{ 184107665Simp // nothing 185107665Simp} 186107665Simp 187209583Simpstatic int 188209583Simpmy_system(const char *command) 189209583Simp{ 190209583Simp pid_t pid, savedpid; 191209583Simp int pstat; 192209583Simp struct sigaction ign, intact, quitact; 193209583Simp sigset_t newsigblock, oldsigblock; 194209583Simp 195209583Simp if (!command) /* just checking... */ 196209583Simp return(1); 197209583Simp 198209583Simp /* 199209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 200209583Simp * existing signal dispositions. 201209583Simp */ 202209583Simp ign.sa_handler = SIG_IGN; 203209583Simp ::sigemptyset(&ign.sa_mask); 204209583Simp ign.sa_flags = 0; 205209583Simp ::sigaction(SIGINT, &ign, &intact); 206209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 207209583Simp ::sigemptyset(&newsigblock); 208209583Simp ::sigaddset(&newsigblock, SIGCHLD); 209209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 210209583Simp switch (pid = ::fork()) { 211209583Simp case -1: /* error */ 212209583Simp break; 213209583Simp case 0: /* child */ 214209583Simp /* 215209583Simp * Restore original signal dispositions and exec the command. 216209583Simp */ 217209583Simp ::sigaction(SIGINT, &intact, NULL); 218209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 219209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 220209583Simp /* 221209583Simp * Close the PID file, and all other open descriptors. 222209583Simp * Inherit std{in,out,err} only. 223209583Simp */ 224209583Simp cfg.close_pidfile(); 225209583Simp ::closefrom(3); 226209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 227209583Simp ::_exit(127); 228209583Simp default: /* parent */ 229209583Simp savedpid = pid; 230209583Simp do { 231209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 232209583Simp } while (pid == -1 && errno == EINTR); 233209583Simp break; 234209583Simp } 235209583Simp ::sigaction(SIGINT, &intact, NULL); 236209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 237209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 238209583Simp return (pid == -1 ? -1 : pstat); 239209583Simp} 240209583Simp 241107665Simpbool 242108014Simpaction::do_action(config &c) 243107665Simp{ 244246134Sian string s = c.expand_string(_cmd.c_str()); 245113790Simp if (Dflag) 246108783Simp fprintf(stderr, "Executing '%s'\n", s.c_str()); 247209583Simp my_system(s.c_str()); 248107665Simp return (true); 249107665Simp} 250107665Simp 251246134Sianmatch::match(config &c, const char *var, const char *re) : 252246134Sian _inv(re[0] == '!'), 253246134Sian _var(var), 254246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 255107665Simp{ 256154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 257107665Simp} 258107665Simp 259107665Simpmatch::~match() 260107665Simp{ 261108014Simp regfree(&_regex); 262107665Simp} 263107665Simp 264107665Simpbool 265108014Simpmatch::do_match(config &c) 266107665Simp{ 267210610Slulf const string &value = c.get_variable(_var); 268108014Simp bool retval; 269108014Simp 270113790Simp if (Dflag) 271226775Shrs fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 272226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 273108783Simp 274108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 275226775Shrs if (_inv == 1) 276226775Shrs retval = (retval == 0) ? 1 : 0; 277226775Shrs 278108014Simp return retval; 279107665Simp} 280107665Simp 281147874Simp#include <sys/sockio.h> 282147874Simp#include <net/if.h> 283147874Simp#include <net/if_media.h> 284147874Simp 285151486Sbrooksmedia::media(config &, const char *var, const char *type) 286147874Simp : _var(var), _type(-1) 287147874Simp{ 288147874Simp static struct ifmedia_description media_types[] = { 289147874Simp { IFM_ETHER, "Ethernet" }, 290147874Simp { IFM_TOKEN, "Tokenring" }, 291147874Simp { IFM_FDDI, "FDDI" }, 292147874Simp { IFM_IEEE80211, "802.11" }, 293147874Simp { IFM_ATM, "ATM" }, 294147874Simp { -1, "unknown" }, 295147874Simp { 0, NULL }, 296147874Simp }; 297243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 298147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 299147874Simp _type = media_types[i].ifmt_word; 300147874Simp break; 301147874Simp } 302147874Simp} 303147874Simp 304147874Simpmedia::~media() 305147874Simp{ 306147874Simp} 307147874Simp 308147874Simpbool 309147874Simpmedia::do_match(config &c) 310147874Simp{ 311150949Simp string value; 312147874Simp struct ifmediareq ifmr; 313147874Simp bool retval; 314147874Simp int s; 315147874Simp 316150949Simp // Since we can be called from both a device attach/detach 317150949Simp // context where device-name is defined and what we want, 318150949Simp // as well as from a link status context, where subsystem is 319150949Simp // the name of interest, first try device-name and fall back 320150949Simp // to subsystem if none exists. 321150949Simp value = c.get_variable("device-name"); 322150949Simp if (value.length() == 0) 323151480Simp value = c.get_variable("subsystem"); 324147874Simp if (Dflag) 325147874Simp fprintf(stderr, "Testing media type of %s against 0x%x\n", 326147874Simp value.c_str(), _type); 327147874Simp 328147874Simp retval = false; 329147874Simp 330147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 331147874Simp if (s >= 0) { 332147874Simp memset(&ifmr, 0, sizeof(ifmr)); 333147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 334147874Simp 335147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 336147874Simp ifmr.ifm_status & IFM_AVALID) { 337147874Simp if (Dflag) 338147874Simp fprintf(stderr, "%s has media type 0x%x\n", 339147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 340147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 341147874Simp } else if (_type == -1) { 342147874Simp if (Dflag) 343147874Simp fprintf(stderr, "%s has unknown media type\n", 344147874Simp value.c_str()); 345147874Simp retval = true; 346147874Simp } 347147874Simp close(s); 348147874Simp } 349147874Simp 350147874Simp return retval; 351147874Simp} 352147874Simp 353107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 354107665Simpconst string var_list::nothing = ""; 355107665Simp 356107665Simpconst string & 357107665Simpvar_list::get_variable(const string &var) const 358107665Simp{ 359107665Simp map<string, string>::const_iterator i; 360107665Simp 361107665Simp i = _vars.find(var); 362107665Simp if (i == _vars.end()) 363108783Simp return (var_list::bogus); 364107665Simp return (i->second); 365107665Simp} 366107665Simp 367107665Simpbool 368107665Simpvar_list::is_set(const string &var) const 369107665Simp{ 370107665Simp return (_vars.find(var) != _vars.end()); 371107665Simp} 372107665Simp 373107665Simpvoid 374107665Simpvar_list::set_variable(const string &var, const string &val) 375107665Simp{ 376113790Simp if (Dflag) 377145218Simp fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 378107665Simp _vars[var] = val; 379107665Simp} 380107665Simp 381107665Simpvoid 382107665Simpconfig::reset(void) 383107665Simp{ 384107665Simp _dir_list.clear(); 385108783Simp delete_and_clear(_var_list_table); 386108783Simp delete_and_clear(_attach_list); 387108783Simp delete_and_clear(_detach_list); 388108783Simp delete_and_clear(_nomatch_list); 389121487Simp delete_and_clear(_notify_list); 390107665Simp} 391107665Simp 392107665Simpvoid 393107665Simpconfig::parse_one_file(const char *fn) 394107665Simp{ 395113790Simp if (Dflag) 396186078Sphk fprintf(stderr, "Parsing %s\n", fn); 397107665Simp yyin = fopen(fn, "r"); 398107665Simp if (yyin == NULL) 399107665Simp err(1, "Cannot open config file %s", fn); 400157746Smaxim lineno = 1; 401107665Simp if (yyparse() != 0) 402107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 403107665Simp fclose(yyin); 404107665Simp} 405107665Simp 406107665Simpvoid 407107665Simpconfig::parse_files_in_dir(const char *dirname) 408107665Simp{ 409107665Simp DIR *dirp; 410107665Simp struct dirent *dp; 411107665Simp char path[PATH_MAX]; 412107665Simp 413113790Simp if (Dflag) 414186078Sphk fprintf(stderr, "Parsing files in %s\n", dirname); 415107665Simp dirp = opendir(dirname); 416107665Simp if (dirp == NULL) 417107665Simp return; 418107665Simp readdir(dirp); /* Skip . */ 419107665Simp readdir(dirp); /* Skip .. */ 420107665Simp while ((dp = readdir(dirp)) != NULL) { 421107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 422107665Simp snprintf(path, sizeof(path), "%s/%s", 423107665Simp dirname, dp->d_name); 424107665Simp parse_one_file(path); 425107665Simp } 426107665Simp } 427215607Skevlo closedir(dirp); 428107665Simp} 429107665Simp 430108783Simpclass epv_greater { 431108783Simppublic: 432243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 433108783Simp { 434108783Simp return (l1->get_priority() > l2->get_priority()); 435108783Simp } 436108783Simp}; 437108783Simp 438107665Simpvoid 439108783Simpconfig::sort_vector(vector<event_proc *> &v) 440108783Simp{ 441243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 442108783Simp} 443108783Simp 444108783Simpvoid 445107665Simpconfig::parse(void) 446107665Simp{ 447107665Simp vector<string>::const_iterator i; 448107665Simp 449152770Sjkoshy parse_one_file(configfile); 450243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 451107665Simp parse_files_in_dir((*i).c_str()); 452108783Simp sort_vector(_attach_list); 453108783Simp sort_vector(_detach_list); 454108783Simp sort_vector(_nomatch_list); 455121487Simp sort_vector(_notify_list); 456107665Simp} 457107665Simp 458107665Simpvoid 459155073Spjdconfig::open_pidfile() 460107665Simp{ 461155073Spjd pid_t otherpid; 462107665Simp 463107665Simp if (_pidfile == "") 464107665Simp return; 465155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 466155073Spjd if (pfh == NULL) { 467155073Spjd if (errno == EEXIST) 468155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 469155073Spjd warn("cannot open pid file"); 470155073Spjd } 471107665Simp} 472107665Simp 473107665Simpvoid 474155073Spjdconfig::write_pidfile() 475155073Spjd{ 476155073Spjd 477155073Spjd pidfile_write(pfh); 478155073Spjd} 479155073Spjd 480155073Spjdvoid 481209583Simpconfig::close_pidfile() 482209583Simp{ 483209583Simp 484209583Simp pidfile_close(pfh); 485209583Simp} 486209583Simp 487209583Simpvoid 488155073Spjdconfig::remove_pidfile() 489155073Spjd{ 490155073Spjd 491155073Spjd pidfile_remove(pfh); 492155073Spjd} 493155073Spjd 494155073Spjdvoid 495107665Simpconfig::add_attach(int prio, event_proc *p) 496107665Simp{ 497107665Simp p->set_priority(prio); 498107665Simp _attach_list.push_back(p); 499107665Simp} 500107665Simp 501107665Simpvoid 502107665Simpconfig::add_detach(int prio, event_proc *p) 503107665Simp{ 504107665Simp p->set_priority(prio); 505107665Simp _detach_list.push_back(p); 506107665Simp} 507107665Simp 508107665Simpvoid 509107665Simpconfig::add_directory(const char *dir) 510107665Simp{ 511107665Simp _dir_list.push_back(string(dir)); 512107665Simp} 513107665Simp 514107665Simpvoid 515107665Simpconfig::add_nomatch(int prio, event_proc *p) 516107665Simp{ 517107665Simp p->set_priority(prio); 518107665Simp _nomatch_list.push_back(p); 519107665Simp} 520107665Simp 521107665Simpvoid 522121487Simpconfig::add_notify(int prio, event_proc *p) 523121487Simp{ 524121487Simp p->set_priority(prio); 525121487Simp _notify_list.push_back(p); 526121487Simp} 527121487Simp 528121487Simpvoid 529107665Simpconfig::set_pidfile(const char *fn) 530107665Simp{ 531107665Simp _pidfile = string(fn); 532107665Simp} 533107665Simp 534107665Simpvoid 535107665Simpconfig::push_var_table() 536107665Simp{ 537107665Simp var_list *vl; 538107665Simp 539107665Simp vl = new var_list(); 540107665Simp _var_list_table.push_back(vl); 541113790Simp if (Dflag) 542108783Simp fprintf(stderr, "Pushing table\n"); 543107665Simp} 544107665Simp 545107665Simpvoid 546107665Simpconfig::pop_var_table() 547107665Simp{ 548107665Simp delete _var_list_table.back(); 549107665Simp _var_list_table.pop_back(); 550113790Simp if (Dflag) 551108783Simp fprintf(stderr, "Popping table\n"); 552107665Simp} 553107665Simp 554107665Simpvoid 555107665Simpconfig::set_variable(const char *var, const char *val) 556107665Simp{ 557107665Simp _var_list_table.back()->set_variable(var, val); 558107665Simp} 559107665Simp 560107665Simpconst string & 561107665Simpconfig::get_variable(const string &var) 562107665Simp{ 563107665Simp vector<var_list *>::reverse_iterator i; 564107665Simp 565243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 566107665Simp if ((*i)->is_set(var)) 567108783Simp return ((*i)->get_variable(var)); 568107665Simp } 569107665Simp return (var_list::nothing); 570107665Simp} 571107665Simp 572108783Simpbool 573243930Seadlerconfig::is_id_char(char ch) const 574108783Simp{ 575108783Simp return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 576108783Simp ch == '-')); 577108783Simp} 578108783Simp 579108014Simpvoid 580114081Simpconfig::expand_one(const char *&src, string &dst) 581107665Simp{ 582108014Simp int count; 583210610Slulf string buffer; 584108014Simp 585108783Simp src++; 586108014Simp // $$ -> $ 587108014Simp if (*src == '$') { 588114081Simp dst.append(src++, 1); 589108014Simp return; 590108014Simp } 591108014Simp 592108014Simp // $(foo) -> $(foo) 593108783Simp // Not sure if I want to support this or not, so for now we just pass 594108783Simp // it through. 595108014Simp if (*src == '(') { 596114081Simp dst.append("$"); 597108014Simp count = 1; 598114081Simp /* If the string ends before ) is matched , return. */ 599114081Simp while (count > 0 && *src) { 600108014Simp if (*src == ')') 601108014Simp count--; 602108014Simp else if (*src == '(') 603108014Simp count++; 604114081Simp dst.append(src++, 1); 605108014Simp } 606108014Simp return; 607108014Simp } 608108014Simp 609108014Simp // ${^A-Za-z] -> $\1 610108014Simp if (!isalpha(*src)) { 611114081Simp dst.append("$"); 612114081Simp dst.append(src++, 1); 613108014Simp return; 614108014Simp } 615108014Simp 616108014Simp // $var -> replace with value 617114081Simp do { 618114081Simp buffer.append(src++, 1); 619114084Simp } while (is_id_char(*src)); 620210610Slulf dst.append(get_variable(buffer.c_str())); 621107665Simp} 622107665Simp 623108014Simpconst string 624246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 625108014Simp{ 626246134Sian const char *var_at; 627114081Simp string dst; 628108014Simp 629246134Sian /* 630246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 631246134Sian * while parsing config files, as tested on 2013-01-30. 632246134Sian */ 633246134Sian dst.reserve(128); 634246134Sian 635246134Sian if (prepend != NULL) 636246134Sian dst = prepend; 637246134Sian 638246134Sian for (;;) { 639246134Sian var_at = strchr(src, '$'); 640246134Sian if (var_at == NULL) { 641246134Sian dst.append(src); 642246134Sian break; 643246134Sian } 644246134Sian dst.append(src, var_at - src); 645246134Sian src = var_at; 646246134Sian expand_one(src, dst); 647108014Simp } 648108014Simp 649246134Sian if (append != NULL) 650246134Sian dst.append(append); 651246134Sian 652114081Simp return (dst); 653108014Simp} 654108014Simp 655108783Simpbool 656247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 657108783Simp{ 658108783Simp char *walker; 659108783Simp 660108783Simp if (*buffer == '\0') 661108783Simp return (false); 662108783Simp walker = lhs = buffer; 663108783Simp while (is_id_char(*walker)) 664108783Simp walker++; 665108783Simp if (*walker != '=') 666108783Simp return (false); 667108783Simp walker++; // skip = 668108783Simp if (*walker == '"') { 669108783Simp walker++; // skip " 670108783Simp rhs = walker; 671108783Simp while (*walker && *walker != '"') 672108783Simp walker++; 673108783Simp if (*walker != '"') 674108783Simp return (false); 675108783Simp rhs[-2] = '\0'; 676108783Simp *walker++ = '\0'; 677108783Simp } else { 678108783Simp rhs = walker; 679108783Simp while (*walker && !isspace(*walker)) 680108783Simp walker++; 681108783Simp if (*walker != '\0') 682108783Simp *walker++ = '\0'; 683108783Simp rhs[-1] = '\0'; 684108783Simp } 685113785Simp while (isspace(*walker)) 686113785Simp walker++; 687108783Simp buffer = walker; 688108783Simp return (true); 689108783Simp} 690108783Simp 691108783Simp 692108783Simpchar * 693108783Simpconfig::set_vars(char *buffer) 694108783Simp{ 695108783Simp char *lhs; 696108783Simp char *rhs; 697108783Simp 698108783Simp while (1) { 699108783Simp if (!chop_var(buffer, lhs, rhs)) 700108783Simp break; 701108783Simp set_variable(lhs, rhs); 702108783Simp } 703108783Simp return (buffer); 704108783Simp} 705108783Simp 706108783Simpvoid 707108783Simpconfig::find_and_execute(char type) 708108783Simp{ 709108783Simp vector<event_proc *> *l; 710108783Simp vector<event_proc *>::const_iterator i; 711151486Sbrooks const char *s; 712108783Simp 713108783Simp switch (type) { 714108783Simp default: 715108783Simp return; 716121487Simp case notify: 717121487Simp l = &_notify_list; 718121487Simp s = "notify"; 719121487Simp break; 720108783Simp case nomatch: 721108783Simp l = &_nomatch_list; 722108783Simp s = "nomatch"; 723108783Simp break; 724108783Simp case attach: 725108783Simp l = &_attach_list; 726108783Simp s = "attach"; 727108783Simp break; 728108783Simp case detach: 729108783Simp l = &_detach_list; 730108783Simp s = "detach"; 731108783Simp break; 732108783Simp } 733113790Simp if (Dflag) 734108783Simp fprintf(stderr, "Processing %s event\n", s); 735243931Seadler for (i = l->begin(); i != l->end(); ++i) { 736108783Simp if ((*i)->matches(*this)) { 737108783Simp (*i)->run(*this); 738108783Simp break; 739108783Simp } 740108783Simp } 741108783Simp 742108783Simp} 743108783Simp 744107665Simp 745107665Simpstatic void 746108783Simpprocess_event(char *buffer) 747107665Simp{ 748107665Simp char type; 749107665Simp char *sp; 750107665Simp 751108783Simp sp = buffer + 1; 752113790Simp if (Dflag) 753108783Simp fprintf(stderr, "Processing event '%s'\n", buffer); 754107665Simp type = *buffer++; 755108783Simp cfg.push_var_table(); 756108783Simp // No match doesn't have a device, and the format is a little 757108783Simp // different, so handle it separately. 758121487Simp switch (type) { 759121487Simp case notify: 760121487Simp sp = cfg.set_vars(sp); 761121487Simp break; 762121487Simp case nomatch: 763145218Simp //? at location pnp-info on bus 764145218Simp sp = strchr(sp, ' '); 765145218Simp if (sp == NULL) 766145218Simp return; /* Can't happen? */ 767145218Simp *sp++ = '\0'; 768213646Simp while (isspace(*sp)) 769213646Simp sp++; 770121487Simp if (strncmp(sp, "at ", 3) == 0) 771121487Simp sp += 3; 772121487Simp sp = cfg.set_vars(sp); 773213646Simp while (isspace(*sp)) 774213646Simp sp++; 775121487Simp if (strncmp(sp, "on ", 3) == 0) 776121487Simp cfg.set_variable("bus", sp + 3); 777121487Simp break; 778121487Simp case attach: /*FALLTHROUGH*/ 779121487Simp case detach: 780108783Simp sp = strchr(sp, ' '); 781108783Simp if (sp == NULL) 782108783Simp return; /* Can't happen? */ 783108783Simp *sp++ = '\0'; 784108783Simp cfg.set_variable("device-name", buffer); 785213646Simp while (isspace(*sp)) 786213646Simp sp++; 787113785Simp if (strncmp(sp, "at ", 3) == 0) 788113785Simp sp += 3; 789113785Simp sp = cfg.set_vars(sp); 790213646Simp while (isspace(*sp)) 791213646Simp sp++; 792113785Simp if (strncmp(sp, "on ", 3) == 0) 793113785Simp cfg.set_variable("bus", sp + 3); 794121487Simp break; 795108783Simp } 796113785Simp 797108783Simp cfg.find_and_execute(type); 798108783Simp cfg.pop_var_table(); 799107665Simp} 800107665Simp 801131397Simpint 802131397Simpcreate_socket(const char *name) 803131397Simp{ 804131397Simp int fd, slen; 805131397Simp struct sockaddr_un sun; 806131397Simp 807131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 808131397Simp err(1, "socket"); 809131397Simp bzero(&sun, sizeof(sun)); 810131397Simp sun.sun_family = AF_UNIX; 811131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 812131397Simp slen = SUN_LEN(&sun); 813131397Simp unlink(name); 814147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 815147973Smarcus err(1, "fcntl"); 816236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 817131397Simp err(1, "bind"); 818131397Simp listen(fd, 4); 819147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 820147973Smarcus chmod(name, 0666); 821131397Simp return (fd); 822131397Simp} 823131397Simp 824246121Sianunsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 825246121Sianunsigned int num_clients; 826131397Simplist<int> clients; 827131397Simp 828131397Simpvoid 829131397Simpnotify_clients(const char *data, int len) 830131397Simp{ 831246121Sian list<int>::iterator i; 832131397Simp 833246121Sian /* 834246121Sian * Deliver the data to all clients. Throw clients overboard at the 835246121Sian * first sign of trouble. This reaps clients who've died or closed 836246121Sian * their sockets, and also clients who are alive but failing to keep up 837246121Sian * (or who are maliciously not reading, to consume buffer space in 838246121Sian * kernel memory or tie up the limited number of available connections). 839246121Sian */ 840246121Sian for (i = clients.begin(); i != clients.end(); ) { 841246121Sian if (write(*i, data, len) != len) { 842246121Sian --num_clients; 843131397Simp close(*i); 844246121Sian i = clients.erase(i); 845246121Sian } else 846246121Sian ++i; 847131397Simp } 848246121Sian} 849131397Simp 850246121Sianvoid 851246121Siancheck_clients(void) 852246121Sian{ 853246121Sian int s; 854246121Sian struct pollfd pfd; 855246121Sian list<int>::iterator i; 856246121Sian 857246121Sian /* 858246121Sian * Check all existing clients to see if any of them have disappeared. 859246121Sian * Normally we reap clients when we get an error trying to send them an 860246121Sian * event. This check eliminates the problem of an ever-growing list of 861246121Sian * zombie clients because we're never writing to them on a system 862246121Sian * without frequent device-change activity. 863246121Sian */ 864246121Sian pfd.events = 0; 865246121Sian for (i = clients.begin(); i != clients.end(); ) { 866246121Sian pfd.fd = *i; 867246121Sian s = poll(&pfd, 1, 0); 868246121Sian if ((s < 0 && s != EINTR ) || 869246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 870246121Sian --num_clients; 871246121Sian close(*i); 872246121Sian i = clients.erase(i); 873246121Sian } else 874246121Sian ++i; 875246121Sian } 876131397Simp} 877131397Simp 878131397Simpvoid 879131397Simpnew_client(int fd) 880131397Simp{ 881131397Simp int s; 882131397Simp 883246121Sian /* 884246121Sian * First go reap any zombie clients, then accept the connection, and 885246121Sian * shut down the read side to stop clients from consuming kernel memory 886246121Sian * by sending large buffers full of data we'll never read. 887246121Sian */ 888246121Sian check_clients(); 889131397Simp s = accept(fd, NULL, NULL); 890246121Sian if (s != -1) { 891246121Sian shutdown(s, SHUT_RD); 892131397Simp clients.push_back(s); 893246121Sian ++num_clients; 894246121Sian } 895131397Simp} 896131397Simp 897107665Simpstatic void 898107665Simpevent_loop(void) 899107665Simp{ 900107665Simp int rv; 901107665Simp int fd; 902107665Simp char buffer[DEVCTL_MAXBUF]; 903113790Simp int once = 0; 904131397Simp int server_fd, max_fd; 905246121Sian int accepting; 906113790Simp timeval tv; 907113790Simp fd_set fds; 908107665Simp 909240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 910107665Simp if (fd == -1) 911131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 912131397Simp server_fd = create_socket(PIPE); 913246121Sian accepting = 1; 914131397Simp max_fd = max(fd, server_fd) + 1; 915107665Simp while (1) { 916107665Simp if (romeo_must_die) 917107665Simp break; 918113790Simp if (!once && !dflag && !nflag) { 919113790Simp // Check to see if we have any events pending. 920113790Simp tv.tv_sec = 0; 921113790Simp tv.tv_usec = 0; 922113790Simp FD_ZERO(&fds); 923113790Simp FD_SET(fd, &fds); 924113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 925113790Simp // No events -> we've processed all pending events 926117944Simp if (rv == 0) { 927113790Simp if (Dflag) 928113790Simp fprintf(stderr, "Calling daemon\n"); 929155073Spjd cfg.remove_pidfile(); 930155073Spjd cfg.open_pidfile(); 931113790Simp daemon(0, 0); 932155073Spjd cfg.write_pidfile(); 933113790Simp once++; 934113790Simp } 935113790Simp } 936246121Sian /* 937246121Sian * When we've already got the max number of clients, stop 938246121Sian * accepting new connections (don't put server_fd in the set), 939246121Sian * shrink the accept() queue to reject connections quickly, and 940246121Sian * poll the existing clients more often, so that we notice more 941246121Sian * quickly when any of them disappear to free up client slots. 942246121Sian */ 943131397Simp FD_ZERO(&fds); 944131397Simp FD_SET(fd, &fds); 945246121Sian if (num_clients < max_clients) { 946246121Sian if (!accepting) { 947246121Sian listen(server_fd, max_clients); 948246121Sian accepting = 1; 949246121Sian } 950246121Sian FD_SET(server_fd, &fds); 951246121Sian tv.tv_sec = 60; 952246121Sian tv.tv_usec = 0; 953246121Sian } else { 954246121Sian if (accepting) { 955246121Sian listen(server_fd, 0); 956246121Sian accepting = 0; 957246121Sian } 958246121Sian tv.tv_sec = 2; 959246121Sian tv.tv_usec = 0; 960246121Sian } 961246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 962131397Simp if (rv == -1) { 963131397Simp if (errno == EINTR) 964131397Simp continue; 965131397Simp err(1, "select"); 966246121Sian } else if (rv == 0) 967246121Sian check_clients(); 968131397Simp if (FD_ISSET(fd, &fds)) { 969131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 970131397Simp if (rv > 0) { 971131397Simp notify_clients(buffer, rv); 972107665Simp buffer[rv] = '\0'; 973131397Simp while (buffer[--rv] == '\n') 974131397Simp buffer[rv] = '\0'; 975131397Simp process_event(buffer); 976131397Simp } else if (rv < 0) { 977131397Simp if (errno != EINTR) 978131397Simp break; 979131397Simp } else { 980131397Simp /* EOF */ 981107665Simp break; 982131397Simp } 983107665Simp } 984131397Simp if (FD_ISSET(server_fd, &fds)) 985131397Simp new_client(server_fd); 986107665Simp } 987107665Simp close(fd); 988107665Simp} 989107665Simp 990107665Simp/* 991107665Simp * functions that the parser uses. 992107665Simp */ 993107665Simpvoid 994107665Simpadd_attach(int prio, event_proc *p) 995107665Simp{ 996107665Simp cfg.add_attach(prio, p); 997107665Simp} 998107665Simp 999107665Simpvoid 1000107665Simpadd_detach(int prio, event_proc *p) 1001107665Simp{ 1002107665Simp cfg.add_detach(prio, p); 1003107665Simp} 1004107665Simp 1005107665Simpvoid 1006107665Simpadd_directory(const char *dir) 1007107665Simp{ 1008107665Simp cfg.add_directory(dir); 1009107665Simp free(const_cast<char *>(dir)); 1010107665Simp} 1011107665Simp 1012107665Simpvoid 1013107665Simpadd_nomatch(int prio, event_proc *p) 1014107665Simp{ 1015107665Simp cfg.add_nomatch(prio, p); 1016107665Simp} 1017107665Simp 1018121487Simpvoid 1019121487Simpadd_notify(int prio, event_proc *p) 1020121487Simp{ 1021121487Simp cfg.add_notify(prio, p); 1022121487Simp} 1023121487Simp 1024107665Simpevent_proc * 1025107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1026107665Simp{ 1027107665Simp if (ep == NULL) 1028107665Simp ep = new event_proc(); 1029107665Simp ep->add(eps); 1030107665Simp return (ep); 1031107665Simp} 1032107665Simp 1033107665Simpeps * 1034107665Simpnew_action(const char *cmd) 1035107665Simp{ 1036107665Simp eps *e = new action(cmd); 1037107665Simp free(const_cast<char *>(cmd)); 1038107665Simp return (e); 1039107665Simp} 1040107665Simp 1041107665Simpeps * 1042107665Simpnew_match(const char *var, const char *re) 1043107665Simp{ 1044108014Simp eps *e = new match(cfg, var, re); 1045107665Simp free(const_cast<char *>(var)); 1046107665Simp free(const_cast<char *>(re)); 1047107665Simp return (e); 1048107665Simp} 1049107665Simp 1050147874Simpeps * 1051147874Simpnew_media(const char *var, const char *re) 1052147874Simp{ 1053147874Simp eps *e = new media(cfg, var, re); 1054147874Simp free(const_cast<char *>(var)); 1055147874Simp free(const_cast<char *>(re)); 1056147874Simp return (e); 1057147874Simp} 1058147874Simp 1059107665Simpvoid 1060107665Simpset_pidfile(const char *name) 1061107665Simp{ 1062107665Simp cfg.set_pidfile(name); 1063107665Simp free(const_cast<char *>(name)); 1064107665Simp} 1065107665Simp 1066107665Simpvoid 1067107665Simpset_variable(const char *var, const char *val) 1068107665Simp{ 1069107665Simp cfg.set_variable(var, val); 1070107665Simp free(const_cast<char *>(var)); 1071107665Simp free(const_cast<char *>(val)); 1072107665Simp} 1073107665Simp 1074107665Simp 1075107665Simp 1076107665Simpstatic void 1077107665Simpgensighand(int) 1078107665Simp{ 1079247754Seadler romeo_must_die = 1; 1080107665Simp _exit(0); 1081107665Simp} 1082107665Simp 1083107665Simpstatic void 1084107665Simpusage() 1085107665Simp{ 1086246121Sian fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n", 1087246121Sian getprogname()); 1088107665Simp exit(1); 1089107665Simp} 1090107665Simp 1091113787Simpstatic void 1092113787Simpcheck_devd_enabled() 1093113787Simp{ 1094113787Simp int val = 0; 1095113787Simp size_t len; 1096113787Simp 1097113787Simp len = sizeof(val); 1098114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1099113787Simp errx(1, "devctl sysctl missing from kernel!"); 1100113787Simp if (val) { 1101113787Simp warnx("Setting " SYSCTL " to 0"); 1102113787Simp val = 0; 1103113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1104113787Simp } 1105113787Simp} 1106113787Simp 1107107665Simp/* 1108107665Simp * main 1109107665Simp */ 1110107665Simpint 1111107665Simpmain(int argc, char **argv) 1112107665Simp{ 1113107665Simp int ch; 1114107665Simp 1115113787Simp check_devd_enabled(); 1116246121Sian while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) { 1117107665Simp switch (ch) { 1118113790Simp case 'D': 1119113790Simp Dflag++; 1120113790Simp break; 1121107665Simp case 'd': 1122107665Simp dflag++; 1123107665Simp break; 1124152770Sjkoshy case 'f': 1125152770Sjkoshy configfile = optarg; 1126152770Sjkoshy break; 1127246121Sian case 'l': 1128246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1129246121Sian break; 1130113790Simp case 'n': 1131113790Simp nflag++; 1132113790Simp break; 1133107665Simp default: 1134107665Simp usage(); 1135107665Simp } 1136107665Simp } 1137107665Simp 1138107665Simp cfg.parse(); 1139117246Simp if (!dflag && nflag) { 1140155073Spjd cfg.open_pidfile(); 1141107665Simp daemon(0, 0); 1142155073Spjd cfg.write_pidfile(); 1143117246Simp } 1144146306Simp signal(SIGPIPE, SIG_IGN); 1145107665Simp signal(SIGHUP, gensighand); 1146107665Simp signal(SIGINT, gensighand); 1147107665Simp signal(SIGTERM, gensighand); 1148107665Simp event_loop(); 1149107665Simp return (0); 1150107665Simp} 1151