devd.cc revision 259362
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 259362 2013-12-13 22:58:57Z asomers $"); 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 76250186Seadler#include <cctype> 77250186Seadler#include <cerrno> 78250186Seadler#include <cstdlib> 79250186Seadler#include <cstdio> 80250186Seadler#include <csignal> 81250186Seadler#include <cstring> 82252508Sasomers#include <cstdarg> 83250186Seadler 84107665Simp#include <dirent.h> 85107665Simp#include <err.h> 86107665Simp#include <fcntl.h> 87155073Spjd#include <libutil.h> 88209583Simp#include <paths.h> 89246121Sian#include <poll.h> 90108014Simp#include <regex.h> 91252481Sasomers#include <syslog.h> 92107665Simp#include <unistd.h> 93107665Simp 94108783Simp#include <algorithm> 95107665Simp#include <map> 96107665Simp#include <string> 97131397Simp#include <list> 98107665Simp#include <vector> 99107665Simp 100114086Simp#include "devd.h" /* C compatible definitions */ 101114086Simp#include "devd.hh" /* C++ class definitions */ 102107665Simp 103131397Simp#define PIPE "/var/run/devd.pipe" 104107665Simp#define CF "/etc/devd.conf" 105113787Simp#define SYSCTL "hw.bus.devctl_disable" 106107665Simp 107259339Sasomers/* 108259339Sasomers * Since the client socket is nonblocking, we must increase its send buffer to 109259339Sasomers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 110259339Sasomers * buffer, so the client can't increate the buffersize by itself. 111259339Sasomers * 112259339Sasomers * For example, when creating a ZFS pool, devd emits one 165 character 113259339Sasomers * resource.fs.zfs.statechange message for each vdev in the pool. A 64k 114259339Sasomers * buffer has enough space for almost 400 drives, which would be very large but 115259339Sasomers * not impossibly large pool. A 128k buffer has enough space for 794 drives, 116259339Sasomers * which is more than can fit in a rack with modern technology. 117259339Sasomers */ 118259339Sasomers#define CLIENT_BUFSIZE 131072 119259339Sasomers 120107665Simpusing namespace std; 121107665Simp 122107665Simpextern FILE *yyin; 123107665Simpextern int lineno; 124107665Simp 125121487Simpstatic const char notify = '!'; 126108783Simpstatic const char nomatch = '?'; 127108783Simpstatic const char attach = '+'; 128108783Simpstatic const char detach = '-'; 129108783Simp 130155073Spjdstatic struct pidfh *pfh; 131155073Spjd 132107665Simpint dflag; 133114000Simpint nflag; 134252482Sasomersstatic unsigned total_events = 0; 135252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0; 136247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 137107665Simp 138152770Sjkoshystatic const char *configfile = CF; 139152770Sjkoshy 140253046Sasomersstatic void devdlog(int priority, const char* message, ...) 141253046Sasomers __printflike(2, 3); 142107665Simpstatic void event_loop(void); 143107665Simpstatic void usage(void); 144107665Simp 145108783Simptemplate <class T> void 146108783Simpdelete_and_clear(vector<T *> &v) 147108783Simp{ 148108783Simp typename vector<T *>::const_iterator i; 149108783Simp 150243931Seadler for (i = v.begin(); i != v.end(); ++i) 151108783Simp delete *i; 152108783Simp v.clear(); 153108783Simp} 154108783Simp 155107665Simpconfig cfg; 156107665Simp 157107665Simpevent_proc::event_proc() : _prio(-1) 158107665Simp{ 159246134Sian _epsvec.reserve(4); 160107665Simp} 161107665Simp 162107665Simpevent_proc::~event_proc() 163107665Simp{ 164152406Sbland delete_and_clear(_epsvec); 165107665Simp} 166107665Simp 167107665Simpvoid 168107665Simpevent_proc::add(eps *eps) 169107665Simp{ 170107665Simp _epsvec.push_back(eps); 171107665Simp} 172107665Simp 173107665Simpbool 174243930Seadlerevent_proc::matches(config &c) const 175107665Simp{ 176107665Simp vector<eps *>::const_iterator i; 177107665Simp 178243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 179107665Simp if (!(*i)->do_match(c)) 180107665Simp return (false); 181107665Simp return (true); 182107665Simp} 183107665Simp 184107665Simpbool 185243930Seadlerevent_proc::run(config &c) const 186107665Simp{ 187107665Simp vector<eps *>::const_iterator i; 188252485Sasomers 189243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 190107665Simp if (!(*i)->do_action(c)) 191107665Simp return (false); 192107665Simp return (true); 193107665Simp} 194107665Simp 195107665Simpaction::action(const char *cmd) 196252485Sasomers : _cmd(cmd) 197107665Simp{ 198107665Simp // nothing 199107665Simp} 200107665Simp 201107665Simpaction::~action() 202107665Simp{ 203107665Simp // nothing 204107665Simp} 205107665Simp 206209583Simpstatic int 207209583Simpmy_system(const char *command) 208209583Simp{ 209209583Simp pid_t pid, savedpid; 210209583Simp int pstat; 211209583Simp struct sigaction ign, intact, quitact; 212209583Simp sigset_t newsigblock, oldsigblock; 213209583Simp 214209583Simp if (!command) /* just checking... */ 215252485Sasomers return (1); 216209583Simp 217209583Simp /* 218209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 219209583Simp * existing signal dispositions. 220209583Simp */ 221209583Simp ign.sa_handler = SIG_IGN; 222209583Simp ::sigemptyset(&ign.sa_mask); 223209583Simp ign.sa_flags = 0; 224209583Simp ::sigaction(SIGINT, &ign, &intact); 225209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 226209583Simp ::sigemptyset(&newsigblock); 227209583Simp ::sigaddset(&newsigblock, SIGCHLD); 228209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 229209583Simp switch (pid = ::fork()) { 230209583Simp case -1: /* error */ 231209583Simp break; 232209583Simp case 0: /* child */ 233209583Simp /* 234209583Simp * Restore original signal dispositions and exec the command. 235209583Simp */ 236209583Simp ::sigaction(SIGINT, &intact, NULL); 237209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 238209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 239209583Simp /* 240209583Simp * Close the PID file, and all other open descriptors. 241209583Simp * Inherit std{in,out,err} only. 242209583Simp */ 243209583Simp cfg.close_pidfile(); 244209583Simp ::closefrom(3); 245209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 246209583Simp ::_exit(127); 247209583Simp default: /* parent */ 248209583Simp savedpid = pid; 249209583Simp do { 250209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 251209583Simp } while (pid == -1 && errno == EINTR); 252209583Simp break; 253209583Simp } 254209583Simp ::sigaction(SIGINT, &intact, NULL); 255209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 256209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 257209583Simp return (pid == -1 ? -1 : pstat); 258209583Simp} 259209583Simp 260107665Simpbool 261108014Simpaction::do_action(config &c) 262107665Simp{ 263246134Sian string s = c.expand_string(_cmd.c_str()); 264257799Savg devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 265209583Simp my_system(s.c_str()); 266107665Simp return (true); 267107665Simp} 268107665Simp 269246134Sianmatch::match(config &c, const char *var, const char *re) : 270246134Sian _inv(re[0] == '!'), 271246134Sian _var(var), 272246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 273107665Simp{ 274154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 275107665Simp} 276107665Simp 277107665Simpmatch::~match() 278107665Simp{ 279108014Simp regfree(&_regex); 280107665Simp} 281107665Simp 282107665Simpbool 283108014Simpmatch::do_match(config &c) 284107665Simp{ 285210610Slulf const string &value = c.get_variable(_var); 286108014Simp bool retval; 287108014Simp 288252485Sasomers /* 289252481Sasomers * This function gets called WAY too often to justify calling syslog() 290252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 291252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 292252481Sasomers * log when we're in -d mode. 293252481Sasomers */ 294252481Sasomers if (dflag) { 295252481Sasomers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 296226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 297252481Sasomers } 298108783Simp 299108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 300226775Shrs if (_inv == 1) 301226775Shrs retval = (retval == 0) ? 1 : 0; 302226775Shrs 303252485Sasomers return (retval); 304107665Simp} 305107665Simp 306147874Simp#include <sys/sockio.h> 307147874Simp#include <net/if.h> 308147874Simp#include <net/if_media.h> 309147874Simp 310151486Sbrooksmedia::media(config &, const char *var, const char *type) 311147874Simp : _var(var), _type(-1) 312147874Simp{ 313147874Simp static struct ifmedia_description media_types[] = { 314147874Simp { IFM_ETHER, "Ethernet" }, 315147874Simp { IFM_TOKEN, "Tokenring" }, 316147874Simp { IFM_FDDI, "FDDI" }, 317147874Simp { IFM_IEEE80211, "802.11" }, 318147874Simp { IFM_ATM, "ATM" }, 319147874Simp { -1, "unknown" }, 320147874Simp { 0, NULL }, 321147874Simp }; 322243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 323147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 324147874Simp _type = media_types[i].ifmt_word; 325147874Simp break; 326147874Simp } 327147874Simp} 328147874Simp 329147874Simpmedia::~media() 330147874Simp{ 331147874Simp} 332147874Simp 333147874Simpbool 334147874Simpmedia::do_match(config &c) 335147874Simp{ 336150949Simp string value; 337147874Simp struct ifmediareq ifmr; 338147874Simp bool retval; 339147874Simp int s; 340147874Simp 341150949Simp // Since we can be called from both a device attach/detach 342150949Simp // context where device-name is defined and what we want, 343150949Simp // as well as from a link status context, where subsystem is 344150949Simp // the name of interest, first try device-name and fall back 345150949Simp // to subsystem if none exists. 346150949Simp value = c.get_variable("device-name"); 347247761Seadler if (value.empty()) 348151480Simp value = c.get_variable("subsystem"); 349252481Sasomers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 350147874Simp value.c_str(), _type); 351147874Simp 352147874Simp retval = false; 353147874Simp 354147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 355147874Simp if (s >= 0) { 356147874Simp memset(&ifmr, 0, sizeof(ifmr)); 357147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 358147874Simp 359147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 360147874Simp ifmr.ifm_status & IFM_AVALID) { 361252481Sasomers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 362147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 363147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 364147874Simp } else if (_type == -1) { 365252481Sasomers devdlog(LOG_DEBUG, "%s has unknown media type\n", 366147874Simp value.c_str()); 367147874Simp retval = true; 368147874Simp } 369147874Simp close(s); 370147874Simp } 371147874Simp 372252485Sasomers return (retval); 373147874Simp} 374147874Simp 375107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 376107665Simpconst string var_list::nothing = ""; 377107665Simp 378107665Simpconst string & 379107665Simpvar_list::get_variable(const string &var) const 380107665Simp{ 381107665Simp map<string, string>::const_iterator i; 382107665Simp 383107665Simp i = _vars.find(var); 384107665Simp if (i == _vars.end()) 385108783Simp return (var_list::bogus); 386107665Simp return (i->second); 387107665Simp} 388107665Simp 389107665Simpbool 390107665Simpvar_list::is_set(const string &var) const 391107665Simp{ 392107665Simp return (_vars.find(var) != _vars.end()); 393107665Simp} 394107665Simp 395107665Simpvoid 396107665Simpvar_list::set_variable(const string &var, const string &val) 397107665Simp{ 398252481Sasomers /* 399252481Sasomers * This function gets called WAY too often to justify calling syslog() 400252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 401252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 402252481Sasomers * log when we're in -d mode. 403252481Sasomers */ 404252481Sasomers if (dflag) 405252481Sasomers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 406107665Simp _vars[var] = val; 407107665Simp} 408107665Simp 409107665Simpvoid 410107665Simpconfig::reset(void) 411107665Simp{ 412107665Simp _dir_list.clear(); 413108783Simp delete_and_clear(_var_list_table); 414108783Simp delete_and_clear(_attach_list); 415108783Simp delete_and_clear(_detach_list); 416108783Simp delete_and_clear(_nomatch_list); 417121487Simp delete_and_clear(_notify_list); 418107665Simp} 419107665Simp 420107665Simpvoid 421107665Simpconfig::parse_one_file(const char *fn) 422107665Simp{ 423252481Sasomers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 424107665Simp yyin = fopen(fn, "r"); 425107665Simp if (yyin == NULL) 426107665Simp err(1, "Cannot open config file %s", fn); 427157746Smaxim lineno = 1; 428107665Simp if (yyparse() != 0) 429107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 430107665Simp fclose(yyin); 431107665Simp} 432107665Simp 433107665Simpvoid 434107665Simpconfig::parse_files_in_dir(const char *dirname) 435107665Simp{ 436107665Simp DIR *dirp; 437107665Simp struct dirent *dp; 438107665Simp char path[PATH_MAX]; 439107665Simp 440252481Sasomers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 441107665Simp dirp = opendir(dirname); 442107665Simp if (dirp == NULL) 443107665Simp return; 444107665Simp readdir(dirp); /* Skip . */ 445107665Simp readdir(dirp); /* Skip .. */ 446107665Simp while ((dp = readdir(dirp)) != NULL) { 447107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 448107665Simp snprintf(path, sizeof(path), "%s/%s", 449107665Simp dirname, dp->d_name); 450107665Simp parse_one_file(path); 451107665Simp } 452107665Simp } 453215607Skevlo closedir(dirp); 454107665Simp} 455107665Simp 456108783Simpclass epv_greater { 457108783Simppublic: 458243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 459108783Simp { 460108783Simp return (l1->get_priority() > l2->get_priority()); 461108783Simp } 462108783Simp}; 463108783Simp 464107665Simpvoid 465108783Simpconfig::sort_vector(vector<event_proc *> &v) 466108783Simp{ 467243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 468108783Simp} 469108783Simp 470108783Simpvoid 471107665Simpconfig::parse(void) 472107665Simp{ 473107665Simp vector<string>::const_iterator i; 474107665Simp 475152770Sjkoshy parse_one_file(configfile); 476243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 477107665Simp parse_files_in_dir((*i).c_str()); 478108783Simp sort_vector(_attach_list); 479108783Simp sort_vector(_detach_list); 480108783Simp sort_vector(_nomatch_list); 481121487Simp sort_vector(_notify_list); 482107665Simp} 483107665Simp 484107665Simpvoid 485155073Spjdconfig::open_pidfile() 486107665Simp{ 487155073Spjd pid_t otherpid; 488252485Sasomers 489247758Seadler if (_pidfile.empty()) 490107665Simp return; 491155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 492155073Spjd if (pfh == NULL) { 493155073Spjd if (errno == EEXIST) 494155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 495155073Spjd warn("cannot open pid file"); 496155073Spjd } 497107665Simp} 498107665Simp 499107665Simpvoid 500155073Spjdconfig::write_pidfile() 501155073Spjd{ 502252485Sasomers 503155073Spjd pidfile_write(pfh); 504155073Spjd} 505155073Spjd 506155073Spjdvoid 507209583Simpconfig::close_pidfile() 508209583Simp{ 509252485Sasomers 510209583Simp pidfile_close(pfh); 511209583Simp} 512209583Simp 513209583Simpvoid 514155073Spjdconfig::remove_pidfile() 515155073Spjd{ 516252485Sasomers 517155073Spjd pidfile_remove(pfh); 518155073Spjd} 519155073Spjd 520155073Spjdvoid 521107665Simpconfig::add_attach(int prio, event_proc *p) 522107665Simp{ 523107665Simp p->set_priority(prio); 524107665Simp _attach_list.push_back(p); 525107665Simp} 526107665Simp 527107665Simpvoid 528107665Simpconfig::add_detach(int prio, event_proc *p) 529107665Simp{ 530107665Simp p->set_priority(prio); 531107665Simp _detach_list.push_back(p); 532107665Simp} 533107665Simp 534107665Simpvoid 535107665Simpconfig::add_directory(const char *dir) 536107665Simp{ 537107665Simp _dir_list.push_back(string(dir)); 538107665Simp} 539107665Simp 540107665Simpvoid 541107665Simpconfig::add_nomatch(int prio, event_proc *p) 542107665Simp{ 543107665Simp p->set_priority(prio); 544107665Simp _nomatch_list.push_back(p); 545107665Simp} 546107665Simp 547107665Simpvoid 548121487Simpconfig::add_notify(int prio, event_proc *p) 549121487Simp{ 550121487Simp p->set_priority(prio); 551121487Simp _notify_list.push_back(p); 552121487Simp} 553121487Simp 554121487Simpvoid 555107665Simpconfig::set_pidfile(const char *fn) 556107665Simp{ 557247758Seadler _pidfile = fn; 558107665Simp} 559107665Simp 560107665Simpvoid 561107665Simpconfig::push_var_table() 562107665Simp{ 563107665Simp var_list *vl; 564252485Sasomers 565107665Simp vl = new var_list(); 566107665Simp _var_list_table.push_back(vl); 567252481Sasomers devdlog(LOG_DEBUG, "Pushing table\n"); 568107665Simp} 569107665Simp 570107665Simpvoid 571107665Simpconfig::pop_var_table() 572107665Simp{ 573107665Simp delete _var_list_table.back(); 574107665Simp _var_list_table.pop_back(); 575252481Sasomers devdlog(LOG_DEBUG, "Popping table\n"); 576107665Simp} 577107665Simp 578107665Simpvoid 579107665Simpconfig::set_variable(const char *var, const char *val) 580107665Simp{ 581107665Simp _var_list_table.back()->set_variable(var, val); 582107665Simp} 583107665Simp 584107665Simpconst string & 585107665Simpconfig::get_variable(const string &var) 586107665Simp{ 587107665Simp vector<var_list *>::reverse_iterator i; 588107665Simp 589243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 590107665Simp if ((*i)->is_set(var)) 591108783Simp return ((*i)->get_variable(var)); 592107665Simp } 593107665Simp return (var_list::nothing); 594107665Simp} 595107665Simp 596108783Simpbool 597243930Seadlerconfig::is_id_char(char ch) const 598108783Simp{ 599252485Sasomers return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 600108783Simp ch == '-')); 601108783Simp} 602108783Simp 603108014Simpvoid 604114081Simpconfig::expand_one(const char *&src, string &dst) 605107665Simp{ 606108014Simp int count; 607210610Slulf string buffer; 608108014Simp 609108783Simp src++; 610108014Simp // $$ -> $ 611108014Simp if (*src == '$') { 612247762Seadler dst += *src++; 613108014Simp return; 614108014Simp } 615252485Sasomers 616108014Simp // $(foo) -> $(foo) 617108783Simp // Not sure if I want to support this or not, so for now we just pass 618108783Simp // it through. 619108014Simp if (*src == '(') { 620247762Seadler dst += '$'; 621108014Simp count = 1; 622114081Simp /* If the string ends before ) is matched , return. */ 623114081Simp while (count > 0 && *src) { 624108014Simp if (*src == ')') 625108014Simp count--; 626108014Simp else if (*src == '(') 627108014Simp count++; 628247762Seadler dst += *src++; 629108014Simp } 630108014Simp return; 631108014Simp } 632252485Sasomers 633247763Seadler // $[^A-Za-z] -> $\1 634108014Simp if (!isalpha(*src)) { 635247762Seadler dst += '$'; 636247762Seadler dst += *src++; 637108014Simp return; 638108014Simp } 639108014Simp 640108014Simp // $var -> replace with value 641114081Simp do { 642247762Seadler buffer += *src++; 643114084Simp } while (is_id_char(*src)); 644247758Seadler dst.append(get_variable(buffer)); 645107665Simp} 646107665Simp 647108014Simpconst string 648246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 649108014Simp{ 650246134Sian const char *var_at; 651114081Simp string dst; 652108014Simp 653246134Sian /* 654246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 655246134Sian * while parsing config files, as tested on 2013-01-30. 656246134Sian */ 657246134Sian dst.reserve(128); 658246134Sian 659246134Sian if (prepend != NULL) 660246134Sian dst = prepend; 661246134Sian 662246134Sian for (;;) { 663246134Sian var_at = strchr(src, '$'); 664246134Sian if (var_at == NULL) { 665246134Sian dst.append(src); 666246134Sian break; 667246134Sian } 668246134Sian dst.append(src, var_at - src); 669246134Sian src = var_at; 670246134Sian expand_one(src, dst); 671108014Simp } 672108014Simp 673246134Sian if (append != NULL) 674246134Sian dst.append(append); 675246134Sian 676114081Simp return (dst); 677108014Simp} 678108014Simp 679108783Simpbool 680247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 681108783Simp{ 682108783Simp char *walker; 683252485Sasomers 684108783Simp if (*buffer == '\0') 685108783Simp return (false); 686108783Simp walker = lhs = buffer; 687108783Simp while (is_id_char(*walker)) 688108783Simp walker++; 689108783Simp if (*walker != '=') 690108783Simp return (false); 691108783Simp walker++; // skip = 692108783Simp if (*walker == '"') { 693108783Simp walker++; // skip " 694108783Simp rhs = walker; 695108783Simp while (*walker && *walker != '"') 696108783Simp walker++; 697108783Simp if (*walker != '"') 698108783Simp return (false); 699108783Simp rhs[-2] = '\0'; 700108783Simp *walker++ = '\0'; 701108783Simp } else { 702108783Simp rhs = walker; 703108783Simp while (*walker && !isspace(*walker)) 704108783Simp walker++; 705108783Simp if (*walker != '\0') 706108783Simp *walker++ = '\0'; 707108783Simp rhs[-1] = '\0'; 708108783Simp } 709113785Simp while (isspace(*walker)) 710113785Simp walker++; 711108783Simp buffer = walker; 712108783Simp return (true); 713108783Simp} 714108783Simp 715108783Simp 716108783Simpchar * 717108783Simpconfig::set_vars(char *buffer) 718108783Simp{ 719108783Simp char *lhs; 720108783Simp char *rhs; 721108783Simp 722108783Simp while (1) { 723108783Simp if (!chop_var(buffer, lhs, rhs)) 724108783Simp break; 725108783Simp set_variable(lhs, rhs); 726108783Simp } 727108783Simp return (buffer); 728108783Simp} 729108783Simp 730108783Simpvoid 731108783Simpconfig::find_and_execute(char type) 732108783Simp{ 733108783Simp vector<event_proc *> *l; 734108783Simp vector<event_proc *>::const_iterator i; 735151486Sbrooks const char *s; 736108783Simp 737108783Simp switch (type) { 738108783Simp default: 739108783Simp return; 740121487Simp case notify: 741121487Simp l = &_notify_list; 742121487Simp s = "notify"; 743121487Simp break; 744108783Simp case nomatch: 745108783Simp l = &_nomatch_list; 746108783Simp s = "nomatch"; 747108783Simp break; 748108783Simp case attach: 749108783Simp l = &_attach_list; 750108783Simp s = "attach"; 751108783Simp break; 752108783Simp case detach: 753108783Simp l = &_detach_list; 754108783Simp s = "detach"; 755108783Simp break; 756108783Simp } 757252481Sasomers devdlog(LOG_DEBUG, "Processing %s event\n", s); 758243931Seadler for (i = l->begin(); i != l->end(); ++i) { 759108783Simp if ((*i)->matches(*this)) { 760108783Simp (*i)->run(*this); 761108783Simp break; 762108783Simp } 763108783Simp } 764108783Simp 765108783Simp} 766108783Simp 767252485Sasomers 768107665Simpstatic void 769108783Simpprocess_event(char *buffer) 770107665Simp{ 771107665Simp char type; 772107665Simp char *sp; 773107665Simp 774108783Simp sp = buffer + 1; 775259362Sasomers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 776107665Simp type = *buffer++; 777108783Simp cfg.push_var_table(); 778108783Simp // No match doesn't have a device, and the format is a little 779108783Simp // different, so handle it separately. 780121487Simp switch (type) { 781121487Simp case notify: 782121487Simp sp = cfg.set_vars(sp); 783121487Simp break; 784121487Simp case nomatch: 785145218Simp //? at location pnp-info on bus 786145218Simp sp = strchr(sp, ' '); 787145218Simp if (sp == NULL) 788145218Simp return; /* Can't happen? */ 789145218Simp *sp++ = '\0'; 790213646Simp while (isspace(*sp)) 791213646Simp sp++; 792121487Simp if (strncmp(sp, "at ", 3) == 0) 793121487Simp sp += 3; 794121487Simp sp = cfg.set_vars(sp); 795213646Simp while (isspace(*sp)) 796213646Simp sp++; 797121487Simp if (strncmp(sp, "on ", 3) == 0) 798121487Simp cfg.set_variable("bus", sp + 3); 799121487Simp break; 800121487Simp case attach: /*FALLTHROUGH*/ 801121487Simp case detach: 802108783Simp sp = strchr(sp, ' '); 803108783Simp if (sp == NULL) 804108783Simp return; /* Can't happen? */ 805108783Simp *sp++ = '\0'; 806108783Simp cfg.set_variable("device-name", buffer); 807213646Simp while (isspace(*sp)) 808213646Simp sp++; 809113785Simp if (strncmp(sp, "at ", 3) == 0) 810113785Simp sp += 3; 811113785Simp sp = cfg.set_vars(sp); 812213646Simp while (isspace(*sp)) 813213646Simp sp++; 814113785Simp if (strncmp(sp, "on ", 3) == 0) 815113785Simp cfg.set_variable("bus", sp + 3); 816121487Simp break; 817108783Simp } 818252485Sasomers 819108783Simp cfg.find_and_execute(type); 820108783Simp cfg.pop_var_table(); 821107665Simp} 822107665Simp 823131397Simpint 824131397Simpcreate_socket(const char *name) 825131397Simp{ 826131397Simp int fd, slen; 827131397Simp struct sockaddr_un sun; 828131397Simp 829131397Simp if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 830131397Simp err(1, "socket"); 831131397Simp bzero(&sun, sizeof(sun)); 832131397Simp sun.sun_family = AF_UNIX; 833131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 834131397Simp slen = SUN_LEN(&sun); 835131397Simp unlink(name); 836147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 837147973Smarcus err(1, "fcntl"); 838236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 839131397Simp err(1, "bind"); 840131397Simp listen(fd, 4); 841147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 842147973Smarcus chmod(name, 0666); 843131397Simp return (fd); 844131397Simp} 845131397Simp 846246121Sianunsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 847246121Sianunsigned int num_clients; 848131397Simplist<int> clients; 849131397Simp 850131397Simpvoid 851131397Simpnotify_clients(const char *data, int len) 852131397Simp{ 853246121Sian list<int>::iterator i; 854131397Simp 855246121Sian /* 856246121Sian * Deliver the data to all clients. Throw clients overboard at the 857246121Sian * first sign of trouble. This reaps clients who've died or closed 858246121Sian * their sockets, and also clients who are alive but failing to keep up 859246121Sian * (or who are maliciously not reading, to consume buffer space in 860246121Sian * kernel memory or tie up the limited number of available connections). 861246121Sian */ 862246121Sian for (i = clients.begin(); i != clients.end(); ) { 863246121Sian if (write(*i, data, len) != len) { 864246121Sian --num_clients; 865131397Simp close(*i); 866246121Sian i = clients.erase(i); 867252481Sasomers devdlog(LOG_WARNING, "notify_clients: write() failed; " 868252481Sasomers "dropping unresponsive client\n"); 869246121Sian } else 870246121Sian ++i; 871131397Simp } 872246121Sian} 873131397Simp 874246121Sianvoid 875246121Siancheck_clients(void) 876246121Sian{ 877246121Sian int s; 878246121Sian struct pollfd pfd; 879246121Sian list<int>::iterator i; 880246121Sian 881246121Sian /* 882246121Sian * Check all existing clients to see if any of them have disappeared. 883246121Sian * Normally we reap clients when we get an error trying to send them an 884246121Sian * event. This check eliminates the problem of an ever-growing list of 885246121Sian * zombie clients because we're never writing to them on a system 886246121Sian * without frequent device-change activity. 887246121Sian */ 888246121Sian pfd.events = 0; 889246121Sian for (i = clients.begin(); i != clients.end(); ) { 890246121Sian pfd.fd = *i; 891246121Sian s = poll(&pfd, 1, 0); 892246121Sian if ((s < 0 && s != EINTR ) || 893246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 894246121Sian --num_clients; 895246121Sian close(*i); 896246121Sian i = clients.erase(i); 897252481Sasomers devdlog(LOG_NOTICE, "check_clients: " 898252481Sasomers "dropping disconnected client\n"); 899246121Sian } else 900246121Sian ++i; 901246121Sian } 902131397Simp} 903131397Simp 904131397Simpvoid 905131397Simpnew_client(int fd) 906131397Simp{ 907131397Simp int s; 908259339Sasomers int sndbuf_size; 909131397Simp 910246121Sian /* 911246121Sian * First go reap any zombie clients, then accept the connection, and 912246121Sian * shut down the read side to stop clients from consuming kernel memory 913246121Sian * by sending large buffers full of data we'll never read. 914246121Sian */ 915246121Sian check_clients(); 916131397Simp s = accept(fd, NULL, NULL); 917246121Sian if (s != -1) { 918259339Sasomers sndbuf_size = CLIENT_BUFSIZE; 919259339Sasomers if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 920259339Sasomers sizeof(sndbuf_size))) 921259339Sasomers err(1, "setsockopt"); 922246121Sian shutdown(s, SHUT_RD); 923131397Simp clients.push_back(s); 924246121Sian ++num_clients; 925259339Sasomers } else 926259339Sasomers err(1, "accept"); 927131397Simp} 928131397Simp 929107665Simpstatic void 930107665Simpevent_loop(void) 931107665Simp{ 932107665Simp int rv; 933107665Simp int fd; 934107665Simp char buffer[DEVCTL_MAXBUF]; 935113790Simp int once = 0; 936131397Simp int server_fd, max_fd; 937246121Sian int accepting; 938113790Simp timeval tv; 939113790Simp fd_set fds; 940107665Simp 941240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 942107665Simp if (fd == -1) 943131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 944131397Simp server_fd = create_socket(PIPE); 945246121Sian accepting = 1; 946131397Simp max_fd = max(fd, server_fd) + 1; 947247756Seadler while (!romeo_must_die) { 948113790Simp if (!once && !dflag && !nflag) { 949113790Simp // Check to see if we have any events pending. 950113790Simp tv.tv_sec = 0; 951113790Simp tv.tv_usec = 0; 952113790Simp FD_ZERO(&fds); 953113790Simp FD_SET(fd, &fds); 954113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 955113790Simp // No events -> we've processed all pending events 956117944Simp if (rv == 0) { 957252481Sasomers devdlog(LOG_DEBUG, "Calling daemon\n"); 958155073Spjd cfg.remove_pidfile(); 959155073Spjd cfg.open_pidfile(); 960113790Simp daemon(0, 0); 961155073Spjd cfg.write_pidfile(); 962113790Simp once++; 963113790Simp } 964113790Simp } 965246121Sian /* 966246121Sian * When we've already got the max number of clients, stop 967246121Sian * accepting new connections (don't put server_fd in the set), 968246121Sian * shrink the accept() queue to reject connections quickly, and 969246121Sian * poll the existing clients more often, so that we notice more 970246121Sian * quickly when any of them disappear to free up client slots. 971246121Sian */ 972131397Simp FD_ZERO(&fds); 973131397Simp FD_SET(fd, &fds); 974246121Sian if (num_clients < max_clients) { 975246121Sian if (!accepting) { 976246121Sian listen(server_fd, max_clients); 977246121Sian accepting = 1; 978246121Sian } 979246121Sian FD_SET(server_fd, &fds); 980246121Sian tv.tv_sec = 60; 981246121Sian tv.tv_usec = 0; 982246121Sian } else { 983246121Sian if (accepting) { 984246121Sian listen(server_fd, 0); 985246121Sian accepting = 0; 986246121Sian } 987246121Sian tv.tv_sec = 2; 988246121Sian tv.tv_usec = 0; 989246121Sian } 990246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 991252482Sasomers if (got_siginfo) { 992259362Sasomers devdlog(LOG_NOTICE, "Events received so far=%u\n", 993252482Sasomers total_events); 994252482Sasomers got_siginfo = 0; 995252482Sasomers } 996131397Simp if (rv == -1) { 997131397Simp if (errno == EINTR) 998131397Simp continue; 999131397Simp err(1, "select"); 1000246121Sian } else if (rv == 0) 1001246121Sian check_clients(); 1002131397Simp if (FD_ISSET(fd, &fds)) { 1003131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 1004131397Simp if (rv > 0) { 1005252482Sasomers total_events++; 1006252481Sasomers if (rv == sizeof(buffer) - 1) { 1007252481Sasomers devdlog(LOG_WARNING, "Warning: " 1008252481Sasomers "available event data exceeded " 1009252481Sasomers "buffer space\n"); 1010252481Sasomers } 1011131397Simp notify_clients(buffer, rv); 1012107665Simp buffer[rv] = '\0'; 1013131397Simp while (buffer[--rv] == '\n') 1014131397Simp buffer[rv] = '\0'; 1015131397Simp process_event(buffer); 1016131397Simp } else if (rv < 0) { 1017131397Simp if (errno != EINTR) 1018131397Simp break; 1019131397Simp } else { 1020131397Simp /* EOF */ 1021107665Simp break; 1022131397Simp } 1023107665Simp } 1024131397Simp if (FD_ISSET(server_fd, &fds)) 1025131397Simp new_client(server_fd); 1026107665Simp } 1027107665Simp close(fd); 1028107665Simp} 1029252485Sasomers 1030107665Simp/* 1031107665Simp * functions that the parser uses. 1032107665Simp */ 1033107665Simpvoid 1034107665Simpadd_attach(int prio, event_proc *p) 1035107665Simp{ 1036107665Simp cfg.add_attach(prio, p); 1037107665Simp} 1038107665Simp 1039107665Simpvoid 1040107665Simpadd_detach(int prio, event_proc *p) 1041107665Simp{ 1042107665Simp cfg.add_detach(prio, p); 1043107665Simp} 1044107665Simp 1045107665Simpvoid 1046107665Simpadd_directory(const char *dir) 1047107665Simp{ 1048107665Simp cfg.add_directory(dir); 1049107665Simp free(const_cast<char *>(dir)); 1050107665Simp} 1051107665Simp 1052107665Simpvoid 1053107665Simpadd_nomatch(int prio, event_proc *p) 1054107665Simp{ 1055107665Simp cfg.add_nomatch(prio, p); 1056107665Simp} 1057107665Simp 1058121487Simpvoid 1059121487Simpadd_notify(int prio, event_proc *p) 1060121487Simp{ 1061121487Simp cfg.add_notify(prio, p); 1062121487Simp} 1063121487Simp 1064107665Simpevent_proc * 1065107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1066107665Simp{ 1067107665Simp if (ep == NULL) 1068107665Simp ep = new event_proc(); 1069107665Simp ep->add(eps); 1070107665Simp return (ep); 1071107665Simp} 1072107665Simp 1073107665Simpeps * 1074107665Simpnew_action(const char *cmd) 1075107665Simp{ 1076107665Simp eps *e = new action(cmd); 1077107665Simp free(const_cast<char *>(cmd)); 1078107665Simp return (e); 1079107665Simp} 1080107665Simp 1081107665Simpeps * 1082107665Simpnew_match(const char *var, const char *re) 1083107665Simp{ 1084108014Simp eps *e = new match(cfg, var, re); 1085107665Simp free(const_cast<char *>(var)); 1086107665Simp free(const_cast<char *>(re)); 1087107665Simp return (e); 1088107665Simp} 1089107665Simp 1090147874Simpeps * 1091147874Simpnew_media(const char *var, const char *re) 1092147874Simp{ 1093147874Simp eps *e = new media(cfg, var, re); 1094147874Simp free(const_cast<char *>(var)); 1095147874Simp free(const_cast<char *>(re)); 1096147874Simp return (e); 1097147874Simp} 1098147874Simp 1099107665Simpvoid 1100107665Simpset_pidfile(const char *name) 1101107665Simp{ 1102107665Simp cfg.set_pidfile(name); 1103107665Simp free(const_cast<char *>(name)); 1104107665Simp} 1105107665Simp 1106107665Simpvoid 1107107665Simpset_variable(const char *var, const char *val) 1108107665Simp{ 1109107665Simp cfg.set_variable(var, val); 1110107665Simp free(const_cast<char *>(var)); 1111107665Simp free(const_cast<char *>(val)); 1112107665Simp} 1113107665Simp 1114107665Simp 1115252485Sasomers 1116107665Simpstatic void 1117107665Simpgensighand(int) 1118107665Simp{ 1119247754Seadler romeo_must_die = 1; 1120107665Simp} 1121107665Simp 1122252481Sasomers/* 1123252482Sasomers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1124252482Sasomers * as appropriate 1125252482Sasomers */ 1126252482Sasomersstatic void 1127252482Sasomerssiginfohand(int) 1128252482Sasomers{ 1129252482Sasomers got_siginfo = 1; 1130252482Sasomers} 1131252482Sasomers 1132252482Sasomers/* 1133257798Savg * Local logging function. Prints to syslog if we're daemonized; stderr 1134252481Sasomers * otherwise. 1135252481Sasomers */ 1136107665Simpstatic void 1137252481Sasomersdevdlog(int priority, const char* fmt, ...) 1138252481Sasomers{ 1139252481Sasomers va_list argp; 1140252481Sasomers 1141252481Sasomers va_start(argp, fmt); 1142252481Sasomers if (dflag) 1143252481Sasomers vfprintf(stderr, fmt, argp); 1144252481Sasomers else 1145252481Sasomers vsyslog(priority, fmt, argp); 1146252481Sasomers va_end(argp); 1147252481Sasomers} 1148252481Sasomers 1149252481Sasomersstatic void 1150107665Simpusage() 1151107665Simp{ 1152252481Sasomers fprintf(stderr, "usage: %s [-dn] [-l connlimit] [-f file]\n", 1153246121Sian getprogname()); 1154107665Simp exit(1); 1155107665Simp} 1156107665Simp 1157113787Simpstatic void 1158113787Simpcheck_devd_enabled() 1159113787Simp{ 1160113787Simp int val = 0; 1161113787Simp size_t len; 1162113787Simp 1163113787Simp len = sizeof(val); 1164114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1165113787Simp errx(1, "devctl sysctl missing from kernel!"); 1166113787Simp if (val) { 1167113787Simp warnx("Setting " SYSCTL " to 0"); 1168113787Simp val = 0; 1169113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1170113787Simp } 1171113787Simp} 1172113787Simp 1173107665Simp/* 1174107665Simp * main 1175107665Simp */ 1176107665Simpint 1177107665Simpmain(int argc, char **argv) 1178107665Simp{ 1179107665Simp int ch; 1180107665Simp 1181113787Simp check_devd_enabled(); 1182252481Sasomers while ((ch = getopt(argc, argv, "df:l:n")) != -1) { 1183107665Simp switch (ch) { 1184107665Simp case 'd': 1185107665Simp dflag++; 1186107665Simp break; 1187152770Sjkoshy case 'f': 1188152770Sjkoshy configfile = optarg; 1189152770Sjkoshy break; 1190246121Sian case 'l': 1191246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1192246121Sian break; 1193113790Simp case 'n': 1194113790Simp nflag++; 1195113790Simp break; 1196107665Simp default: 1197107665Simp usage(); 1198107665Simp } 1199107665Simp } 1200107665Simp 1201107665Simp cfg.parse(); 1202117246Simp if (!dflag && nflag) { 1203155073Spjd cfg.open_pidfile(); 1204107665Simp daemon(0, 0); 1205155073Spjd cfg.write_pidfile(); 1206117246Simp } 1207146306Simp signal(SIGPIPE, SIG_IGN); 1208107665Simp signal(SIGHUP, gensighand); 1209107665Simp signal(SIGINT, gensighand); 1210107665Simp signal(SIGTERM, gensighand); 1211252482Sasomers signal(SIGINFO, siginfohand); 1212107665Simp event_loop(); 1213107665Simp return (0); 1214107665Simp} 1215