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$"); 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> 83247576Sian#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 104260520Sasomers/* 105260520Sasomers * Since the client socket is nonblocking, we must increase its send buffer to 106260520Sasomers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 107260520Sasomers * buffer, so the client can't increate the buffersize by itself. 108260520Sasomers * 109260520Sasomers * For example, when creating a ZFS pool, devd emits one 165 character 110260520Sasomers * resource.fs.zfs.statechange message for each vdev in the pool. A 64k 111260520Sasomers * buffer has enough space for almost 400 drives, which would be very large but 112260520Sasomers * not impossibly large pool. A 128k buffer has enough space for 794 drives, 113260520Sasomers * which is more than can fit in a rack with modern technology. 114260520Sasomers */ 115260520Sasomers#define CLIENT_BUFSIZE 131072 116260520Sasomers 117107665Simpusing namespace std; 118107665Simp 119107665Simpextern FILE *yyin; 120107665Simpextern int lineno; 121107665Simp 122121487Simpstatic const char notify = '!'; 123108783Simpstatic const char nomatch = '?'; 124108783Simpstatic const char attach = '+'; 125108783Simpstatic const char detach = '-'; 126108783Simp 127155073Spjdstatic struct pidfh *pfh; 128155073Spjd 129113790Simpint Dflag; 130107665Simpint dflag; 131114000Simpint nflag; 132107665Simpint romeo_must_die = 0; 133107665Simp 134152770Sjkoshystatic const char *configfile = CF; 135152770Sjkoshy 136107665Simpstatic void event_loop(void); 137107665Simpstatic void usage(void); 138107665Simp 139108783Simptemplate <class T> void 140108783Simpdelete_and_clear(vector<T *> &v) 141108783Simp{ 142108783Simp typename vector<T *>::const_iterator i; 143108783Simp 144246270Seadler for (i = v.begin(); i != v.end(); ++i) 145108783Simp delete *i; 146108783Simp v.clear(); 147108783Simp} 148108783Simp 149107665Simpconfig cfg; 150107665Simp 151107665Simpevent_proc::event_proc() : _prio(-1) 152107665Simp{ 153247803Sian _epsvec.reserve(4); 154107665Simp} 155107665Simp 156107665Simpevent_proc::~event_proc() 157107665Simp{ 158152406Sbland delete_and_clear(_epsvec); 159107665Simp} 160107665Simp 161107665Simpvoid 162107665Simpevent_proc::add(eps *eps) 163107665Simp{ 164107665Simp _epsvec.push_back(eps); 165107665Simp} 166107665Simp 167107665Simpbool 168246309Seadlerevent_proc::matches(config &c) const 169107665Simp{ 170107665Simp vector<eps *>::const_iterator i; 171107665Simp 172246270Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 173107665Simp if (!(*i)->do_match(c)) 174107665Simp return (false); 175107665Simp return (true); 176107665Simp} 177107665Simp 178107665Simpbool 179246309Seadlerevent_proc::run(config &c) const 180107665Simp{ 181107665Simp vector<eps *>::const_iterator i; 182107665Simp 183246270Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 184107665Simp if (!(*i)->do_action(c)) 185107665Simp return (false); 186107665Simp return (true); 187107665Simp} 188107665Simp 189107665Simpaction::action(const char *cmd) 190107665Simp : _cmd(cmd) 191107665Simp{ 192107665Simp // nothing 193107665Simp} 194107665Simp 195107665Simpaction::~action() 196107665Simp{ 197107665Simp // nothing 198107665Simp} 199107665Simp 200209583Simpstatic int 201209583Simpmy_system(const char *command) 202209583Simp{ 203209583Simp pid_t pid, savedpid; 204209583Simp int pstat; 205209583Simp struct sigaction ign, intact, quitact; 206209583Simp sigset_t newsigblock, oldsigblock; 207209583Simp 208209583Simp if (!command) /* just checking... */ 209209583Simp return(1); 210209583Simp 211209583Simp /* 212209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 213209583Simp * existing signal dispositions. 214209583Simp */ 215209583Simp ign.sa_handler = SIG_IGN; 216209583Simp ::sigemptyset(&ign.sa_mask); 217209583Simp ign.sa_flags = 0; 218209583Simp ::sigaction(SIGINT, &ign, &intact); 219209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 220209583Simp ::sigemptyset(&newsigblock); 221209583Simp ::sigaddset(&newsigblock, SIGCHLD); 222209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 223209583Simp switch (pid = ::fork()) { 224209583Simp case -1: /* error */ 225209583Simp break; 226209583Simp case 0: /* child */ 227209583Simp /* 228209583Simp * Restore original signal dispositions and exec the command. 229209583Simp */ 230209583Simp ::sigaction(SIGINT, &intact, NULL); 231209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 232209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 233209583Simp /* 234209583Simp * Close the PID file, and all other open descriptors. 235209583Simp * Inherit std{in,out,err} only. 236209583Simp */ 237209583Simp cfg.close_pidfile(); 238209583Simp ::closefrom(3); 239209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 240209583Simp ::_exit(127); 241209583Simp default: /* parent */ 242209583Simp savedpid = pid; 243209583Simp do { 244209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 245209583Simp } while (pid == -1 && errno == EINTR); 246209583Simp break; 247209583Simp } 248209583Simp ::sigaction(SIGINT, &intact, NULL); 249209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 250209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 251209583Simp return (pid == -1 ? -1 : pstat); 252209583Simp} 253209583Simp 254107665Simpbool 255108014Simpaction::do_action(config &c) 256107665Simp{ 257247803Sian string s = c.expand_string(_cmd.c_str()); 258113790Simp if (Dflag) 259108783Simp fprintf(stderr, "Executing '%s'\n", s.c_str()); 260209583Simp my_system(s.c_str()); 261107665Simp return (true); 262107665Simp} 263107665Simp 264247803Sianmatch::match(config &c, const char *var, const char *re) : 265247803Sian _inv(re[0] == '!'), 266247803Sian _var(var), 267247803Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 268107665Simp{ 269154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 270107665Simp} 271107665Simp 272107665Simpmatch::~match() 273107665Simp{ 274108014Simp regfree(&_regex); 275107665Simp} 276107665Simp 277107665Simpbool 278108014Simpmatch::do_match(config &c) 279107665Simp{ 280210610Slulf const string &value = c.get_variable(_var); 281108014Simp bool retval; 282108014Simp 283113790Simp if (Dflag) 284247767Shrs fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 285247767Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 286108783Simp 287108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 288247767Shrs if (_inv == 1) 289247767Shrs retval = (retval == 0) ? 1 : 0; 290247767Shrs 291108014Simp return retval; 292107665Simp} 293107665Simp 294147874Simp#include <sys/sockio.h> 295147874Simp#include <net/if.h> 296147874Simp#include <net/if_media.h> 297147874Simp 298151486Sbrooksmedia::media(config &, const char *var, const char *type) 299147874Simp : _var(var), _type(-1) 300147874Simp{ 301147874Simp static struct ifmedia_description media_types[] = { 302147874Simp { IFM_ETHER, "Ethernet" }, 303147874Simp { IFM_TOKEN, "Tokenring" }, 304147874Simp { IFM_FDDI, "FDDI" }, 305147874Simp { IFM_IEEE80211, "802.11" }, 306147874Simp { IFM_ATM, "ATM" }, 307147874Simp { IFM_CARP, "CARP" }, 308147874Simp { -1, "unknown" }, 309147874Simp { 0, NULL }, 310147874Simp }; 311246270Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 312147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 313147874Simp _type = media_types[i].ifmt_word; 314147874Simp break; 315147874Simp } 316147874Simp} 317147874Simp 318147874Simpmedia::~media() 319147874Simp{ 320147874Simp} 321147874Simp 322147874Simpbool 323147874Simpmedia::do_match(config &c) 324147874Simp{ 325150949Simp string value; 326147874Simp struct ifmediareq ifmr; 327147874Simp bool retval; 328147874Simp int s; 329147874Simp 330150949Simp // Since we can be called from both a device attach/detach 331150949Simp // context where device-name is defined and what we want, 332150949Simp // as well as from a link status context, where subsystem is 333150949Simp // the name of interest, first try device-name and fall back 334150949Simp // to subsystem if none exists. 335150949Simp value = c.get_variable("device-name"); 336150949Simp if (value.length() == 0) 337151480Simp value = c.get_variable("subsystem"); 338147874Simp if (Dflag) 339147874Simp fprintf(stderr, "Testing media type of %s against 0x%x\n", 340147874Simp value.c_str(), _type); 341147874Simp 342147874Simp retval = false; 343147874Simp 344147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 345147874Simp if (s >= 0) { 346147874Simp memset(&ifmr, 0, sizeof(ifmr)); 347147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 348147874Simp 349147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 350147874Simp ifmr.ifm_status & IFM_AVALID) { 351147874Simp if (Dflag) 352147874Simp fprintf(stderr, "%s has media type 0x%x\n", 353147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 354147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 355147874Simp } else if (_type == -1) { 356147874Simp if (Dflag) 357147874Simp fprintf(stderr, "%s has unknown media type\n", 358147874Simp value.c_str()); 359147874Simp retval = true; 360147874Simp } 361147874Simp close(s); 362147874Simp } 363147874Simp 364147874Simp return retval; 365147874Simp} 366147874Simp 367107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 368107665Simpconst string var_list::nothing = ""; 369107665Simp 370107665Simpconst string & 371107665Simpvar_list::get_variable(const string &var) const 372107665Simp{ 373107665Simp map<string, string>::const_iterator i; 374107665Simp 375107665Simp i = _vars.find(var); 376107665Simp if (i == _vars.end()) 377108783Simp return (var_list::bogus); 378107665Simp return (i->second); 379107665Simp} 380107665Simp 381107665Simpbool 382107665Simpvar_list::is_set(const string &var) const 383107665Simp{ 384107665Simp return (_vars.find(var) != _vars.end()); 385107665Simp} 386107665Simp 387107665Simpvoid 388107665Simpvar_list::set_variable(const string &var, const string &val) 389107665Simp{ 390113790Simp if (Dflag) 391145218Simp fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 392107665Simp _vars[var] = val; 393107665Simp} 394107665Simp 395107665Simpvoid 396107665Simpconfig::reset(void) 397107665Simp{ 398107665Simp _dir_list.clear(); 399108783Simp delete_and_clear(_var_list_table); 400108783Simp delete_and_clear(_attach_list); 401108783Simp delete_and_clear(_detach_list); 402108783Simp delete_and_clear(_nomatch_list); 403121487Simp delete_and_clear(_notify_list); 404107665Simp} 405107665Simp 406107665Simpvoid 407107665Simpconfig::parse_one_file(const char *fn) 408107665Simp{ 409113790Simp if (Dflag) 410186078Sphk fprintf(stderr, "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 427113790Simp if (Dflag) 428186078Sphk fprintf(stderr, "Parsing files in %s\n", dirname); 429107665Simp dirp = opendir(dirname); 430107665Simp if (dirp == NULL) 431107665Simp return; 432107665Simp readdir(dirp); /* Skip . */ 433107665Simp readdir(dirp); /* Skip .. */ 434107665Simp while ((dp = readdir(dirp)) != NULL) { 435107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 436107665Simp snprintf(path, sizeof(path), "%s/%s", 437107665Simp dirname, dp->d_name); 438107665Simp parse_one_file(path); 439107665Simp } 440107665Simp } 441215607Skevlo closedir(dirp); 442107665Simp} 443107665Simp 444108783Simpclass epv_greater { 445108783Simppublic: 446246309Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 447108783Simp { 448108783Simp return (l1->get_priority() > l2->get_priority()); 449108783Simp } 450108783Simp}; 451108783Simp 452107665Simpvoid 453108783Simpconfig::sort_vector(vector<event_proc *> &v) 454108783Simp{ 455244430Sdim stable_sort(v.begin(), v.end(), epv_greater()); 456108783Simp} 457108783Simp 458108783Simpvoid 459107665Simpconfig::parse(void) 460107665Simp{ 461107665Simp vector<string>::const_iterator i; 462107665Simp 463152770Sjkoshy parse_one_file(configfile); 464246270Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 465107665Simp parse_files_in_dir((*i).c_str()); 466108783Simp sort_vector(_attach_list); 467108783Simp sort_vector(_detach_list); 468108783Simp sort_vector(_nomatch_list); 469121487Simp sort_vector(_notify_list); 470107665Simp} 471107665Simp 472107665Simpvoid 473155073Spjdconfig::open_pidfile() 474107665Simp{ 475155073Spjd pid_t otherpid; 476107665Simp 477107665Simp if (_pidfile == "") 478107665Simp return; 479155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 480155073Spjd if (pfh == NULL) { 481155073Spjd if (errno == EEXIST) 482155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 483155073Spjd warn("cannot open pid file"); 484155073Spjd } 485107665Simp} 486107665Simp 487107665Simpvoid 488155073Spjdconfig::write_pidfile() 489155073Spjd{ 490155073Spjd 491155073Spjd pidfile_write(pfh); 492155073Spjd} 493155073Spjd 494155073Spjdvoid 495209583Simpconfig::close_pidfile() 496209583Simp{ 497209583Simp 498209583Simp pidfile_close(pfh); 499209583Simp} 500209583Simp 501209583Simpvoid 502155073Spjdconfig::remove_pidfile() 503155073Spjd{ 504155073Spjd 505155073Spjd pidfile_remove(pfh); 506155073Spjd} 507155073Spjd 508155073Spjdvoid 509107665Simpconfig::add_attach(int prio, event_proc *p) 510107665Simp{ 511107665Simp p->set_priority(prio); 512107665Simp _attach_list.push_back(p); 513107665Simp} 514107665Simp 515107665Simpvoid 516107665Simpconfig::add_detach(int prio, event_proc *p) 517107665Simp{ 518107665Simp p->set_priority(prio); 519107665Simp _detach_list.push_back(p); 520107665Simp} 521107665Simp 522107665Simpvoid 523107665Simpconfig::add_directory(const char *dir) 524107665Simp{ 525107665Simp _dir_list.push_back(string(dir)); 526107665Simp} 527107665Simp 528107665Simpvoid 529107665Simpconfig::add_nomatch(int prio, event_proc *p) 530107665Simp{ 531107665Simp p->set_priority(prio); 532107665Simp _nomatch_list.push_back(p); 533107665Simp} 534107665Simp 535107665Simpvoid 536121487Simpconfig::add_notify(int prio, event_proc *p) 537121487Simp{ 538121487Simp p->set_priority(prio); 539121487Simp _notify_list.push_back(p); 540121487Simp} 541121487Simp 542121487Simpvoid 543107665Simpconfig::set_pidfile(const char *fn) 544107665Simp{ 545107665Simp _pidfile = string(fn); 546107665Simp} 547107665Simp 548107665Simpvoid 549107665Simpconfig::push_var_table() 550107665Simp{ 551107665Simp var_list *vl; 552107665Simp 553107665Simp vl = new var_list(); 554107665Simp _var_list_table.push_back(vl); 555113790Simp if (Dflag) 556108783Simp fprintf(stderr, "Pushing table\n"); 557107665Simp} 558107665Simp 559107665Simpvoid 560107665Simpconfig::pop_var_table() 561107665Simp{ 562107665Simp delete _var_list_table.back(); 563107665Simp _var_list_table.pop_back(); 564113790Simp if (Dflag) 565108783Simp fprintf(stderr, "Popping table\n"); 566107665Simp} 567107665Simp 568107665Simpvoid 569107665Simpconfig::set_variable(const char *var, const char *val) 570107665Simp{ 571107665Simp _var_list_table.back()->set_variable(var, val); 572107665Simp} 573107665Simp 574107665Simpconst string & 575107665Simpconfig::get_variable(const string &var) 576107665Simp{ 577107665Simp vector<var_list *>::reverse_iterator i; 578107665Simp 579246270Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 580107665Simp if ((*i)->is_set(var)) 581108783Simp return ((*i)->get_variable(var)); 582107665Simp } 583107665Simp return (var_list::nothing); 584107665Simp} 585107665Simp 586108783Simpbool 587246309Seadlerconfig::is_id_char(char ch) const 588108783Simp{ 589108783Simp return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 590108783Simp ch == '-')); 591108783Simp} 592108783Simp 593108014Simpvoid 594114081Simpconfig::expand_one(const char *&src, string &dst) 595107665Simp{ 596108014Simp int count; 597210610Slulf string buffer; 598108014Simp 599108783Simp src++; 600108014Simp // $$ -> $ 601108014Simp if (*src == '$') { 602114081Simp dst.append(src++, 1); 603108014Simp return; 604108014Simp } 605108014Simp 606108014Simp // $(foo) -> $(foo) 607108783Simp // Not sure if I want to support this or not, so for now we just pass 608108783Simp // it through. 609108014Simp if (*src == '(') { 610114081Simp dst.append("$"); 611108014Simp count = 1; 612114081Simp /* If the string ends before ) is matched , return. */ 613114081Simp while (count > 0 && *src) { 614108014Simp if (*src == ')') 615108014Simp count--; 616108014Simp else if (*src == '(') 617108014Simp count++; 618114081Simp dst.append(src++, 1); 619108014Simp } 620108014Simp return; 621108014Simp } 622108014Simp 623108014Simp // ${^A-Za-z] -> $\1 624108014Simp if (!isalpha(*src)) { 625114081Simp dst.append("$"); 626114081Simp dst.append(src++, 1); 627108014Simp return; 628108014Simp } 629108014Simp 630108014Simp // $var -> replace with value 631114081Simp do { 632114081Simp buffer.append(src++, 1); 633114084Simp } while (is_id_char(*src)); 634210610Slulf dst.append(get_variable(buffer.c_str())); 635107665Simp} 636107665Simp 637108014Simpconst string 638247803Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 639108014Simp{ 640247803Sian const char *var_at; 641114081Simp string dst; 642108014Simp 643247803Sian /* 644247803Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 645247803Sian * while parsing config files, as tested on 2013-01-30. 646247803Sian */ 647247803Sian dst.reserve(128); 648247803Sian 649247803Sian if (prepend != NULL) 650247803Sian dst = prepend; 651247803Sian 652247803Sian for (;;) { 653247803Sian var_at = strchr(src, '$'); 654247803Sian if (var_at == NULL) { 655247803Sian dst.append(src); 656247803Sian break; 657247803Sian } 658247803Sian dst.append(src, var_at - src); 659247803Sian src = var_at; 660247803Sian expand_one(src, dst); 661108014Simp } 662108014Simp 663247803Sian if (append != NULL) 664247803Sian dst.append(append); 665247803Sian 666114081Simp return (dst); 667108014Simp} 668108014Simp 669108783Simpbool 670108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) 671108783Simp{ 672108783Simp char *walker; 673108783Simp 674108783Simp if (*buffer == '\0') 675108783Simp return (false); 676108783Simp walker = lhs = buffer; 677108783Simp while (is_id_char(*walker)) 678108783Simp walker++; 679108783Simp if (*walker != '=') 680108783Simp return (false); 681108783Simp walker++; // skip = 682108783Simp if (*walker == '"') { 683108783Simp walker++; // skip " 684108783Simp rhs = walker; 685108783Simp while (*walker && *walker != '"') 686108783Simp walker++; 687108783Simp if (*walker != '"') 688108783Simp return (false); 689108783Simp rhs[-2] = '\0'; 690108783Simp *walker++ = '\0'; 691108783Simp } else { 692108783Simp rhs = walker; 693108783Simp while (*walker && !isspace(*walker)) 694108783Simp walker++; 695108783Simp if (*walker != '\0') 696108783Simp *walker++ = '\0'; 697108783Simp rhs[-1] = '\0'; 698108783Simp } 699113785Simp while (isspace(*walker)) 700113785Simp walker++; 701108783Simp buffer = walker; 702108783Simp return (true); 703108783Simp} 704108783Simp 705108783Simp 706108783Simpchar * 707108783Simpconfig::set_vars(char *buffer) 708108783Simp{ 709108783Simp char *lhs; 710108783Simp char *rhs; 711108783Simp 712108783Simp while (1) { 713108783Simp if (!chop_var(buffer, lhs, rhs)) 714108783Simp break; 715108783Simp set_variable(lhs, rhs); 716108783Simp } 717108783Simp return (buffer); 718108783Simp} 719108783Simp 720108783Simpvoid 721108783Simpconfig::find_and_execute(char type) 722108783Simp{ 723108783Simp vector<event_proc *> *l; 724108783Simp vector<event_proc *>::const_iterator i; 725151486Sbrooks const char *s; 726108783Simp 727108783Simp switch (type) { 728108783Simp default: 729108783Simp return; 730121487Simp case notify: 731121487Simp l = &_notify_list; 732121487Simp s = "notify"; 733121487Simp break; 734108783Simp case nomatch: 735108783Simp l = &_nomatch_list; 736108783Simp s = "nomatch"; 737108783Simp break; 738108783Simp case attach: 739108783Simp l = &_attach_list; 740108783Simp s = "attach"; 741108783Simp break; 742108783Simp case detach: 743108783Simp l = &_detach_list; 744108783Simp s = "detach"; 745108783Simp break; 746108783Simp } 747113790Simp if (Dflag) 748108783Simp fprintf(stderr, "Processing %s event\n", s); 749246270Seadler for (i = l->begin(); i != l->end(); ++i) { 750108783Simp if ((*i)->matches(*this)) { 751108783Simp (*i)->run(*this); 752108783Simp break; 753108783Simp } 754108783Simp } 755108783Simp 756108783Simp} 757108783Simp 758107665Simp 759107665Simpstatic void 760108783Simpprocess_event(char *buffer) 761107665Simp{ 762107665Simp char type; 763107665Simp char *sp; 764107665Simp 765108783Simp sp = buffer + 1; 766113790Simp if (Dflag) 767108783Simp fprintf(stderr, "Processing event '%s'\n", buffer); 768107665Simp type = *buffer++; 769108783Simp cfg.push_var_table(); 770108783Simp // No match doesn't have a device, and the format is a little 771108783Simp // different, so handle it separately. 772121487Simp switch (type) { 773121487Simp case notify: 774121487Simp sp = cfg.set_vars(sp); 775121487Simp break; 776121487Simp case nomatch: 777145218Simp //? at location pnp-info on bus 778145218Simp sp = strchr(sp, ' '); 779145218Simp if (sp == NULL) 780145218Simp return; /* Can't happen? */ 781145218Simp *sp++ = '\0'; 782213646Simp while (isspace(*sp)) 783213646Simp sp++; 784121487Simp if (strncmp(sp, "at ", 3) == 0) 785121487Simp sp += 3; 786121487Simp sp = cfg.set_vars(sp); 787213646Simp while (isspace(*sp)) 788213646Simp sp++; 789121487Simp if (strncmp(sp, "on ", 3) == 0) 790121487Simp cfg.set_variable("bus", sp + 3); 791121487Simp break; 792121487Simp case attach: /*FALLTHROUGH*/ 793121487Simp case detach: 794108783Simp sp = strchr(sp, ' '); 795108783Simp if (sp == NULL) 796108783Simp return; /* Can't happen? */ 797108783Simp *sp++ = '\0'; 798108783Simp cfg.set_variable("device-name", buffer); 799213646Simp while (isspace(*sp)) 800213646Simp sp++; 801113785Simp if (strncmp(sp, "at ", 3) == 0) 802113785Simp sp += 3; 803113785Simp sp = cfg.set_vars(sp); 804213646Simp while (isspace(*sp)) 805213646Simp sp++; 806113785Simp if (strncmp(sp, "on ", 3) == 0) 807113785Simp cfg.set_variable("bus", sp + 3); 808121487Simp break; 809108783Simp } 810113785Simp 811108783Simp cfg.find_and_execute(type); 812108783Simp cfg.pop_var_table(); 813107665Simp} 814107665Simp 815131397Simpint 816131397Simpcreate_socket(const char *name) 817131397Simp{ 818131397Simp int fd, slen; 819131397Simp struct sockaddr_un sun; 820131397Simp 821131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 822131397Simp err(1, "socket"); 823131397Simp bzero(&sun, sizeof(sun)); 824131397Simp sun.sun_family = AF_UNIX; 825131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 826131397Simp slen = SUN_LEN(&sun); 827131397Simp unlink(name); 828147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 829147973Smarcus err(1, "fcntl"); 830236540Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 831131397Simp err(1, "bind"); 832131397Simp listen(fd, 4); 833147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 834147973Smarcus chmod(name, 0666); 835131397Simp return (fd); 836131397Simp} 837131397Simp 838247576Sianunsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 839247576Sianunsigned int num_clients; 840131397Simplist<int> clients; 841131397Simp 842131397Simpvoid 843131397Simpnotify_clients(const char *data, int len) 844131397Simp{ 845247576Sian list<int>::iterator i; 846131397Simp 847247576Sian /* 848247576Sian * Deliver the data to all clients. Throw clients overboard at the 849247576Sian * first sign of trouble. This reaps clients who've died or closed 850247576Sian * their sockets, and also clients who are alive but failing to keep up 851247576Sian * (or who are maliciously not reading, to consume buffer space in 852247576Sian * kernel memory or tie up the limited number of available connections). 853247576Sian */ 854247576Sian for (i = clients.begin(); i != clients.end(); ) { 855247576Sian if (write(*i, data, len) != len) { 856247576Sian --num_clients; 857131397Simp close(*i); 858247576Sian i = clients.erase(i); 859247576Sian } else 860247576Sian ++i; 861131397Simp } 862247576Sian} 863131397Simp 864247576Sianvoid 865247576Siancheck_clients(void) 866247576Sian{ 867247576Sian int s; 868247576Sian struct pollfd pfd; 869247576Sian list<int>::iterator i; 870247576Sian 871247576Sian /* 872247576Sian * Check all existing clients to see if any of them have disappeared. 873247576Sian * Normally we reap clients when we get an error trying to send them an 874247576Sian * event. This check eliminates the problem of an ever-growing list of 875247576Sian * zombie clients because we're never writing to them on a system 876247576Sian * without frequent device-change activity. 877247576Sian */ 878247576Sian pfd.events = 0; 879247576Sian for (i = clients.begin(); i != clients.end(); ) { 880247576Sian pfd.fd = *i; 881247576Sian s = poll(&pfd, 1, 0); 882247576Sian if ((s < 0 && s != EINTR ) || 883247576Sian (s > 0 && (pfd.revents & POLLHUP))) { 884247576Sian --num_clients; 885247576Sian close(*i); 886247576Sian i = clients.erase(i); 887247576Sian } else 888247576Sian ++i; 889247576Sian } 890131397Simp} 891131397Simp 892131397Simpvoid 893131397Simpnew_client(int fd) 894131397Simp{ 895131397Simp int s; 896260520Sasomers int sndbuf_size; 897131397Simp 898247576Sian /* 899247576Sian * First go reap any zombie clients, then accept the connection, and 900247576Sian * shut down the read side to stop clients from consuming kernel memory 901247576Sian * by sending large buffers full of data we'll never read. 902247576Sian */ 903247576Sian check_clients(); 904131397Simp s = accept(fd, NULL, NULL); 905247576Sian if (s != -1) { 906260520Sasomers sndbuf_size = CLIENT_BUFSIZE; 907260520Sasomers if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 908260520Sasomers sizeof(sndbuf_size))) 909260520Sasomers err(1, "setsockopt"); 910247576Sian shutdown(s, SHUT_RD); 911131397Simp clients.push_back(s); 912247576Sian ++num_clients; 913260520Sasomers } else 914260520Sasomers err(1, "accept"); 915131397Simp} 916131397Simp 917107665Simpstatic void 918107665Simpevent_loop(void) 919107665Simp{ 920107665Simp int rv; 921107665Simp int fd; 922107665Simp char buffer[DEVCTL_MAXBUF]; 923113790Simp int once = 0; 924131397Simp int server_fd, max_fd; 925247576Sian int accepting; 926113790Simp timeval tv; 927113790Simp fd_set fds; 928107665Simp 929107665Simp fd = open(PATH_DEVCTL, O_RDONLY); 930107665Simp if (fd == -1) 931131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 932107665Simp if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 933131397Simp err(1, "Can't set close-on-exec flag on devctl"); 934131397Simp server_fd = create_socket(PIPE); 935247576Sian accepting = 1; 936131397Simp max_fd = max(fd, server_fd) + 1; 937107665Simp while (1) { 938107665Simp if (romeo_must_die) 939107665Simp break; 940113790Simp if (!once && !dflag && !nflag) { 941113790Simp // Check to see if we have any events pending. 942113790Simp tv.tv_sec = 0; 943113790Simp tv.tv_usec = 0; 944113790Simp FD_ZERO(&fds); 945113790Simp FD_SET(fd, &fds); 946113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 947113790Simp // No events -> we've processed all pending events 948117944Simp if (rv == 0) { 949113790Simp if (Dflag) 950113790Simp fprintf(stderr, "Calling daemon\n"); 951155073Spjd cfg.remove_pidfile(); 952155073Spjd cfg.open_pidfile(); 953113790Simp daemon(0, 0); 954155073Spjd cfg.write_pidfile(); 955113790Simp once++; 956113790Simp } 957113790Simp } 958247576Sian /* 959247576Sian * When we've already got the max number of clients, stop 960247576Sian * accepting new connections (don't put server_fd in the set), 961247576Sian * shrink the accept() queue to reject connections quickly, and 962247576Sian * poll the existing clients more often, so that we notice more 963247576Sian * quickly when any of them disappear to free up client slots. 964247576Sian */ 965131397Simp FD_ZERO(&fds); 966131397Simp FD_SET(fd, &fds); 967247576Sian if (num_clients < max_clients) { 968247576Sian if (!accepting) { 969247576Sian listen(server_fd, max_clients); 970247576Sian accepting = 1; 971247576Sian } 972247576Sian FD_SET(server_fd, &fds); 973247576Sian tv.tv_sec = 60; 974247576Sian tv.tv_usec = 0; 975247576Sian } else { 976247576Sian if (accepting) { 977247576Sian listen(server_fd, 0); 978247576Sian accepting = 0; 979247576Sian } 980247576Sian tv.tv_sec = 2; 981247576Sian tv.tv_usec = 0; 982247576Sian } 983247576Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 984131397Simp if (rv == -1) { 985131397Simp if (errno == EINTR) 986131397Simp continue; 987131397Simp err(1, "select"); 988247576Sian } else if (rv == 0) 989247576Sian check_clients(); 990131397Simp if (FD_ISSET(fd, &fds)) { 991131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 992131397Simp if (rv > 0) { 993131397Simp notify_clients(buffer, rv); 994107665Simp buffer[rv] = '\0'; 995131397Simp while (buffer[--rv] == '\n') 996131397Simp buffer[rv] = '\0'; 997131397Simp process_event(buffer); 998131397Simp } else if (rv < 0) { 999131397Simp if (errno != EINTR) 1000131397Simp break; 1001131397Simp } else { 1002131397Simp /* EOF */ 1003107665Simp break; 1004131397Simp } 1005107665Simp } 1006131397Simp if (FD_ISSET(server_fd, &fds)) 1007131397Simp new_client(server_fd); 1008107665Simp } 1009107665Simp close(fd); 1010107665Simp} 1011107665Simp 1012107665Simp/* 1013107665Simp * functions that the parser uses. 1014107665Simp */ 1015107665Simpvoid 1016107665Simpadd_attach(int prio, event_proc *p) 1017107665Simp{ 1018107665Simp cfg.add_attach(prio, p); 1019107665Simp} 1020107665Simp 1021107665Simpvoid 1022107665Simpadd_detach(int prio, event_proc *p) 1023107665Simp{ 1024107665Simp cfg.add_detach(prio, p); 1025107665Simp} 1026107665Simp 1027107665Simpvoid 1028107665Simpadd_directory(const char *dir) 1029107665Simp{ 1030107665Simp cfg.add_directory(dir); 1031107665Simp free(const_cast<char *>(dir)); 1032107665Simp} 1033107665Simp 1034107665Simpvoid 1035107665Simpadd_nomatch(int prio, event_proc *p) 1036107665Simp{ 1037107665Simp cfg.add_nomatch(prio, p); 1038107665Simp} 1039107665Simp 1040121487Simpvoid 1041121487Simpadd_notify(int prio, event_proc *p) 1042121487Simp{ 1043121487Simp cfg.add_notify(prio, p); 1044121487Simp} 1045121487Simp 1046107665Simpevent_proc * 1047107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1048107665Simp{ 1049107665Simp if (ep == NULL) 1050107665Simp ep = new event_proc(); 1051107665Simp ep->add(eps); 1052107665Simp return (ep); 1053107665Simp} 1054107665Simp 1055107665Simpeps * 1056107665Simpnew_action(const char *cmd) 1057107665Simp{ 1058107665Simp eps *e = new action(cmd); 1059107665Simp free(const_cast<char *>(cmd)); 1060107665Simp return (e); 1061107665Simp} 1062107665Simp 1063107665Simpeps * 1064107665Simpnew_match(const char *var, const char *re) 1065107665Simp{ 1066108014Simp eps *e = new match(cfg, var, re); 1067107665Simp free(const_cast<char *>(var)); 1068107665Simp free(const_cast<char *>(re)); 1069107665Simp return (e); 1070107665Simp} 1071107665Simp 1072147874Simpeps * 1073147874Simpnew_media(const char *var, const char *re) 1074147874Simp{ 1075147874Simp eps *e = new media(cfg, var, re); 1076147874Simp free(const_cast<char *>(var)); 1077147874Simp free(const_cast<char *>(re)); 1078147874Simp return (e); 1079147874Simp} 1080147874Simp 1081107665Simpvoid 1082107665Simpset_pidfile(const char *name) 1083107665Simp{ 1084107665Simp cfg.set_pidfile(name); 1085107665Simp free(const_cast<char *>(name)); 1086107665Simp} 1087107665Simp 1088107665Simpvoid 1089107665Simpset_variable(const char *var, const char *val) 1090107665Simp{ 1091107665Simp cfg.set_variable(var, val); 1092107665Simp free(const_cast<char *>(var)); 1093107665Simp free(const_cast<char *>(val)); 1094107665Simp} 1095107665Simp 1096107665Simp 1097107665Simp 1098107665Simpstatic void 1099107665Simpgensighand(int) 1100107665Simp{ 1101107665Simp romeo_must_die++; 1102107665Simp _exit(0); 1103107665Simp} 1104107665Simp 1105107665Simpstatic void 1106107665Simpusage() 1107107665Simp{ 1108247576Sian fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n", 1109247576Sian getprogname()); 1110107665Simp exit(1); 1111107665Simp} 1112107665Simp 1113113787Simpstatic void 1114113787Simpcheck_devd_enabled() 1115113787Simp{ 1116113787Simp int val = 0; 1117113787Simp size_t len; 1118113787Simp 1119113787Simp len = sizeof(val); 1120114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1121113787Simp errx(1, "devctl sysctl missing from kernel!"); 1122113787Simp if (val) { 1123113787Simp warnx("Setting " SYSCTL " to 0"); 1124113787Simp val = 0; 1125113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1126113787Simp } 1127113787Simp} 1128113787Simp 1129107665Simp/* 1130107665Simp * main 1131107665Simp */ 1132107665Simpint 1133107665Simpmain(int argc, char **argv) 1134107665Simp{ 1135107665Simp int ch; 1136107665Simp 1137113787Simp check_devd_enabled(); 1138247576Sian while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) { 1139107665Simp switch (ch) { 1140113790Simp case 'D': 1141113790Simp Dflag++; 1142113790Simp break; 1143107665Simp case 'd': 1144107665Simp dflag++; 1145107665Simp break; 1146152770Sjkoshy case 'f': 1147152770Sjkoshy configfile = optarg; 1148152770Sjkoshy break; 1149247576Sian case 'l': 1150247576Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1151247576Sian break; 1152113790Simp case 'n': 1153113790Simp nflag++; 1154113790Simp break; 1155107665Simp default: 1156107665Simp usage(); 1157107665Simp } 1158107665Simp } 1159107665Simp 1160107665Simp cfg.parse(); 1161117246Simp if (!dflag && nflag) { 1162155073Spjd cfg.open_pidfile(); 1163107665Simp daemon(0, 0); 1164155073Spjd cfg.write_pidfile(); 1165117246Simp } 1166146306Simp signal(SIGPIPE, SIG_IGN); 1167107665Simp signal(SIGHUP, gensighand); 1168107665Simp signal(SIGINT, gensighand); 1169107665Simp signal(SIGTERM, gensighand); 1170107665Simp event_loop(); 1171107665Simp return (0); 1172107665Simp} 1173