devd.cc revision 226775
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 226775 2011-10-26 02:11:28Z hrs $"); 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 = "^"; 254226775Shrs if (!c.expand_string(string(re)).empty() && 255226775Shrs c.expand_string(string(re)).at(0) == '!') { 256226775Shrs _re.append(c.expand_string(string(re)).substr(1)); 257226775Shrs _inv = 1; 258226775Shrs } else { 259226775Shrs _re.append(c.expand_string(string(re))); 260226775Shrs _inv = 0; 261226775Shrs } 262108783Simp _re.append("$"); 263154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 264107665Simp} 265107665Simp 266107665Simpmatch::~match() 267107665Simp{ 268108014Simp regfree(&_regex); 269107665Simp} 270107665Simp 271107665Simpbool 272108014Simpmatch::do_match(config &c) 273107665Simp{ 274210610Slulf const string &value = c.get_variable(_var); 275108014Simp bool retval; 276108014Simp 277113790Simp if (Dflag) 278226775Shrs fprintf(stderr, "Testing %s=%s against %s, invert=%d\n", 279226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 280108783Simp 281108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 282226775Shrs if (_inv == 1) 283226775Shrs retval = (retval == 0) ? 1 : 0; 284226775Shrs 285108014Simp return retval; 286107665Simp} 287107665Simp 288147874Simp#include <sys/sockio.h> 289147874Simp#include <net/if.h> 290147874Simp#include <net/if_media.h> 291147874Simp 292151486Sbrooksmedia::media(config &, const char *var, const char *type) 293147874Simp : _var(var), _type(-1) 294147874Simp{ 295147874Simp static struct ifmedia_description media_types[] = { 296147874Simp { IFM_ETHER, "Ethernet" }, 297147874Simp { IFM_TOKEN, "Tokenring" }, 298147874Simp { IFM_FDDI, "FDDI" }, 299147874Simp { IFM_IEEE80211, "802.11" }, 300147874Simp { IFM_ATM, "ATM" }, 301147874Simp { IFM_CARP, "CARP" }, 302147874Simp { -1, "unknown" }, 303147874Simp { 0, NULL }, 304147874Simp }; 305147874Simp for (int i = 0; media_types[i].ifmt_string != NULL; i++) 306147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 307147874Simp _type = media_types[i].ifmt_word; 308147874Simp break; 309147874Simp } 310147874Simp} 311147874Simp 312147874Simpmedia::~media() 313147874Simp{ 314147874Simp} 315147874Simp 316147874Simpbool 317147874Simpmedia::do_match(config &c) 318147874Simp{ 319150949Simp string value; 320147874Simp struct ifmediareq ifmr; 321147874Simp bool retval; 322147874Simp int s; 323147874Simp 324150949Simp // Since we can be called from both a device attach/detach 325150949Simp // context where device-name is defined and what we want, 326150949Simp // as well as from a link status context, where subsystem is 327150949Simp // the name of interest, first try device-name and fall back 328150949Simp // to subsystem if none exists. 329150949Simp value = c.get_variable("device-name"); 330150949Simp if (value.length() == 0) 331151480Simp value = c.get_variable("subsystem"); 332147874Simp if (Dflag) 333147874Simp fprintf(stderr, "Testing media type of %s against 0x%x\n", 334147874Simp value.c_str(), _type); 335147874Simp 336147874Simp retval = false; 337147874Simp 338147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 339147874Simp if (s >= 0) { 340147874Simp memset(&ifmr, 0, sizeof(ifmr)); 341147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 342147874Simp 343147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 344147874Simp ifmr.ifm_status & IFM_AVALID) { 345147874Simp if (Dflag) 346147874Simp fprintf(stderr, "%s has media type 0x%x\n", 347147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 348147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 349147874Simp } else if (_type == -1) { 350147874Simp if (Dflag) 351147874Simp fprintf(stderr, "%s has unknown media type\n", 352147874Simp value.c_str()); 353147874Simp retval = true; 354147874Simp } 355147874Simp close(s); 356147874Simp } 357147874Simp 358147874Simp return retval; 359147874Simp} 360147874Simp 361107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 362107665Simpconst string var_list::nothing = ""; 363107665Simp 364107665Simpconst string & 365107665Simpvar_list::get_variable(const string &var) const 366107665Simp{ 367107665Simp map<string, string>::const_iterator i; 368107665Simp 369107665Simp i = _vars.find(var); 370107665Simp if (i == _vars.end()) 371108783Simp return (var_list::bogus); 372107665Simp return (i->second); 373107665Simp} 374107665Simp 375107665Simpbool 376107665Simpvar_list::is_set(const string &var) const 377107665Simp{ 378107665Simp return (_vars.find(var) != _vars.end()); 379107665Simp} 380107665Simp 381107665Simpvoid 382107665Simpvar_list::set_variable(const string &var, const string &val) 383107665Simp{ 384113790Simp if (Dflag) 385145218Simp fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 386107665Simp _vars[var] = val; 387107665Simp} 388107665Simp 389107665Simpvoid 390107665Simpconfig::reset(void) 391107665Simp{ 392107665Simp _dir_list.clear(); 393108783Simp delete_and_clear(_var_list_table); 394108783Simp delete_and_clear(_attach_list); 395108783Simp delete_and_clear(_detach_list); 396108783Simp delete_and_clear(_nomatch_list); 397121487Simp delete_and_clear(_notify_list); 398107665Simp} 399107665Simp 400107665Simpvoid 401107665Simpconfig::parse_one_file(const char *fn) 402107665Simp{ 403113790Simp if (Dflag) 404186078Sphk fprintf(stderr, "Parsing %s\n", fn); 405107665Simp yyin = fopen(fn, "r"); 406107665Simp if (yyin == NULL) 407107665Simp err(1, "Cannot open config file %s", fn); 408157746Smaxim lineno = 1; 409107665Simp if (yyparse() != 0) 410107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 411107665Simp fclose(yyin); 412107665Simp} 413107665Simp 414107665Simpvoid 415107665Simpconfig::parse_files_in_dir(const char *dirname) 416107665Simp{ 417107665Simp DIR *dirp; 418107665Simp struct dirent *dp; 419107665Simp char path[PATH_MAX]; 420107665Simp 421113790Simp if (Dflag) 422186078Sphk fprintf(stderr, "Parsing files in %s\n", dirname); 423107665Simp dirp = opendir(dirname); 424107665Simp if (dirp == NULL) 425107665Simp return; 426107665Simp readdir(dirp); /* Skip . */ 427107665Simp readdir(dirp); /* Skip .. */ 428107665Simp while ((dp = readdir(dirp)) != NULL) { 429107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 430107665Simp snprintf(path, sizeof(path), "%s/%s", 431107665Simp dirname, dp->d_name); 432107665Simp parse_one_file(path); 433107665Simp } 434107665Simp } 435215607Skevlo closedir(dirp); 436107665Simp} 437107665Simp 438108783Simpclass epv_greater { 439108783Simppublic: 440108783Simp int operator()(event_proc *const&l1, event_proc *const&l2) 441108783Simp { 442108783Simp return (l1->get_priority() > l2->get_priority()); 443108783Simp } 444108783Simp}; 445108783Simp 446107665Simpvoid 447108783Simpconfig::sort_vector(vector<event_proc *> &v) 448108783Simp{ 449108783Simp sort(v.begin(), v.end(), epv_greater()); 450108783Simp} 451108783Simp 452108783Simpvoid 453107665Simpconfig::parse(void) 454107665Simp{ 455107665Simp vector<string>::const_iterator i; 456107665Simp 457152770Sjkoshy parse_one_file(configfile); 458107665Simp for (i = _dir_list.begin(); i != _dir_list.end(); i++) 459107665Simp parse_files_in_dir((*i).c_str()); 460108783Simp sort_vector(_attach_list); 461108783Simp sort_vector(_detach_list); 462108783Simp sort_vector(_nomatch_list); 463121487Simp sort_vector(_notify_list); 464107665Simp} 465107665Simp 466107665Simpvoid 467155073Spjdconfig::open_pidfile() 468107665Simp{ 469155073Spjd pid_t otherpid; 470107665Simp 471107665Simp if (_pidfile == "") 472107665Simp return; 473155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 474155073Spjd if (pfh == NULL) { 475155073Spjd if (errno == EEXIST) 476155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 477155073Spjd warn("cannot open pid file"); 478155073Spjd } 479107665Simp} 480107665Simp 481107665Simpvoid 482155073Spjdconfig::write_pidfile() 483155073Spjd{ 484155073Spjd 485155073Spjd pidfile_write(pfh); 486155073Spjd} 487155073Spjd 488155073Spjdvoid 489209583Simpconfig::close_pidfile() 490209583Simp{ 491209583Simp 492209583Simp pidfile_close(pfh); 493209583Simp} 494209583Simp 495209583Simpvoid 496155073Spjdconfig::remove_pidfile() 497155073Spjd{ 498155073Spjd 499155073Spjd pidfile_remove(pfh); 500155073Spjd} 501155073Spjd 502155073Spjdvoid 503107665Simpconfig::add_attach(int prio, event_proc *p) 504107665Simp{ 505107665Simp p->set_priority(prio); 506107665Simp _attach_list.push_back(p); 507107665Simp} 508107665Simp 509107665Simpvoid 510107665Simpconfig::add_detach(int prio, event_proc *p) 511107665Simp{ 512107665Simp p->set_priority(prio); 513107665Simp _detach_list.push_back(p); 514107665Simp} 515107665Simp 516107665Simpvoid 517107665Simpconfig::add_directory(const char *dir) 518107665Simp{ 519107665Simp _dir_list.push_back(string(dir)); 520107665Simp} 521107665Simp 522107665Simpvoid 523107665Simpconfig::add_nomatch(int prio, event_proc *p) 524107665Simp{ 525107665Simp p->set_priority(prio); 526107665Simp _nomatch_list.push_back(p); 527107665Simp} 528107665Simp 529107665Simpvoid 530121487Simpconfig::add_notify(int prio, event_proc *p) 531121487Simp{ 532121487Simp p->set_priority(prio); 533121487Simp _notify_list.push_back(p); 534121487Simp} 535121487Simp 536121487Simpvoid 537107665Simpconfig::set_pidfile(const char *fn) 538107665Simp{ 539107665Simp _pidfile = string(fn); 540107665Simp} 541107665Simp 542107665Simpvoid 543107665Simpconfig::push_var_table() 544107665Simp{ 545107665Simp var_list *vl; 546107665Simp 547107665Simp vl = new var_list(); 548107665Simp _var_list_table.push_back(vl); 549113790Simp if (Dflag) 550108783Simp fprintf(stderr, "Pushing table\n"); 551107665Simp} 552107665Simp 553107665Simpvoid 554107665Simpconfig::pop_var_table() 555107665Simp{ 556107665Simp delete _var_list_table.back(); 557107665Simp _var_list_table.pop_back(); 558113790Simp if (Dflag) 559108783Simp fprintf(stderr, "Popping table\n"); 560107665Simp} 561107665Simp 562107665Simpvoid 563107665Simpconfig::set_variable(const char *var, const char *val) 564107665Simp{ 565107665Simp _var_list_table.back()->set_variable(var, val); 566107665Simp} 567107665Simp 568107665Simpconst string & 569107665Simpconfig::get_variable(const string &var) 570107665Simp{ 571107665Simp vector<var_list *>::reverse_iterator i; 572107665Simp 573107665Simp for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 574107665Simp if ((*i)->is_set(var)) 575108783Simp return ((*i)->get_variable(var)); 576107665Simp } 577107665Simp return (var_list::nothing); 578107665Simp} 579107665Simp 580108783Simpbool 581108783Simpconfig::is_id_char(char ch) 582108783Simp{ 583108783Simp return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 584108783Simp ch == '-')); 585108783Simp} 586108783Simp 587108014Simpvoid 588114081Simpconfig::expand_one(const char *&src, string &dst) 589107665Simp{ 590108014Simp int count; 591210610Slulf string buffer; 592108014Simp 593108783Simp src++; 594108014Simp // $$ -> $ 595108014Simp if (*src == '$') { 596114081Simp dst.append(src++, 1); 597108014Simp return; 598108014Simp } 599108014Simp 600108014Simp // $(foo) -> $(foo) 601108783Simp // Not sure if I want to support this or not, so for now we just pass 602108783Simp // it through. 603108014Simp if (*src == '(') { 604114081Simp dst.append("$"); 605108014Simp count = 1; 606114081Simp /* If the string ends before ) is matched , return. */ 607114081Simp while (count > 0 && *src) { 608108014Simp if (*src == ')') 609108014Simp count--; 610108014Simp else if (*src == '(') 611108014Simp count++; 612114081Simp dst.append(src++, 1); 613108014Simp } 614108014Simp return; 615108014Simp } 616108014Simp 617108014Simp // ${^A-Za-z] -> $\1 618108014Simp if (!isalpha(*src)) { 619114081Simp dst.append("$"); 620114081Simp dst.append(src++, 1); 621108014Simp return; 622108014Simp } 623108014Simp 624108014Simp // $var -> replace with value 625114081Simp do { 626114081Simp buffer.append(src++, 1); 627114084Simp } while (is_id_char(*src)); 628114081Simp buffer.append("", 1); 629210610Slulf dst.append(get_variable(buffer.c_str())); 630107665Simp} 631107665Simp 632108014Simpconst string 633108014Simpconfig::expand_string(const string &s) 634108014Simp{ 635108014Simp const char *src; 636114081Simp string dst; 637108014Simp 638108014Simp src = s.c_str(); 639108014Simp while (*src) { 640108014Simp if (*src == '$') 641114081Simp expand_one(src, dst); 642108014Simp else 643114081Simp dst.append(src++, 1); 644108014Simp } 645114081Simp dst.append("", 1); 646108014Simp 647114081Simp return (dst); 648108014Simp} 649108014Simp 650108783Simpbool 651108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) 652108783Simp{ 653108783Simp char *walker; 654108783Simp 655108783Simp if (*buffer == '\0') 656108783Simp return (false); 657108783Simp walker = lhs = buffer; 658108783Simp while (is_id_char(*walker)) 659108783Simp walker++; 660108783Simp if (*walker != '=') 661108783Simp return (false); 662108783Simp walker++; // skip = 663108783Simp if (*walker == '"') { 664108783Simp walker++; // skip " 665108783Simp rhs = walker; 666108783Simp while (*walker && *walker != '"') 667108783Simp walker++; 668108783Simp if (*walker != '"') 669108783Simp return (false); 670108783Simp rhs[-2] = '\0'; 671108783Simp *walker++ = '\0'; 672108783Simp } else { 673108783Simp rhs = walker; 674108783Simp while (*walker && !isspace(*walker)) 675108783Simp walker++; 676108783Simp if (*walker != '\0') 677108783Simp *walker++ = '\0'; 678108783Simp rhs[-1] = '\0'; 679108783Simp } 680113785Simp while (isspace(*walker)) 681113785Simp walker++; 682108783Simp buffer = walker; 683108783Simp return (true); 684108783Simp} 685108783Simp 686108783Simp 687108783Simpchar * 688108783Simpconfig::set_vars(char *buffer) 689108783Simp{ 690108783Simp char *lhs; 691108783Simp char *rhs; 692108783Simp 693108783Simp while (1) { 694108783Simp if (!chop_var(buffer, lhs, rhs)) 695108783Simp break; 696108783Simp set_variable(lhs, rhs); 697108783Simp } 698108783Simp return (buffer); 699108783Simp} 700108783Simp 701108783Simpvoid 702108783Simpconfig::find_and_execute(char type) 703108783Simp{ 704108783Simp vector<event_proc *> *l; 705108783Simp vector<event_proc *>::const_iterator i; 706151486Sbrooks const char *s; 707108783Simp 708108783Simp switch (type) { 709108783Simp default: 710108783Simp return; 711121487Simp case notify: 712121487Simp l = &_notify_list; 713121487Simp s = "notify"; 714121487Simp break; 715108783Simp case nomatch: 716108783Simp l = &_nomatch_list; 717108783Simp s = "nomatch"; 718108783Simp break; 719108783Simp case attach: 720108783Simp l = &_attach_list; 721108783Simp s = "attach"; 722108783Simp break; 723108783Simp case detach: 724108783Simp l = &_detach_list; 725108783Simp s = "detach"; 726108783Simp break; 727108783Simp } 728113790Simp if (Dflag) 729108783Simp fprintf(stderr, "Processing %s event\n", s); 730108783Simp for (i = l->begin(); i != l->end(); i++) { 731108783Simp if ((*i)->matches(*this)) { 732108783Simp (*i)->run(*this); 733108783Simp break; 734108783Simp } 735108783Simp } 736108783Simp 737108783Simp} 738108783Simp 739107665Simp 740107665Simpstatic void 741108783Simpprocess_event(char *buffer) 742107665Simp{ 743107665Simp char type; 744107665Simp char *sp; 745107665Simp 746108783Simp sp = buffer + 1; 747113790Simp if (Dflag) 748108783Simp fprintf(stderr, "Processing event '%s'\n", buffer); 749107665Simp type = *buffer++; 750108783Simp cfg.push_var_table(); 751108783Simp // No match doesn't have a device, and the format is a little 752108783Simp // different, so handle it separately. 753121487Simp switch (type) { 754121487Simp case notify: 755121487Simp sp = cfg.set_vars(sp); 756121487Simp break; 757121487Simp case nomatch: 758145218Simp //? at location pnp-info on bus 759145218Simp sp = strchr(sp, ' '); 760145218Simp if (sp == NULL) 761145218Simp return; /* Can't happen? */ 762145218Simp *sp++ = '\0'; 763213646Simp while (isspace(*sp)) 764213646Simp sp++; 765121487Simp if (strncmp(sp, "at ", 3) == 0) 766121487Simp sp += 3; 767121487Simp sp = cfg.set_vars(sp); 768213646Simp while (isspace(*sp)) 769213646Simp sp++; 770121487Simp if (strncmp(sp, "on ", 3) == 0) 771121487Simp cfg.set_variable("bus", sp + 3); 772121487Simp break; 773121487Simp case attach: /*FALLTHROUGH*/ 774121487Simp case detach: 775108783Simp sp = strchr(sp, ' '); 776108783Simp if (sp == NULL) 777108783Simp return; /* Can't happen? */ 778108783Simp *sp++ = '\0'; 779108783Simp cfg.set_variable("device-name", buffer); 780213646Simp while (isspace(*sp)) 781213646Simp sp++; 782113785Simp if (strncmp(sp, "at ", 3) == 0) 783113785Simp sp += 3; 784113785Simp sp = cfg.set_vars(sp); 785213646Simp while (isspace(*sp)) 786213646Simp sp++; 787113785Simp if (strncmp(sp, "on ", 3) == 0) 788113785Simp cfg.set_variable("bus", sp + 3); 789121487Simp break; 790108783Simp } 791113785Simp 792108783Simp cfg.find_and_execute(type); 793108783Simp cfg.pop_var_table(); 794107665Simp} 795107665Simp 796131397Simpint 797131397Simpcreate_socket(const char *name) 798131397Simp{ 799131397Simp int fd, slen; 800131397Simp struct sockaddr_un sun; 801131397Simp 802131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 803131397Simp err(1, "socket"); 804131397Simp bzero(&sun, sizeof(sun)); 805131397Simp sun.sun_family = AF_UNIX; 806131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 807131397Simp slen = SUN_LEN(&sun); 808131397Simp unlink(name); 809147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 810147973Smarcus err(1, "fcntl"); 811131397Simp if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 812131397Simp err(1, "bind"); 813131397Simp listen(fd, 4); 814147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 815147973Smarcus chmod(name, 0666); 816131397Simp return (fd); 817131397Simp} 818131397Simp 819131397Simplist<int> clients; 820131397Simp 821131397Simpvoid 822131397Simpnotify_clients(const char *data, int len) 823131397Simp{ 824131397Simp list<int> bad; 825131397Simp list<int>::const_iterator i; 826131397Simp 827131397Simp for (i = clients.begin(); i != clients.end(); i++) { 828131397Simp if (write(*i, data, len) <= 0) { 829131397Simp bad.push_back(*i); 830131397Simp close(*i); 831131397Simp } 832131397Simp } 833131397Simp 834131397Simp for (i = bad.begin(); i != bad.end(); i++) 835131397Simp clients.erase(find(clients.begin(), clients.end(), *i)); 836131397Simp} 837131397Simp 838131397Simpvoid 839131397Simpnew_client(int fd) 840131397Simp{ 841131397Simp int s; 842131397Simp 843131397Simp s = accept(fd, NULL, NULL); 844131397Simp if (s != -1) 845131397Simp clients.push_back(s); 846131397Simp} 847131397Simp 848107665Simpstatic void 849107665Simpevent_loop(void) 850107665Simp{ 851107665Simp int rv; 852107665Simp int fd; 853107665Simp char buffer[DEVCTL_MAXBUF]; 854113790Simp int once = 0; 855131397Simp int server_fd, max_fd; 856113790Simp timeval tv; 857113790Simp fd_set fds; 858107665Simp 859107665Simp fd = open(PATH_DEVCTL, O_RDONLY); 860107665Simp if (fd == -1) 861131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 862107665Simp if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 863131397Simp err(1, "Can't set close-on-exec flag on devctl"); 864131397Simp server_fd = create_socket(PIPE); 865131397Simp max_fd = max(fd, server_fd) + 1; 866107665Simp while (1) { 867107665Simp if (romeo_must_die) 868107665Simp break; 869113790Simp if (!once && !dflag && !nflag) { 870113790Simp // Check to see if we have any events pending. 871113790Simp tv.tv_sec = 0; 872113790Simp tv.tv_usec = 0; 873113790Simp FD_ZERO(&fds); 874113790Simp FD_SET(fd, &fds); 875113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 876113790Simp // No events -> we've processed all pending events 877117944Simp if (rv == 0) { 878113790Simp if (Dflag) 879113790Simp fprintf(stderr, "Calling daemon\n"); 880155073Spjd cfg.remove_pidfile(); 881155073Spjd cfg.open_pidfile(); 882113790Simp daemon(0, 0); 883155073Spjd cfg.write_pidfile(); 884113790Simp once++; 885113790Simp } 886113790Simp } 887131397Simp FD_ZERO(&fds); 888131397Simp FD_SET(fd, &fds); 889131397Simp FD_SET(server_fd, &fds); 890131397Simp rv = select(max_fd, &fds, NULL, NULL, NULL); 891131397Simp if (rv == -1) { 892131397Simp if (errno == EINTR) 893131397Simp continue; 894131397Simp err(1, "select"); 895131397Simp } 896131397Simp if (FD_ISSET(fd, &fds)) { 897131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 898131397Simp if (rv > 0) { 899131397Simp notify_clients(buffer, rv); 900107665Simp buffer[rv] = '\0'; 901131397Simp while (buffer[--rv] == '\n') 902131397Simp buffer[rv] = '\0'; 903131397Simp process_event(buffer); 904131397Simp } else if (rv < 0) { 905131397Simp if (errno != EINTR) 906131397Simp break; 907131397Simp } else { 908131397Simp /* EOF */ 909107665Simp break; 910131397Simp } 911107665Simp } 912131397Simp if (FD_ISSET(server_fd, &fds)) 913131397Simp new_client(server_fd); 914107665Simp } 915107665Simp close(fd); 916107665Simp} 917107665Simp 918107665Simp/* 919107665Simp * functions that the parser uses. 920107665Simp */ 921107665Simpvoid 922107665Simpadd_attach(int prio, event_proc *p) 923107665Simp{ 924107665Simp cfg.add_attach(prio, p); 925107665Simp} 926107665Simp 927107665Simpvoid 928107665Simpadd_detach(int prio, event_proc *p) 929107665Simp{ 930107665Simp cfg.add_detach(prio, p); 931107665Simp} 932107665Simp 933107665Simpvoid 934107665Simpadd_directory(const char *dir) 935107665Simp{ 936107665Simp cfg.add_directory(dir); 937107665Simp free(const_cast<char *>(dir)); 938107665Simp} 939107665Simp 940107665Simpvoid 941107665Simpadd_nomatch(int prio, event_proc *p) 942107665Simp{ 943107665Simp cfg.add_nomatch(prio, p); 944107665Simp} 945107665Simp 946121487Simpvoid 947121487Simpadd_notify(int prio, event_proc *p) 948121487Simp{ 949121487Simp cfg.add_notify(prio, p); 950121487Simp} 951121487Simp 952107665Simpevent_proc * 953107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 954107665Simp{ 955107665Simp if (ep == NULL) 956107665Simp ep = new event_proc(); 957107665Simp ep->add(eps); 958107665Simp return (ep); 959107665Simp} 960107665Simp 961107665Simpeps * 962107665Simpnew_action(const char *cmd) 963107665Simp{ 964107665Simp eps *e = new action(cmd); 965107665Simp free(const_cast<char *>(cmd)); 966107665Simp return (e); 967107665Simp} 968107665Simp 969107665Simpeps * 970107665Simpnew_match(const char *var, const char *re) 971107665Simp{ 972108014Simp eps *e = new match(cfg, var, re); 973107665Simp free(const_cast<char *>(var)); 974107665Simp free(const_cast<char *>(re)); 975107665Simp return (e); 976107665Simp} 977107665Simp 978147874Simpeps * 979147874Simpnew_media(const char *var, const char *re) 980147874Simp{ 981147874Simp eps *e = new media(cfg, var, re); 982147874Simp free(const_cast<char *>(var)); 983147874Simp free(const_cast<char *>(re)); 984147874Simp return (e); 985147874Simp} 986147874Simp 987107665Simpvoid 988107665Simpset_pidfile(const char *name) 989107665Simp{ 990107665Simp cfg.set_pidfile(name); 991107665Simp free(const_cast<char *>(name)); 992107665Simp} 993107665Simp 994107665Simpvoid 995107665Simpset_variable(const char *var, const char *val) 996107665Simp{ 997107665Simp cfg.set_variable(var, val); 998107665Simp free(const_cast<char *>(var)); 999107665Simp free(const_cast<char *>(val)); 1000107665Simp} 1001107665Simp 1002107665Simp 1003107665Simp 1004107665Simpstatic void 1005107665Simpgensighand(int) 1006107665Simp{ 1007107665Simp romeo_must_die++; 1008107665Simp _exit(0); 1009107665Simp} 1010107665Simp 1011107665Simpstatic void 1012107665Simpusage() 1013107665Simp{ 1014162388Sru fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 1015107665Simp exit(1); 1016107665Simp} 1017107665Simp 1018113787Simpstatic void 1019113787Simpcheck_devd_enabled() 1020113787Simp{ 1021113787Simp int val = 0; 1022113787Simp size_t len; 1023113787Simp 1024113787Simp len = sizeof(val); 1025114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1026113787Simp errx(1, "devctl sysctl missing from kernel!"); 1027113787Simp if (val) { 1028113787Simp warnx("Setting " SYSCTL " to 0"); 1029113787Simp val = 0; 1030113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1031113787Simp } 1032113787Simp} 1033113787Simp 1034107665Simp/* 1035107665Simp * main 1036107665Simp */ 1037107665Simpint 1038107665Simpmain(int argc, char **argv) 1039107665Simp{ 1040107665Simp int ch; 1041107665Simp 1042113787Simp check_devd_enabled(); 1043152770Sjkoshy while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 1044107665Simp switch (ch) { 1045113790Simp case 'D': 1046113790Simp Dflag++; 1047113790Simp break; 1048107665Simp case 'd': 1049107665Simp dflag++; 1050107665Simp break; 1051152770Sjkoshy case 'f': 1052152770Sjkoshy configfile = optarg; 1053152770Sjkoshy break; 1054113790Simp case 'n': 1055113790Simp nflag++; 1056113790Simp break; 1057107665Simp default: 1058107665Simp usage(); 1059107665Simp } 1060107665Simp } 1061107665Simp 1062107665Simp cfg.parse(); 1063117246Simp if (!dflag && nflag) { 1064155073Spjd cfg.open_pidfile(); 1065107665Simp daemon(0, 0); 1066155073Spjd cfg.write_pidfile(); 1067117246Simp } 1068146306Simp signal(SIGPIPE, SIG_IGN); 1069107665Simp signal(SIGHUP, gensighand); 1070107665Simp signal(SIGINT, gensighand); 1071107665Simp signal(SIGTERM, gensighand); 1072107665Simp event_loop(); 1073107665Simp return (0); 1074107665Simp} 1075