devd.cc revision 186078
1107665Simp/*- 2113785Simp * Copyright (c) 2002-2003 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. 25107665Simp */ 26107665Simp 27107665Simp/* 28107665Simp * DEVD control daemon. 29107665Simp */ 30107665Simp 31107665Simp// TODO list: 32107665Simp// o devd.conf and devd man pages need a lot of help: 33131397Simp// - devd needs to document the unix domain socket 34107665Simp// - devd.conf needs more details on the supported statements. 35107665Simp 36107665Simp#include <sys/cdefs.h> 37107665Simp__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 186078 2008-12-14 11:48:51Z phk $"); 38107665Simp 39107665Simp#include <sys/param.h> 40131397Simp#include <sys/socket.h> 41131397Simp#include <sys/stat.h> 42131397Simp#include <sys/sysctl.h> 43107665Simp#include <sys/types.h> 44131397Simp#include <sys/un.h> 45107665Simp 46108014Simp#include <ctype.h> 47107665Simp#include <dirent.h> 48107665Simp#include <errno.h> 49107665Simp#include <err.h> 50107665Simp#include <fcntl.h> 51155073Spjd#include <libutil.h> 52108014Simp#include <regex.h> 53146306Simp#include <signal.h> 54107665Simp#include <stdlib.h> 55107665Simp#include <stdio.h> 56107665Simp#include <string.h> 57107665Simp#include <unistd.h> 58107665Simp 59108783Simp#include <algorithm> 60107665Simp#include <map> 61107665Simp#include <string> 62131397Simp#include <list> 63107665Simp#include <vector> 64107665Simp 65114086Simp#include "devd.h" /* C compatible definitions */ 66114086Simp#include "devd.hh" /* C++ class definitions */ 67107665Simp 68131397Simp#define PIPE "/var/run/devd.pipe" 69107665Simp#define CF "/etc/devd.conf" 70113787Simp#define SYSCTL "hw.bus.devctl_disable" 71107665Simp 72107665Simpusing namespace std; 73107665Simp 74107665Simpextern FILE *yyin; 75107665Simpextern int lineno; 76107665Simp 77121487Simpstatic const char notify = '!'; 78108783Simpstatic const char nomatch = '?'; 79108783Simpstatic const char attach = '+'; 80108783Simpstatic const char detach = '-'; 81108783Simp 82155073Spjdstatic struct pidfh *pfh; 83155073Spjd 84113790Simpint Dflag; 85107665Simpint dflag; 86114000Simpint nflag; 87107665Simpint romeo_must_die = 0; 88107665Simp 89152770Sjkoshystatic const char *configfile = CF; 90152770Sjkoshy 91107665Simpstatic void event_loop(void); 92107665Simpstatic void usage(void); 93107665Simp 94108783Simptemplate <class T> void 95108783Simpdelete_and_clear(vector<T *> &v) 96108783Simp{ 97108783Simp typename vector<T *>::const_iterator i; 98108783Simp 99108783Simp for (i = v.begin(); i != v.end(); i++) 100108783Simp delete *i; 101108783Simp v.clear(); 102108783Simp} 103108783Simp 104107665Simpconfig cfg; 105107665Simp 106107665Simpevent_proc::event_proc() : _prio(-1) 107107665Simp{ 108107665Simp // nothing 109107665Simp} 110107665Simp 111107665Simpevent_proc::~event_proc() 112107665Simp{ 113152406Sbland delete_and_clear(_epsvec); 114107665Simp} 115107665Simp 116107665Simpvoid 117107665Simpevent_proc::add(eps *eps) 118107665Simp{ 119107665Simp _epsvec.push_back(eps); 120107665Simp} 121107665Simp 122107665Simpbool 123107665Simpevent_proc::matches(config &c) 124107665Simp{ 125107665Simp vector<eps *>::const_iterator i; 126107665Simp 127107665Simp for (i = _epsvec.begin(); i != _epsvec.end(); i++) 128107665Simp if (!(*i)->do_match(c)) 129107665Simp return (false); 130107665Simp return (true); 131107665Simp} 132107665Simp 133107665Simpbool 134107665Simpevent_proc::run(config &c) 135107665Simp{ 136107665Simp vector<eps *>::const_iterator i; 137107665Simp 138107665Simp for (i = _epsvec.begin(); i != _epsvec.end(); i++) 139107665Simp if (!(*i)->do_action(c)) 140107665Simp return (false); 141107665Simp return (true); 142107665Simp} 143107665Simp 144107665Simpaction::action(const char *cmd) 145107665Simp : _cmd(cmd) 146107665Simp{ 147107665Simp // nothing 148107665Simp} 149107665Simp 150107665Simpaction::~action() 151107665Simp{ 152107665Simp // nothing 153107665Simp} 154107665Simp 155107665Simpbool 156108014Simpaction::do_action(config &c) 157107665Simp{ 158108783Simp string s = c.expand_string(_cmd); 159113790Simp if (Dflag) 160108783Simp fprintf(stderr, "Executing '%s'\n", s.c_str()); 161108783Simp ::system(s.c_str()); 162107665Simp return (true); 163107665Simp} 164107665Simp 165108014Simpmatch::match(config &c, const char *var, const char *re) 166108783Simp : _var(var) 167107665Simp{ 168108783Simp string pattern = re; 169108783Simp _re = "^"; 170108783Simp _re.append(c.expand_string(string(re))); 171108783Simp _re.append("$"); 172154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 173107665Simp} 174107665Simp 175107665Simpmatch::~match() 176107665Simp{ 177108014Simp regfree(&_regex); 178107665Simp} 179107665Simp 180107665Simpbool 181108014Simpmatch::do_match(config &c) 182107665Simp{ 183108014Simp string value = c.get_variable(_var); 184108014Simp bool retval; 185108014Simp 186113790Simp if (Dflag) 187108783Simp fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(), 188108783Simp value.c_str(), _re.c_str()); 189108783Simp 190108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 191108014Simp return retval; 192107665Simp} 193107665Simp 194147874Simp#include <sys/sockio.h> 195147874Simp#include <net/if.h> 196147874Simp#include <net/if_media.h> 197147874Simp 198151486Sbrooksmedia::media(config &, const char *var, const char *type) 199147874Simp : _var(var), _type(-1) 200147874Simp{ 201147874Simp static struct ifmedia_description media_types[] = { 202147874Simp { IFM_ETHER, "Ethernet" }, 203147874Simp { IFM_TOKEN, "Tokenring" }, 204147874Simp { IFM_FDDI, "FDDI" }, 205147874Simp { IFM_IEEE80211, "802.11" }, 206147874Simp { IFM_ATM, "ATM" }, 207147874Simp { IFM_CARP, "CARP" }, 208147874Simp { -1, "unknown" }, 209147874Simp { 0, NULL }, 210147874Simp }; 211147874Simp for (int i = 0; media_types[i].ifmt_string != NULL; i++) 212147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 213147874Simp _type = media_types[i].ifmt_word; 214147874Simp break; 215147874Simp } 216147874Simp} 217147874Simp 218147874Simpmedia::~media() 219147874Simp{ 220147874Simp} 221147874Simp 222147874Simpbool 223147874Simpmedia::do_match(config &c) 224147874Simp{ 225150949Simp string value; 226147874Simp struct ifmediareq ifmr; 227147874Simp bool retval; 228147874Simp int s; 229147874Simp 230150949Simp // Since we can be called from both a device attach/detach 231150949Simp // context where device-name is defined and what we want, 232150949Simp // as well as from a link status context, where subsystem is 233150949Simp // the name of interest, first try device-name and fall back 234150949Simp // to subsystem if none exists. 235150949Simp value = c.get_variable("device-name"); 236150949Simp if (value.length() == 0) 237151480Simp value = c.get_variable("subsystem"); 238147874Simp if (Dflag) 239147874Simp fprintf(stderr, "Testing media type of %s against 0x%x\n", 240147874Simp value.c_str(), _type); 241147874Simp 242147874Simp retval = false; 243147874Simp 244147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 245147874Simp if (s >= 0) { 246147874Simp memset(&ifmr, 0, sizeof(ifmr)); 247147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 248147874Simp 249147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 250147874Simp ifmr.ifm_status & IFM_AVALID) { 251147874Simp if (Dflag) 252147874Simp fprintf(stderr, "%s has media type 0x%x\n", 253147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 254147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 255147874Simp } else if (_type == -1) { 256147874Simp if (Dflag) 257147874Simp fprintf(stderr, "%s has unknown media type\n", 258147874Simp value.c_str()); 259147874Simp retval = true; 260147874Simp } 261147874Simp close(s); 262147874Simp } 263147874Simp 264147874Simp return retval; 265147874Simp} 266147874Simp 267107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 268107665Simpconst string var_list::nothing = ""; 269107665Simp 270107665Simpconst string & 271107665Simpvar_list::get_variable(const string &var) const 272107665Simp{ 273107665Simp map<string, string>::const_iterator i; 274107665Simp 275107665Simp i = _vars.find(var); 276107665Simp if (i == _vars.end()) 277108783Simp return (var_list::bogus); 278107665Simp return (i->second); 279107665Simp} 280107665Simp 281107665Simpbool 282107665Simpvar_list::is_set(const string &var) const 283107665Simp{ 284107665Simp return (_vars.find(var) != _vars.end()); 285107665Simp} 286107665Simp 287107665Simpvoid 288107665Simpvar_list::set_variable(const string &var, const string &val) 289107665Simp{ 290113790Simp if (Dflag) 291145218Simp fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str()); 292107665Simp _vars[var] = val; 293107665Simp} 294107665Simp 295107665Simpvoid 296107665Simpconfig::reset(void) 297107665Simp{ 298107665Simp _dir_list.clear(); 299108783Simp delete_and_clear(_var_list_table); 300108783Simp delete_and_clear(_attach_list); 301108783Simp delete_and_clear(_detach_list); 302108783Simp delete_and_clear(_nomatch_list); 303121487Simp delete_and_clear(_notify_list); 304107665Simp} 305107665Simp 306107665Simpvoid 307107665Simpconfig::parse_one_file(const char *fn) 308107665Simp{ 309113790Simp if (Dflag) 310186078Sphk fprintf(stderr, "Parsing %s\n", fn); 311107665Simp yyin = fopen(fn, "r"); 312107665Simp if (yyin == NULL) 313107665Simp err(1, "Cannot open config file %s", fn); 314157746Smaxim lineno = 1; 315107665Simp if (yyparse() != 0) 316107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 317107665Simp fclose(yyin); 318107665Simp} 319107665Simp 320107665Simpvoid 321107665Simpconfig::parse_files_in_dir(const char *dirname) 322107665Simp{ 323107665Simp DIR *dirp; 324107665Simp struct dirent *dp; 325107665Simp char path[PATH_MAX]; 326107665Simp 327113790Simp if (Dflag) 328186078Sphk fprintf(stderr, "Parsing files in %s\n", dirname); 329107665Simp dirp = opendir(dirname); 330107665Simp if (dirp == NULL) 331107665Simp return; 332107665Simp readdir(dirp); /* Skip . */ 333107665Simp readdir(dirp); /* Skip .. */ 334107665Simp while ((dp = readdir(dirp)) != NULL) { 335107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 336107665Simp snprintf(path, sizeof(path), "%s/%s", 337107665Simp dirname, dp->d_name); 338107665Simp parse_one_file(path); 339107665Simp } 340107665Simp } 341107665Simp} 342107665Simp 343108783Simpclass epv_greater { 344108783Simppublic: 345108783Simp int operator()(event_proc *const&l1, event_proc *const&l2) 346108783Simp { 347108783Simp return (l1->get_priority() > l2->get_priority()); 348108783Simp } 349108783Simp}; 350108783Simp 351107665Simpvoid 352108783Simpconfig::sort_vector(vector<event_proc *> &v) 353108783Simp{ 354108783Simp sort(v.begin(), v.end(), epv_greater()); 355108783Simp} 356108783Simp 357108783Simpvoid 358107665Simpconfig::parse(void) 359107665Simp{ 360107665Simp vector<string>::const_iterator i; 361107665Simp 362152770Sjkoshy parse_one_file(configfile); 363107665Simp for (i = _dir_list.begin(); i != _dir_list.end(); i++) 364107665Simp parse_files_in_dir((*i).c_str()); 365108783Simp sort_vector(_attach_list); 366108783Simp sort_vector(_detach_list); 367108783Simp sort_vector(_nomatch_list); 368121487Simp sort_vector(_notify_list); 369107665Simp} 370107665Simp 371107665Simpvoid 372155073Spjdconfig::open_pidfile() 373107665Simp{ 374155073Spjd pid_t otherpid; 375107665Simp 376107665Simp if (_pidfile == "") 377107665Simp return; 378155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 379155073Spjd if (pfh == NULL) { 380155073Spjd if (errno == EEXIST) 381155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 382155073Spjd warn("cannot open pid file"); 383155073Spjd } 384107665Simp} 385107665Simp 386107665Simpvoid 387155073Spjdconfig::write_pidfile() 388155073Spjd{ 389155073Spjd 390155073Spjd pidfile_write(pfh); 391155073Spjd} 392155073Spjd 393155073Spjdvoid 394155073Spjdconfig::remove_pidfile() 395155073Spjd{ 396155073Spjd 397155073Spjd pidfile_remove(pfh); 398155073Spjd} 399155073Spjd 400155073Spjdvoid 401107665Simpconfig::add_attach(int prio, event_proc *p) 402107665Simp{ 403107665Simp p->set_priority(prio); 404107665Simp _attach_list.push_back(p); 405107665Simp} 406107665Simp 407107665Simpvoid 408107665Simpconfig::add_detach(int prio, event_proc *p) 409107665Simp{ 410107665Simp p->set_priority(prio); 411107665Simp _detach_list.push_back(p); 412107665Simp} 413107665Simp 414107665Simpvoid 415107665Simpconfig::add_directory(const char *dir) 416107665Simp{ 417107665Simp _dir_list.push_back(string(dir)); 418107665Simp} 419107665Simp 420107665Simpvoid 421107665Simpconfig::add_nomatch(int prio, event_proc *p) 422107665Simp{ 423107665Simp p->set_priority(prio); 424107665Simp _nomatch_list.push_back(p); 425107665Simp} 426107665Simp 427107665Simpvoid 428121487Simpconfig::add_notify(int prio, event_proc *p) 429121487Simp{ 430121487Simp p->set_priority(prio); 431121487Simp _notify_list.push_back(p); 432121487Simp} 433121487Simp 434121487Simpvoid 435107665Simpconfig::set_pidfile(const char *fn) 436107665Simp{ 437107665Simp _pidfile = string(fn); 438107665Simp} 439107665Simp 440107665Simpvoid 441107665Simpconfig::push_var_table() 442107665Simp{ 443107665Simp var_list *vl; 444107665Simp 445107665Simp vl = new var_list(); 446107665Simp _var_list_table.push_back(vl); 447113790Simp if (Dflag) 448108783Simp fprintf(stderr, "Pushing table\n"); 449107665Simp} 450107665Simp 451107665Simpvoid 452107665Simpconfig::pop_var_table() 453107665Simp{ 454107665Simp delete _var_list_table.back(); 455107665Simp _var_list_table.pop_back(); 456113790Simp if (Dflag) 457108783Simp fprintf(stderr, "Popping table\n"); 458107665Simp} 459107665Simp 460107665Simpvoid 461107665Simpconfig::set_variable(const char *var, const char *val) 462107665Simp{ 463107665Simp _var_list_table.back()->set_variable(var, val); 464107665Simp} 465107665Simp 466107665Simpconst string & 467107665Simpconfig::get_variable(const string &var) 468107665Simp{ 469107665Simp vector<var_list *>::reverse_iterator i; 470107665Simp 471107665Simp for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) { 472107665Simp if ((*i)->is_set(var)) 473108783Simp return ((*i)->get_variable(var)); 474107665Simp } 475107665Simp return (var_list::nothing); 476107665Simp} 477107665Simp 478108783Simpbool 479108783Simpconfig::is_id_char(char ch) 480108783Simp{ 481108783Simp return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 482108783Simp ch == '-')); 483108783Simp} 484108783Simp 485108014Simpvoid 486114081Simpconfig::expand_one(const char *&src, string &dst) 487107665Simp{ 488108014Simp int count; 489114081Simp string buffer, varstr; 490108014Simp 491108783Simp src++; 492108014Simp // $$ -> $ 493108014Simp if (*src == '$') { 494114081Simp dst.append(src++, 1); 495108014Simp return; 496108014Simp } 497108014Simp 498108014Simp // $(foo) -> $(foo) 499108783Simp // Not sure if I want to support this or not, so for now we just pass 500108783Simp // it through. 501108014Simp if (*src == '(') { 502114081Simp dst.append("$"); 503108014Simp count = 1; 504114081Simp /* If the string ends before ) is matched , return. */ 505114081Simp while (count > 0 && *src) { 506108014Simp if (*src == ')') 507108014Simp count--; 508108014Simp else if (*src == '(') 509108014Simp count++; 510114081Simp dst.append(src++, 1); 511108014Simp } 512108014Simp return; 513108014Simp } 514108014Simp 515108014Simp // ${^A-Za-z] -> $\1 516108014Simp if (!isalpha(*src)) { 517114081Simp dst.append("$"); 518114081Simp dst.append(src++, 1); 519108014Simp return; 520108014Simp } 521108014Simp 522108014Simp // $var -> replace with value 523114081Simp do { 524114081Simp buffer.append(src++, 1); 525114084Simp } while (is_id_char(*src)); 526114081Simp buffer.append("", 1); 527114081Simp varstr = get_variable(buffer.c_str()); 528114081Simp dst.append(varstr); 529107665Simp} 530107665Simp 531108014Simpconst string 532108014Simpconfig::expand_string(const string &s) 533108014Simp{ 534108014Simp const char *src; 535114081Simp string dst; 536108014Simp 537108014Simp src = s.c_str(); 538108014Simp while (*src) { 539108014Simp if (*src == '$') 540114081Simp expand_one(src, dst); 541108014Simp else 542114081Simp dst.append(src++, 1); 543108014Simp } 544114081Simp dst.append("", 1); 545108014Simp 546114081Simp return (dst); 547108014Simp} 548108014Simp 549108783Simpbool 550108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) 551108783Simp{ 552108783Simp char *walker; 553108783Simp 554108783Simp if (*buffer == '\0') 555108783Simp return (false); 556108783Simp walker = lhs = buffer; 557108783Simp while (is_id_char(*walker)) 558108783Simp walker++; 559108783Simp if (*walker != '=') 560108783Simp return (false); 561108783Simp walker++; // skip = 562108783Simp if (*walker == '"') { 563108783Simp walker++; // skip " 564108783Simp rhs = walker; 565108783Simp while (*walker && *walker != '"') 566108783Simp walker++; 567108783Simp if (*walker != '"') 568108783Simp return (false); 569108783Simp rhs[-2] = '\0'; 570108783Simp *walker++ = '\0'; 571108783Simp } else { 572108783Simp rhs = walker; 573108783Simp while (*walker && !isspace(*walker)) 574108783Simp walker++; 575108783Simp if (*walker != '\0') 576108783Simp *walker++ = '\0'; 577108783Simp rhs[-1] = '\0'; 578108783Simp } 579113785Simp while (isspace(*walker)) 580113785Simp walker++; 581108783Simp buffer = walker; 582108783Simp return (true); 583108783Simp} 584108783Simp 585108783Simp 586108783Simpchar * 587108783Simpconfig::set_vars(char *buffer) 588108783Simp{ 589108783Simp char *lhs; 590108783Simp char *rhs; 591108783Simp 592108783Simp while (1) { 593108783Simp if (!chop_var(buffer, lhs, rhs)) 594108783Simp break; 595108783Simp set_variable(lhs, rhs); 596108783Simp } 597108783Simp return (buffer); 598108783Simp} 599108783Simp 600108783Simpvoid 601108783Simpconfig::find_and_execute(char type) 602108783Simp{ 603108783Simp vector<event_proc *> *l; 604108783Simp vector<event_proc *>::const_iterator i; 605151486Sbrooks const char *s; 606108783Simp 607108783Simp switch (type) { 608108783Simp default: 609108783Simp return; 610121487Simp case notify: 611121487Simp l = &_notify_list; 612121487Simp s = "notify"; 613121487Simp break; 614108783Simp case nomatch: 615108783Simp l = &_nomatch_list; 616108783Simp s = "nomatch"; 617108783Simp break; 618108783Simp case attach: 619108783Simp l = &_attach_list; 620108783Simp s = "attach"; 621108783Simp break; 622108783Simp case detach: 623108783Simp l = &_detach_list; 624108783Simp s = "detach"; 625108783Simp break; 626108783Simp } 627113790Simp if (Dflag) 628108783Simp fprintf(stderr, "Processing %s event\n", s); 629108783Simp for (i = l->begin(); i != l->end(); i++) { 630108783Simp if ((*i)->matches(*this)) { 631108783Simp (*i)->run(*this); 632108783Simp break; 633108783Simp } 634108783Simp } 635108783Simp 636108783Simp} 637108783Simp 638107665Simp 639107665Simpstatic void 640108783Simpprocess_event(char *buffer) 641107665Simp{ 642107665Simp char type; 643107665Simp char *sp; 644107665Simp 645108783Simp sp = buffer + 1; 646113790Simp if (Dflag) 647108783Simp fprintf(stderr, "Processing event '%s'\n", buffer); 648107665Simp type = *buffer++; 649108783Simp cfg.push_var_table(); 650108783Simp // No match doesn't have a device, and the format is a little 651108783Simp // different, so handle it separately. 652121487Simp switch (type) { 653121487Simp case notify: 654121487Simp sp = cfg.set_vars(sp); 655121487Simp break; 656121487Simp case nomatch: 657145218Simp //? at location pnp-info on bus 658145218Simp sp = strchr(sp, ' '); 659145218Simp if (sp == NULL) 660145218Simp return; /* Can't happen? */ 661145218Simp *sp++ = '\0'; 662121487Simp if (strncmp(sp, "at ", 3) == 0) 663121487Simp sp += 3; 664121487Simp sp = cfg.set_vars(sp); 665121487Simp if (strncmp(sp, "on ", 3) == 0) 666121487Simp cfg.set_variable("bus", sp + 3); 667121487Simp break; 668121487Simp case attach: /*FALLTHROUGH*/ 669121487Simp case detach: 670108783Simp sp = strchr(sp, ' '); 671108783Simp if (sp == NULL) 672108783Simp return; /* Can't happen? */ 673108783Simp *sp++ = '\0'; 674108783Simp cfg.set_variable("device-name", buffer); 675113785Simp if (strncmp(sp, "at ", 3) == 0) 676113785Simp sp += 3; 677113785Simp sp = cfg.set_vars(sp); 678113785Simp if (strncmp(sp, "on ", 3) == 0) 679113785Simp cfg.set_variable("bus", sp + 3); 680121487Simp break; 681108783Simp } 682113785Simp 683108783Simp cfg.find_and_execute(type); 684108783Simp cfg.pop_var_table(); 685107665Simp} 686107665Simp 687131397Simpint 688131397Simpcreate_socket(const char *name) 689131397Simp{ 690131397Simp int fd, slen; 691131397Simp struct sockaddr_un sun; 692131397Simp 693131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 694131397Simp err(1, "socket"); 695131397Simp bzero(&sun, sizeof(sun)); 696131397Simp sun.sun_family = AF_UNIX; 697131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 698131397Simp slen = SUN_LEN(&sun); 699131397Simp unlink(name); 700147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 701147973Smarcus err(1, "fcntl"); 702131397Simp if (bind(fd, (struct sockaddr *) & sun, slen) < 0) 703131397Simp err(1, "bind"); 704131397Simp listen(fd, 4); 705147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 706147973Smarcus chmod(name, 0666); 707131397Simp return (fd); 708131397Simp} 709131397Simp 710131397Simplist<int> clients; 711131397Simp 712131397Simpvoid 713131397Simpnotify_clients(const char *data, int len) 714131397Simp{ 715131397Simp list<int> bad; 716131397Simp list<int>::const_iterator i; 717131397Simp 718131397Simp for (i = clients.begin(); i != clients.end(); i++) { 719131397Simp if (write(*i, data, len) <= 0) { 720131397Simp bad.push_back(*i); 721131397Simp close(*i); 722131397Simp } 723131397Simp } 724131397Simp 725131397Simp for (i = bad.begin(); i != bad.end(); i++) 726131397Simp clients.erase(find(clients.begin(), clients.end(), *i)); 727131397Simp} 728131397Simp 729131397Simpvoid 730131397Simpnew_client(int fd) 731131397Simp{ 732131397Simp int s; 733131397Simp 734131397Simp s = accept(fd, NULL, NULL); 735131397Simp if (s != -1) 736131397Simp clients.push_back(s); 737131397Simp} 738131397Simp 739107665Simpstatic void 740107665Simpevent_loop(void) 741107665Simp{ 742107665Simp int rv; 743107665Simp int fd; 744107665Simp char buffer[DEVCTL_MAXBUF]; 745113790Simp int once = 0; 746131397Simp int server_fd, max_fd; 747113790Simp timeval tv; 748113790Simp fd_set fds; 749107665Simp 750107665Simp fd = open(PATH_DEVCTL, O_RDONLY); 751107665Simp if (fd == -1) 752131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 753107665Simp if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) 754131397Simp err(1, "Can't set close-on-exec flag on devctl"); 755131397Simp server_fd = create_socket(PIPE); 756131397Simp max_fd = max(fd, server_fd) + 1; 757107665Simp while (1) { 758107665Simp if (romeo_must_die) 759107665Simp break; 760113790Simp if (!once && !dflag && !nflag) { 761113790Simp // Check to see if we have any events pending. 762113790Simp tv.tv_sec = 0; 763113790Simp tv.tv_usec = 0; 764113790Simp FD_ZERO(&fds); 765113790Simp FD_SET(fd, &fds); 766113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 767113790Simp // No events -> we've processed all pending events 768117944Simp if (rv == 0) { 769113790Simp if (Dflag) 770113790Simp fprintf(stderr, "Calling daemon\n"); 771155073Spjd cfg.remove_pidfile(); 772155073Spjd cfg.open_pidfile(); 773113790Simp daemon(0, 0); 774155073Spjd cfg.write_pidfile(); 775113790Simp once++; 776113790Simp } 777113790Simp } 778131397Simp FD_ZERO(&fds); 779131397Simp FD_SET(fd, &fds); 780131397Simp FD_SET(server_fd, &fds); 781131397Simp rv = select(max_fd, &fds, NULL, NULL, NULL); 782131397Simp if (rv == -1) { 783131397Simp if (errno == EINTR) 784131397Simp continue; 785131397Simp err(1, "select"); 786131397Simp } 787131397Simp if (FD_ISSET(fd, &fds)) { 788131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 789131397Simp if (rv > 0) { 790131397Simp notify_clients(buffer, rv); 791107665Simp buffer[rv] = '\0'; 792131397Simp while (buffer[--rv] == '\n') 793131397Simp buffer[rv] = '\0'; 794131397Simp process_event(buffer); 795131397Simp } else if (rv < 0) { 796131397Simp if (errno != EINTR) 797131397Simp break; 798131397Simp } else { 799131397Simp /* EOF */ 800107665Simp break; 801131397Simp } 802107665Simp } 803131397Simp if (FD_ISSET(server_fd, &fds)) 804131397Simp new_client(server_fd); 805107665Simp } 806107665Simp close(fd); 807107665Simp} 808107665Simp 809107665Simp/* 810107665Simp * functions that the parser uses. 811107665Simp */ 812107665Simpvoid 813107665Simpadd_attach(int prio, event_proc *p) 814107665Simp{ 815107665Simp cfg.add_attach(prio, p); 816107665Simp} 817107665Simp 818107665Simpvoid 819107665Simpadd_detach(int prio, event_proc *p) 820107665Simp{ 821107665Simp cfg.add_detach(prio, p); 822107665Simp} 823107665Simp 824107665Simpvoid 825107665Simpadd_directory(const char *dir) 826107665Simp{ 827107665Simp cfg.add_directory(dir); 828107665Simp free(const_cast<char *>(dir)); 829107665Simp} 830107665Simp 831107665Simpvoid 832107665Simpadd_nomatch(int prio, event_proc *p) 833107665Simp{ 834107665Simp cfg.add_nomatch(prio, p); 835107665Simp} 836107665Simp 837121487Simpvoid 838121487Simpadd_notify(int prio, event_proc *p) 839121487Simp{ 840121487Simp cfg.add_notify(prio, p); 841121487Simp} 842121487Simp 843107665Simpevent_proc * 844107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 845107665Simp{ 846107665Simp if (ep == NULL) 847107665Simp ep = new event_proc(); 848107665Simp ep->add(eps); 849107665Simp return (ep); 850107665Simp} 851107665Simp 852107665Simpeps * 853107665Simpnew_action(const char *cmd) 854107665Simp{ 855107665Simp eps *e = new action(cmd); 856107665Simp free(const_cast<char *>(cmd)); 857107665Simp return (e); 858107665Simp} 859107665Simp 860107665Simpeps * 861107665Simpnew_match(const char *var, const char *re) 862107665Simp{ 863108014Simp eps *e = new match(cfg, var, re); 864107665Simp free(const_cast<char *>(var)); 865107665Simp free(const_cast<char *>(re)); 866107665Simp return (e); 867107665Simp} 868107665Simp 869147874Simpeps * 870147874Simpnew_media(const char *var, const char *re) 871147874Simp{ 872147874Simp eps *e = new media(cfg, var, re); 873147874Simp free(const_cast<char *>(var)); 874147874Simp free(const_cast<char *>(re)); 875147874Simp return (e); 876147874Simp} 877147874Simp 878107665Simpvoid 879107665Simpset_pidfile(const char *name) 880107665Simp{ 881107665Simp cfg.set_pidfile(name); 882107665Simp free(const_cast<char *>(name)); 883107665Simp} 884107665Simp 885107665Simpvoid 886107665Simpset_variable(const char *var, const char *val) 887107665Simp{ 888107665Simp cfg.set_variable(var, val); 889107665Simp free(const_cast<char *>(var)); 890107665Simp free(const_cast<char *>(val)); 891107665Simp} 892107665Simp 893107665Simp 894107665Simp 895107665Simpstatic void 896107665Simpgensighand(int) 897107665Simp{ 898107665Simp romeo_must_die++; 899107665Simp _exit(0); 900107665Simp} 901107665Simp 902107665Simpstatic void 903107665Simpusage() 904107665Simp{ 905162388Sru fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname()); 906107665Simp exit(1); 907107665Simp} 908107665Simp 909113787Simpstatic void 910113787Simpcheck_devd_enabled() 911113787Simp{ 912113787Simp int val = 0; 913113787Simp size_t len; 914113787Simp 915113787Simp len = sizeof(val); 916114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 917113787Simp errx(1, "devctl sysctl missing from kernel!"); 918113787Simp if (val) { 919113787Simp warnx("Setting " SYSCTL " to 0"); 920113787Simp val = 0; 921113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 922113787Simp } 923113787Simp} 924113787Simp 925107665Simp/* 926107665Simp * main 927107665Simp */ 928107665Simpint 929107665Simpmain(int argc, char **argv) 930107665Simp{ 931107665Simp int ch; 932107665Simp 933113787Simp check_devd_enabled(); 934152770Sjkoshy while ((ch = getopt(argc, argv, "Ddf:n")) != -1) { 935107665Simp switch (ch) { 936113790Simp case 'D': 937113790Simp Dflag++; 938113790Simp break; 939107665Simp case 'd': 940107665Simp dflag++; 941107665Simp break; 942152770Sjkoshy case 'f': 943152770Sjkoshy configfile = optarg; 944152770Sjkoshy break; 945113790Simp case 'n': 946113790Simp nflag++; 947113790Simp break; 948107665Simp default: 949107665Simp usage(); 950107665Simp } 951107665Simp } 952107665Simp 953107665Simp cfg.parse(); 954117246Simp if (!dflag && nflag) { 955155073Spjd cfg.open_pidfile(); 956107665Simp daemon(0, 0); 957155073Spjd cfg.write_pidfile(); 958117246Simp } 959146306Simp signal(SIGPIPE, SIG_IGN); 960107665Simp signal(SIGHUP, gensighand); 961107665Simp signal(SIGINT, gensighand); 962107665Simp signal(SIGTERM, gensighand); 963107665Simp event_loop(); 964107665Simp return (0); 965107665Simp} 966