devd.cc revision 215607
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 215607 2010-11-21 04:02:37Z kevlo $"); 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> 83108014Simp#include <regex.h> 84146306Simp#include <signal.h> 85107665Simp#include <stdlib.h> 86107665Simp#include <stdio.h> 87107665Simp#include <string.h> 88107665Simp#include <unistd.h> 89107665Simp 90108783Simp#include <algorithm> 91107665Simp#include <map> 92107665Simp#include <string> 93131397Simp#include <list> 94107665Simp#include <vector> 95107665Simp 96114086Simp#include "devd.h" /* C compatible definitions */ 97114086Simp#include "devd.hh" /* C++ class definitions */ 98107665Simp 99131397Simp#define PIPE "/var/run/devd.pipe" 100107665Simp#define CF "/etc/devd.conf" 101113787Simp#define SYSCTL "hw.bus.devctl_disable" 102107665Simp 103107665Simpusing namespace std; 104107665Simp 105107665Simpextern FILE *yyin; 106107665Simpextern int lineno; 107107665Simp 108121487Simpstatic const char notify = '!'; 109108783Simpstatic const char nomatch = '?'; 110108783Simpstatic const char attach = '+'; 111108783Simpstatic const char detach = '-'; 112108783Simp 113155073Spjdstatic struct pidfh *pfh; 114155073Spjd 115113790Simpint Dflag; 116107665Simpint dflag; 117114000Simpint nflag; 118107665Simpint romeo_must_die = 0; 119107665Simp 120152770Sjkoshystatic const char *configfile = CF; 121152770Sjkoshy 122107665Simpstatic void event_loop(void); 123107665Simpstatic void usage(void); 124107665Simp 125108783Simptemplate <class T> void 126108783Simpdelete_and_clear(vector<T *> &v) 127108783Simp{ 128108783Simp typename vector<T *>::const_iterator i; 129108783Simp 130108783Simp for (i = v.begin(); i != v.end(); i++) 131108783Simp delete *i; 132108783Simp v.clear(); 133108783Simp} 134108783Simp 135107665Simpconfig cfg; 136107665Simp 137107665Simpevent_proc::event_proc() : _prio(-1) 138107665Simp{ 139107665Simp // nothing 140107665Simp} 141107665Simp 142107665Simpevent_proc::~event_proc() 143107665Simp{ 144152406Sbland delete_and_clear(_epsvec); 145107665Simp} 146107665Simp 147107665Simpvoid 148107665Simpevent_proc::add(eps *eps) 149107665Simp{ 150107665Simp _epsvec.push_back(eps); 151107665Simp} 152107665Simp 153107665Simpbool 154107665Simpevent_proc::matches(config &c) 155107665Simp{ 156107665Simp vector<eps *>::const_iterator i; 157107665Simp 158107665Simp for (i = _epsvec.begin(); i != _epsvec.end(); i++) 159107665Simp if (!(*i)->do_match(c)) 160107665Simp return (false); 161107665Simp return (true); 162107665Simp} 163107665Simp 164107665Simpbool 165107665Simpevent_proc::run(config &c) 166107665Simp{ 167107665Simp vector<eps *>::const_iterator i; 168107665Simp 169107665Simp for (i = _epsvec.begin(); i != _epsvec.end(); i++) 170107665Simp if (!(*i)->do_action(c)) 171107665Simp return (false); 172107665Simp return (true); 173107665Simp} 174107665Simp 175107665Simpaction::action(const char *cmd) 176107665Simp : _cmd(cmd) 177107665Simp{ 178107665Simp // nothing 179107665Simp} 180107665Simp 181107665Simpaction::~action() 182107665Simp{ 183107665Simp // nothing 184107665Simp} 185107665Simp 186209583Simpstatic int 187209583Simpmy_system(const char *command) 188209583Simp{ 189209583Simp pid_t pid, savedpid; 190209583Simp int pstat; 191209583Simp struct sigaction ign, intact, quitact; 192209583Simp sigset_t newsigblock, oldsigblock; 193209583Simp 194209583Simp if (!command) /* just checking... */ 195209583Simp return(1); 196209583Simp 197209583Simp /* 198209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 199209583Simp * existing signal dispositions. 200209583Simp */ 201209583Simp ign.sa_handler = SIG_IGN; 202209583Simp ::sigemptyset(&ign.sa_mask); 203209583Simp ign.sa_flags = 0; 204209583Simp ::sigaction(SIGINT, &ign, &intact); 205209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 206209583Simp ::sigemptyset(&newsigblock); 207209583Simp ::sigaddset(&newsigblock, SIGCHLD); 208209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 209209583Simp switch (pid = ::fork()) { 210209583Simp case -1: /* error */ 211209583Simp break; 212209583Simp case 0: /* child */ 213209583Simp /* 214209583Simp * Restore original signal dispositions and exec the command. 215209583Simp */ 216209583Simp ::sigaction(SIGINT, &intact, NULL); 217209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 218209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 219209583Simp /* 220209583Simp * Close the PID file, and all other open descriptors. 221209583Simp * Inherit std{in,out,err} only. 222209583Simp */ 223209583Simp cfg.close_pidfile(); 224209583Simp ::closefrom(3); 225209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 226209583Simp ::_exit(127); 227209583Simp default: /* parent */ 228209583Simp savedpid = pid; 229209583Simp do { 230209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 231209583Simp } while (pid == -1 && errno == EINTR); 232209583Simp break; 233209583Simp } 234209583Simp ::sigaction(SIGINT, &intact, NULL); 235209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 236209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 237209583Simp return (pid == -1 ? -1 : pstat); 238209583Simp} 239209583Simp 240107665Simpbool 241108014Simpaction::do_action(config &c) 242107665Simp{ 243108783Simp string s = c.expand_string(_cmd); 244113790Simp if (Dflag) 245108783Simp fprintf(stderr, "Executing '%s'\n", s.c_str()); 246209583Simp my_system(s.c_str()); 247107665Simp return (true); 248107665Simp} 249107665Simp 250108014Simpmatch::match(config &c, const char *var, const char *re) 251108783Simp : _var(var) 252107665Simp{ 253108783Simp _re = "^"; 254108783Simp _re.append(c.expand_string(string(re))); 255108783Simp _re.append("$"); 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) 271108783Simp fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(), 272108783Simp value.c_str(), _re.c_str()); 273108783Simp 274108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 275108014Simp return retval; 276107665Simp} 277107665Simp 278147874Simp#include <sys/sockio.h> 279147874Simp#include <net/if.h> 280147874Simp#include <net/if_media.h> 281147874Simp 282151486Sbrooksmedia::media(config &, const char *var, const char *type) 283147874Simp : _var(var), _type(-1) 284147874Simp{ 285147874Simp static struct ifmedia_description media_types[] = { 286147874Simp { IFM_ETHER, "Ethernet" }, 287147874Simp { IFM_TOKEN, "Tokenring" }, 288147874Simp { IFM_FDDI, "FDDI" }, 289147874Simp { IFM_IEEE80211, "802.11" }, 290147874Simp { IFM_ATM, "ATM" }, 291147874Simp { IFM_CARP, "CARP" }, 292147874Simp { -1, "unknown" }, 293147874Simp { 0, NULL }, 294147874Simp }; 295147874Simp for (int i = 0; media_types[i].ifmt_string != NULL; i++) 296147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 297147874Simp _type = media_types[i].ifmt_word; 298147874Simp break; 299147874Simp } 300147874Simp} 301147874Simp 302147874Simpmedia::~media() 303147874Simp{ 304147874Simp} 305147874Simp 306147874Simpbool 307147874Simpmedia::do_match(config &c) 308147874Simp{ 309150949Simp string value; 310147874Simp struct ifmediareq ifmr; 311147874Simp bool retval; 312147874Simp int s; 313147874Simp 314150949Simp // Since we can be called from both a device attach/detach 315150949Simp // context where device-name is defined and what we want, 316150949Simp // as well as from a link status context, where subsystem is 317150949Simp // the name of interest, first try device-name and fall back 318150949Simp // to subsystem if none exists. 319150949Simp value = c.get_variable("device-name"); 320150949Simp if (value.length() == 0) 321151480Simp value = c.get_variable("subsystem"); 322147874Simp if (Dflag) 323147874Simp fprintf(stderr, "Testing media type of %s against 0x%x\n", 324147874Simp value.c_str(), _type); 325147874Simp 326147874Simp retval = false; 327147874Simp 328147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 329147874Simp if (s >= 0) { 330147874Simp memset(&ifmr, 0, sizeof(ifmr)); 331147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 332147874Simp 333147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 334147874Simp ifmr.ifm_status & IFM_AVALID) { 335147874Simp if (Dflag) 336147874Simp fprintf(stderr, "%s has media type 0x%x\n", 337147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 338147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 339147874Simp } else if (_type == -1) { 340147874Simp if (Dflag) 341147874Simp fprintf(stderr, "%s has unknown media type\n", 342147874Simp value.c_str()); 343147874Simp retval = true; 344147874Simp } 345147874Simp close(s); 346147874Simp } 347147874Simp 348147874Simp return retval; 349147874Simp} 350147874Simp 351107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 352107665Simpconst string var_list::nothing = ""; 353107665Simp 354107665Simpconst string & 355107665Simpvar_list::get_variable(const string &var) const 356107665Simp{ 357107665Simp map<string, string>::const_iterator i; 358107665Simp 359107665Simp i = _vars.find(var); 360107665Simp if (i == _vars.end()) 361108783Simp return (var_list::bogus); 362107665Simp return (i->second); 363107665Simp} 364107665Simp 365107665Simpbool 366107665Simpvar_list::is_set(const string &var) const 367107665Simp{ 368107665Simp return (_vars.find(var) != _vars.end()); 369107665Simp} 370107665Simp 371107665Simpvoid 372107665Simpvar_list::set_variable(const string &var, const string &val) 373107665Simp{ 374113790Simp if (Dflag) 375145218Simp fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 376107665Simp _vars[var] = val; 377107665Simp} 378107665Simp 379107665Simpvoid 380107665Simpconfig::reset(void) 381107665Simp{ 382107665Simp _dir_list.clear(); 383108783Simp delete_and_clear(_var_list_table); 384108783Simp delete_and_clear(_attach_list); 385108783Simp delete_and_clear(_detach_list); 386108783Simp delete_and_clear(_nomatch_list); 387121487Simp delete_and_clear(_notify_list); 388107665Simp} 389107665Simp 390107665Simpvoid 391107665Simpconfig::parse_one_file(const char *fn) 392107665Simp{ 393113790Simp if (Dflag) 394186078Sphk fprintf(stderr, "Parsing %s\n", fn); 395107665Simp yyin = fopen(fn, "r"); 396107665Simp if (yyin == NULL) 397107665Simp err(1, "Cannot open config file %s", fn); 398157746Smaxim lineno = 1; 399107665Simp if (yyparse() != 0) 400107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 401107665Simp fclose(yyin); 402107665Simp} 403107665Simp 404107665Simpvoid 405107665Simpconfig::parse_files_in_dir(const char *dirname) 406107665Simp{ 407107665Simp DIR *dirp; 408107665Simp struct dirent *dp; 409107665Simp char path[PATH_MAX]; 410107665Simp 411113790Simp if (Dflag) 412186078Sphk fprintf(stderr, "Parsing files in %s\n", dirname); 413107665Simp dirp = opendir(dirname); 414107665Simp if (dirp == NULL) 415107665Simp return; 416107665Simp readdir(dirp); /* Skip . */ 417107665Simp readdir(dirp); /* Skip .. */ 418107665Simp while ((dp = readdir(dirp)) != NULL) { 419107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 420107665Simp snprintf(path, sizeof(path), "%s/%s", 421107665Simp dirname, dp->d_name); 422107665Simp parse_one_file(path); 423107665Simp } 424107665Simp } 425215607Skevlo closedir(dirp); 426107665Simp} 427107665Simp 428108783Simpclass epv_greater { 429108783Simppublic: 430108783Simp int operator()(event_proc *const&l1, event_proc *const&l2) 431108783Simp { 432108783Simp return (l1->get_priority() > l2->get_priority()); 433108783Simp } 434108783Simp}; 435108783Simp 436107665Simpvoid 437108783Simpconfig::sort_vector(vector<event_proc *> &v) 438108783Simp{ 439108783Simp sort(v.begin(), v.end(), epv_greater()); 440108783Simp} 441108783Simp 442108783Simpvoid 443107665Simpconfig::parse(void) 444107665Simp{ 445107665Simp vector<string>::const_iterator i; 446107665Simp 447152770Sjkoshy parse_one_file(configfile); 448107665Simp for (i = _dir_list.begin(); i != _dir_list.end(); i++) 449107665Simp parse_files_in_dir((*i).c_str()); 450108783Simp sort_vector(_attach_list); 451108783Simp sort_vector(_detach_list); 452108783Simp sort_vector(_nomatch_list); 453121487Simp sort_vector(_notify_list); 454107665Simp} 455107665Simp 456107665Simpvoid 457155073Spjdconfig::open_pidfile() 458107665Simp{ 459155073Spjd pid_t otherpid; 460107665Simp 461107665Simp if (_pidfile == "") 462107665Simp return; 463155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 464155073Spjd if (pfh == NULL) { 465155073Spjd if (errno == EEXIST) 466155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 467155073Spjd warn("cannot open pid file"); 468155073Spjd } 469107665Simp} 470107665Simp 471107665Simpvoid 472155073Spjdconfig::write_pidfile() 473155073Spjd{ 474155073Spjd 475155073Spjd pidfile_write(pfh); 476155073Spjd} 477155073Spjd 478155073Spjdvoid 479209583Simpconfig::close_pidfile() 480209583Simp{ 481209583Simp 482209583Simp pidfile_close(pfh); 483209583Simp} 484209583Simp 485209583Simpvoid 486155073Spjdconfig::remove_pidfile() 487155073Spjd{ 488155073Spjd 489155073Spjd pidfile_remove(pfh); 490155073Spjd} 491155073Spjd 492155073Spjdvoid 493107665Simpconfig::add_attach(int prio, event_proc *p) 494107665Simp{ 495107665Simp p->set_priority(prio); 496107665Simp _attach_list.push_back(p); 497107665Simp} 498107665Simp 499107665Simpvoid 500107665Simpconfig::add_detach(int prio, event_proc *p) 501107665Simp{ 502107665Simp p->set_priority(prio); 503107665Simp _detach_list.push_back(p); 504107665Simp} 505107665Simp 506107665Simpvoid 507107665Simpconfig::add_directory(const char *dir) 508107665Simp{ 509107665Simp _dir_list.push_back(string(dir)); 510107665Simp} 511107665Simp 512107665Simpvoid 513107665Simpconfig::add_nomatch(int prio, event_proc *p) 514107665Simp{ 515107665Simp p->set_priority(prio); 516107665Simp _nomatch_list.push_back(p); 517107665Simp} 518107665Simp 519107665Simpvoid 520121487Simpconfig::add_notify(int prio, event_proc *p) 521121487Simp{ 522121487Simp p->set_priority(prio); 523121487Simp _notify_list.push_back(p); 524121487Simp} 525121487Simp 526121487Simpvoid 527107665Simpconfig::set_pidfile(const char *fn) 528107665Simp{ 529107665Simp _pidfile = string(fn); 530107665Simp} 531107665Simp 532107665Simpvoid 533107665Simpconfig::push_var_table() 534107665Simp{ 535107665Simp var_list *vl; 536107665Simp 537107665Simp vl = new var_list(); 538107665Simp _var_list_table.push_back(vl); 539113790Simp if (Dflag) 540108783Simp fprintf(stderr, "Pushing table\n"); 541107665Simp} 542107665Simp 543107665Simpvoid 544107665Simpconfig::pop_var_table() 545107665Simp{ 546107665Simp delete _var_list_table.back(); 547107665Simp _var_list_table.pop_back(); 548113790Simp if (Dflag) 549108783Simp fprintf(stderr, "Popping table\n"); 550107665Simp} 551107665Simp 552107665Simpvoid 553107665Simpconfig::set_variable(const char *var, const char *val) 554107665Simp{ 555107665Simp _var_list_table.back()->set_variable(var, val); 556107665Simp} 557107665Simp 558107665Simpconst string & 559107665Simpconfig::get_variable(const string &var) 560107665Simp{ 561107665Simp vector<var_list *>::reverse_iterator i; 562107665Simp 563107665Simp for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 564107665Simp if ((*i)->is_set(var)) 565108783Simp return ((*i)->get_variable(var)); 566107665Simp } 567107665Simp return (var_list::nothing); 568107665Simp} 569107665Simp 570108783Simpbool 571108783Simpconfig::is_id_char(char ch) 572108783Simp{ 573108783Simp return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 574108783Simp ch == '-')); 575108783Simp} 576108783Simp 577108014Simpvoid 578114081Simpconfig::expand_one(const char *&src, string &dst) 579107665Simp{ 580108014Simp int count; 581210610Slulf string buffer; 582108014Simp 583108783Simp src++; 584108014Simp // $$ -> $ 585108014Simp if (*src == '$') { 586114081Simp dst.append(src++, 1); 587108014Simp return; 588108014Simp } 589108014Simp 590108014Simp // $(foo) -> $(foo) 591108783Simp // Not sure if I want to support this or not, so for now we just pass 592108783Simp // it through. 593108014Simp if (*src == '(') { 594114081Simp dst.append("$"); 595108014Simp count = 1; 596114081Simp /* If the string ends before ) is matched , return. */ 597114081Simp while (count > 0 && *src) { 598108014Simp if (*src == ')') 599108014Simp count--; 600108014Simp else if (*src == '(') 601108014Simp count++; 602114081Simp dst.append(src++, 1); 603108014Simp } 604108014Simp return; 605108014Simp } 606108014Simp 607108014Simp // ${^A-Za-z] -> $\1 608108014Simp if (!isalpha(*src)) { 609114081Simp dst.append("$"); 610114081Simp dst.append(src++, 1); 611108014Simp return; 612108014Simp } 613108014Simp 614108014Simp // $var -> replace with value 615114081Simp do { 616114081Simp buffer.append(src++, 1); 617114084Simp } while (is_id_char(*src)); 618114081Simp buffer.append("", 1); 619210610Slulf dst.append(get_variable(buffer.c_str())); 620107665Simp} 621107665Simp 622108014Simpconst string 623108014Simpconfig::expand_string(const string &s) 624108014Simp{ 625108014Simp const char *src; 626114081Simp string dst; 627108014Simp 628108014Simp src = s.c_str(); 629108014Simp while (*src) { 630108014Simp if (*src == '$') 631114081Simp expand_one(src, dst); 632108014Simp else 633114081Simp dst.append(src++, 1); 634108014Simp } 635114081Simp dst.append("", 1); 636108014Simp 637114081Simp return (dst); 638108014Simp} 639108014Simp 640108783Simpbool 641108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) 642108783Simp{ 643108783Simp char *walker; 644108783Simp 645108783Simp if (*buffer == '\0') 646108783Simp return (false); 647108783Simp walker = lhs = buffer; 648108783Simp while (is_id_char(*walker)) 649108783Simp walker++; 650108783Simp if (*walker != '=') 651108783Simp return (false); 652108783Simp walker++; // skip = 653108783Simp if (*walker == '"') { 654108783Simp walker++; // skip " 655108783Simp rhs = walker; 656108783Simp while (*walker && *walker != '"') 657108783Simp walker++; 658108783Simp if (*walker != '"') 659108783Simp return (false); 660108783Simp rhs[-2] = '\0'; 661108783Simp *walker++ = '\0'; 662108783Simp } else { 663108783Simp rhs = walker; 664108783Simp while (*walker && !isspace(*walker)) 665108783Simp walker++; 666108783Simp if (*walker != '\0') 667108783Simp *walker++ = '\0'; 668108783Simp rhs[-1] = '\0'; 669108783Simp } 670113785Simp while (isspace(*walker)) 671113785Simp walker++; 672108783Simp buffer = walker; 673108783Simp return (true); 674108783Simp} 675108783Simp 676108783Simp 677108783Simpchar * 678108783Simpconfig::set_vars(char *buffer) 679108783Simp{ 680108783Simp char *lhs; 681108783Simp char *rhs; 682108783Simp 683108783Simp while (1) { 684108783Simp if (!chop_var(buffer, lhs, rhs)) 685108783Simp break; 686108783Simp set_variable(lhs, rhs); 687108783Simp } 688108783Simp return (buffer); 689108783Simp} 690108783Simp 691108783Simpvoid 692108783Simpconfig::find_and_execute(char type) 693108783Simp{ 694108783Simp vector<event_proc *> *l; 695108783Simp vector<event_proc *>::const_iterator i; 696151486Sbrooks const char *s; 697108783Simp 698108783Simp switch (type) { 699108783Simp default: 700108783Simp return; 701121487Simp case notify: 702121487Simp l = &_notify_list; 703121487Simp s = "notify"; 704121487Simp break; 705108783Simp case nomatch: 706108783Simp l = &_nomatch_list; 707108783Simp s = "nomatch"; 708108783Simp break; 709108783Simp case attach: 710108783Simp l = &_attach_list; 711108783Simp s = "attach"; 712108783Simp break; 713108783Simp case detach: 714108783Simp l = &_detach_list; 715108783Simp s = "detach"; 716108783Simp break; 717108783Simp } 718113790Simp if (Dflag) 719108783Simp fprintf(stderr, "Processing %s event\n", s); 720108783Simp for (i = l->begin(); i != l->end(); i++) { 721108783Simp if ((*i)->matches(*this)) { 722108783Simp (*i)->run(*this); 723108783Simp break; 724108783Simp } 725108783Simp } 726108783Simp 727108783Simp} 728108783Simp 729107665Simp 730107665Simpstatic void 731108783Simpprocess_event(char *buffer) 732107665Simp{ 733107665Simp char type; 734107665Simp char *sp; 735107665Simp 736108783Simp sp = buffer + 1; 737113790Simp if (Dflag) 738108783Simp fprintf(stderr, "Processing event '%s'\n", buffer); 739107665Simp type = *buffer++; 740108783Simp cfg.push_var_table(); 741108783Simp // No match doesn't have a device, and the format is a little 742108783Simp // different, so handle it separately. 743121487Simp switch (type) { 744121487Simp case notify: 745121487Simp sp = cfg.set_vars(sp); 746121487Simp break; 747121487Simp case nomatch: 748145218Simp //? at location pnp-info on bus 749145218Simp sp = strchr(sp, ' '); 750145218Simp if (sp == NULL) 751145218Simp return; /* Can't happen? */ 752145218Simp *sp++ = '\0'; 753213646Simp while (isspace(*sp)) 754213646Simp sp++; 755121487Simp if (strncmp(sp, "at ", 3) == 0) 756121487Simp sp += 3; 757121487Simp sp = cfg.set_vars(sp); 758213646Simp while (isspace(*sp)) 759213646Simp sp++; 760121487Simp if (strncmp(sp, "on ", 3) == 0) 761121487Simp cfg.set_variable("bus", sp + 3); 762121487Simp break; 763121487Simp case attach: /*FALLTHROUGH*/ 764121487Simp case detach: 765108783Simp sp = strchr(sp, ' '); 766108783Simp if (sp == NULL) 767108783Simp return; /* Can't happen? */ 768108783Simp *sp++ = '\0'; 769108783Simp cfg.set_variable("device-name", buffer); 770213646Simp while (isspace(*sp)) 771213646Simp sp++; 772113785Simp if (strncmp(sp, "at ", 3) == 0) 773113785Simp sp += 3; 774113785Simp sp = cfg.set_vars(sp); 775213646Simp while (isspace(*sp)) 776213646Simp sp++; 777113785Simp if (strncmp(sp, "on ", 3) == 0) 778113785Simp cfg.set_variable("bus", sp + 3); 779121487Simp break; 780108783Simp } 781113785Simp 782108783Simp cfg.find_and_execute(type); 783108783Simp cfg.pop_var_table(); 784107665Simp} 785107665Simp 786131397Simpint 787131397Simpcreate_socket(const char *name) 788131397Simp{ 789131397Simp int fd, slen; 790131397Simp struct sockaddr_un sun; 791131397Simp 792131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 793131397Simp err(1, "socket"); 794131397Simp bzero(&sun, sizeof(sun)); 795131397Simp sun.sun_family = AF_UNIX; 796131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 797131397Simp slen = SUN_LEN(&sun); 798131397Simp unlink(name); 799147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 800147973Smarcus err(1, "fcntl"); 801131397Simp if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 802131397Simp err(1, "bind"); 803131397Simp listen(fd, 4); 804147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 805147973Smarcus chmod(name, 0666); 806131397Simp return (fd); 807131397Simp} 808131397Simp 809131397Simplist<int> clients; 810131397Simp 811131397Simpvoid 812131397Simpnotify_clients(const char *data, int len) 813131397Simp{ 814131397Simp list<int> bad; 815131397Simp list<int>::const_iterator i; 816131397Simp 817131397Simp for (i = clients.begin(); i != clients.end(); i++) { 818131397Simp if (write(*i, data, len) <= 0) { 819131397Simp bad.push_back(*i); 820131397Simp close(*i); 821131397Simp } 822131397Simp } 823131397Simp 824131397Simp for (i = bad.begin(); i != bad.end(); i++) 825131397Simp clients.erase(find(clients.begin(), clients.end(), *i)); 826131397Simp} 827131397Simp 828131397Simpvoid 829131397Simpnew_client(int fd) 830131397Simp{ 831131397Simp int s; 832131397Simp 833131397Simp s = accept(fd, NULL, NULL); 834131397Simp if (s != -1) 835131397Simp clients.push_back(s); 836131397Simp} 837131397Simp 838107665Simpstatic void 839107665Simpevent_loop(void) 840107665Simp{ 841107665Simp int rv; 842107665Simp int fd; 843107665Simp char buffer[DEVCTL_MAXBUF]; 844113790Simp int once = 0; 845131397Simp int server_fd, max_fd; 846113790Simp timeval tv; 847113790Simp fd_set fds; 848107665Simp 849107665Simp fd = open(PATH_DEVCTL, O_RDONLY); 850107665Simp if (fd == -1) 851131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 852107665Simp if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 853131397Simp err(1, "Can't set close-on-exec flag on devctl"); 854131397Simp server_fd = create_socket(PIPE); 855131397Simp max_fd = max(fd, server_fd) + 1; 856107665Simp while (1) { 857107665Simp if (romeo_must_die) 858107665Simp break; 859113790Simp if (!once && !dflag && !nflag) { 860113790Simp // Check to see if we have any events pending. 861113790Simp tv.tv_sec = 0; 862113790Simp tv.tv_usec = 0; 863113790Simp FD_ZERO(&fds); 864113790Simp FD_SET(fd, &fds); 865113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 866113790Simp // No events -> we've processed all pending events 867117944Simp if (rv == 0) { 868113790Simp if (Dflag) 869113790Simp fprintf(stderr, "Calling daemon\n"); 870155073Spjd cfg.remove_pidfile(); 871155073Spjd cfg.open_pidfile(); 872113790Simp daemon(0, 0); 873155073Spjd cfg.write_pidfile(); 874113790Simp once++; 875113790Simp } 876113790Simp } 877131397Simp FD_ZERO(&fds); 878131397Simp FD_SET(fd, &fds); 879131397Simp FD_SET(server_fd, &fds); 880131397Simp rv = select(max_fd, &fds, NULL, NULL, NULL); 881131397Simp if (rv == -1) { 882131397Simp if (errno == EINTR) 883131397Simp continue; 884131397Simp err(1, "select"); 885131397Simp } 886131397Simp if (FD_ISSET(fd, &fds)) { 887131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 888131397Simp if (rv > 0) { 889131397Simp notify_clients(buffer, rv); 890107665Simp buffer[rv] = '\0'; 891131397Simp while (buffer[--rv] == '\n') 892131397Simp buffer[rv] = '\0'; 893131397Simp process_event(buffer); 894131397Simp } else if (rv < 0) { 895131397Simp if (errno != EINTR) 896131397Simp break; 897131397Simp } else { 898131397Simp /* EOF */ 899107665Simp break; 900131397Simp } 901107665Simp } 902131397Simp if (FD_ISSET(server_fd, &fds)) 903131397Simp new_client(server_fd); 904107665Simp } 905107665Simp close(fd); 906107665Simp} 907107665Simp 908107665Simp/* 909107665Simp * functions that the parser uses. 910107665Simp */ 911107665Simpvoid 912107665Simpadd_attach(int prio, event_proc *p) 913107665Simp{ 914107665Simp cfg.add_attach(prio, p); 915107665Simp} 916107665Simp 917107665Simpvoid 918107665Simpadd_detach(int prio, event_proc *p) 919107665Simp{ 920107665Simp cfg.add_detach(prio, p); 921107665Simp} 922107665Simp 923107665Simpvoid 924107665Simpadd_directory(const char *dir) 925107665Simp{ 926107665Simp cfg.add_directory(dir); 927107665Simp free(const_cast<char *>(dir)); 928107665Simp} 929107665Simp 930107665Simpvoid 931107665Simpadd_nomatch(int prio, event_proc *p) 932107665Simp{ 933107665Simp cfg.add_nomatch(prio, p); 934107665Simp} 935107665Simp 936121487Simpvoid 937121487Simpadd_notify(int prio, event_proc *p) 938121487Simp{ 939121487Simp cfg.add_notify(prio, p); 940121487Simp} 941121487Simp 942107665Simpevent_proc * 943107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 944107665Simp{ 945107665Simp if (ep == NULL) 946107665Simp ep = new event_proc(); 947107665Simp ep->add(eps); 948107665Simp return (ep); 949107665Simp} 950107665Simp 951107665Simpeps * 952107665Simpnew_action(const char *cmd) 953107665Simp{ 954107665Simp eps *e = new action(cmd); 955107665Simp free(const_cast<char *>(cmd)); 956107665Simp return (e); 957107665Simp} 958107665Simp 959107665Simpeps * 960107665Simpnew_match(const char *var, const char *re) 961107665Simp{ 962108014Simp eps *e = new match(cfg, var, re); 963107665Simp free(const_cast<char *>(var)); 964107665Simp free(const_cast<char *>(re)); 965107665Simp return (e); 966107665Simp} 967107665Simp 968147874Simpeps * 969147874Simpnew_media(const char *var, const char *re) 970147874Simp{ 971147874Simp eps *e = new media(cfg, var, re); 972147874Simp free(const_cast<char *>(var)); 973147874Simp free(const_cast<char *>(re)); 974147874Simp return (e); 975147874Simp} 976147874Simp 977107665Simpvoid 978107665Simpset_pidfile(const char *name) 979107665Simp{ 980107665Simp cfg.set_pidfile(name); 981107665Simp free(const_cast<char *>(name)); 982107665Simp} 983107665Simp 984107665Simpvoid 985107665Simpset_variable(const char *var, const char *val) 986107665Simp{ 987107665Simp cfg.set_variable(var, val); 988107665Simp free(const_cast<char *>(var)); 989107665Simp free(const_cast<char *>(val)); 990107665Simp} 991107665Simp 992107665Simp 993107665Simp 994107665Simpstatic void 995107665Simpgensighand(int) 996107665Simp{ 997107665Simp romeo_must_die++; 998107665Simp _exit(0); 999107665Simp} 1000107665Simp 1001107665Simpstatic void 1002107665Simpusage() 1003107665Simp{ 1004162388Sru fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 1005107665Simp exit(1); 1006107665Simp} 1007107665Simp 1008113787Simpstatic void 1009113787Simpcheck_devd_enabled() 1010113787Simp{ 1011113787Simp int val = 0; 1012113787Simp size_t len; 1013113787Simp 1014113787Simp len = sizeof(val); 1015114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1016113787Simp errx(1, "devctl sysctl missing from kernel!"); 1017113787Simp if (val) { 1018113787Simp warnx("Setting " SYSCTL " to 0"); 1019113787Simp val = 0; 1020113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1021113787Simp } 1022113787Simp} 1023113787Simp 1024107665Simp/* 1025107665Simp * main 1026107665Simp */ 1027107665Simpint 1028107665Simpmain(int argc, char **argv) 1029107665Simp{ 1030107665Simp int ch; 1031107665Simp 1032113787Simp check_devd_enabled(); 1033152770Sjkoshy while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 1034107665Simp switch (ch) { 1035113790Simp case 'D': 1036113790Simp Dflag++; 1037113790Simp break; 1038107665Simp case 'd': 1039107665Simp dflag++; 1040107665Simp break; 1041152770Sjkoshy case 'f': 1042152770Sjkoshy configfile = optarg; 1043152770Sjkoshy break; 1044113790Simp case 'n': 1045113790Simp nflag++; 1046113790Simp break; 1047107665Simp default: 1048107665Simp usage(); 1049107665Simp } 1050107665Simp } 1051107665Simp 1052107665Simp cfg.parse(); 1053117246Simp if (!dflag && nflag) { 1054155073Spjd cfg.open_pidfile(); 1055107665Simp daemon(0, 0); 1056155073Spjd cfg.write_pidfile(); 1057117246Simp } 1058146306Simp signal(SIGPIPE, SIG_IGN); 1059107665Simp signal(SIGHUP, gensighand); 1060107665Simp signal(SIGINT, gensighand); 1061107665Simp signal(SIGTERM, gensighand); 1062107665Simp event_loop(); 1063107665Simp return (0); 1064107665Simp} 1065