1107665Simp/*- 2209583Simp * Copyright (c) 2002-2010 M. Warner Losh. 3107665Simp * All rights reserved. 4107665Simp * 5107665Simp * Redistribution and use in source and binary forms, with or without 6107665Simp * modification, are permitted provided that the following conditions 7107665Simp * are met: 8107665Simp * 1. Redistributions of source code must retain the above copyright 9107665Simp * notice, this list of conditions and the following disclaimer. 10107665Simp * 2. Redistributions in binary form must reproduce the above copyright 11107665Simp * notice, this list of conditions and the following disclaimer in the 12107665Simp * documentation and/or other materials provided with the distribution. 13107665Simp * 14107665Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15107665Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16107665Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17107665Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18107665Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19107665Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20107665Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21107665Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22107665Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23107665Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24107665Simp * SUCH DAMAGE. 25209583Simp * 26209583Simp * my_system is a variation on lib/libc/stdlib/system.c: 27209583Simp * 28209583Simp * Copyright (c) 1988, 1993 29209583Simp * The Regents of the University of California. All rights reserved. 30209583Simp * 31209583Simp * Redistribution and use in source and binary forms, with or without 32209583Simp * modification, are permitted provided that the following conditions 33209583Simp * are met: 34209583Simp * 1. Redistributions of source code must retain the above copyright 35209583Simp * notice, this list of conditions and the following disclaimer. 36209583Simp * 2. Redistributions in binary form must reproduce the above copyright 37209583Simp * notice, this list of conditions and the following disclaimer in the 38209583Simp * documentation and/or other materials provided with the distribution. 39209583Simp * 4. Neither the name of the University nor the names of its contributors 40209583Simp * may be used to endorse or promote products derived from this software 41209583Simp * without specific prior written permission. 42209583Simp * 43209583Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44209583Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45209583Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46209583Simp * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47209583Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48209583Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49209583Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50209583Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51209583Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52209583Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53209583Simp * SUCH DAMAGE. 54107665Simp */ 55107665Simp 56107665Simp/* 57107665Simp * DEVD control daemon. 58107665Simp */ 59107665Simp 60107665Simp// TODO list: 61107665Simp// o devd.conf and devd man pages need a lot of help: 62131397Simp// - devd needs to document the unix domain socket 63107665Simp// - devd.conf needs more details on the supported statements. 64107665Simp 65107665Simp#include <sys/cdefs.h> 66107665Simp__FBSDID("$FreeBSD$"); 67107665Simp 68107665Simp#include <sys/param.h> 69131397Simp#include <sys/socket.h> 70131397Simp#include <sys/stat.h> 71131397Simp#include <sys/sysctl.h> 72107665Simp#include <sys/types.h> 73209583Simp#include <sys/wait.h> 74131397Simp#include <sys/un.h> 75107665Simp 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 103270242Sasomers#define STREAMPIPE "/var/run/devd.pipe" 104270242Sasomers#define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 105107665Simp#define CF "/etc/devd.conf" 106263963Smjg#define SYSCTL "hw.bus.devctl_queue" 107107665Simp 108260519Sasomers/* 109260519Sasomers * Since the client socket is nonblocking, we must increase its send buffer to 110260519Sasomers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 111260519Sasomers * buffer, so the client can't increate the buffersize by itself. 112260519Sasomers * 113260519Sasomers * For example, when creating a ZFS pool, devd emits one 165 character 114260519Sasomers * resource.fs.zfs.statechange message for each vdev in the pool. A 64k 115260519Sasomers * buffer has enough space for almost 400 drives, which would be very large but 116260519Sasomers * not impossibly large pool. A 128k buffer has enough space for 794 drives, 117260519Sasomers * which is more than can fit in a rack with modern technology. 118260519Sasomers */ 119260519Sasomers#define CLIENT_BUFSIZE 131072 120260519Sasomers 121107665Simpusing namespace std; 122107665Simp 123270242Sasomerstypedef struct client { 124270242Sasomers int fd; 125270242Sasomers int socktype; 126270242Sasomers} client_t; 127270242Sasomers 128107665Simpextern FILE *yyin; 129107665Simpextern int lineno; 130107665Simp 131121487Simpstatic const char notify = '!'; 132108783Simpstatic const char nomatch = '?'; 133108783Simpstatic const char attach = '+'; 134108783Simpstatic const char detach = '-'; 135108783Simp 136155073Spjdstatic struct pidfh *pfh; 137155073Spjd 138263880Sasomersstatic int no_daemon = 0; 139263880Sasomersstatic int daemonize_quick = 0; 140263880Sasomersstatic int quiet_mode = 0; 141252482Sasomersstatic unsigned total_events = 0; 142252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0; 143247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 144107665Simp 145152770Sjkoshystatic const char *configfile = CF; 146152770Sjkoshy 147253046Sasomersstatic void devdlog(int priority, const char* message, ...) 148253046Sasomers __printflike(2, 3); 149107665Simpstatic void event_loop(void); 150107665Simpstatic void usage(void); 151107665Simp 152108783Simptemplate <class T> void 153108783Simpdelete_and_clear(vector<T *> &v) 154108783Simp{ 155108783Simp typename vector<T *>::const_iterator i; 156108783Simp 157243931Seadler for (i = v.begin(); i != v.end(); ++i) 158108783Simp delete *i; 159108783Simp v.clear(); 160108783Simp} 161108783Simp 162107665Simpconfig cfg; 163107665Simp 164107665Simpevent_proc::event_proc() : _prio(-1) 165107665Simp{ 166246134Sian _epsvec.reserve(4); 167107665Simp} 168107665Simp 169107665Simpevent_proc::~event_proc() 170107665Simp{ 171152406Sbland delete_and_clear(_epsvec); 172107665Simp} 173107665Simp 174107665Simpvoid 175107665Simpevent_proc::add(eps *eps) 176107665Simp{ 177107665Simp _epsvec.push_back(eps); 178107665Simp} 179107665Simp 180107665Simpbool 181243930Seadlerevent_proc::matches(config &c) const 182107665Simp{ 183107665Simp vector<eps *>::const_iterator i; 184107665Simp 185243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 186107665Simp if (!(*i)->do_match(c)) 187107665Simp return (false); 188107665Simp return (true); 189107665Simp} 190107665Simp 191107665Simpbool 192243930Seadlerevent_proc::run(config &c) const 193107665Simp{ 194107665Simp vector<eps *>::const_iterator i; 195252485Sasomers 196243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 197107665Simp if (!(*i)->do_action(c)) 198107665Simp return (false); 199107665Simp return (true); 200107665Simp} 201107665Simp 202107665Simpaction::action(const char *cmd) 203252485Sasomers : _cmd(cmd) 204107665Simp{ 205107665Simp // nothing 206107665Simp} 207107665Simp 208107665Simpaction::~action() 209107665Simp{ 210107665Simp // nothing 211107665Simp} 212107665Simp 213209583Simpstatic int 214209583Simpmy_system(const char *command) 215209583Simp{ 216209583Simp pid_t pid, savedpid; 217209583Simp int pstat; 218209583Simp struct sigaction ign, intact, quitact; 219209583Simp sigset_t newsigblock, oldsigblock; 220209583Simp 221209583Simp if (!command) /* just checking... */ 222252485Sasomers return (1); 223209583Simp 224209583Simp /* 225209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 226209583Simp * existing signal dispositions. 227209583Simp */ 228209583Simp ign.sa_handler = SIG_IGN; 229209583Simp ::sigemptyset(&ign.sa_mask); 230209583Simp ign.sa_flags = 0; 231209583Simp ::sigaction(SIGINT, &ign, &intact); 232209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 233209583Simp ::sigemptyset(&newsigblock); 234209583Simp ::sigaddset(&newsigblock, SIGCHLD); 235209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 236209583Simp switch (pid = ::fork()) { 237209583Simp case -1: /* error */ 238209583Simp break; 239209583Simp case 0: /* child */ 240209583Simp /* 241209583Simp * Restore original signal dispositions and exec the command. 242209583Simp */ 243209583Simp ::sigaction(SIGINT, &intact, NULL); 244209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 245209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 246209583Simp /* 247209583Simp * Close the PID file, and all other open descriptors. 248209583Simp * Inherit std{in,out,err} only. 249209583Simp */ 250209583Simp cfg.close_pidfile(); 251209583Simp ::closefrom(3); 252209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 253209583Simp ::_exit(127); 254209583Simp default: /* parent */ 255209583Simp savedpid = pid; 256209583Simp do { 257209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 258209583Simp } while (pid == -1 && errno == EINTR); 259209583Simp break; 260209583Simp } 261209583Simp ::sigaction(SIGINT, &intact, NULL); 262209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 263209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 264209583Simp return (pid == -1 ? -1 : pstat); 265209583Simp} 266209583Simp 267107665Simpbool 268108014Simpaction::do_action(config &c) 269107665Simp{ 270246134Sian string s = c.expand_string(_cmd.c_str()); 271252481Sasomers devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str()); 272209583Simp my_system(s.c_str()); 273107665Simp return (true); 274107665Simp} 275107665Simp 276246134Sianmatch::match(config &c, const char *var, const char *re) : 277246134Sian _inv(re[0] == '!'), 278246134Sian _var(var), 279246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 280107665Simp{ 281154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 282107665Simp} 283107665Simp 284107665Simpmatch::~match() 285107665Simp{ 286108014Simp regfree(&_regex); 287107665Simp} 288107665Simp 289107665Simpbool 290108014Simpmatch::do_match(config &c) 291107665Simp{ 292210610Slulf const string &value = c.get_variable(_var); 293108014Simp bool retval; 294108014Simp 295252485Sasomers /* 296252481Sasomers * This function gets called WAY too often to justify calling syslog() 297252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 298252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 299252481Sasomers * log when we're in -d mode. 300252481Sasomers */ 301263880Sasomers if (no_daemon) { 302252481Sasomers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 303226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 304252481Sasomers } 305108783Simp 306108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 307226775Shrs if (_inv == 1) 308226775Shrs retval = (retval == 0) ? 1 : 0; 309226775Shrs 310252485Sasomers return (retval); 311107665Simp} 312107665Simp 313147874Simp#include <sys/sockio.h> 314147874Simp#include <net/if.h> 315147874Simp#include <net/if_media.h> 316147874Simp 317151486Sbrooksmedia::media(config &, const char *var, const char *type) 318147874Simp : _var(var), _type(-1) 319147874Simp{ 320147874Simp static struct ifmedia_description media_types[] = { 321147874Simp { IFM_ETHER, "Ethernet" }, 322147874Simp { IFM_TOKEN, "Tokenring" }, 323147874Simp { IFM_FDDI, "FDDI" }, 324147874Simp { IFM_IEEE80211, "802.11" }, 325147874Simp { IFM_ATM, "ATM" }, 326147874Simp { -1, "unknown" }, 327147874Simp { 0, NULL }, 328147874Simp }; 329243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 330147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 331147874Simp _type = media_types[i].ifmt_word; 332147874Simp break; 333147874Simp } 334147874Simp} 335147874Simp 336147874Simpmedia::~media() 337147874Simp{ 338147874Simp} 339147874Simp 340147874Simpbool 341147874Simpmedia::do_match(config &c) 342147874Simp{ 343150949Simp string value; 344147874Simp struct ifmediareq ifmr; 345147874Simp bool retval; 346147874Simp int s; 347147874Simp 348150949Simp // Since we can be called from both a device attach/detach 349150949Simp // context where device-name is defined and what we want, 350150949Simp // as well as from a link status context, where subsystem is 351150949Simp // the name of interest, first try device-name and fall back 352150949Simp // to subsystem if none exists. 353150949Simp value = c.get_variable("device-name"); 354247761Seadler if (value.empty()) 355151480Simp value = c.get_variable("subsystem"); 356252481Sasomers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 357147874Simp value.c_str(), _type); 358147874Simp 359147874Simp retval = false; 360147874Simp 361147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 362147874Simp if (s >= 0) { 363147874Simp memset(&ifmr, 0, sizeof(ifmr)); 364147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 365147874Simp 366147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 367147874Simp ifmr.ifm_status & IFM_AVALID) { 368252481Sasomers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 369147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 370147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 371147874Simp } else if (_type == -1) { 372252481Sasomers devdlog(LOG_DEBUG, "%s has unknown media type\n", 373147874Simp value.c_str()); 374147874Simp retval = true; 375147874Simp } 376147874Simp close(s); 377147874Simp } 378147874Simp 379252485Sasomers return (retval); 380147874Simp} 381147874Simp 382107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 383107665Simpconst string var_list::nothing = ""; 384107665Simp 385107665Simpconst string & 386107665Simpvar_list::get_variable(const string &var) const 387107665Simp{ 388107665Simp map<string, string>::const_iterator i; 389107665Simp 390107665Simp i = _vars.find(var); 391107665Simp if (i == _vars.end()) 392108783Simp return (var_list::bogus); 393107665Simp return (i->second); 394107665Simp} 395107665Simp 396107665Simpbool 397107665Simpvar_list::is_set(const string &var) const 398107665Simp{ 399107665Simp return (_vars.find(var) != _vars.end()); 400107665Simp} 401107665Simp 402107665Simpvoid 403107665Simpvar_list::set_variable(const string &var, const string &val) 404107665Simp{ 405252481Sasomers /* 406252481Sasomers * This function gets called WAY too often to justify calling syslog() 407252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 408252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 409252481Sasomers * log when we're in -d mode. 410252481Sasomers */ 411263880Sasomers if (no_daemon) 412252481Sasomers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 413107665Simp _vars[var] = val; 414107665Simp} 415107665Simp 416107665Simpvoid 417107665Simpconfig::reset(void) 418107665Simp{ 419107665Simp _dir_list.clear(); 420108783Simp delete_and_clear(_var_list_table); 421108783Simp delete_and_clear(_attach_list); 422108783Simp delete_and_clear(_detach_list); 423108783Simp delete_and_clear(_nomatch_list); 424121487Simp delete_and_clear(_notify_list); 425107665Simp} 426107665Simp 427107665Simpvoid 428107665Simpconfig::parse_one_file(const char *fn) 429107665Simp{ 430252481Sasomers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 431107665Simp yyin = fopen(fn, "r"); 432107665Simp if (yyin == NULL) 433107665Simp err(1, "Cannot open config file %s", fn); 434157746Smaxim lineno = 1; 435107665Simp if (yyparse() != 0) 436107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 437107665Simp fclose(yyin); 438107665Simp} 439107665Simp 440107665Simpvoid 441107665Simpconfig::parse_files_in_dir(const char *dirname) 442107665Simp{ 443107665Simp DIR *dirp; 444107665Simp struct dirent *dp; 445107665Simp char path[PATH_MAX]; 446107665Simp 447252481Sasomers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 448107665Simp dirp = opendir(dirname); 449107665Simp if (dirp == NULL) 450107665Simp return; 451107665Simp readdir(dirp); /* Skip . */ 452107665Simp readdir(dirp); /* Skip .. */ 453107665Simp while ((dp = readdir(dirp)) != NULL) { 454107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 455107665Simp snprintf(path, sizeof(path), "%s/%s", 456107665Simp dirname, dp->d_name); 457107665Simp parse_one_file(path); 458107665Simp } 459107665Simp } 460215607Skevlo closedir(dirp); 461107665Simp} 462107665Simp 463108783Simpclass epv_greater { 464108783Simppublic: 465243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 466108783Simp { 467108783Simp return (l1->get_priority() > l2->get_priority()); 468108783Simp } 469108783Simp}; 470108783Simp 471107665Simpvoid 472108783Simpconfig::sort_vector(vector<event_proc *> &v) 473108783Simp{ 474243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 475108783Simp} 476108783Simp 477108783Simpvoid 478107665Simpconfig::parse(void) 479107665Simp{ 480107665Simp vector<string>::const_iterator i; 481107665Simp 482152770Sjkoshy parse_one_file(configfile); 483243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 484107665Simp parse_files_in_dir((*i).c_str()); 485108783Simp sort_vector(_attach_list); 486108783Simp sort_vector(_detach_list); 487108783Simp sort_vector(_nomatch_list); 488121487Simp sort_vector(_notify_list); 489107665Simp} 490107665Simp 491107665Simpvoid 492155073Spjdconfig::open_pidfile() 493107665Simp{ 494155073Spjd pid_t otherpid; 495252485Sasomers 496247758Seadler if (_pidfile.empty()) 497107665Simp return; 498155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 499155073Spjd if (pfh == NULL) { 500155073Spjd if (errno == EEXIST) 501155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 502155073Spjd warn("cannot open pid file"); 503155073Spjd } 504107665Simp} 505107665Simp 506107665Simpvoid 507155073Spjdconfig::write_pidfile() 508155073Spjd{ 509252485Sasomers 510155073Spjd pidfile_write(pfh); 511155073Spjd} 512155073Spjd 513155073Spjdvoid 514209583Simpconfig::close_pidfile() 515209583Simp{ 516252485Sasomers 517209583Simp pidfile_close(pfh); 518209583Simp} 519209583Simp 520209583Simpvoid 521155073Spjdconfig::remove_pidfile() 522155073Spjd{ 523252485Sasomers 524155073Spjd pidfile_remove(pfh); 525155073Spjd} 526155073Spjd 527155073Spjdvoid 528107665Simpconfig::add_attach(int prio, event_proc *p) 529107665Simp{ 530107665Simp p->set_priority(prio); 531107665Simp _attach_list.push_back(p); 532107665Simp} 533107665Simp 534107665Simpvoid 535107665Simpconfig::add_detach(int prio, event_proc *p) 536107665Simp{ 537107665Simp p->set_priority(prio); 538107665Simp _detach_list.push_back(p); 539107665Simp} 540107665Simp 541107665Simpvoid 542107665Simpconfig::add_directory(const char *dir) 543107665Simp{ 544107665Simp _dir_list.push_back(string(dir)); 545107665Simp} 546107665Simp 547107665Simpvoid 548107665Simpconfig::add_nomatch(int prio, event_proc *p) 549107665Simp{ 550107665Simp p->set_priority(prio); 551107665Simp _nomatch_list.push_back(p); 552107665Simp} 553107665Simp 554107665Simpvoid 555121487Simpconfig::add_notify(int prio, event_proc *p) 556121487Simp{ 557121487Simp p->set_priority(prio); 558121487Simp _notify_list.push_back(p); 559121487Simp} 560121487Simp 561121487Simpvoid 562107665Simpconfig::set_pidfile(const char *fn) 563107665Simp{ 564247758Seadler _pidfile = fn; 565107665Simp} 566107665Simp 567107665Simpvoid 568107665Simpconfig::push_var_table() 569107665Simp{ 570107665Simp var_list *vl; 571252485Sasomers 572107665Simp vl = new var_list(); 573107665Simp _var_list_table.push_back(vl); 574252481Sasomers devdlog(LOG_DEBUG, "Pushing table\n"); 575107665Simp} 576107665Simp 577107665Simpvoid 578107665Simpconfig::pop_var_table() 579107665Simp{ 580107665Simp delete _var_list_table.back(); 581107665Simp _var_list_table.pop_back(); 582252481Sasomers devdlog(LOG_DEBUG, "Popping table\n"); 583107665Simp} 584107665Simp 585107665Simpvoid 586107665Simpconfig::set_variable(const char *var, const char *val) 587107665Simp{ 588107665Simp _var_list_table.back()->set_variable(var, val); 589107665Simp} 590107665Simp 591107665Simpconst string & 592107665Simpconfig::get_variable(const string &var) 593107665Simp{ 594107665Simp vector<var_list *>::reverse_iterator i; 595107665Simp 596243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 597107665Simp if ((*i)->is_set(var)) 598108783Simp return ((*i)->get_variable(var)); 599107665Simp } 600107665Simp return (var_list::nothing); 601107665Simp} 602107665Simp 603108783Simpbool 604243930Seadlerconfig::is_id_char(char ch) const 605108783Simp{ 606252485Sasomers return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 607108783Simp ch == '-')); 608108783Simp} 609108783Simp 610108014Simpvoid 611114081Simpconfig::expand_one(const char *&src, string &dst) 612107665Simp{ 613108014Simp int count; 614210610Slulf string buffer; 615108014Simp 616108783Simp src++; 617108014Simp // $$ -> $ 618108014Simp if (*src == '$') { 619247762Seadler dst += *src++; 620108014Simp return; 621108014Simp } 622252485Sasomers 623108014Simp // $(foo) -> $(foo) 624108783Simp // Not sure if I want to support this or not, so for now we just pass 625108783Simp // it through. 626108014Simp if (*src == '(') { 627247762Seadler dst += '$'; 628108014Simp count = 1; 629114081Simp /* If the string ends before ) is matched , return. */ 630114081Simp while (count > 0 && *src) { 631108014Simp if (*src == ')') 632108014Simp count--; 633108014Simp else if (*src == '(') 634108014Simp count++; 635247762Seadler dst += *src++; 636108014Simp } 637108014Simp return; 638108014Simp } 639252485Sasomers 640247763Seadler // $[^A-Za-z] -> $\1 641108014Simp if (!isalpha(*src)) { 642247762Seadler dst += '$'; 643247762Seadler dst += *src++; 644108014Simp return; 645108014Simp } 646108014Simp 647108014Simp // $var -> replace with value 648114081Simp do { 649247762Seadler buffer += *src++; 650114084Simp } while (is_id_char(*src)); 651247758Seadler dst.append(get_variable(buffer)); 652107665Simp} 653107665Simp 654108014Simpconst string 655246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 656108014Simp{ 657246134Sian const char *var_at; 658114081Simp string dst; 659108014Simp 660246134Sian /* 661246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 662246134Sian * while parsing config files, as tested on 2013-01-30. 663246134Sian */ 664246134Sian dst.reserve(128); 665246134Sian 666246134Sian if (prepend != NULL) 667246134Sian dst = prepend; 668246134Sian 669246134Sian for (;;) { 670246134Sian var_at = strchr(src, '$'); 671246134Sian if (var_at == NULL) { 672246134Sian dst.append(src); 673246134Sian break; 674246134Sian } 675246134Sian dst.append(src, var_at - src); 676246134Sian src = var_at; 677246134Sian expand_one(src, dst); 678108014Simp } 679108014Simp 680246134Sian if (append != NULL) 681246134Sian dst.append(append); 682246134Sian 683114081Simp return (dst); 684108014Simp} 685108014Simp 686108783Simpbool 687247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 688108783Simp{ 689108783Simp char *walker; 690252485Sasomers 691108783Simp if (*buffer == '\0') 692108783Simp return (false); 693108783Simp walker = lhs = buffer; 694108783Simp while (is_id_char(*walker)) 695108783Simp walker++; 696108783Simp if (*walker != '=') 697108783Simp return (false); 698108783Simp walker++; // skip = 699108783Simp if (*walker == '"') { 700108783Simp walker++; // skip " 701108783Simp rhs = walker; 702108783Simp while (*walker && *walker != '"') 703108783Simp walker++; 704108783Simp if (*walker != '"') 705108783Simp return (false); 706108783Simp rhs[-2] = '\0'; 707108783Simp *walker++ = '\0'; 708108783Simp } else { 709108783Simp rhs = walker; 710108783Simp while (*walker && !isspace(*walker)) 711108783Simp walker++; 712108783Simp if (*walker != '\0') 713108783Simp *walker++ = '\0'; 714108783Simp rhs[-1] = '\0'; 715108783Simp } 716113785Simp while (isspace(*walker)) 717113785Simp walker++; 718108783Simp buffer = walker; 719108783Simp return (true); 720108783Simp} 721108783Simp 722108783Simp 723108783Simpchar * 724108783Simpconfig::set_vars(char *buffer) 725108783Simp{ 726108783Simp char *lhs; 727108783Simp char *rhs; 728108783Simp 729108783Simp while (1) { 730108783Simp if (!chop_var(buffer, lhs, rhs)) 731108783Simp break; 732108783Simp set_variable(lhs, rhs); 733108783Simp } 734108783Simp return (buffer); 735108783Simp} 736108783Simp 737108783Simpvoid 738108783Simpconfig::find_and_execute(char type) 739108783Simp{ 740108783Simp vector<event_proc *> *l; 741108783Simp vector<event_proc *>::const_iterator i; 742151486Sbrooks const char *s; 743108783Simp 744108783Simp switch (type) { 745108783Simp default: 746108783Simp return; 747121487Simp case notify: 748121487Simp l = &_notify_list; 749121487Simp s = "notify"; 750121487Simp break; 751108783Simp case nomatch: 752108783Simp l = &_nomatch_list; 753108783Simp s = "nomatch"; 754108783Simp break; 755108783Simp case attach: 756108783Simp l = &_attach_list; 757108783Simp s = "attach"; 758108783Simp break; 759108783Simp case detach: 760108783Simp l = &_detach_list; 761108783Simp s = "detach"; 762108783Simp break; 763108783Simp } 764252481Sasomers devdlog(LOG_DEBUG, "Processing %s event\n", s); 765243931Seadler for (i = l->begin(); i != l->end(); ++i) { 766108783Simp if ((*i)->matches(*this)) { 767108783Simp (*i)->run(*this); 768108783Simp break; 769108783Simp } 770108783Simp } 771108783Simp 772108783Simp} 773108783Simp 774252485Sasomers 775107665Simpstatic void 776108783Simpprocess_event(char *buffer) 777107665Simp{ 778107665Simp char type; 779107665Simp char *sp; 780107665Simp 781108783Simp sp = buffer + 1; 782260519Sasomers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 783107665Simp type = *buffer++; 784108783Simp cfg.push_var_table(); 785108783Simp // No match doesn't have a device, and the format is a little 786108783Simp // different, so handle it separately. 787121487Simp switch (type) { 788121487Simp case notify: 789121487Simp sp = cfg.set_vars(sp); 790121487Simp break; 791121487Simp case nomatch: 792145218Simp //? at location pnp-info on bus 793145218Simp sp = strchr(sp, ' '); 794145218Simp if (sp == NULL) 795145218Simp return; /* Can't happen? */ 796145218Simp *sp++ = '\0'; 797213646Simp while (isspace(*sp)) 798213646Simp sp++; 799121487Simp if (strncmp(sp, "at ", 3) == 0) 800121487Simp sp += 3; 801121487Simp sp = cfg.set_vars(sp); 802213646Simp while (isspace(*sp)) 803213646Simp sp++; 804121487Simp if (strncmp(sp, "on ", 3) == 0) 805121487Simp cfg.set_variable("bus", sp + 3); 806121487Simp break; 807121487Simp case attach: /*FALLTHROUGH*/ 808121487Simp case detach: 809108783Simp sp = strchr(sp, ' '); 810108783Simp if (sp == NULL) 811108783Simp return; /* Can't happen? */ 812108783Simp *sp++ = '\0'; 813108783Simp cfg.set_variable("device-name", buffer); 814213646Simp while (isspace(*sp)) 815213646Simp sp++; 816113785Simp if (strncmp(sp, "at ", 3) == 0) 817113785Simp sp += 3; 818113785Simp sp = cfg.set_vars(sp); 819213646Simp while (isspace(*sp)) 820213646Simp sp++; 821113785Simp if (strncmp(sp, "on ", 3) == 0) 822113785Simp cfg.set_variable("bus", sp + 3); 823121487Simp break; 824108783Simp } 825252485Sasomers 826108783Simp cfg.find_and_execute(type); 827108783Simp cfg.pop_var_table(); 828107665Simp} 829107665Simp 830131397Simpint 831270242Sasomerscreate_socket(const char *name, int socktype) 832131397Simp{ 833131397Simp int fd, slen; 834131397Simp struct sockaddr_un sun; 835131397Simp 836270242Sasomers if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 837131397Simp err(1, "socket"); 838131397Simp bzero(&sun, sizeof(sun)); 839131397Simp sun.sun_family = AF_UNIX; 840131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 841131397Simp slen = SUN_LEN(&sun); 842131397Simp unlink(name); 843147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 844147973Smarcus err(1, "fcntl"); 845236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 846131397Simp err(1, "bind"); 847131397Simp listen(fd, 4); 848147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 849147973Smarcus chmod(name, 0666); 850131397Simp return (fd); 851131397Simp} 852131397Simp 853246121Sianunsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 854246121Sianunsigned int num_clients; 855131397Simp 856270242Sasomerslist<client_t> clients; 857270242Sasomers 858131397Simpvoid 859131397Simpnotify_clients(const char *data, int len) 860131397Simp{ 861270242Sasomers list<client_t>::iterator i; 862131397Simp 863246121Sian /* 864246121Sian * Deliver the data to all clients. Throw clients overboard at the 865246121Sian * first sign of trouble. This reaps clients who've died or closed 866246121Sian * their sockets, and also clients who are alive but failing to keep up 867246121Sian * (or who are maliciously not reading, to consume buffer space in 868246121Sian * kernel memory or tie up the limited number of available connections). 869246121Sian */ 870246121Sian for (i = clients.begin(); i != clients.end(); ) { 871270242Sasomers int flags; 872270242Sasomers if (i->socktype == SOCK_SEQPACKET) 873270242Sasomers flags = MSG_EOR; 874270242Sasomers else 875270242Sasomers flags = 0; 876270242Sasomers 877270242Sasomers if (send(i->fd, data, len, flags) != len) { 878246121Sian --num_clients; 879270242Sasomers close(i->fd); 880246121Sian i = clients.erase(i); 881270242Sasomers devdlog(LOG_WARNING, "notify_clients: send() failed; " 882252481Sasomers "dropping unresponsive client\n"); 883246121Sian } else 884246121Sian ++i; 885131397Simp } 886246121Sian} 887131397Simp 888246121Sianvoid 889246121Siancheck_clients(void) 890246121Sian{ 891246121Sian int s; 892246121Sian struct pollfd pfd; 893270242Sasomers list<client_t>::iterator i; 894246121Sian 895246121Sian /* 896246121Sian * Check all existing clients to see if any of them have disappeared. 897246121Sian * Normally we reap clients when we get an error trying to send them an 898246121Sian * event. This check eliminates the problem of an ever-growing list of 899246121Sian * zombie clients because we're never writing to them on a system 900246121Sian * without frequent device-change activity. 901246121Sian */ 902246121Sian pfd.events = 0; 903246121Sian for (i = clients.begin(); i != clients.end(); ) { 904270242Sasomers pfd.fd = i->fd; 905246121Sian s = poll(&pfd, 1, 0); 906246121Sian if ((s < 0 && s != EINTR ) || 907246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 908246121Sian --num_clients; 909270242Sasomers close(i->fd); 910246121Sian i = clients.erase(i); 911252481Sasomers devdlog(LOG_NOTICE, "check_clients: " 912252481Sasomers "dropping disconnected client\n"); 913246121Sian } else 914246121Sian ++i; 915246121Sian } 916131397Simp} 917131397Simp 918131397Simpvoid 919270242Sasomersnew_client(int fd, int socktype) 920131397Simp{ 921270242Sasomers client_t s; 922260519Sasomers int sndbuf_size; 923131397Simp 924246121Sian /* 925246121Sian * First go reap any zombie clients, then accept the connection, and 926246121Sian * shut down the read side to stop clients from consuming kernel memory 927246121Sian * by sending large buffers full of data we'll never read. 928246121Sian */ 929246121Sian check_clients(); 930270242Sasomers s.socktype = socktype; 931270242Sasomers s.fd = accept(fd, NULL, NULL); 932270242Sasomers if (s.fd != -1) { 933260519Sasomers sndbuf_size = CLIENT_BUFSIZE; 934270242Sasomers if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 935260519Sasomers sizeof(sndbuf_size))) 936260519Sasomers err(1, "setsockopt"); 937270242Sasomers shutdown(s.fd, SHUT_RD); 938131397Simp clients.push_back(s); 939246121Sian ++num_clients; 940260519Sasomers } else 941260519Sasomers err(1, "accept"); 942131397Simp} 943131397Simp 944107665Simpstatic void 945107665Simpevent_loop(void) 946107665Simp{ 947107665Simp int rv; 948107665Simp int fd; 949107665Simp char buffer[DEVCTL_MAXBUF]; 950113790Simp int once = 0; 951270242Sasomers int stream_fd, seqpacket_fd, max_fd; 952246121Sian int accepting; 953113790Simp timeval tv; 954113790Simp fd_set fds; 955107665Simp 956240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 957107665Simp if (fd == -1) 958131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 959270242Sasomers stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 960270242Sasomers seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 961246121Sian accepting = 1; 962270242Sasomers max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 963247756Seadler while (!romeo_must_die) { 964263880Sasomers if (!once && !no_daemon && !daemonize_quick) { 965113790Simp // Check to see if we have any events pending. 966113790Simp tv.tv_sec = 0; 967113790Simp tv.tv_usec = 0; 968113790Simp FD_ZERO(&fds); 969113790Simp FD_SET(fd, &fds); 970113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 971113790Simp // No events -> we've processed all pending events 972117944Simp if (rv == 0) { 973252481Sasomers devdlog(LOG_DEBUG, "Calling daemon\n"); 974155073Spjd cfg.remove_pidfile(); 975155073Spjd cfg.open_pidfile(); 976113790Simp daemon(0, 0); 977155073Spjd cfg.write_pidfile(); 978113790Simp once++; 979113790Simp } 980113790Simp } 981246121Sian /* 982246121Sian * When we've already got the max number of clients, stop 983270242Sasomers * accepting new connections (don't put the listening sockets in 984270242Sasomers * the set), shrink the accept() queue to reject connections 985270242Sasomers * quickly, and poll the existing clients more often, so that we 986270242Sasomers * notice more quickly when any of them disappear to free up 987270242Sasomers * client slots. 988246121Sian */ 989131397Simp FD_ZERO(&fds); 990131397Simp FD_SET(fd, &fds); 991246121Sian if (num_clients < max_clients) { 992246121Sian if (!accepting) { 993270242Sasomers listen(stream_fd, max_clients); 994270242Sasomers listen(seqpacket_fd, max_clients); 995246121Sian accepting = 1; 996246121Sian } 997270242Sasomers FD_SET(stream_fd, &fds); 998270242Sasomers FD_SET(seqpacket_fd, &fds); 999246121Sian tv.tv_sec = 60; 1000246121Sian tv.tv_usec = 0; 1001246121Sian } else { 1002246121Sian if (accepting) { 1003270242Sasomers listen(stream_fd, 0); 1004270242Sasomers listen(seqpacket_fd, 0); 1005246121Sian accepting = 0; 1006246121Sian } 1007246121Sian tv.tv_sec = 2; 1008246121Sian tv.tv_usec = 0; 1009246121Sian } 1010246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 1011252482Sasomers if (got_siginfo) { 1012260519Sasomers devdlog(LOG_NOTICE, "Events received so far=%u\n", 1013252482Sasomers total_events); 1014252482Sasomers got_siginfo = 0; 1015252482Sasomers } 1016131397Simp if (rv == -1) { 1017131397Simp if (errno == EINTR) 1018131397Simp continue; 1019131397Simp err(1, "select"); 1020246121Sian } else if (rv == 0) 1021246121Sian check_clients(); 1022131397Simp if (FD_ISSET(fd, &fds)) { 1023131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 1024131397Simp if (rv > 0) { 1025252482Sasomers total_events++; 1026252481Sasomers if (rv == sizeof(buffer) - 1) { 1027252481Sasomers devdlog(LOG_WARNING, "Warning: " 1028252481Sasomers "available event data exceeded " 1029252481Sasomers "buffer space\n"); 1030252481Sasomers } 1031131397Simp notify_clients(buffer, rv); 1032107665Simp buffer[rv] = '\0'; 1033131397Simp while (buffer[--rv] == '\n') 1034131397Simp buffer[rv] = '\0'; 1035131397Simp process_event(buffer); 1036131397Simp } else if (rv < 0) { 1037131397Simp if (errno != EINTR) 1038131397Simp break; 1039131397Simp } else { 1040131397Simp /* EOF */ 1041107665Simp break; 1042131397Simp } 1043107665Simp } 1044270242Sasomers if (FD_ISSET(stream_fd, &fds)) 1045270242Sasomers new_client(stream_fd, SOCK_STREAM); 1046270242Sasomers /* 1047270242Sasomers * Aside from the socket type, both sockets use the same 1048270242Sasomers * protocol, so we can process clients the same way. 1049270242Sasomers */ 1050270242Sasomers if (FD_ISSET(seqpacket_fd, &fds)) 1051270242Sasomers new_client(seqpacket_fd, SOCK_SEQPACKET); 1052107665Simp } 1053107665Simp close(fd); 1054107665Simp} 1055252485Sasomers 1056107665Simp/* 1057107665Simp * functions that the parser uses. 1058107665Simp */ 1059107665Simpvoid 1060107665Simpadd_attach(int prio, event_proc *p) 1061107665Simp{ 1062107665Simp cfg.add_attach(prio, p); 1063107665Simp} 1064107665Simp 1065107665Simpvoid 1066107665Simpadd_detach(int prio, event_proc *p) 1067107665Simp{ 1068107665Simp cfg.add_detach(prio, p); 1069107665Simp} 1070107665Simp 1071107665Simpvoid 1072107665Simpadd_directory(const char *dir) 1073107665Simp{ 1074107665Simp cfg.add_directory(dir); 1075107665Simp free(const_cast<char *>(dir)); 1076107665Simp} 1077107665Simp 1078107665Simpvoid 1079107665Simpadd_nomatch(int prio, event_proc *p) 1080107665Simp{ 1081107665Simp cfg.add_nomatch(prio, p); 1082107665Simp} 1083107665Simp 1084121487Simpvoid 1085121487Simpadd_notify(int prio, event_proc *p) 1086121487Simp{ 1087121487Simp cfg.add_notify(prio, p); 1088121487Simp} 1089121487Simp 1090107665Simpevent_proc * 1091107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1092107665Simp{ 1093107665Simp if (ep == NULL) 1094107665Simp ep = new event_proc(); 1095107665Simp ep->add(eps); 1096107665Simp return (ep); 1097107665Simp} 1098107665Simp 1099107665Simpeps * 1100107665Simpnew_action(const char *cmd) 1101107665Simp{ 1102107665Simp eps *e = new action(cmd); 1103107665Simp free(const_cast<char *>(cmd)); 1104107665Simp return (e); 1105107665Simp} 1106107665Simp 1107107665Simpeps * 1108107665Simpnew_match(const char *var, const char *re) 1109107665Simp{ 1110108014Simp eps *e = new match(cfg, var, re); 1111107665Simp free(const_cast<char *>(var)); 1112107665Simp free(const_cast<char *>(re)); 1113107665Simp return (e); 1114107665Simp} 1115107665Simp 1116147874Simpeps * 1117147874Simpnew_media(const char *var, const char *re) 1118147874Simp{ 1119147874Simp eps *e = new media(cfg, var, re); 1120147874Simp free(const_cast<char *>(var)); 1121147874Simp free(const_cast<char *>(re)); 1122147874Simp return (e); 1123147874Simp} 1124147874Simp 1125107665Simpvoid 1126107665Simpset_pidfile(const char *name) 1127107665Simp{ 1128107665Simp cfg.set_pidfile(name); 1129107665Simp free(const_cast<char *>(name)); 1130107665Simp} 1131107665Simp 1132107665Simpvoid 1133107665Simpset_variable(const char *var, const char *val) 1134107665Simp{ 1135107665Simp cfg.set_variable(var, val); 1136107665Simp free(const_cast<char *>(var)); 1137107665Simp free(const_cast<char *>(val)); 1138107665Simp} 1139107665Simp 1140107665Simp 1141252485Sasomers 1142107665Simpstatic void 1143107665Simpgensighand(int) 1144107665Simp{ 1145247754Seadler romeo_must_die = 1; 1146107665Simp} 1147107665Simp 1148252481Sasomers/* 1149252482Sasomers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1150252482Sasomers * as appropriate 1151252482Sasomers */ 1152252482Sasomersstatic void 1153252482Sasomerssiginfohand(int) 1154252482Sasomers{ 1155252482Sasomers got_siginfo = 1; 1156252482Sasomers} 1157252482Sasomers 1158252482Sasomers/* 1159252481Sasomers * Local logging function. Prints to syslog if we're daemonized; syslog 1160252481Sasomers * otherwise. 1161252481Sasomers */ 1162107665Simpstatic void 1163252481Sasomersdevdlog(int priority, const char* fmt, ...) 1164252481Sasomers{ 1165252481Sasomers va_list argp; 1166252481Sasomers 1167252481Sasomers va_start(argp, fmt); 1168263880Sasomers if (no_daemon) 1169252481Sasomers vfprintf(stderr, fmt, argp); 1170263880Sasomers else if ((! quiet_mode) || (priority <= LOG_WARNING)) 1171252481Sasomers vsyslog(priority, fmt, argp); 1172252481Sasomers va_end(argp); 1173252481Sasomers} 1174252481Sasomers 1175252481Sasomersstatic void 1176107665Simpusage() 1177107665Simp{ 1178263880Sasomers fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1179246121Sian getprogname()); 1180107665Simp exit(1); 1181107665Simp} 1182107665Simp 1183113787Simpstatic void 1184113787Simpcheck_devd_enabled() 1185113787Simp{ 1186113787Simp int val = 0; 1187113787Simp size_t len; 1188113787Simp 1189113787Simp len = sizeof(val); 1190114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1191113787Simp errx(1, "devctl sysctl missing from kernel!"); 1192263963Smjg if (val == 0) { 1193263963Smjg warnx("Setting " SYSCTL " to 1000"); 1194263963Smjg val = 1000; 1195113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1196113787Simp } 1197113787Simp} 1198113787Simp 1199107665Simp/* 1200107665Simp * main 1201107665Simp */ 1202107665Simpint 1203107665Simpmain(int argc, char **argv) 1204107665Simp{ 1205107665Simp int ch; 1206107665Simp 1207113787Simp check_devd_enabled(); 1208263880Sasomers while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 1209107665Simp switch (ch) { 1210107665Simp case 'd': 1211263880Sasomers no_daemon = 1; 1212107665Simp break; 1213152770Sjkoshy case 'f': 1214152770Sjkoshy configfile = optarg; 1215152770Sjkoshy break; 1216246121Sian case 'l': 1217246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1218246121Sian break; 1219113790Simp case 'n': 1220263880Sasomers daemonize_quick = 1; 1221113790Simp break; 1222263880Sasomers case 'q': 1223263880Sasomers quiet_mode = 1; 1224263880Sasomers break; 1225107665Simp default: 1226107665Simp usage(); 1227107665Simp } 1228107665Simp } 1229107665Simp 1230107665Simp cfg.parse(); 1231263880Sasomers if (!no_daemon && daemonize_quick) { 1232155073Spjd cfg.open_pidfile(); 1233107665Simp daemon(0, 0); 1234155073Spjd cfg.write_pidfile(); 1235117246Simp } 1236146306Simp signal(SIGPIPE, SIG_IGN); 1237107665Simp signal(SIGHUP, gensighand); 1238107665Simp signal(SIGINT, gensighand); 1239107665Simp signal(SIGTERM, gensighand); 1240252482Sasomers signal(SIGINFO, siginfohand); 1241107665Simp event_loop(); 1242107665Simp return (0); 1243107665Simp} 1244