devd.cc revision 292020
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 292020 2015-12-09 18:55:25Z 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 103270004Sasomers#define STREAMPIPE "/var/run/devd.pipe" 104270004Sasomers#define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 105107665Simp#define CF "/etc/devd.conf" 106263758Smjg#define SYSCTL "hw.bus.devctl_queue" 107107665Simp 108259339Sasomers/* 109259339Sasomers * Since the client socket is nonblocking, we must increase its send buffer to 110259339Sasomers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 111292020Sasomers * buffer, so the client can't increase the buffersize by itself. 112259339Sasomers * 113259339Sasomers * For example, when creating a ZFS pool, devd emits one 165 character 114292020Sasomers * resource.fs.zfs.statechange message for each vdev in the pool. The kernel 115292020Sasomers * allocates a 4608B mbuf for each message. Modern technology places a limit of 116292020Sasomers * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger 117292020Sasomers * than that. 118292020Sasomers * 119292020Sasomers * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf 120292020Sasomers * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf 121292020Sasomers * 122292020Sasomers * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. 123292020Sasomers * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * 124292020Sasomers * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by 125292020Sasomers * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we 126292020Sasomers * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the 127292020Sasomers * kernel will set the mbuf limit to 2MB, which is just large enough for 450 128292020Sasomers * drives. It also happens to be the same as the hardcoded maximum value. 129259339Sasomers */ 130292020Sasomers#define CLIENT_BUFSIZE 262144 131259339Sasomers 132107665Simpusing namespace std; 133107665Simp 134270004Sasomerstypedef struct client { 135270004Sasomers int fd; 136270004Sasomers int socktype; 137270004Sasomers} client_t; 138270004Sasomers 139107665Simpextern FILE *yyin; 140107665Simpextern int lineno; 141107665Simp 142121487Simpstatic const char notify = '!'; 143108783Simpstatic const char nomatch = '?'; 144108783Simpstatic const char attach = '+'; 145108783Simpstatic const char detach = '-'; 146108783Simp 147155073Spjdstatic struct pidfh *pfh; 148155073Spjd 149262914Sasomersstatic int no_daemon = 0; 150262914Sasomersstatic int daemonize_quick = 0; 151262914Sasomersstatic int quiet_mode = 0; 152252482Sasomersstatic unsigned total_events = 0; 153252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0; 154247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0; 155107665Simp 156152770Sjkoshystatic const char *configfile = CF; 157152770Sjkoshy 158253046Sasomersstatic void devdlog(int priority, const char* message, ...) 159253046Sasomers __printflike(2, 3); 160107665Simpstatic void event_loop(void); 161107665Simpstatic void usage(void); 162107665Simp 163108783Simptemplate <class T> void 164108783Simpdelete_and_clear(vector<T *> &v) 165108783Simp{ 166108783Simp typename vector<T *>::const_iterator i; 167108783Simp 168243931Seadler for (i = v.begin(); i != v.end(); ++i) 169108783Simp delete *i; 170108783Simp v.clear(); 171108783Simp} 172108783Simp 173107665Simpconfig cfg; 174107665Simp 175107665Simpevent_proc::event_proc() : _prio(-1) 176107665Simp{ 177246134Sian _epsvec.reserve(4); 178107665Simp} 179107665Simp 180107665Simpevent_proc::~event_proc() 181107665Simp{ 182152406Sbland delete_and_clear(_epsvec); 183107665Simp} 184107665Simp 185107665Simpvoid 186107665Simpevent_proc::add(eps *eps) 187107665Simp{ 188107665Simp _epsvec.push_back(eps); 189107665Simp} 190107665Simp 191107665Simpbool 192243930Seadlerevent_proc::matches(config &c) const 193107665Simp{ 194107665Simp vector<eps *>::const_iterator i; 195107665Simp 196243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 197107665Simp if (!(*i)->do_match(c)) 198107665Simp return (false); 199107665Simp return (true); 200107665Simp} 201107665Simp 202107665Simpbool 203243930Seadlerevent_proc::run(config &c) const 204107665Simp{ 205107665Simp vector<eps *>::const_iterator i; 206252485Sasomers 207243931Seadler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 208107665Simp if (!(*i)->do_action(c)) 209107665Simp return (false); 210107665Simp return (true); 211107665Simp} 212107665Simp 213107665Simpaction::action(const char *cmd) 214252485Sasomers : _cmd(cmd) 215107665Simp{ 216107665Simp // nothing 217107665Simp} 218107665Simp 219107665Simpaction::~action() 220107665Simp{ 221107665Simp // nothing 222107665Simp} 223107665Simp 224209583Simpstatic int 225209583Simpmy_system(const char *command) 226209583Simp{ 227209583Simp pid_t pid, savedpid; 228209583Simp int pstat; 229209583Simp struct sigaction ign, intact, quitact; 230209583Simp sigset_t newsigblock, oldsigblock; 231209583Simp 232209583Simp if (!command) /* just checking... */ 233252485Sasomers return (1); 234209583Simp 235209583Simp /* 236209583Simp * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 237209583Simp * existing signal dispositions. 238209583Simp */ 239209583Simp ign.sa_handler = SIG_IGN; 240209583Simp ::sigemptyset(&ign.sa_mask); 241209583Simp ign.sa_flags = 0; 242209583Simp ::sigaction(SIGINT, &ign, &intact); 243209583Simp ::sigaction(SIGQUIT, &ign, &quitact); 244209583Simp ::sigemptyset(&newsigblock); 245209583Simp ::sigaddset(&newsigblock, SIGCHLD); 246209583Simp ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 247209583Simp switch (pid = ::fork()) { 248209583Simp case -1: /* error */ 249209583Simp break; 250209583Simp case 0: /* child */ 251209583Simp /* 252209583Simp * Restore original signal dispositions and exec the command. 253209583Simp */ 254209583Simp ::sigaction(SIGINT, &intact, NULL); 255209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 256209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 257209583Simp /* 258209583Simp * Close the PID file, and all other open descriptors. 259209583Simp * Inherit std{in,out,err} only. 260209583Simp */ 261209583Simp cfg.close_pidfile(); 262209583Simp ::closefrom(3); 263209583Simp ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 264209583Simp ::_exit(127); 265209583Simp default: /* parent */ 266209583Simp savedpid = pid; 267209583Simp do { 268209583Simp pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 269209583Simp } while (pid == -1 && errno == EINTR); 270209583Simp break; 271209583Simp } 272209583Simp ::sigaction(SIGINT, &intact, NULL); 273209583Simp ::sigaction(SIGQUIT, &quitact, NULL); 274209583Simp ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 275209583Simp return (pid == -1 ? -1 : pstat); 276209583Simp} 277209583Simp 278107665Simpbool 279108014Simpaction::do_action(config &c) 280107665Simp{ 281246134Sian string s = c.expand_string(_cmd.c_str()); 282257799Savg devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 283209583Simp my_system(s.c_str()); 284107665Simp return (true); 285107665Simp} 286107665Simp 287246134Sianmatch::match(config &c, const char *var, const char *re) : 288246134Sian _inv(re[0] == '!'), 289246134Sian _var(var), 290246134Sian _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 291107665Simp{ 292154109Simp regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 293107665Simp} 294107665Simp 295107665Simpmatch::~match() 296107665Simp{ 297108014Simp regfree(&_regex); 298107665Simp} 299107665Simp 300107665Simpbool 301108014Simpmatch::do_match(config &c) 302107665Simp{ 303210610Slulf const string &value = c.get_variable(_var); 304108014Simp bool retval; 305108014Simp 306252485Sasomers /* 307252481Sasomers * This function gets called WAY too often to justify calling syslog() 308252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 309252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 310252481Sasomers * log when we're in -d mode. 311252481Sasomers */ 312262914Sasomers if (no_daemon) { 313252481Sasomers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 314226775Shrs _var.c_str(), value.c_str(), _re.c_str(), _inv); 315252481Sasomers } 316108783Simp 317108014Simp retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 318226775Shrs if (_inv == 1) 319226775Shrs retval = (retval == 0) ? 1 : 0; 320226775Shrs 321252485Sasomers return (retval); 322107665Simp} 323107665Simp 324147874Simp#include <sys/sockio.h> 325147874Simp#include <net/if.h> 326147874Simp#include <net/if_media.h> 327147874Simp 328151486Sbrooksmedia::media(config &, const char *var, const char *type) 329147874Simp : _var(var), _type(-1) 330147874Simp{ 331147874Simp static struct ifmedia_description media_types[] = { 332147874Simp { IFM_ETHER, "Ethernet" }, 333147874Simp { IFM_TOKEN, "Tokenring" }, 334147874Simp { IFM_FDDI, "FDDI" }, 335147874Simp { IFM_IEEE80211, "802.11" }, 336147874Simp { IFM_ATM, "ATM" }, 337147874Simp { -1, "unknown" }, 338147874Simp { 0, NULL }, 339147874Simp }; 340243931Seadler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 341147874Simp if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 342147874Simp _type = media_types[i].ifmt_word; 343147874Simp break; 344147874Simp } 345147874Simp} 346147874Simp 347147874Simpmedia::~media() 348147874Simp{ 349147874Simp} 350147874Simp 351147874Simpbool 352147874Simpmedia::do_match(config &c) 353147874Simp{ 354150949Simp string value; 355147874Simp struct ifmediareq ifmr; 356147874Simp bool retval; 357147874Simp int s; 358147874Simp 359150949Simp // Since we can be called from both a device attach/detach 360150949Simp // context where device-name is defined and what we want, 361150949Simp // as well as from a link status context, where subsystem is 362150949Simp // the name of interest, first try device-name and fall back 363150949Simp // to subsystem if none exists. 364150949Simp value = c.get_variable("device-name"); 365247761Seadler if (value.empty()) 366151480Simp value = c.get_variable("subsystem"); 367252481Sasomers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 368147874Simp value.c_str(), _type); 369147874Simp 370147874Simp retval = false; 371147874Simp 372147874Simp s = socket(PF_INET, SOCK_DGRAM, 0); 373147874Simp if (s >= 0) { 374147874Simp memset(&ifmr, 0, sizeof(ifmr)); 375147874Simp strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 376147874Simp 377147874Simp if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 378147874Simp ifmr.ifm_status & IFM_AVALID) { 379252481Sasomers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 380147874Simp value.c_str(), IFM_TYPE(ifmr.ifm_active)); 381147874Simp retval = (IFM_TYPE(ifmr.ifm_active) == _type); 382147874Simp } else if (_type == -1) { 383252481Sasomers devdlog(LOG_DEBUG, "%s has unknown media type\n", 384147874Simp value.c_str()); 385147874Simp retval = true; 386147874Simp } 387147874Simp close(s); 388147874Simp } 389147874Simp 390252485Sasomers return (retval); 391147874Simp} 392147874Simp 393107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 394107665Simpconst string var_list::nothing = ""; 395107665Simp 396107665Simpconst string & 397107665Simpvar_list::get_variable(const string &var) const 398107665Simp{ 399107665Simp map<string, string>::const_iterator i; 400107665Simp 401107665Simp i = _vars.find(var); 402107665Simp if (i == _vars.end()) 403108783Simp return (var_list::bogus); 404107665Simp return (i->second); 405107665Simp} 406107665Simp 407107665Simpbool 408107665Simpvar_list::is_set(const string &var) const 409107665Simp{ 410107665Simp return (_vars.find(var) != _vars.end()); 411107665Simp} 412107665Simp 413107665Simpvoid 414107665Simpvar_list::set_variable(const string &var, const string &val) 415107665Simp{ 416252481Sasomers /* 417252481Sasomers * This function gets called WAY too often to justify calling syslog() 418252481Sasomers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 419252481Sasomers * can consume excessive amounts of systime inside of connect(). Only 420252481Sasomers * log when we're in -d mode. 421252481Sasomers */ 422262914Sasomers if (no_daemon) 423252481Sasomers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 424107665Simp _vars[var] = val; 425107665Simp} 426107665Simp 427107665Simpvoid 428107665Simpconfig::reset(void) 429107665Simp{ 430107665Simp _dir_list.clear(); 431108783Simp delete_and_clear(_var_list_table); 432108783Simp delete_and_clear(_attach_list); 433108783Simp delete_and_clear(_detach_list); 434108783Simp delete_and_clear(_nomatch_list); 435121487Simp delete_and_clear(_notify_list); 436107665Simp} 437107665Simp 438107665Simpvoid 439107665Simpconfig::parse_one_file(const char *fn) 440107665Simp{ 441252481Sasomers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 442107665Simp yyin = fopen(fn, "r"); 443107665Simp if (yyin == NULL) 444107665Simp err(1, "Cannot open config file %s", fn); 445157746Smaxim lineno = 1; 446107665Simp if (yyparse() != 0) 447107665Simp errx(1, "Cannot parse %s at line %d", fn, lineno); 448107665Simp fclose(yyin); 449107665Simp} 450107665Simp 451107665Simpvoid 452107665Simpconfig::parse_files_in_dir(const char *dirname) 453107665Simp{ 454107665Simp DIR *dirp; 455107665Simp struct dirent *dp; 456107665Simp char path[PATH_MAX]; 457107665Simp 458252481Sasomers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 459107665Simp dirp = opendir(dirname); 460107665Simp if (dirp == NULL) 461107665Simp return; 462107665Simp readdir(dirp); /* Skip . */ 463107665Simp readdir(dirp); /* Skip .. */ 464107665Simp while ((dp = readdir(dirp)) != NULL) { 465107665Simp if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 466107665Simp snprintf(path, sizeof(path), "%s/%s", 467107665Simp dirname, dp->d_name); 468107665Simp parse_one_file(path); 469107665Simp } 470107665Simp } 471215607Skevlo closedir(dirp); 472107665Simp} 473107665Simp 474108783Simpclass epv_greater { 475108783Simppublic: 476243930Seadler int operator()(event_proc *const&l1, event_proc *const&l2) const 477108783Simp { 478108783Simp return (l1->get_priority() > l2->get_priority()); 479108783Simp } 480108783Simp}; 481108783Simp 482107665Simpvoid 483108783Simpconfig::sort_vector(vector<event_proc *> &v) 484108783Simp{ 485243907Sdim stable_sort(v.begin(), v.end(), epv_greater()); 486108783Simp} 487108783Simp 488108783Simpvoid 489107665Simpconfig::parse(void) 490107665Simp{ 491107665Simp vector<string>::const_iterator i; 492107665Simp 493152770Sjkoshy parse_one_file(configfile); 494243931Seadler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 495107665Simp parse_files_in_dir((*i).c_str()); 496108783Simp sort_vector(_attach_list); 497108783Simp sort_vector(_detach_list); 498108783Simp sort_vector(_nomatch_list); 499121487Simp sort_vector(_notify_list); 500107665Simp} 501107665Simp 502107665Simpvoid 503155073Spjdconfig::open_pidfile() 504107665Simp{ 505155073Spjd pid_t otherpid; 506252485Sasomers 507247758Seadler if (_pidfile.empty()) 508107665Simp return; 509155073Spjd pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 510155073Spjd if (pfh == NULL) { 511155073Spjd if (errno == EEXIST) 512155073Spjd errx(1, "devd already running, pid: %d", (int)otherpid); 513155073Spjd warn("cannot open pid file"); 514155073Spjd } 515107665Simp} 516107665Simp 517107665Simpvoid 518155073Spjdconfig::write_pidfile() 519155073Spjd{ 520252485Sasomers 521155073Spjd pidfile_write(pfh); 522155073Spjd} 523155073Spjd 524155073Spjdvoid 525209583Simpconfig::close_pidfile() 526209583Simp{ 527252485Sasomers 528209583Simp pidfile_close(pfh); 529209583Simp} 530209583Simp 531209583Simpvoid 532155073Spjdconfig::remove_pidfile() 533155073Spjd{ 534252485Sasomers 535155073Spjd pidfile_remove(pfh); 536155073Spjd} 537155073Spjd 538155073Spjdvoid 539107665Simpconfig::add_attach(int prio, event_proc *p) 540107665Simp{ 541107665Simp p->set_priority(prio); 542107665Simp _attach_list.push_back(p); 543107665Simp} 544107665Simp 545107665Simpvoid 546107665Simpconfig::add_detach(int prio, event_proc *p) 547107665Simp{ 548107665Simp p->set_priority(prio); 549107665Simp _detach_list.push_back(p); 550107665Simp} 551107665Simp 552107665Simpvoid 553107665Simpconfig::add_directory(const char *dir) 554107665Simp{ 555107665Simp _dir_list.push_back(string(dir)); 556107665Simp} 557107665Simp 558107665Simpvoid 559107665Simpconfig::add_nomatch(int prio, event_proc *p) 560107665Simp{ 561107665Simp p->set_priority(prio); 562107665Simp _nomatch_list.push_back(p); 563107665Simp} 564107665Simp 565107665Simpvoid 566121487Simpconfig::add_notify(int prio, event_proc *p) 567121487Simp{ 568121487Simp p->set_priority(prio); 569121487Simp _notify_list.push_back(p); 570121487Simp} 571121487Simp 572121487Simpvoid 573107665Simpconfig::set_pidfile(const char *fn) 574107665Simp{ 575247758Seadler _pidfile = fn; 576107665Simp} 577107665Simp 578107665Simpvoid 579107665Simpconfig::push_var_table() 580107665Simp{ 581107665Simp var_list *vl; 582252485Sasomers 583107665Simp vl = new var_list(); 584107665Simp _var_list_table.push_back(vl); 585252481Sasomers devdlog(LOG_DEBUG, "Pushing table\n"); 586107665Simp} 587107665Simp 588107665Simpvoid 589107665Simpconfig::pop_var_table() 590107665Simp{ 591107665Simp delete _var_list_table.back(); 592107665Simp _var_list_table.pop_back(); 593252481Sasomers devdlog(LOG_DEBUG, "Popping table\n"); 594107665Simp} 595107665Simp 596107665Simpvoid 597107665Simpconfig::set_variable(const char *var, const char *val) 598107665Simp{ 599107665Simp _var_list_table.back()->set_variable(var, val); 600107665Simp} 601107665Simp 602107665Simpconst string & 603107665Simpconfig::get_variable(const string &var) 604107665Simp{ 605107665Simp vector<var_list *>::reverse_iterator i; 606107665Simp 607243931Seadler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 608107665Simp if ((*i)->is_set(var)) 609108783Simp return ((*i)->get_variable(var)); 610107665Simp } 611107665Simp return (var_list::nothing); 612107665Simp} 613107665Simp 614108783Simpbool 615243930Seadlerconfig::is_id_char(char ch) const 616108783Simp{ 617252485Sasomers return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 618108783Simp ch == '-')); 619108783Simp} 620108783Simp 621108014Simpvoid 622114081Simpconfig::expand_one(const char *&src, string &dst) 623107665Simp{ 624108014Simp int count; 625210610Slulf string buffer; 626108014Simp 627108783Simp src++; 628108014Simp // $$ -> $ 629108014Simp if (*src == '$') { 630247762Seadler dst += *src++; 631108014Simp return; 632108014Simp } 633252485Sasomers 634108014Simp // $(foo) -> $(foo) 635108783Simp // Not sure if I want to support this or not, so for now we just pass 636108783Simp // it through. 637108014Simp if (*src == '(') { 638247762Seadler dst += '$'; 639108014Simp count = 1; 640114081Simp /* If the string ends before ) is matched , return. */ 641114081Simp while (count > 0 && *src) { 642108014Simp if (*src == ')') 643108014Simp count--; 644108014Simp else if (*src == '(') 645108014Simp count++; 646247762Seadler dst += *src++; 647108014Simp } 648108014Simp return; 649108014Simp } 650252485Sasomers 651247763Seadler // $[^A-Za-z] -> $\1 652108014Simp if (!isalpha(*src)) { 653247762Seadler dst += '$'; 654247762Seadler dst += *src++; 655108014Simp return; 656108014Simp } 657108014Simp 658108014Simp // $var -> replace with value 659114081Simp do { 660247762Seadler buffer += *src++; 661114084Simp } while (is_id_char(*src)); 662247758Seadler dst.append(get_variable(buffer)); 663107665Simp} 664107665Simp 665108014Simpconst string 666246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append) 667108014Simp{ 668246134Sian const char *var_at; 669114081Simp string dst; 670108014Simp 671246134Sian /* 672246134Sian * 128 bytes is enough for 2427 of 2438 expansions that happen 673246134Sian * while parsing config files, as tested on 2013-01-30. 674246134Sian */ 675246134Sian dst.reserve(128); 676246134Sian 677246134Sian if (prepend != NULL) 678246134Sian dst = prepend; 679246134Sian 680246134Sian for (;;) { 681246134Sian var_at = strchr(src, '$'); 682246134Sian if (var_at == NULL) { 683246134Sian dst.append(src); 684246134Sian break; 685246134Sian } 686246134Sian dst.append(src, var_at - src); 687246134Sian src = var_at; 688246134Sian expand_one(src, dst); 689108014Simp } 690108014Simp 691246134Sian if (append != NULL) 692246134Sian dst.append(append); 693246134Sian 694114081Simp return (dst); 695108014Simp} 696108014Simp 697108783Simpbool 698247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const 699108783Simp{ 700108783Simp char *walker; 701252485Sasomers 702108783Simp if (*buffer == '\0') 703108783Simp return (false); 704108783Simp walker = lhs = buffer; 705108783Simp while (is_id_char(*walker)) 706108783Simp walker++; 707108783Simp if (*walker != '=') 708108783Simp return (false); 709108783Simp walker++; // skip = 710108783Simp if (*walker == '"') { 711108783Simp walker++; // skip " 712108783Simp rhs = walker; 713108783Simp while (*walker && *walker != '"') 714108783Simp walker++; 715108783Simp if (*walker != '"') 716108783Simp return (false); 717108783Simp rhs[-2] = '\0'; 718108783Simp *walker++ = '\0'; 719108783Simp } else { 720108783Simp rhs = walker; 721108783Simp while (*walker && !isspace(*walker)) 722108783Simp walker++; 723108783Simp if (*walker != '\0') 724108783Simp *walker++ = '\0'; 725108783Simp rhs[-1] = '\0'; 726108783Simp } 727113785Simp while (isspace(*walker)) 728113785Simp walker++; 729108783Simp buffer = walker; 730108783Simp return (true); 731108783Simp} 732108783Simp 733108783Simp 734108783Simpchar * 735108783Simpconfig::set_vars(char *buffer) 736108783Simp{ 737108783Simp char *lhs; 738108783Simp char *rhs; 739108783Simp 740108783Simp while (1) { 741108783Simp if (!chop_var(buffer, lhs, rhs)) 742108783Simp break; 743108783Simp set_variable(lhs, rhs); 744108783Simp } 745108783Simp return (buffer); 746108783Simp} 747108783Simp 748108783Simpvoid 749108783Simpconfig::find_and_execute(char type) 750108783Simp{ 751108783Simp vector<event_proc *> *l; 752108783Simp vector<event_proc *>::const_iterator i; 753151486Sbrooks const char *s; 754108783Simp 755108783Simp switch (type) { 756108783Simp default: 757108783Simp return; 758121487Simp case notify: 759121487Simp l = &_notify_list; 760121487Simp s = "notify"; 761121487Simp break; 762108783Simp case nomatch: 763108783Simp l = &_nomatch_list; 764108783Simp s = "nomatch"; 765108783Simp break; 766108783Simp case attach: 767108783Simp l = &_attach_list; 768108783Simp s = "attach"; 769108783Simp break; 770108783Simp case detach: 771108783Simp l = &_detach_list; 772108783Simp s = "detach"; 773108783Simp break; 774108783Simp } 775252481Sasomers devdlog(LOG_DEBUG, "Processing %s event\n", s); 776243931Seadler for (i = l->begin(); i != l->end(); ++i) { 777108783Simp if ((*i)->matches(*this)) { 778108783Simp (*i)->run(*this); 779108783Simp break; 780108783Simp } 781108783Simp } 782108783Simp 783108783Simp} 784108783Simp 785252485Sasomers 786107665Simpstatic void 787108783Simpprocess_event(char *buffer) 788107665Simp{ 789107665Simp char type; 790107665Simp char *sp; 791107665Simp 792108783Simp sp = buffer + 1; 793259362Sasomers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 794107665Simp type = *buffer++; 795108783Simp cfg.push_var_table(); 796108783Simp // No match doesn't have a device, and the format is a little 797108783Simp // different, so handle it separately. 798121487Simp switch (type) { 799121487Simp case notify: 800121487Simp sp = cfg.set_vars(sp); 801121487Simp break; 802121487Simp case nomatch: 803145218Simp //? at location pnp-info on bus 804145218Simp sp = strchr(sp, ' '); 805145218Simp if (sp == NULL) 806145218Simp return; /* Can't happen? */ 807145218Simp *sp++ = '\0'; 808213646Simp while (isspace(*sp)) 809213646Simp sp++; 810121487Simp if (strncmp(sp, "at ", 3) == 0) 811121487Simp sp += 3; 812121487Simp sp = cfg.set_vars(sp); 813213646Simp while (isspace(*sp)) 814213646Simp sp++; 815121487Simp if (strncmp(sp, "on ", 3) == 0) 816121487Simp cfg.set_variable("bus", sp + 3); 817121487Simp break; 818121487Simp case attach: /*FALLTHROUGH*/ 819121487Simp case detach: 820108783Simp sp = strchr(sp, ' '); 821108783Simp if (sp == NULL) 822108783Simp return; /* Can't happen? */ 823108783Simp *sp++ = '\0'; 824108783Simp cfg.set_variable("device-name", buffer); 825213646Simp while (isspace(*sp)) 826213646Simp sp++; 827113785Simp if (strncmp(sp, "at ", 3) == 0) 828113785Simp sp += 3; 829113785Simp sp = cfg.set_vars(sp); 830213646Simp while (isspace(*sp)) 831213646Simp sp++; 832113785Simp if (strncmp(sp, "on ", 3) == 0) 833113785Simp cfg.set_variable("bus", sp + 3); 834121487Simp break; 835108783Simp } 836252485Sasomers 837108783Simp cfg.find_and_execute(type); 838108783Simp cfg.pop_var_table(); 839107665Simp} 840107665Simp 841131397Simpint 842270004Sasomerscreate_socket(const char *name, int socktype) 843131397Simp{ 844131397Simp int fd, slen; 845131397Simp struct sockaddr_un sun; 846131397Simp 847270004Sasomers if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 848131397Simp err(1, "socket"); 849131397Simp bzero(&sun, sizeof(sun)); 850131397Simp sun.sun_family = AF_UNIX; 851131397Simp strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 852131397Simp slen = SUN_LEN(&sun); 853131397Simp unlink(name); 854147973Smarcus if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 855147973Smarcus err(1, "fcntl"); 856236388Sdim if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 857131397Simp err(1, "bind"); 858131397Simp listen(fd, 4); 859147972Smarcus chown(name, 0, 0); /* XXX - root.wheel */ 860147973Smarcus chmod(name, 0666); 861131397Simp return (fd); 862131397Simp} 863131397Simp 864289677Seadlerunsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ 865246121Sianunsigned int num_clients; 866131397Simp 867270004Sasomerslist<client_t> clients; 868270004Sasomers 869131397Simpvoid 870131397Simpnotify_clients(const char *data, int len) 871131397Simp{ 872270004Sasomers list<client_t>::iterator i; 873131397Simp 874246121Sian /* 875246121Sian * Deliver the data to all clients. Throw clients overboard at the 876246121Sian * first sign of trouble. This reaps clients who've died or closed 877246121Sian * their sockets, and also clients who are alive but failing to keep up 878246121Sian * (or who are maliciously not reading, to consume buffer space in 879246121Sian * kernel memory or tie up the limited number of available connections). 880246121Sian */ 881246121Sian for (i = clients.begin(); i != clients.end(); ) { 882270004Sasomers int flags; 883270004Sasomers if (i->socktype == SOCK_SEQPACKET) 884270004Sasomers flags = MSG_EOR; 885270004Sasomers else 886270004Sasomers flags = 0; 887270004Sasomers 888270004Sasomers if (send(i->fd, data, len, flags) != len) { 889246121Sian --num_clients; 890270004Sasomers close(i->fd); 891246121Sian i = clients.erase(i); 892270004Sasomers devdlog(LOG_WARNING, "notify_clients: send() failed; " 893252481Sasomers "dropping unresponsive client\n"); 894246121Sian } else 895246121Sian ++i; 896131397Simp } 897246121Sian} 898131397Simp 899246121Sianvoid 900246121Siancheck_clients(void) 901246121Sian{ 902246121Sian int s; 903246121Sian struct pollfd pfd; 904270004Sasomers list<client_t>::iterator i; 905246121Sian 906246121Sian /* 907246121Sian * Check all existing clients to see if any of them have disappeared. 908246121Sian * Normally we reap clients when we get an error trying to send them an 909246121Sian * event. This check eliminates the problem of an ever-growing list of 910246121Sian * zombie clients because we're never writing to them on a system 911246121Sian * without frequent device-change activity. 912246121Sian */ 913246121Sian pfd.events = 0; 914246121Sian for (i = clients.begin(); i != clients.end(); ) { 915270004Sasomers pfd.fd = i->fd; 916246121Sian s = poll(&pfd, 1, 0); 917246121Sian if ((s < 0 && s != EINTR ) || 918246121Sian (s > 0 && (pfd.revents & POLLHUP))) { 919246121Sian --num_clients; 920270004Sasomers close(i->fd); 921246121Sian i = clients.erase(i); 922252481Sasomers devdlog(LOG_NOTICE, "check_clients: " 923252481Sasomers "dropping disconnected client\n"); 924246121Sian } else 925246121Sian ++i; 926246121Sian } 927131397Simp} 928131397Simp 929131397Simpvoid 930270004Sasomersnew_client(int fd, int socktype) 931131397Simp{ 932270004Sasomers client_t s; 933259339Sasomers int sndbuf_size; 934131397Simp 935246121Sian /* 936246121Sian * First go reap any zombie clients, then accept the connection, and 937246121Sian * shut down the read side to stop clients from consuming kernel memory 938246121Sian * by sending large buffers full of data we'll never read. 939246121Sian */ 940246121Sian check_clients(); 941270004Sasomers s.socktype = socktype; 942270004Sasomers s.fd = accept(fd, NULL, NULL); 943270004Sasomers if (s.fd != -1) { 944259339Sasomers sndbuf_size = CLIENT_BUFSIZE; 945270004Sasomers if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 946259339Sasomers sizeof(sndbuf_size))) 947259339Sasomers err(1, "setsockopt"); 948270004Sasomers shutdown(s.fd, SHUT_RD); 949131397Simp clients.push_back(s); 950246121Sian ++num_clients; 951259339Sasomers } else 952259339Sasomers err(1, "accept"); 953131397Simp} 954131397Simp 955107665Simpstatic void 956107665Simpevent_loop(void) 957107665Simp{ 958107665Simp int rv; 959107665Simp int fd; 960107665Simp char buffer[DEVCTL_MAXBUF]; 961113790Simp int once = 0; 962270004Sasomers int stream_fd, seqpacket_fd, max_fd; 963246121Sian int accepting; 964113790Simp timeval tv; 965113790Simp fd_set fds; 966107665Simp 967240823Spjd fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 968107665Simp if (fd == -1) 969131397Simp err(1, "Can't open devctl device %s", PATH_DEVCTL); 970270004Sasomers stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 971270004Sasomers seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 972246121Sian accepting = 1; 973270004Sasomers max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 974247756Seadler while (!romeo_must_die) { 975262914Sasomers if (!once && !no_daemon && !daemonize_quick) { 976113790Simp // Check to see if we have any events pending. 977113790Simp tv.tv_sec = 0; 978113790Simp tv.tv_usec = 0; 979113790Simp FD_ZERO(&fds); 980113790Simp FD_SET(fd, &fds); 981113790Simp rv = select(fd + 1, &fds, &fds, &fds, &tv); 982113790Simp // No events -> we've processed all pending events 983117944Simp if (rv == 0) { 984252481Sasomers devdlog(LOG_DEBUG, "Calling daemon\n"); 985155073Spjd cfg.remove_pidfile(); 986155073Spjd cfg.open_pidfile(); 987113790Simp daemon(0, 0); 988155073Spjd cfg.write_pidfile(); 989113790Simp once++; 990113790Simp } 991113790Simp } 992246121Sian /* 993246121Sian * When we've already got the max number of clients, stop 994270004Sasomers * accepting new connections (don't put the listening sockets in 995270004Sasomers * the set), shrink the accept() queue to reject connections 996270004Sasomers * quickly, and poll the existing clients more often, so that we 997270004Sasomers * notice more quickly when any of them disappear to free up 998270004Sasomers * client slots. 999246121Sian */ 1000131397Simp FD_ZERO(&fds); 1001131397Simp FD_SET(fd, &fds); 1002246121Sian if (num_clients < max_clients) { 1003246121Sian if (!accepting) { 1004270004Sasomers listen(stream_fd, max_clients); 1005270004Sasomers listen(seqpacket_fd, max_clients); 1006246121Sian accepting = 1; 1007246121Sian } 1008270004Sasomers FD_SET(stream_fd, &fds); 1009270004Sasomers FD_SET(seqpacket_fd, &fds); 1010246121Sian tv.tv_sec = 60; 1011246121Sian tv.tv_usec = 0; 1012246121Sian } else { 1013246121Sian if (accepting) { 1014270004Sasomers listen(stream_fd, 0); 1015270004Sasomers listen(seqpacket_fd, 0); 1016246121Sian accepting = 0; 1017246121Sian } 1018246121Sian tv.tv_sec = 2; 1019246121Sian tv.tv_usec = 0; 1020246121Sian } 1021246121Sian rv = select(max_fd, &fds, NULL, NULL, &tv); 1022252482Sasomers if (got_siginfo) { 1023259362Sasomers devdlog(LOG_NOTICE, "Events received so far=%u\n", 1024252482Sasomers total_events); 1025252482Sasomers got_siginfo = 0; 1026252482Sasomers } 1027131397Simp if (rv == -1) { 1028131397Simp if (errno == EINTR) 1029131397Simp continue; 1030131397Simp err(1, "select"); 1031246121Sian } else if (rv == 0) 1032246121Sian check_clients(); 1033131397Simp if (FD_ISSET(fd, &fds)) { 1034131397Simp rv = read(fd, buffer, sizeof(buffer) - 1); 1035131397Simp if (rv > 0) { 1036252482Sasomers total_events++; 1037252481Sasomers if (rv == sizeof(buffer) - 1) { 1038252481Sasomers devdlog(LOG_WARNING, "Warning: " 1039252481Sasomers "available event data exceeded " 1040252481Sasomers "buffer space\n"); 1041252481Sasomers } 1042131397Simp notify_clients(buffer, rv); 1043107665Simp buffer[rv] = '\0'; 1044131397Simp while (buffer[--rv] == '\n') 1045131397Simp buffer[rv] = '\0'; 1046131397Simp process_event(buffer); 1047131397Simp } else if (rv < 0) { 1048131397Simp if (errno != EINTR) 1049131397Simp break; 1050131397Simp } else { 1051131397Simp /* EOF */ 1052107665Simp break; 1053131397Simp } 1054107665Simp } 1055270004Sasomers if (FD_ISSET(stream_fd, &fds)) 1056270004Sasomers new_client(stream_fd, SOCK_STREAM); 1057270004Sasomers /* 1058270004Sasomers * Aside from the socket type, both sockets use the same 1059270004Sasomers * protocol, so we can process clients the same way. 1060270004Sasomers */ 1061270004Sasomers if (FD_ISSET(seqpacket_fd, &fds)) 1062270004Sasomers new_client(seqpacket_fd, SOCK_SEQPACKET); 1063107665Simp } 1064107665Simp close(fd); 1065107665Simp} 1066252485Sasomers 1067107665Simp/* 1068107665Simp * functions that the parser uses. 1069107665Simp */ 1070107665Simpvoid 1071107665Simpadd_attach(int prio, event_proc *p) 1072107665Simp{ 1073107665Simp cfg.add_attach(prio, p); 1074107665Simp} 1075107665Simp 1076107665Simpvoid 1077107665Simpadd_detach(int prio, event_proc *p) 1078107665Simp{ 1079107665Simp cfg.add_detach(prio, p); 1080107665Simp} 1081107665Simp 1082107665Simpvoid 1083107665Simpadd_directory(const char *dir) 1084107665Simp{ 1085107665Simp cfg.add_directory(dir); 1086107665Simp free(const_cast<char *>(dir)); 1087107665Simp} 1088107665Simp 1089107665Simpvoid 1090107665Simpadd_nomatch(int prio, event_proc *p) 1091107665Simp{ 1092107665Simp cfg.add_nomatch(prio, p); 1093107665Simp} 1094107665Simp 1095121487Simpvoid 1096121487Simpadd_notify(int prio, event_proc *p) 1097121487Simp{ 1098121487Simp cfg.add_notify(prio, p); 1099121487Simp} 1100121487Simp 1101107665Simpevent_proc * 1102107665Simpadd_to_event_proc(event_proc *ep, eps *eps) 1103107665Simp{ 1104107665Simp if (ep == NULL) 1105107665Simp ep = new event_proc(); 1106107665Simp ep->add(eps); 1107107665Simp return (ep); 1108107665Simp} 1109107665Simp 1110107665Simpeps * 1111107665Simpnew_action(const char *cmd) 1112107665Simp{ 1113107665Simp eps *e = new action(cmd); 1114107665Simp free(const_cast<char *>(cmd)); 1115107665Simp return (e); 1116107665Simp} 1117107665Simp 1118107665Simpeps * 1119107665Simpnew_match(const char *var, const char *re) 1120107665Simp{ 1121108014Simp eps *e = new match(cfg, var, re); 1122107665Simp free(const_cast<char *>(var)); 1123107665Simp free(const_cast<char *>(re)); 1124107665Simp return (e); 1125107665Simp} 1126107665Simp 1127147874Simpeps * 1128147874Simpnew_media(const char *var, const char *re) 1129147874Simp{ 1130147874Simp eps *e = new media(cfg, var, re); 1131147874Simp free(const_cast<char *>(var)); 1132147874Simp free(const_cast<char *>(re)); 1133147874Simp return (e); 1134147874Simp} 1135147874Simp 1136107665Simpvoid 1137107665Simpset_pidfile(const char *name) 1138107665Simp{ 1139107665Simp cfg.set_pidfile(name); 1140107665Simp free(const_cast<char *>(name)); 1141107665Simp} 1142107665Simp 1143107665Simpvoid 1144107665Simpset_variable(const char *var, const char *val) 1145107665Simp{ 1146107665Simp cfg.set_variable(var, val); 1147107665Simp free(const_cast<char *>(var)); 1148107665Simp free(const_cast<char *>(val)); 1149107665Simp} 1150107665Simp 1151107665Simp 1152252485Sasomers 1153107665Simpstatic void 1154107665Simpgensighand(int) 1155107665Simp{ 1156247754Seadler romeo_must_die = 1; 1157107665Simp} 1158107665Simp 1159252481Sasomers/* 1160252482Sasomers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1161252482Sasomers * as appropriate 1162252482Sasomers */ 1163252482Sasomersstatic void 1164252482Sasomerssiginfohand(int) 1165252482Sasomers{ 1166252482Sasomers got_siginfo = 1; 1167252482Sasomers} 1168252482Sasomers 1169252482Sasomers/* 1170257798Savg * Local logging function. Prints to syslog if we're daemonized; stderr 1171252481Sasomers * otherwise. 1172252481Sasomers */ 1173107665Simpstatic void 1174252481Sasomersdevdlog(int priority, const char* fmt, ...) 1175252481Sasomers{ 1176252481Sasomers va_list argp; 1177252481Sasomers 1178252481Sasomers va_start(argp, fmt); 1179262914Sasomers if (no_daemon) 1180252481Sasomers vfprintf(stderr, fmt, argp); 1181262914Sasomers else if ((! quiet_mode) || (priority <= LOG_WARNING)) 1182252481Sasomers vsyslog(priority, fmt, argp); 1183252481Sasomers va_end(argp); 1184252481Sasomers} 1185252481Sasomers 1186252481Sasomersstatic void 1187107665Simpusage() 1188107665Simp{ 1189262914Sasomers fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1190246121Sian getprogname()); 1191107665Simp exit(1); 1192107665Simp} 1193107665Simp 1194113787Simpstatic void 1195113787Simpcheck_devd_enabled() 1196113787Simp{ 1197113787Simp int val = 0; 1198113787Simp size_t len; 1199113787Simp 1200113787Simp len = sizeof(val); 1201114541Simp if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1202113787Simp errx(1, "devctl sysctl missing from kernel!"); 1203263758Smjg if (val == 0) { 1204263758Smjg warnx("Setting " SYSCTL " to 1000"); 1205263758Smjg val = 1000; 1206113787Simp sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 1207113787Simp } 1208113787Simp} 1209113787Simp 1210107665Simp/* 1211107665Simp * main 1212107665Simp */ 1213107665Simpint 1214107665Simpmain(int argc, char **argv) 1215107665Simp{ 1216107665Simp int ch; 1217107665Simp 1218113787Simp check_devd_enabled(); 1219262914Sasomers while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 1220107665Simp switch (ch) { 1221107665Simp case 'd': 1222262914Sasomers no_daemon = 1; 1223107665Simp break; 1224152770Sjkoshy case 'f': 1225152770Sjkoshy configfile = optarg; 1226152770Sjkoshy break; 1227246121Sian case 'l': 1228246121Sian max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1229246121Sian break; 1230113790Simp case 'n': 1231262914Sasomers daemonize_quick = 1; 1232113790Simp break; 1233262914Sasomers case 'q': 1234262914Sasomers quiet_mode = 1; 1235262914Sasomers break; 1236107665Simp default: 1237107665Simp usage(); 1238107665Simp } 1239107665Simp } 1240107665Simp 1241107665Simp cfg.parse(); 1242262914Sasomers if (!no_daemon && daemonize_quick) { 1243155073Spjd cfg.open_pidfile(); 1244107665Simp daemon(0, 0); 1245155073Spjd cfg.write_pidfile(); 1246117246Simp } 1247146306Simp signal(SIGPIPE, SIG_IGN); 1248107665Simp signal(SIGHUP, gensighand); 1249107665Simp signal(SIGINT, gensighand); 1250107665Simp signal(SIGTERM, gensighand); 1251252482Sasomers signal(SIGINFO, siginfohand); 1252107665Simp event_loop(); 1253107665Simp return (0); 1254107665Simp} 1255