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: stable/10/sbin/devd/devd.cc 321827 2017-07-31 22:28:33Z 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> 98314425Sasomers#include <stdexcept> 99107665Simp#include <vector> 100107665Simp 101114086Simp#include "devd.h" /* C compatible definitions */ 102114086Simp#include "devd.hh" /* C++ class definitions */ 103107665Simp 104270242Sasomers#define STREAMPIPE "/var/run/devd.pipe" 105270242Sasomers#define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 106107665Simp#define CF "/etc/devd.conf" 107263963Smjg#define SYSCTL "hw.bus.devctl_queue" 108107665Simp 109260519Sasomers/* 110260519Sasomers * Since the client socket is nonblocking, we must increase its send buffer to 111260519Sasomers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 112293695Sasomers * buffer, so the client can't increase the buffersize by itself. 113260519Sasomers * 114260519Sasomers * For example, when creating a ZFS pool, devd emits one 165 character 115293695Sasomers * resource.fs.zfs.statechange message for each vdev in the pool. The kernel 116293695Sasomers * allocates a 4608B mbuf for each message. Modern technology places a limit of 117293695Sasomers * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger 118293695Sasomers * than that. 119293695Sasomers * 120293695Sasomers * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf 121293695Sasomers * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf 122293695Sasomers * 123293695Sasomers * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. 124293695Sasomers * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * 125293695Sasomers * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by 126293695Sasomers * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we 127293695Sasomers * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the 128293695Sasomers * kernel will set the mbuf limit to 2MB, which is just large enough for 450 129293695Sasomers * drives. It also happens to be the same as the hardcoded maximum value. 130260519Sasomers */ 131293695Sasomers#define CLIENT_BUFSIZE 262144 132260519Sasomers 133107665Simpusing namespace std; 134107665Simp 135270242Sasomerstypedef struct client { 136270242Sasomers int fd; 137270242Sasomers int socktype; 138270242Sasomers} client_t; 139270242Sasomers 140107665Simpextern FILE *yyin; 141107665Simpextern int lineno; 142107665Simp 143121487Simpstatic const char notify = '!'; 144108783Simpstatic const char nomatch = '?'; 145108783Simpstatic const char attach = '+'; 146108783Simpstatic const char detach = '-'; 147108783Simp 148155073Spjdstatic struct pidfh *pfh; 149155073Spjd 150263880Sasomersstatic int no_daemon = 0; 151263880Sasomersstatic int daemonize_quick = 0; 152263880Sasomersstatic int quiet_mode = 0; 153252482Sasomersstatic unsigned total_events = 0; 154252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0; 155247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 156107665Simp 157152770Sjkoshystatic const char *configfile = CF; 158152770Sjkoshy 159253046Sasomersstatic void devdlog(int priority, const char* message, ...) 160253046Sasomers __printflike(2, 3); 161107665Simpstatic void event_loop(void); 162107665Simpstatic void usage(void); 163107665Simp 164108783Simptemplate <class T> void 165108783Simpdelete_and_clear(vector<T *> &v) 166108783Simp{ 167108783Simp typename vector<T *>::const_iterator i; 168108783Simp 169243931Seadler for (i = v.begin(); i != v.end(); ++i) 170108783Simp delete *i; 171108783Simp v.clear(); 172108783Simp} 173108783Simp 174107665Simpconfig cfg; 175107665Simp 176107665Simpevent_proc::event_proc() : _prio(-1) 177107665Simp{ 178246134Sian _epsvec.reserve(4); 179107665Simp} 180107665Simp 181107665Simpevent_proc::~event_proc() 182107665Simp{ 183152406Sbland delete_and_clear(_epsvec); 184107665Simp} 185107665Simp 186107665Simpvoid 187107665Simpevent_proc::add(eps *eps) 188107665Simp{ 189107665Simp _epsvec.push_back(eps); 190107665Simp} 191107665Simp 192107665Simpbool 193243930Seadlerevent_proc::matches(config &c) const 194107665Simp{ 195107665Simp vector<eps *>::const_iterator i; 196107665Simp 197243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 198107665Simp if (!(*i)->do_match(c)) 199107665Simp return (false); 200107665Simp return (true); 201107665Simp} 202107665Simp 203107665Simpbool 204243930Seadlerevent_proc::run(config &c) const 205107665Simp{ 206107665Simp vector<eps *>::const_iterator i; 207252485Sasomers 208243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 209107665Simp if (!(*i)->do_action(c)) 210107665Simp return (false); 211107665Simp return (true); 212107665Simp} 213107665Simp 214107665Simpaction::action(const char *cmd) 215252485Sasomers : _cmd(cmd) 216107665Simp{ 217107665Simp // nothing 218107665Simp} 219107665Simp 220107665Simpaction::~action() 221107665Simp{ 222107665Simp // nothing 223107665Simp} 224107665Simp 225209583Simpstatic int 226209583Simpmy_system(const char *command) 227209583Simp{ 228209583Simp pid_t pid, savedpid; 229209583Simp int pstat; 230209583Simp struct sigaction ign, intact, quitact; 231209583Simp sigset_t newsigblock, oldsigblock; 232209583Simp 233209583Simp if (!command) /* just checking... */ 234252485Sasomers return (1); 235209583Simp 236209583Simp /* 237209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 238209583Simp * existing signal dispositions. 239209583Simp */ 240209583Simp ign.sa_handler = SIG_IGN; 241209583Simp ::sigemptyset(&ign.sa_mask); 242209583Simp ign.sa_flags = 0; 243209583Simp ::sigaction(SIGINT, &ign, &intact); 244209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 245209583Simp ::sigemptyset(&newsigblock); 246209583Simp ::sigaddset(&newsigblock, SIGCHLD); 247209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 248209583Simp switch (pid = ::fork()) { 249209583Simp case -1: /* error */ 250209583Simp break; 251209583Simp case 0: /* child */ 252209583Simp /* 253209583Simp * Restore original signal dispositions and exec the command. 254209583Simp */ 255209583Simp ::sigaction(SIGINT, &intact, NULL); 256209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 257209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 258209583Simp /* 259209583Simp * Close the PID file, and all other open descriptors. 260209583Simp * Inherit std{in,out,err} only. 261209583Simp */ 262209583Simp cfg.close_pidfile(); 263209583Simp ::closefrom(3); 264209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 265209583Simp ::_exit(127); 266209583Simp default: /* parent */ 267209583Simp savedpid = pid; 268209583Simp do { 269209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 270209583Simp } while (pid == -1 && errno == EINTR); 271209583Simp break; 272209583Simp } 273209583Simp ::sigaction(SIGINT, &intact, NULL); 274209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 275209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 276209583Simp return (pid == -1 ? -1 : pstat); 277209583Simp} 278209583Simp 279107665Simpbool 280108014Simpaction::do_action(config &c) 281107665Simp{ 282246134Sian string s = c.expand_string(_cmd.c_str()); 283252481Sasomers devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str()); 284209583Simp my_system(s.c_str()); 285107665Simp return (true); 286107665Simp} 287107665Simp 288246134Sianmatch::match(config &c, const char *var, const char *re) : 289246134Sian _inv(re[0] == '!'), 290246134Sian _var(var), 291246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 292107665Simp{ 293154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 294107665Simp} 295107665Simp 296107665Simpmatch::~match() 297107665Simp{ 298108014Simp regfree(&_regex); 299107665Simp} 300107665Simp 301107665Simpbool 302108014Simpmatch::do_match(config &c) 303107665Simp{ 304210610Slulf const string &value = c.get_variable(_var); 305108014Simp bool retval; 306108014Simp 307252485Sasomers /* 308252481Sasomers * This function gets called WAY too often to justify calling syslog() 309252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 310252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 311252481Sasomers * log when we're in -d mode. 312252481Sasomers */ 313263880Sasomers if (no_daemon) { 314252481Sasomers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 315226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 316252481Sasomers } 317108783Simp 318108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 319226775Shrs if (_inv == 1) 320226775Shrs retval = (retval == 0) ? 1 : 0; 321226775Shrs 322252485Sasomers return (retval); 323107665Simp} 324107665Simp 325147874Simp#include <sys/sockio.h> 326147874Simp#include <net/if.h> 327147874Simp#include <net/if_media.h> 328147874Simp 329151486Sbrooksmedia::media(config &, const char *var, const char *type) 330147874Simp : _var(var), _type(-1) 331147874Simp{ 332147874Simp static struct ifmedia_description media_types[] = { 333147874Simp { IFM_ETHER, "Ethernet" }, 334147874Simp { IFM_TOKEN, "Tokenring" }, 335147874Simp { IFM_FDDI, "FDDI" }, 336147874Simp { IFM_IEEE80211, "802.11" }, 337147874Simp { IFM_ATM, "ATM" }, 338147874Simp { -1, "unknown" }, 339147874Simp { 0, NULL }, 340147874Simp }; 341243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 342147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 343147874Simp _type = media_types[i].ifmt_word; 344147874Simp break; 345147874Simp } 346147874Simp} 347147874Simp 348147874Simpmedia::~media() 349147874Simp{ 350147874Simp} 351147874Simp 352147874Simpbool 353147874Simpmedia::do_match(config &c) 354147874Simp{ 355150949Simp string value; 356147874Simp struct ifmediareq ifmr; 357147874Simp bool retval; 358147874Simp int s; 359147874Simp 360150949Simp // Since we can be called from both a device attach/detach 361150949Simp // context where device-name is defined and what we want, 362150949Simp // as well as from a link status context, where subsystem is 363150949Simp // the name of interest, first try device-name and fall back 364150949Simp // to subsystem if none exists. 365150949Simp value = c.get_variable("device-name"); 366247761Seadler if (value.empty()) 367151480Simp value = c.get_variable("subsystem"); 368252481Sasomers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 369147874Simp value.c_str(), _type); 370147874Simp 371147874Simp retval = false; 372147874Simp 373147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 374147874Simp if (s >= 0) { 375147874Simp memset(&ifmr, 0, sizeof(ifmr)); 376314425Sasomers strlcpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 377147874Simp 378147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 379147874Simp ifmr.ifm_status & IFM_AVALID) { 380252481Sasomers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 381147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 382147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 383147874Simp } else if (_type == -1) { 384252481Sasomers devdlog(LOG_DEBUG, "%s has unknown media type\n", 385147874Simp value.c_str()); 386147874Simp retval = true; 387147874Simp } 388147874Simp close(s); 389147874Simp } 390147874Simp 391252485Sasomers return (retval); 392147874Simp} 393147874Simp 394107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 395107665Simpconst string var_list::nothing = ""; 396107665Simp 397107665Simpconst string & 398107665Simpvar_list::get_variable(const string &var) const 399107665Simp{ 400107665Simp map<string, string>::const_iterator i; 401107665Simp 402107665Simp i = _vars.find(var); 403107665Simp if (i == _vars.end()) 404108783Simp return (var_list::bogus); 405107665Simp return (i->second); 406107665Simp} 407107665Simp 408107665Simpbool 409107665Simpvar_list::is_set(const string &var) const 410107665Simp{ 411107665Simp return (_vars.find(var) != _vars.end()); 412107665Simp} 413107665Simp 414107665Simpvoid 415107665Simpvar_list::set_variable(const string &var, const string &val) 416107665Simp{ 417252481Sasomers /* 418252481Sasomers * This function gets called WAY too often to justify calling syslog() 419252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 420252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 421252481Sasomers * log when we're in -d mode. 422252481Sasomers */ 423263880Sasomers if (no_daemon) 424252481Sasomers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 425107665Simp _vars[var] = val; 426107665Simp} 427107665Simp 428107665Simpvoid 429107665Simpconfig::reset(void) 430107665Simp{ 431107665Simp _dir_list.clear(); 432108783Simp delete_and_clear(_var_list_table); 433108783Simp delete_and_clear(_attach_list); 434108783Simp delete_and_clear(_detach_list); 435108783Simp delete_and_clear(_nomatch_list); 436121487Simp delete_and_clear(_notify_list); 437107665Simp} 438107665Simp 439107665Simpvoid 440107665Simpconfig::parse_one_file(const char *fn) 441107665Simp{ 442252481Sasomers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 443107665Simp yyin = fopen(fn, "r"); 444107665Simp if (yyin == NULL) 445107665Simp err(1, "Cannot open config file %s", fn); 446157746Smaxim lineno = 1; 447107665Simp if (yyparse() != 0) 448107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 449107665Simp fclose(yyin); 450107665Simp} 451107665Simp 452107665Simpvoid 453107665Simpconfig::parse_files_in_dir(const char *dirname) 454107665Simp{ 455107665Simp DIR *dirp; 456107665Simp struct dirent *dp; 457107665Simp char path[PATH_MAX]; 458107665Simp 459252481Sasomers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 460107665Simp dirp = opendir(dirname); 461107665Simp if (dirp == NULL) 462107665Simp return; 463107665Simp readdir(dirp); /* Skip . */ 464107665Simp readdir(dirp); /* Skip .. */ 465107665Simp while ((dp = readdir(dirp)) != NULL) { 466107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 467107665Simp snprintf(path, sizeof(path), "%s/%s", 468107665Simp dirname, dp->d_name); 469107665Simp parse_one_file(path); 470107665Simp } 471107665Simp } 472215607Skevlo closedir(dirp); 473107665Simp} 474107665Simp 475108783Simpclass epv_greater { 476108783Simppublic: 477243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 478108783Simp { 479108783Simp return (l1->get_priority() > l2->get_priority()); 480108783Simp } 481108783Simp}; 482108783Simp 483107665Simpvoid 484108783Simpconfig::sort_vector(vector<event_proc *> &v) 485108783Simp{ 486243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 487108783Simp} 488108783Simp 489108783Simpvoid 490107665Simpconfig::parse(void) 491107665Simp{ 492107665Simp vector<string>::const_iterator i; 493107665Simp 494152770Sjkoshy parse_one_file(configfile); 495243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 496107665Simp parse_files_in_dir((*i).c_str()); 497108783Simp sort_vector(_attach_list); 498108783Simp sort_vector(_detach_list); 499108783Simp sort_vector(_nomatch_list); 500121487Simp sort_vector(_notify_list); 501107665Simp} 502107665Simp 503107665Simpvoid 504155073Spjdconfig::open_pidfile() 505107665Simp{ 506155073Spjd pid_t otherpid; 507252485Sasomers 508247758Seadler if (_pidfile.empty()) 509107665Simp return; 510155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 511155073Spjd if (pfh == NULL) { 512155073Spjd if (errno == EEXIST) 513155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 514155073Spjd warn("cannot open pid file"); 515155073Spjd } 516107665Simp} 517107665Simp 518107665Simpvoid 519155073Spjdconfig::write_pidfile() 520155073Spjd{ 521252485Sasomers 522155073Spjd pidfile_write(pfh); 523155073Spjd} 524155073Spjd 525155073Spjdvoid 526209583Simpconfig::close_pidfile() 527209583Simp{ 528252485Sasomers 529209583Simp pidfile_close(pfh); 530209583Simp} 531209583Simp 532209583Simpvoid 533155073Spjdconfig::remove_pidfile() 534155073Spjd{ 535252485Sasomers 536155073Spjd pidfile_remove(pfh); 537155073Spjd} 538155073Spjd 539155073Spjdvoid 540107665Simpconfig::add_attach(int prio, event_proc *p) 541107665Simp{ 542107665Simp p->set_priority(prio); 543107665Simp _attach_list.push_back(p); 544107665Simp} 545107665Simp 546107665Simpvoid 547107665Simpconfig::add_detach(int prio, event_proc *p) 548107665Simp{ 549107665Simp p->set_priority(prio); 550107665Simp _detach_list.push_back(p); 551107665Simp} 552107665Simp 553107665Simpvoid 554107665Simpconfig::add_directory(const char *dir) 555107665Simp{ 556107665Simp _dir_list.push_back(string(dir)); 557107665Simp} 558107665Simp 559107665Simpvoid 560107665Simpconfig::add_nomatch(int prio, event_proc *p) 561107665Simp{ 562107665Simp p->set_priority(prio); 563107665Simp _nomatch_list.push_back(p); 564107665Simp} 565107665Simp 566107665Simpvoid 567121487Simpconfig::add_notify(int prio, event_proc *p) 568121487Simp{ 569121487Simp p->set_priority(prio); 570121487Simp _notify_list.push_back(p); 571121487Simp} 572121487Simp 573121487Simpvoid 574107665Simpconfig::set_pidfile(const char *fn) 575107665Simp{ 576247758Seadler _pidfile = fn; 577107665Simp} 578107665Simp 579107665Simpvoid 580107665Simpconfig::push_var_table() 581107665Simp{ 582107665Simp var_list *vl; 583252485Sasomers 584107665Simp vl = new var_list(); 585107665Simp _var_list_table.push_back(vl); 586252481Sasomers devdlog(LOG_DEBUG, "Pushing table\n"); 587107665Simp} 588107665Simp 589107665Simpvoid 590107665Simpconfig::pop_var_table() 591107665Simp{ 592107665Simp delete _var_list_table.back(); 593107665Simp _var_list_table.pop_back(); 594252481Sasomers devdlog(LOG_DEBUG, "Popping table\n"); 595107665Simp} 596107665Simp 597107665Simpvoid 598107665Simpconfig::set_variable(const char *var, const char *val) 599107665Simp{ 600107665Simp _var_list_table.back()->set_variable(var, val); 601107665Simp} 602107665Simp 603107665Simpconst string & 604107665Simpconfig::get_variable(const string &var) 605107665Simp{ 606107665Simp vector<var_list *>::reverse_iterator i; 607107665Simp 608243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 609107665Simp if ((*i)->is_set(var)) 610108783Simp return ((*i)->get_variable(var)); 611107665Simp } 612107665Simp return (var_list::nothing); 613107665Simp} 614107665Simp 615108783Simpbool 616243930Seadlerconfig::is_id_char(char ch) const 617108783Simp{ 618252485Sasomers return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 619108783Simp ch == '-')); 620108783Simp} 621108783Simp 622108014Simpvoid 623114081Simpconfig::expand_one(const char *&src, string &dst) 624107665Simp{ 625108014Simp int count; 626210610Slulf string buffer; 627108014Simp 628108783Simp src++; 629108014Simp // $$ -> $ 630108014Simp if (*src == '$') { 631247762Seadler dst += *src++; 632108014Simp return; 633108014Simp } 634252485Sasomers 635108014Simp // $(foo) -> $(foo) 636108783Simp // Not sure if I want to support this or not, so for now we just pass 637108783Simp // it through. 638108014Simp if (*src == '(') { 639247762Seadler dst += '$'; 640108014Simp count = 1; 641114081Simp /* If the string ends before ) is matched , return. */ 642114081Simp while (count > 0 && *src) { 643108014Simp if (*src == ')') 644108014Simp count--; 645108014Simp else if (*src == '(') 646108014Simp count++; 647247762Seadler dst += *src++; 648108014Simp } 649108014Simp return; 650108014Simp } 651252485Sasomers 652247763Seadler // $[^A-Za-z] -> $\1 653108014Simp if (!isalpha(*src)) { 654247762Seadler dst += '$'; 655247762Seadler dst += *src++; 656108014Simp return; 657108014Simp } 658108014Simp 659108014Simp // $var -> replace with value 660114081Simp do { 661247762Seadler buffer += *src++; 662114084Simp } while (is_id_char(*src)); 663247758Seadler dst.append(get_variable(buffer)); 664107665Simp} 665107665Simp 666108014Simpconst string 667246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 668108014Simp{ 669246134Sian const char *var_at; 670114081Simp string dst; 671108014Simp 672246134Sian /* 673246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 674246134Sian * while parsing config files, as tested on 2013-01-30. 675246134Sian */ 676246134Sian dst.reserve(128); 677246134Sian 678246134Sian if (prepend != NULL) 679246134Sian dst = prepend; 680246134Sian 681246134Sian for (;;) { 682246134Sian var_at = strchr(src, '$'); 683246134Sian if (var_at == NULL) { 684246134Sian dst.append(src); 685246134Sian break; 686246134Sian } 687246134Sian dst.append(src, var_at - src); 688246134Sian src = var_at; 689246134Sian expand_one(src, dst); 690108014Simp } 691108014Simp 692246134Sian if (append != NULL) 693246134Sian dst.append(append); 694246134Sian 695114081Simp return (dst); 696108014Simp} 697108014Simp 698108783Simpbool 699247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 700108783Simp{ 701108783Simp char *walker; 702252485Sasomers 703108783Simp if (*buffer == '\0') 704108783Simp return (false); 705108783Simp walker = lhs = buffer; 706108783Simp while (is_id_char(*walker)) 707108783Simp walker++; 708108783Simp if (*walker != '=') 709108783Simp return (false); 710108783Simp walker++; // skip = 711108783Simp if (*walker == '"') { 712108783Simp walker++; // skip " 713108783Simp rhs = walker; 714108783Simp while (*walker && *walker != '"') 715108783Simp walker++; 716108783Simp if (*walker != '"') 717108783Simp return (false); 718108783Simp rhs[-2] = '\0'; 719108783Simp *walker++ = '\0'; 720108783Simp } else { 721108783Simp rhs = walker; 722108783Simp while (*walker && !isspace(*walker)) 723108783Simp walker++; 724108783Simp if (*walker != '\0') 725108783Simp *walker++ = '\0'; 726108783Simp rhs[-1] = '\0'; 727108783Simp } 728113785Simp while (isspace(*walker)) 729113785Simp walker++; 730108783Simp buffer = walker; 731108783Simp return (true); 732108783Simp} 733108783Simp 734108783Simp 735108783Simpchar * 736108783Simpconfig::set_vars(char *buffer) 737108783Simp{ 738108783Simp char *lhs; 739108783Simp char *rhs; 740108783Simp 741108783Simp while (1) { 742108783Simp if (!chop_var(buffer, lhs, rhs)) 743108783Simp break; 744108783Simp set_variable(lhs, rhs); 745108783Simp } 746108783Simp return (buffer); 747108783Simp} 748108783Simp 749108783Simpvoid 750108783Simpconfig::find_and_execute(char type) 751108783Simp{ 752108783Simp vector<event_proc *> *l; 753108783Simp vector<event_proc *>::const_iterator i; 754151486Sbrooks const char *s; 755108783Simp 756108783Simp switch (type) { 757108783Simp default: 758108783Simp return; 759121487Simp case notify: 760121487Simp l = &_notify_list; 761121487Simp s = "notify"; 762121487Simp break; 763108783Simp case nomatch: 764108783Simp l = &_nomatch_list; 765108783Simp s = "nomatch"; 766108783Simp break; 767108783Simp case attach: 768108783Simp l = &_attach_list; 769108783Simp s = "attach"; 770108783Simp break; 771108783Simp case detach: 772108783Simp l = &_detach_list; 773108783Simp s = "detach"; 774108783Simp break; 775108783Simp } 776252481Sasomers devdlog(LOG_DEBUG, "Processing %s event\n", s); 777243931Seadler for (i = l->begin(); i != l->end(); ++i) { 778108783Simp if ((*i)->matches(*this)) { 779108783Simp (*i)->run(*this); 780108783Simp break; 781108783Simp } 782108783Simp } 783108783Simp 784108783Simp} 785108783Simp 786252485Sasomers 787107665Simpstatic void 788108783Simpprocess_event(char *buffer) 789107665Simp{ 790107665Simp char type; 791107665Simp char *sp; 792107665Simp 793108783Simp sp = buffer + 1; 794260519Sasomers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 795107665Simp type = *buffer++; 796108783Simp cfg.push_var_table(); 797108783Simp // No match doesn't have a device, and the format is a little 798108783Simp // different, so handle it separately. 799121487Simp switch (type) { 800121487Simp case notify: 801121487Simp sp = cfg.set_vars(sp); 802121487Simp break; 803121487Simp case nomatch: 804145218Simp //? at location pnp-info on bus 805145218Simp sp = strchr(sp, ' '); 806145218Simp if (sp == NULL) 807145218Simp return; /* Can't happen? */ 808145218Simp *sp++ = '\0'; 809213646Simp while (isspace(*sp)) 810213646Simp sp++; 811121487Simp if (strncmp(sp, "at ", 3) == 0) 812121487Simp sp += 3; 813121487Simp sp = cfg.set_vars(sp); 814213646Simp while (isspace(*sp)) 815213646Simp sp++; 816121487Simp if (strncmp(sp, "on ", 3) == 0) 817121487Simp cfg.set_variable("bus", sp + 3); 818121487Simp break; 819121487Simp case attach: /*FALLTHROUGH*/ 820121487Simp case detach: 821108783Simp sp = strchr(sp, ' '); 822108783Simp if (sp == NULL) 823108783Simp return; /* Can't happen? */ 824108783Simp *sp++ = '\0'; 825108783Simp cfg.set_variable("device-name", buffer); 826213646Simp while (isspace(*sp)) 827213646Simp sp++; 828113785Simp if (strncmp(sp, "at ", 3) == 0) 829113785Simp sp += 3; 830113785Simp sp = cfg.set_vars(sp); 831213646Simp while (isspace(*sp)) 832213646Simp sp++; 833113785Simp if (strncmp(sp, "on ", 3) == 0) 834113785Simp cfg.set_variable("bus", sp + 3); 835121487Simp break; 836108783Simp } 837252485Sasomers 838108783Simp cfg.find_and_execute(type); 839108783Simp cfg.pop_var_table(); 840107665Simp} 841107665Simp 842131397Simpint 843270242Sasomerscreate_socket(const char *name, int socktype) 844131397Simp{ 845131397Simp int fd, slen; 846131397Simp struct sockaddr_un sun; 847131397Simp 848270242Sasomers if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 849131397Simp err(1, "socket"); 850131397Simp bzero(&sun, sizeof(sun)); 851131397Simp sun.sun_family = AF_UNIX; 852131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 853131397Simp slen = SUN_LEN(&sun); 854131397Simp unlink(name); 855147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 856147973Smarcus err(1, "fcntl"); 857236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 858131397Simp err(1, "bind"); 859131397Simp listen(fd, 4); 860314425Sasomers if (chown(name, 0, 0)) /* XXX - root.wheel */ 861314425Sasomers err(1, "chown"); 862314425Sasomers if (chmod(name, 0666)) 863314425Sasomers err(1, "chmod"); 864131397Simp return (fd); 865131397Simp} 866131397Simp 867293290Sbdreweryunsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ 868246121Sianunsigned int num_clients; 869131397Simp 870270242Sasomerslist<client_t> clients; 871270242Sasomers 872131397Simpvoid 873131397Simpnotify_clients(const char *data, int len) 874131397Simp{ 875270242Sasomers list<client_t>::iterator i; 876131397Simp 877246121Sian /* 878246121Sian * Deliver the data to all clients. Throw clients overboard at the 879246121Sian * first sign of trouble. This reaps clients who've died or closed 880246121Sian * their sockets, and also clients who are alive but failing to keep up 881246121Sian * (or who are maliciously not reading, to consume buffer space in 882246121Sian * kernel memory or tie up the limited number of available connections). 883246121Sian */ 884246121Sian for (i = clients.begin(); i != clients.end(); ) { 885270242Sasomers int flags; 886270242Sasomers if (i->socktype == SOCK_SEQPACKET) 887270242Sasomers flags = MSG_EOR; 888270242Sasomers else 889270242Sasomers flags = 0; 890270242Sasomers 891270242Sasomers if (send(i->fd, data, len, flags) != len) { 892246121Sian --num_clients; 893270242Sasomers close(i->fd); 894246121Sian i = clients.erase(i); 895270242Sasomers devdlog(LOG_WARNING, "notify_clients: send() failed; " 896252481Sasomers "dropping unresponsive client\n"); 897246121Sian } else 898246121Sian ++i; 899131397Simp } 900246121Sian} 901131397Simp 902246121Sianvoid 903246121Siancheck_clients(void) 904246121Sian{ 905246121Sian int s; 906246121Sian struct pollfd pfd; 907270242Sasomers list<client_t>::iterator i; 908246121Sian 909246121Sian /* 910246121Sian * Check all existing clients to see if any of them have disappeared. 911246121Sian * Normally we reap clients when we get an error trying to send them an 912246121Sian * event. This check eliminates the problem of an ever-growing list of 913246121Sian * zombie clients because we're never writing to them on a system 914246121Sian * without frequent device-change activity. 915246121Sian */ 916246121Sian pfd.events = 0; 917246121Sian for (i = clients.begin(); i != clients.end(); ) { 918270242Sasomers pfd.fd = i->fd; 919246121Sian s = poll(&pfd, 1, 0); 920246121Sian if ((s < 0 && s != EINTR ) || 921246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 922246121Sian --num_clients; 923270242Sasomers close(i->fd); 924246121Sian i = clients.erase(i); 925252481Sasomers devdlog(LOG_NOTICE, "check_clients: " 926252481Sasomers "dropping disconnected client\n"); 927246121Sian } else 928246121Sian ++i; 929246121Sian } 930131397Simp} 931131397Simp 932131397Simpvoid 933270242Sasomersnew_client(int fd, int socktype) 934131397Simp{ 935270242Sasomers client_t s; 936260519Sasomers int sndbuf_size; 937131397Simp 938246121Sian /* 939246121Sian * First go reap any zombie clients, then accept the connection, and 940246121Sian * shut down the read side to stop clients from consuming kernel memory 941246121Sian * by sending large buffers full of data we'll never read. 942246121Sian */ 943246121Sian check_clients(); 944270242Sasomers s.socktype = socktype; 945270242Sasomers s.fd = accept(fd, NULL, NULL); 946270242Sasomers if (s.fd != -1) { 947260519Sasomers sndbuf_size = CLIENT_BUFSIZE; 948270242Sasomers if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 949260519Sasomers sizeof(sndbuf_size))) 950260519Sasomers err(1, "setsockopt"); 951270242Sasomers shutdown(s.fd, SHUT_RD); 952131397Simp clients.push_back(s); 953246121Sian ++num_clients; 954260519Sasomers } else 955260519Sasomers err(1, "accept"); 956131397Simp} 957131397Simp 958107665Simpstatic void 959107665Simpevent_loop(void) 960107665Simp{ 961107665Simp int rv; 962107665Simp int fd; 963107665Simp char buffer[DEVCTL_MAXBUF]; 964113790Simp int once = 0; 965270242Sasomers int stream_fd, seqpacket_fd, max_fd; 966246121Sian int accepting; 967113790Simp timeval tv; 968113790Simp fd_set fds; 969107665Simp 970240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 971107665Simp if (fd == -1) 972131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 973270242Sasomers stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 974270242Sasomers seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 975246121Sian accepting = 1; 976270242Sasomers max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 977247756Seadler while (!romeo_must_die) { 978263880Sasomers if (!once && !no_daemon && !daemonize_quick) { 979113790Simp // Check to see if we have any events pending. 980113790Simp tv.tv_sec = 0; 981113790Simp tv.tv_usec = 0; 982113790Simp FD_ZERO(&fds); 983113790Simp FD_SET(fd, &fds); 984113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 985113790Simp // No events -> we've processed all pending events 986117944Simp if (rv == 0) { 987252481Sasomers devdlog(LOG_DEBUG, "Calling daemon\n"); 988155073Spjd cfg.remove_pidfile(); 989155073Spjd cfg.open_pidfile(); 990113790Simp daemon(0, 0); 991155073Spjd cfg.write_pidfile(); 992113790Simp once++; 993113790Simp } 994113790Simp } 995246121Sian /* 996246121Sian * When we've already got the max number of clients, stop 997270242Sasomers * accepting new connections (don't put the listening sockets in 998270242Sasomers * the set), shrink the accept() queue to reject connections 999270242Sasomers * quickly, and poll the existing clients more often, so that we 1000270242Sasomers * notice more quickly when any of them disappear to free up 1001270242Sasomers * client slots. 1002246121Sian */ 1003131397Simp FD_ZERO(&fds); 1004131397Simp FD_SET(fd, &fds); 1005246121Sian if (num_clients < max_clients) { 1006246121Sian if (!accepting) { 1007270242Sasomers listen(stream_fd, max_clients); 1008270242Sasomers listen(seqpacket_fd, max_clients); 1009246121Sian accepting = 1; 1010246121Sian } 1011270242Sasomers FD_SET(stream_fd, &fds); 1012270242Sasomers FD_SET(seqpacket_fd, &fds); 1013246121Sian tv.tv_sec = 60; 1014246121Sian tv.tv_usec = 0; 1015246121Sian } else { 1016246121Sian if (accepting) { 1017270242Sasomers listen(stream_fd, 0); 1018270242Sasomers listen(seqpacket_fd, 0); 1019246121Sian accepting = 0; 1020246121Sian } 1021246121Sian tv.tv_sec = 2; 1022246121Sian tv.tv_usec = 0; 1023246121Sian } 1024246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 1025252482Sasomers if (got_siginfo) { 1026260519Sasomers devdlog(LOG_NOTICE, "Events received so far=%u\n", 1027252482Sasomers total_events); 1028252482Sasomers got_siginfo = 0; 1029252482Sasomers } 1030131397Simp if (rv == -1) { 1031131397Simp if (errno == EINTR) 1032131397Simp continue; 1033131397Simp err(1, "select"); 1034246121Sian } else if (rv == 0) 1035246121Sian check_clients(); 1036131397Simp if (FD_ISSET(fd, &fds)) { 1037131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 1038131397Simp if (rv > 0) { 1039252482Sasomers total_events++; 1040252481Sasomers if (rv == sizeof(buffer) - 1) { 1041252481Sasomers devdlog(LOG_WARNING, "Warning: " 1042252481Sasomers "available event data exceeded " 1043252481Sasomers "buffer space\n"); 1044252481Sasomers } 1045131397Simp notify_clients(buffer, rv); 1046107665Simp buffer[rv] = '\0'; 1047131397Simp while (buffer[--rv] == '\n') 1048131397Simp buffer[rv] = '\0'; 1049314425Sasomers try { 1050314425Sasomers process_event(buffer); 1051314425Sasomers } 1052314425Sasomers catch (std::length_error e) { 1053314425Sasomers devdlog(LOG_ERR, "Dropping event %s " 1054314425Sasomers "due to low memory", buffer); 1055314425Sasomers } 1056131397Simp } else if (rv < 0) { 1057131397Simp if (errno != EINTR) 1058131397Simp break; 1059131397Simp } else { 1060131397Simp /* EOF */ 1061107665Simp break; 1062131397Simp } 1063107665Simp } 1064270242Sasomers if (FD_ISSET(stream_fd, &fds)) 1065270242Sasomers new_client(stream_fd, SOCK_STREAM); 1066270242Sasomers /* 1067270242Sasomers * Aside from the socket type, both sockets use the same 1068270242Sasomers * protocol, so we can process clients the same way. 1069270242Sasomers */ 1070270242Sasomers if (FD_ISSET(seqpacket_fd, &fds)) 1071270242Sasomers new_client(seqpacket_fd, SOCK_SEQPACKET); 1072107665Simp } 1073321827Sasomers cfg.remove_pidfile(); 1074314425Sasomers close(seqpacket_fd); 1075314425Sasomers close(stream_fd); 1076107665Simp close(fd); 1077107665Simp} 1078252485Sasomers 1079107665Simp/* 1080107665Simp * functions that the parser uses. 1081107665Simp */ 1082107665Simpvoid 1083107665Simpadd_attach(int prio, event_proc *p) 1084107665Simp{ 1085107665Simp cfg.add_attach(prio, p); 1086107665Simp} 1087107665Simp 1088107665Simpvoid 1089107665Simpadd_detach(int prio, event_proc *p) 1090107665Simp{ 1091107665Simp cfg.add_detach(prio, p); 1092107665Simp} 1093107665Simp 1094107665Simpvoid 1095107665Simpadd_directory(const char *dir) 1096107665Simp{ 1097107665Simp cfg.add_directory(dir); 1098107665Simp free(const_cast<char *>(dir)); 1099107665Simp} 1100107665Simp 1101107665Simpvoid 1102107665Simpadd_nomatch(int prio, event_proc *p) 1103107665Simp{ 1104107665Simp cfg.add_nomatch(prio, p); 1105107665Simp} 1106107665Simp 1107121487Simpvoid 1108121487Simpadd_notify(int prio, event_proc *p) 1109121487Simp{ 1110121487Simp cfg.add_notify(prio, p); 1111121487Simp} 1112121487Simp 1113107665Simpevent_proc * 1114107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1115107665Simp{ 1116107665Simp if (ep == NULL) 1117107665Simp ep = new event_proc(); 1118107665Simp ep->add(eps); 1119107665Simp return (ep); 1120107665Simp} 1121107665Simp 1122107665Simpeps * 1123107665Simpnew_action(const char *cmd) 1124107665Simp{ 1125107665Simp eps *e = new action(cmd); 1126107665Simp free(const_cast<char *>(cmd)); 1127107665Simp return (e); 1128107665Simp} 1129107665Simp 1130107665Simpeps * 1131107665Simpnew_match(const char *var, const char *re) 1132107665Simp{ 1133108014Simp eps *e = new match(cfg, var, re); 1134107665Simp free(const_cast<char *>(var)); 1135107665Simp free(const_cast<char *>(re)); 1136107665Simp return (e); 1137107665Simp} 1138107665Simp 1139147874Simpeps * 1140147874Simpnew_media(const char *var, const char *re) 1141147874Simp{ 1142147874Simp eps *e = new media(cfg, var, re); 1143147874Simp free(const_cast<char *>(var)); 1144147874Simp free(const_cast<char *>(re)); 1145147874Simp return (e); 1146147874Simp} 1147147874Simp 1148107665Simpvoid 1149107665Simpset_pidfile(const char *name) 1150107665Simp{ 1151107665Simp cfg.set_pidfile(name); 1152107665Simp free(const_cast<char *>(name)); 1153107665Simp} 1154107665Simp 1155107665Simpvoid 1156107665Simpset_variable(const char *var, const char *val) 1157107665Simp{ 1158107665Simp cfg.set_variable(var, val); 1159107665Simp free(const_cast<char *>(var)); 1160107665Simp free(const_cast<char *>(val)); 1161107665Simp} 1162107665Simp 1163107665Simp 1164252485Sasomers 1165107665Simpstatic void 1166107665Simpgensighand(int) 1167107665Simp{ 1168247754Seadler romeo_must_die = 1; 1169107665Simp} 1170107665Simp 1171252481Sasomers/* 1172252482Sasomers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1173252482Sasomers * as appropriate 1174252482Sasomers */ 1175252482Sasomersstatic void 1176252482Sasomerssiginfohand(int) 1177252482Sasomers{ 1178252482Sasomers got_siginfo = 1; 1179252482Sasomers} 1180252482Sasomers 1181252482Sasomers/* 1182252481Sasomers * Local logging function. Prints to syslog if we're daemonized; syslog 1183252481Sasomers * otherwise. 1184252481Sasomers */ 1185107665Simpstatic void 1186252481Sasomersdevdlog(int priority, const char* fmt, ...) 1187252481Sasomers{ 1188252481Sasomers va_list argp; 1189252481Sasomers 1190252481Sasomers va_start(argp, fmt); 1191263880Sasomers if (no_daemon) 1192252481Sasomers vfprintf(stderr, fmt, argp); 1193263880Sasomers else if ((! quiet_mode) || (priority <= LOG_WARNING)) 1194252481Sasomers vsyslog(priority, fmt, argp); 1195252481Sasomers va_end(argp); 1196252481Sasomers} 1197252481Sasomers 1198252481Sasomersstatic void 1199107665Simpusage() 1200107665Simp{ 1201263880Sasomers fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1202246121Sian getprogname()); 1203107665Simp exit(1); 1204107665Simp} 1205107665Simp 1206113787Simpstatic void 1207113787Simpcheck_devd_enabled() 1208113787Simp{ 1209113787Simp int val = 0; 1210113787Simp size_t len; 1211113787Simp 1212113787Simp len = sizeof(val); 1213114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1214113787Simp errx(1, "devctl sysctl missing from kernel!"); 1215263963Smjg if (val == 0) { 1216263963Smjg warnx("Setting " SYSCTL " to 1000"); 1217263963Smjg val = 1000; 1218314425Sasomers if (sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val))) 1219314425Sasomers err(1, "sysctlbyname"); 1220113787Simp } 1221113787Simp} 1222113787Simp 1223107665Simp/* 1224107665Simp * main 1225107665Simp */ 1226107665Simpint 1227107665Simpmain(int argc, char **argv) 1228107665Simp{ 1229107665Simp int ch; 1230107665Simp 1231113787Simp check_devd_enabled(); 1232263880Sasomers while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 1233107665Simp switch (ch) { 1234107665Simp case 'd': 1235263880Sasomers no_daemon = 1; 1236107665Simp break; 1237152770Sjkoshy case 'f': 1238152770Sjkoshy configfile = optarg; 1239152770Sjkoshy break; 1240246121Sian case 'l': 1241246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1242246121Sian break; 1243113790Simp case 'n': 1244263880Sasomers daemonize_quick = 1; 1245113790Simp break; 1246263880Sasomers case 'q': 1247263880Sasomers quiet_mode = 1; 1248263880Sasomers break; 1249107665Simp default: 1250107665Simp usage(); 1251107665Simp } 1252107665Simp } 1253107665Simp 1254107665Simp cfg.parse(); 1255263880Sasomers if (!no_daemon && daemonize_quick) { 1256155073Spjd cfg.open_pidfile(); 1257107665Simp daemon(0, 0); 1258155073Spjd cfg.write_pidfile(); 1259117246Simp } 1260146306Simp signal(SIGPIPE, SIG_IGN); 1261107665Simp signal(SIGHUP, gensighand); 1262107665Simp signal(SIGINT, gensighand); 1263107665Simp signal(SIGTERM, gensighand); 1264252482Sasomers signal(SIGINFO, siginfohand); 1265107665Simp event_loop(); 1266107665Simp return (0); 1267107665Simp} 1268