13054f218SWarner Losh /*- 2b8f92ce4SWarner Losh * Copyright (c) 2002-2010 M. Warner Losh. 33054f218SWarner Losh * All rights reserved. 43054f218SWarner Losh * 53054f218SWarner Losh * Redistribution and use in source and binary forms, with or without 63054f218SWarner Losh * modification, are permitted provided that the following conditions 73054f218SWarner Losh * are met: 83054f218SWarner Losh * 1. Redistributions of source code must retain the above copyright 93054f218SWarner Losh * notice, this list of conditions and the following disclaimer. 103054f218SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 113054f218SWarner Losh * notice, this list of conditions and the following disclaimer in the 123054f218SWarner Losh * documentation and/or other materials provided with the distribution. 133054f218SWarner Losh * 143054f218SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 153054f218SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 163054f218SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 173054f218SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 183054f218SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 193054f218SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 203054f218SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 213054f218SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 223054f218SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 233054f218SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 243054f218SWarner Losh * SUCH DAMAGE. 25b8f92ce4SWarner Losh * 26b8f92ce4SWarner Losh * my_system is a variation on lib/libc/stdlib/system.c: 27b8f92ce4SWarner Losh * 28b8f92ce4SWarner Losh * Copyright (c) 1988, 1993 29b8f92ce4SWarner Losh * The Regents of the University of California. All rights reserved. 30b8f92ce4SWarner Losh * 31b8f92ce4SWarner Losh * Redistribution and use in source and binary forms, with or without 32b8f92ce4SWarner Losh * modification, are permitted provided that the following conditions 33b8f92ce4SWarner Losh * are met: 34b8f92ce4SWarner Losh * 1. Redistributions of source code must retain the above copyright 35b8f92ce4SWarner Losh * notice, this list of conditions and the following disclaimer. 36b8f92ce4SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 37b8f92ce4SWarner Losh * notice, this list of conditions and the following disclaimer in the 38b8f92ce4SWarner Losh * documentation and/or other materials provided with the distribution. 39b8f92ce4SWarner Losh * 4. Neither the name of the University nor the names of its contributors 40b8f92ce4SWarner Losh * may be used to endorse or promote products derived from this software 41b8f92ce4SWarner Losh * without specific prior written permission. 42b8f92ce4SWarner Losh * 43b8f92ce4SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 44b8f92ce4SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 45b8f92ce4SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 46b8f92ce4SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 47b8f92ce4SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 48b8f92ce4SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 49b8f92ce4SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 50b8f92ce4SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 51b8f92ce4SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 52b8f92ce4SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 53b8f92ce4SWarner Losh * SUCH DAMAGE. 543054f218SWarner Losh */ 553054f218SWarner Losh 563054f218SWarner Losh /* 573054f218SWarner Losh * DEVD control daemon. 583054f218SWarner Losh */ 593054f218SWarner Losh 603054f218SWarner Losh // TODO list: 613054f218SWarner Losh // o devd.conf and devd man pages need a lot of help: 62251bedd1SWarner Losh // - devd needs to document the unix domain socket 633054f218SWarner Losh // - devd.conf needs more details on the supported statements. 643054f218SWarner Losh 653054f218SWarner Losh #include <sys/cdefs.h> 663054f218SWarner Losh __FBSDID("$FreeBSD$"); 673054f218SWarner Losh 683054f218SWarner Losh #include <sys/param.h> 69251bedd1SWarner Losh #include <sys/socket.h> 70251bedd1SWarner Losh #include <sys/stat.h> 719c180898SWarner Losh #include <sys/sysctl.h> 72251bedd1SWarner Losh #include <sys/types.h> 73b8f92ce4SWarner Losh #include <sys/wait.h> 74251bedd1SWarner Losh #include <sys/un.h> 753054f218SWarner Losh 76d005340fSEitan Adler #include <cctype> 77d005340fSEitan Adler #include <cerrno> 78d005340fSEitan Adler #include <cstdlib> 79d005340fSEitan Adler #include <cstdio> 80d005340fSEitan Adler #include <csignal> 81d005340fSEitan Adler #include <cstring> 82967ecf4aSAlan Somers #include <cstdarg> 83d005340fSEitan Adler 843054f218SWarner Losh #include <dirent.h> 853054f218SWarner Losh #include <err.h> 863054f218SWarner Losh #include <fcntl.h> 871a0cc6b1SPawel Jakub Dawidek #include <libutil.h> 88b8f92ce4SWarner Losh #include <paths.h> 89e1334f93SIan Lepore #include <poll.h> 905102ef84SWarner Losh #include <regex.h> 916d1014a3SAlan Somers #include <syslog.h> 923054f218SWarner Losh #include <unistd.h> 933054f218SWarner Losh 948b823408SWarner Losh #include <algorithm> 953054f218SWarner Losh #include <map> 963054f218SWarner Losh #include <string> 97251bedd1SWarner Losh #include <list> 983054f218SWarner Losh #include <vector> 993054f218SWarner Losh 1006aeeca8eSWarner Losh #include "devd.h" /* C compatible definitions */ 1016aeeca8eSWarner Losh #include "devd.hh" /* C++ class definitions */ 1023054f218SWarner Losh 103251bedd1SWarner Losh #define PIPE "/var/run/devd.pipe" 1043054f218SWarner Losh #define CF "/etc/devd.conf" 1059c180898SWarner Losh #define SYSCTL "hw.bus.devctl_disable" 1063054f218SWarner Losh 1073054f218SWarner Losh using namespace std; 1083054f218SWarner Losh 1093054f218SWarner Losh extern FILE *yyin; 1103054f218SWarner Losh extern int lineno; 1113054f218SWarner Losh 112842ccec5SWarner Losh static const char notify = '!'; 1138b823408SWarner Losh static const char nomatch = '?'; 1148b823408SWarner Losh static const char attach = '+'; 1158b823408SWarner Losh static const char detach = '-'; 1168b823408SWarner Losh 1171a0cc6b1SPawel Jakub Dawidek static struct pidfh *pfh; 1181a0cc6b1SPawel Jakub Dawidek 1193054f218SWarner Losh int dflag; 120812f53a7SWarner Losh int nflag; 121be685e01SAlan Somers static unsigned total_events = 0; 122be685e01SAlan Somers static volatile sig_atomic_t got_siginfo = 0; 123fcdcaa88SEitan Adler static volatile sig_atomic_t romeo_must_die = 0; 1243054f218SWarner Losh 1258334958aSJoseph Koshy static const char *configfile = CF; 1268334958aSJoseph Koshy 1273449b15aSAlan Somers static void devdlog(int priority, const char* message, ...) 1283449b15aSAlan Somers __printflike(2, 3); 1293054f218SWarner Losh static void event_loop(void); 1303054f218SWarner Losh static void usage(void); 1313054f218SWarner Losh 1328b823408SWarner Losh template <class T> void 1338b823408SWarner Losh delete_and_clear(vector<T *> &v) 1348b823408SWarner Losh { 1358b823408SWarner Losh typename vector<T *>::const_iterator i; 1363054f218SWarner Losh 137edee691dSEitan Adler for (i = v.begin(); i != v.end(); ++i) 1388b823408SWarner Losh delete *i; 1398b823408SWarner Losh v.clear(); 1408b823408SWarner Losh } 1418b823408SWarner Losh 1423054f218SWarner Losh config cfg; 1433054f218SWarner Losh 1443054f218SWarner Losh event_proc::event_proc() : _prio(-1) 1453054f218SWarner Losh { 1465dfc0f6cSIan Lepore _epsvec.reserve(4); 1473054f218SWarner Losh } 1483054f218SWarner Losh 1493054f218SWarner Losh event_proc::~event_proc() 1503054f218SWarner Losh { 15113f8bb71SAlexander Nedotsukov delete_and_clear(_epsvec); 1523054f218SWarner Losh } 1533054f218SWarner Losh 1543054f218SWarner Losh void 1553054f218SWarner Losh event_proc::add(eps *eps) 1563054f218SWarner Losh { 1573054f218SWarner Losh _epsvec.push_back(eps); 1583054f218SWarner Losh } 1593054f218SWarner Losh 1603054f218SWarner Losh bool 161ef370346SEitan Adler event_proc::matches(config &c) const 1623054f218SWarner Losh { 1633054f218SWarner Losh vector<eps *>::const_iterator i; 1643054f218SWarner Losh 165edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 1663054f218SWarner Losh if (!(*i)->do_match(c)) 1673054f218SWarner Losh return (false); 1683054f218SWarner Losh return (true); 1693054f218SWarner Losh } 1703054f218SWarner Losh 1713054f218SWarner Losh bool 172ef370346SEitan Adler event_proc::run(config &c) const 1733054f218SWarner Losh { 1743054f218SWarner Losh vector<eps *>::const_iterator i; 1753054f218SWarner Losh 176edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 1773054f218SWarner Losh if (!(*i)->do_action(c)) 1783054f218SWarner Losh return (false); 1793054f218SWarner Losh return (true); 1803054f218SWarner Losh } 1813054f218SWarner Losh 1823054f218SWarner Losh action::action(const char *cmd) 1833054f218SWarner Losh : _cmd(cmd) 1843054f218SWarner Losh { 1853054f218SWarner Losh // nothing 1863054f218SWarner Losh } 1873054f218SWarner Losh 1883054f218SWarner Losh action::~action() 1893054f218SWarner Losh { 1903054f218SWarner Losh // nothing 1913054f218SWarner Losh } 1923054f218SWarner Losh 193b8f92ce4SWarner Losh static int 194b8f92ce4SWarner Losh my_system(const char *command) 195b8f92ce4SWarner Losh { 196b8f92ce4SWarner Losh pid_t pid, savedpid; 197b8f92ce4SWarner Losh int pstat; 198b8f92ce4SWarner Losh struct sigaction ign, intact, quitact; 199b8f92ce4SWarner Losh sigset_t newsigblock, oldsigblock; 200b8f92ce4SWarner Losh 201b8f92ce4SWarner Losh if (!command) /* just checking... */ 202b8f92ce4SWarner Losh return (1); 203b8f92ce4SWarner Losh 204b8f92ce4SWarner Losh /* 205b8f92ce4SWarner Losh * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 206b8f92ce4SWarner Losh * existing signal dispositions. 207b8f92ce4SWarner Losh */ 208b8f92ce4SWarner Losh ign.sa_handler = SIG_IGN; 209b8f92ce4SWarner Losh ::sigemptyset(&ign.sa_mask); 210b8f92ce4SWarner Losh ign.sa_flags = 0; 211b8f92ce4SWarner Losh ::sigaction(SIGINT, &ign, &intact); 212b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &ign, &quitact); 213b8f92ce4SWarner Losh ::sigemptyset(&newsigblock); 214b8f92ce4SWarner Losh ::sigaddset(&newsigblock, SIGCHLD); 215b8f92ce4SWarner Losh ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 216b8f92ce4SWarner Losh switch (pid = ::fork()) { 217b8f92ce4SWarner Losh case -1: /* error */ 218b8f92ce4SWarner Losh break; 219b8f92ce4SWarner Losh case 0: /* child */ 220b8f92ce4SWarner Losh /* 221b8f92ce4SWarner Losh * Restore original signal dispositions and exec the command. 222b8f92ce4SWarner Losh */ 223b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 224b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 225b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 226b8f92ce4SWarner Losh /* 227b8f92ce4SWarner Losh * Close the PID file, and all other open descriptors. 228b8f92ce4SWarner Losh * Inherit std{in,out,err} only. 229b8f92ce4SWarner Losh */ 230b8f92ce4SWarner Losh cfg.close_pidfile(); 231b8f92ce4SWarner Losh ::closefrom(3); 232b8f92ce4SWarner Losh ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 233b8f92ce4SWarner Losh ::_exit(127); 234b8f92ce4SWarner Losh default: /* parent */ 235b8f92ce4SWarner Losh savedpid = pid; 236b8f92ce4SWarner Losh do { 237b8f92ce4SWarner Losh pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 238b8f92ce4SWarner Losh } while (pid == -1 && errno == EINTR); 239b8f92ce4SWarner Losh break; 240b8f92ce4SWarner Losh } 241b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 242b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 243b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 244b8f92ce4SWarner Losh return (pid == -1 ? -1 : pstat); 245b8f92ce4SWarner Losh } 246b8f92ce4SWarner Losh 2473054f218SWarner Losh bool 2485102ef84SWarner Losh action::do_action(config &c) 2493054f218SWarner Losh { 2505dfc0f6cSIan Lepore string s = c.expand_string(_cmd.c_str()); 251*1bb0777eSAndriy Gapon devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 252b8f92ce4SWarner Losh my_system(s.c_str()); 2533054f218SWarner Losh return (true); 2543054f218SWarner Losh } 2553054f218SWarner Losh 2565dfc0f6cSIan Lepore match::match(config &c, const char *var, const char *re) : 2575dfc0f6cSIan Lepore _inv(re[0] == '!'), 2585dfc0f6cSIan Lepore _var(var), 2595dfc0f6cSIan Lepore _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 2603054f218SWarner Losh { 2619cb4a7bdSWarner Losh regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 2623054f218SWarner Losh } 2633054f218SWarner Losh 2643054f218SWarner Losh match::~match() 2653054f218SWarner Losh { 2665102ef84SWarner Losh regfree(&_regex); 2673054f218SWarner Losh } 2683054f218SWarner Losh 2693054f218SWarner Losh bool 2705102ef84SWarner Losh match::do_match(config &c) 2713054f218SWarner Losh { 2725bf08422SUlf Lilleengen const string &value = c.get_variable(_var); 2735102ef84SWarner Losh bool retval; 2745102ef84SWarner Losh 2756d1014a3SAlan Somers /* 2766d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 2776d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 2786d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 2796d1014a3SAlan Somers * log when we're in -d mode. 2806d1014a3SAlan Somers */ 2816d1014a3SAlan Somers if (dflag) { 2826d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 2830321b694SHiroki Sato _var.c_str(), value.c_str(), _re.c_str(), _inv); 2846d1014a3SAlan Somers } 2858b823408SWarner Losh 2865102ef84SWarner Losh retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 2870321b694SHiroki Sato if (_inv == 1) 2880321b694SHiroki Sato retval = (retval == 0) ? 1 : 0; 2890321b694SHiroki Sato 2900285e9b1SAlan Somers return (retval); 2913054f218SWarner Losh } 2923054f218SWarner Losh 293cd70782bSWarner Losh #include <sys/sockio.h> 294cd70782bSWarner Losh #include <net/if.h> 295cd70782bSWarner Losh #include <net/if_media.h> 296cd70782bSWarner Losh 29794d2d4ebSBrooks Davis media::media(config &, const char *var, const char *type) 298cd70782bSWarner Losh : _var(var), _type(-1) 299cd70782bSWarner Losh { 300cd70782bSWarner Losh static struct ifmedia_description media_types[] = { 301cd70782bSWarner Losh { IFM_ETHER, "Ethernet" }, 302cd70782bSWarner Losh { IFM_TOKEN, "Tokenring" }, 303cd70782bSWarner Losh { IFM_FDDI, "FDDI" }, 304cd70782bSWarner Losh { IFM_IEEE80211, "802.11" }, 305cd70782bSWarner Losh { IFM_ATM, "ATM" }, 306cd70782bSWarner Losh { -1, "unknown" }, 307cd70782bSWarner Losh { 0, NULL }, 308cd70782bSWarner Losh }; 309edee691dSEitan Adler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 310cd70782bSWarner Losh if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 311cd70782bSWarner Losh _type = media_types[i].ifmt_word; 312cd70782bSWarner Losh break; 313cd70782bSWarner Losh } 314cd70782bSWarner Losh } 315cd70782bSWarner Losh 316cd70782bSWarner Losh media::~media() 317cd70782bSWarner Losh { 318cd70782bSWarner Losh } 319cd70782bSWarner Losh 320cd70782bSWarner Losh bool 321cd70782bSWarner Losh media::do_match(config &c) 322cd70782bSWarner Losh { 32309c47e76SWarner Losh string value; 324cd70782bSWarner Losh struct ifmediareq ifmr; 325cd70782bSWarner Losh bool retval; 326cd70782bSWarner Losh int s; 327cd70782bSWarner Losh 32809c47e76SWarner Losh // Since we can be called from both a device attach/detach 32909c47e76SWarner Losh // context where device-name is defined and what we want, 33009c47e76SWarner Losh // as well as from a link status context, where subsystem is 33109c47e76SWarner Losh // the name of interest, first try device-name and fall back 33209c47e76SWarner Losh // to subsystem if none exists. 33309c47e76SWarner Losh value = c.get_variable("device-name"); 334a6393aa1SEitan Adler if (value.empty()) 335de02eec8SWarner Losh value = c.get_variable("subsystem"); 3366d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 337cd70782bSWarner Losh value.c_str(), _type); 338cd70782bSWarner Losh 339cd70782bSWarner Losh retval = false; 340cd70782bSWarner Losh 341cd70782bSWarner Losh s = socket(PF_INET, SOCK_DGRAM, 0); 342cd70782bSWarner Losh if (s >= 0) { 343cd70782bSWarner Losh memset(&ifmr, 0, sizeof(ifmr)); 344cd70782bSWarner Losh strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 345cd70782bSWarner Losh 346cd70782bSWarner Losh if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 347cd70782bSWarner Losh ifmr.ifm_status & IFM_AVALID) { 3486d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 349cd70782bSWarner Losh value.c_str(), IFM_TYPE(ifmr.ifm_active)); 350cd70782bSWarner Losh retval = (IFM_TYPE(ifmr.ifm_active) == _type); 351cd70782bSWarner Losh } else if (_type == -1) { 3526d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has unknown media type\n", 353cd70782bSWarner Losh value.c_str()); 354cd70782bSWarner Losh retval = true; 355cd70782bSWarner Losh } 356cd70782bSWarner Losh close(s); 357cd70782bSWarner Losh } 358cd70782bSWarner Losh 3590285e9b1SAlan Somers return (retval); 360cd70782bSWarner Losh } 361cd70782bSWarner Losh 3623054f218SWarner Losh const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 3633054f218SWarner Losh const string var_list::nothing = ""; 3643054f218SWarner Losh 3653054f218SWarner Losh const string & 3663054f218SWarner Losh var_list::get_variable(const string &var) const 3673054f218SWarner Losh { 3683054f218SWarner Losh map<string, string>::const_iterator i; 3693054f218SWarner Losh 3703054f218SWarner Losh i = _vars.find(var); 3713054f218SWarner Losh if (i == _vars.end()) 3728b823408SWarner Losh return (var_list::bogus); 3733054f218SWarner Losh return (i->second); 3743054f218SWarner Losh } 3753054f218SWarner Losh 3763054f218SWarner Losh bool 3773054f218SWarner Losh var_list::is_set(const string &var) const 3783054f218SWarner Losh { 3793054f218SWarner Losh return (_vars.find(var) != _vars.end()); 3803054f218SWarner Losh } 3813054f218SWarner Losh 3823054f218SWarner Losh void 3833054f218SWarner Losh var_list::set_variable(const string &var, const string &val) 3843054f218SWarner Losh { 3856d1014a3SAlan Somers /* 3866d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 3876d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 3886d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 3896d1014a3SAlan Somers * log when we're in -d mode. 3906d1014a3SAlan Somers */ 3916d1014a3SAlan Somers if (dflag) 3926d1014a3SAlan Somers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 3933054f218SWarner Losh _vars[var] = val; 3943054f218SWarner Losh } 3953054f218SWarner Losh 3963054f218SWarner Losh void 3973054f218SWarner Losh config::reset(void) 3983054f218SWarner Losh { 3993054f218SWarner Losh _dir_list.clear(); 4008b823408SWarner Losh delete_and_clear(_var_list_table); 4018b823408SWarner Losh delete_and_clear(_attach_list); 4028b823408SWarner Losh delete_and_clear(_detach_list); 4038b823408SWarner Losh delete_and_clear(_nomatch_list); 404842ccec5SWarner Losh delete_and_clear(_notify_list); 4053054f218SWarner Losh } 4063054f218SWarner Losh 4073054f218SWarner Losh void 4083054f218SWarner Losh config::parse_one_file(const char *fn) 4093054f218SWarner Losh { 4106d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 4113054f218SWarner Losh yyin = fopen(fn, "r"); 4123054f218SWarner Losh if (yyin == NULL) 4133054f218SWarner Losh err(1, "Cannot open config file %s", fn); 4144a3050fcSMaxim Konovalov lineno = 1; 4153054f218SWarner Losh if (yyparse() != 0) 4163054f218SWarner Losh errx(1, "Cannot parse %s at line %d", fn, lineno); 4173054f218SWarner Losh fclose(yyin); 4183054f218SWarner Losh } 4193054f218SWarner Losh 4203054f218SWarner Losh void 4213054f218SWarner Losh config::parse_files_in_dir(const char *dirname) 4223054f218SWarner Losh { 4233054f218SWarner Losh DIR *dirp; 4243054f218SWarner Losh struct dirent *dp; 4253054f218SWarner Losh char path[PATH_MAX]; 4263054f218SWarner Losh 4276d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 4283054f218SWarner Losh dirp = opendir(dirname); 4293054f218SWarner Losh if (dirp == NULL) 4303054f218SWarner Losh return; 4313054f218SWarner Losh readdir(dirp); /* Skip . */ 4323054f218SWarner Losh readdir(dirp); /* Skip .. */ 4333054f218SWarner Losh while ((dp = readdir(dirp)) != NULL) { 4343054f218SWarner Losh if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 4353054f218SWarner Losh snprintf(path, sizeof(path), "%s/%s", 4363054f218SWarner Losh dirname, dp->d_name); 4373054f218SWarner Losh parse_one_file(path); 4383054f218SWarner Losh } 4393054f218SWarner Losh } 4405983e891SKevin Lo closedir(dirp); 4413054f218SWarner Losh } 4423054f218SWarner Losh 4438b823408SWarner Losh class epv_greater { 4448b823408SWarner Losh public: 445ef370346SEitan Adler int operator()(event_proc *const&l1, event_proc *const&l2) const 4468b823408SWarner Losh { 4478b823408SWarner Losh return (l1->get_priority() > l2->get_priority()); 4488b823408SWarner Losh } 4498b823408SWarner Losh }; 4508b823408SWarner Losh 4518b823408SWarner Losh void 4528b823408SWarner Losh config::sort_vector(vector<event_proc *> &v) 4538b823408SWarner Losh { 4540c04d185SDimitry Andric stable_sort(v.begin(), v.end(), epv_greater()); 4558b823408SWarner Losh } 4568b823408SWarner Losh 4573054f218SWarner Losh void 4583054f218SWarner Losh config::parse(void) 4593054f218SWarner Losh { 4603054f218SWarner Losh vector<string>::const_iterator i; 4613054f218SWarner Losh 4628334958aSJoseph Koshy parse_one_file(configfile); 463edee691dSEitan Adler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 4643054f218SWarner Losh parse_files_in_dir((*i).c_str()); 4658b823408SWarner Losh sort_vector(_attach_list); 4668b823408SWarner Losh sort_vector(_detach_list); 4678b823408SWarner Losh sort_vector(_nomatch_list); 468842ccec5SWarner Losh sort_vector(_notify_list); 4693054f218SWarner Losh } 4703054f218SWarner Losh 4713054f218SWarner Losh void 4721a0cc6b1SPawel Jakub Dawidek config::open_pidfile() 4733054f218SWarner Losh { 4741a0cc6b1SPawel Jakub Dawidek pid_t otherpid; 4753054f218SWarner Losh 4765a3b1a3dSEitan Adler if (_pidfile.empty()) 4773054f218SWarner Losh return; 4781a0cc6b1SPawel Jakub Dawidek pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 4791a0cc6b1SPawel Jakub Dawidek if (pfh == NULL) { 4801a0cc6b1SPawel Jakub Dawidek if (errno == EEXIST) 4811a0cc6b1SPawel Jakub Dawidek errx(1, "devd already running, pid: %d", (int)otherpid); 4821a0cc6b1SPawel Jakub Dawidek warn("cannot open pid file"); 4831a0cc6b1SPawel Jakub Dawidek } 4841a0cc6b1SPawel Jakub Dawidek } 4851a0cc6b1SPawel Jakub Dawidek 4861a0cc6b1SPawel Jakub Dawidek void 4871a0cc6b1SPawel Jakub Dawidek config::write_pidfile() 4881a0cc6b1SPawel Jakub Dawidek { 4891a0cc6b1SPawel Jakub Dawidek 4901a0cc6b1SPawel Jakub Dawidek pidfile_write(pfh); 4911a0cc6b1SPawel Jakub Dawidek } 4921a0cc6b1SPawel Jakub Dawidek 4931a0cc6b1SPawel Jakub Dawidek void 494b8f92ce4SWarner Losh config::close_pidfile() 495b8f92ce4SWarner Losh { 496b8f92ce4SWarner Losh 497b8f92ce4SWarner Losh pidfile_close(pfh); 498b8f92ce4SWarner Losh } 499b8f92ce4SWarner Losh 500b8f92ce4SWarner Losh void 5011a0cc6b1SPawel Jakub Dawidek config::remove_pidfile() 5021a0cc6b1SPawel Jakub Dawidek { 5031a0cc6b1SPawel Jakub Dawidek 5041a0cc6b1SPawel Jakub Dawidek pidfile_remove(pfh); 5053054f218SWarner Losh } 5063054f218SWarner Losh 5073054f218SWarner Losh void 5083054f218SWarner Losh config::add_attach(int prio, event_proc *p) 5093054f218SWarner Losh { 5103054f218SWarner Losh p->set_priority(prio); 5113054f218SWarner Losh _attach_list.push_back(p); 5123054f218SWarner Losh } 5133054f218SWarner Losh 5143054f218SWarner Losh void 5153054f218SWarner Losh config::add_detach(int prio, event_proc *p) 5163054f218SWarner Losh { 5173054f218SWarner Losh p->set_priority(prio); 5183054f218SWarner Losh _detach_list.push_back(p); 5193054f218SWarner Losh } 5203054f218SWarner Losh 5213054f218SWarner Losh void 5223054f218SWarner Losh config::add_directory(const char *dir) 5233054f218SWarner Losh { 5243054f218SWarner Losh _dir_list.push_back(string(dir)); 5253054f218SWarner Losh } 5263054f218SWarner Losh 5273054f218SWarner Losh void 5283054f218SWarner Losh config::add_nomatch(int prio, event_proc *p) 5293054f218SWarner Losh { 5303054f218SWarner Losh p->set_priority(prio); 5313054f218SWarner Losh _nomatch_list.push_back(p); 5323054f218SWarner Losh } 5333054f218SWarner Losh 5343054f218SWarner Losh void 535842ccec5SWarner Losh config::add_notify(int prio, event_proc *p) 536842ccec5SWarner Losh { 537842ccec5SWarner Losh p->set_priority(prio); 538842ccec5SWarner Losh _notify_list.push_back(p); 539842ccec5SWarner Losh } 540842ccec5SWarner Losh 541842ccec5SWarner Losh void 5423054f218SWarner Losh config::set_pidfile(const char *fn) 5433054f218SWarner Losh { 5445a3b1a3dSEitan Adler _pidfile = fn; 5453054f218SWarner Losh } 5463054f218SWarner Losh 5473054f218SWarner Losh void 5483054f218SWarner Losh config::push_var_table() 5493054f218SWarner Losh { 5503054f218SWarner Losh var_list *vl; 5513054f218SWarner Losh 5523054f218SWarner Losh vl = new var_list(); 5533054f218SWarner Losh _var_list_table.push_back(vl); 5546d1014a3SAlan Somers devdlog(LOG_DEBUG, "Pushing table\n"); 5553054f218SWarner Losh } 5563054f218SWarner Losh 5573054f218SWarner Losh void 5583054f218SWarner Losh config::pop_var_table() 5593054f218SWarner Losh { 5603054f218SWarner Losh delete _var_list_table.back(); 5613054f218SWarner Losh _var_list_table.pop_back(); 5626d1014a3SAlan Somers devdlog(LOG_DEBUG, "Popping table\n"); 5633054f218SWarner Losh } 5643054f218SWarner Losh 5653054f218SWarner Losh void 5663054f218SWarner Losh config::set_variable(const char *var, const char *val) 5673054f218SWarner Losh { 5683054f218SWarner Losh _var_list_table.back()->set_variable(var, val); 5693054f218SWarner Losh } 5703054f218SWarner Losh 5713054f218SWarner Losh const string & 5723054f218SWarner Losh config::get_variable(const string &var) 5733054f218SWarner Losh { 5743054f218SWarner Losh vector<var_list *>::reverse_iterator i; 5753054f218SWarner Losh 576edee691dSEitan Adler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 5773054f218SWarner Losh if ((*i)->is_set(var)) 5788b823408SWarner Losh return ((*i)->get_variable(var)); 5793054f218SWarner Losh } 5803054f218SWarner Losh return (var_list::nothing); 5813054f218SWarner Losh } 5823054f218SWarner Losh 5838b823408SWarner Losh bool 584ef370346SEitan Adler config::is_id_char(char ch) const 5858b823408SWarner Losh { 5868b823408SWarner Losh return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 5878b823408SWarner Losh ch == '-')); 5888b823408SWarner Losh } 5898b823408SWarner Losh 5905102ef84SWarner Losh void 5916cabd675SWarner Losh config::expand_one(const char *&src, string &dst) 5923054f218SWarner Losh { 5935102ef84SWarner Losh int count; 5945bf08422SUlf Lilleengen string buffer; 5955102ef84SWarner Losh 5968b823408SWarner Losh src++; 5975102ef84SWarner Losh // $$ -> $ 5985102ef84SWarner Losh if (*src == '$') { 5997d9e9c60SEitan Adler dst += *src++; 6005102ef84SWarner Losh return; 6015102ef84SWarner Losh } 6025102ef84SWarner Losh 6035102ef84SWarner Losh // $(foo) -> $(foo) 6048b823408SWarner Losh // Not sure if I want to support this or not, so for now we just pass 6058b823408SWarner Losh // it through. 6065102ef84SWarner Losh if (*src == '(') { 6077d9e9c60SEitan Adler dst += '$'; 6085102ef84SWarner Losh count = 1; 6096cabd675SWarner Losh /* If the string ends before ) is matched , return. */ 6106cabd675SWarner Losh while (count > 0 && *src) { 6115102ef84SWarner Losh if (*src == ')') 6125102ef84SWarner Losh count--; 6135102ef84SWarner Losh else if (*src == '(') 6145102ef84SWarner Losh count++; 6157d9e9c60SEitan Adler dst += *src++; 6165102ef84SWarner Losh } 6175102ef84SWarner Losh return; 6185102ef84SWarner Losh } 6195102ef84SWarner Losh 6204cb9d1beSEitan Adler // $[^A-Za-z] -> $\1 6215102ef84SWarner Losh if (!isalpha(*src)) { 6227d9e9c60SEitan Adler dst += '$'; 6237d9e9c60SEitan Adler dst += *src++; 6245102ef84SWarner Losh return; 6255102ef84SWarner Losh } 6265102ef84SWarner Losh 6275102ef84SWarner Losh // $var -> replace with value 6286cabd675SWarner Losh do { 6297d9e9c60SEitan Adler buffer += *src++; 63066ff6821SWarner Losh } while (is_id_char(*src)); 6315a3b1a3dSEitan Adler dst.append(get_variable(buffer)); 6325102ef84SWarner Losh } 6335102ef84SWarner Losh 6345102ef84SWarner Losh const string 6355dfc0f6cSIan Lepore config::expand_string(const char *src, const char *prepend, const char *append) 6365102ef84SWarner Losh { 6375dfc0f6cSIan Lepore const char *var_at; 6386cabd675SWarner Losh string dst; 6395102ef84SWarner Losh 6405dfc0f6cSIan Lepore /* 6415dfc0f6cSIan Lepore * 128 bytes is enough for 2427 of 2438 expansions that happen 6425dfc0f6cSIan Lepore * while parsing config files, as tested on 2013-01-30. 6435dfc0f6cSIan Lepore */ 6445dfc0f6cSIan Lepore dst.reserve(128); 6455dfc0f6cSIan Lepore 6465dfc0f6cSIan Lepore if (prepend != NULL) 6475dfc0f6cSIan Lepore dst = prepend; 6485dfc0f6cSIan Lepore 6495dfc0f6cSIan Lepore for (;;) { 6505dfc0f6cSIan Lepore var_at = strchr(src, '$'); 6515dfc0f6cSIan Lepore if (var_at == NULL) { 6525dfc0f6cSIan Lepore dst.append(src); 6535dfc0f6cSIan Lepore break; 6545102ef84SWarner Losh } 6555dfc0f6cSIan Lepore dst.append(src, var_at - src); 6565dfc0f6cSIan Lepore src = var_at; 6575dfc0f6cSIan Lepore expand_one(src, dst); 6585dfc0f6cSIan Lepore } 6595dfc0f6cSIan Lepore 6605dfc0f6cSIan Lepore if (append != NULL) 6615dfc0f6cSIan Lepore dst.append(append); 6625102ef84SWarner Losh 6636cabd675SWarner Losh return (dst); 6643054f218SWarner Losh } 6653054f218SWarner Losh 6668b823408SWarner Losh bool 66711fd1366SEitan Adler config::chop_var(char *&buffer, char *&lhs, char *&rhs) const 6688b823408SWarner Losh { 6698b823408SWarner Losh char *walker; 6708b823408SWarner Losh 6718b823408SWarner Losh if (*buffer == '\0') 6728b823408SWarner Losh return (false); 6738b823408SWarner Losh walker = lhs = buffer; 6748b823408SWarner Losh while (is_id_char(*walker)) 6758b823408SWarner Losh walker++; 6768b823408SWarner Losh if (*walker != '=') 6778b823408SWarner Losh return (false); 6788b823408SWarner Losh walker++; // skip = 6798b823408SWarner Losh if (*walker == '"') { 6808b823408SWarner Losh walker++; // skip " 6818b823408SWarner Losh rhs = walker; 6828b823408SWarner Losh while (*walker && *walker != '"') 6838b823408SWarner Losh walker++; 6848b823408SWarner Losh if (*walker != '"') 6858b823408SWarner Losh return (false); 6868b823408SWarner Losh rhs[-2] = '\0'; 6878b823408SWarner Losh *walker++ = '\0'; 6888b823408SWarner Losh } else { 6898b823408SWarner Losh rhs = walker; 6908b823408SWarner Losh while (*walker && !isspace(*walker)) 6918b823408SWarner Losh walker++; 6928b823408SWarner Losh if (*walker != '\0') 6938b823408SWarner Losh *walker++ = '\0'; 6948b823408SWarner Losh rhs[-1] = '\0'; 6958b823408SWarner Losh } 696b1ee04b1SWarner Losh while (isspace(*walker)) 697b1ee04b1SWarner Losh walker++; 6988b823408SWarner Losh buffer = walker; 6998b823408SWarner Losh return (true); 7008b823408SWarner Losh } 7018b823408SWarner Losh 7028b823408SWarner Losh 7038b823408SWarner Losh char * 7048b823408SWarner Losh config::set_vars(char *buffer) 7058b823408SWarner Losh { 7068b823408SWarner Losh char *lhs; 7078b823408SWarner Losh char *rhs; 7088b823408SWarner Losh 7098b823408SWarner Losh while (1) { 7108b823408SWarner Losh if (!chop_var(buffer, lhs, rhs)) 7118b823408SWarner Losh break; 7128b823408SWarner Losh set_variable(lhs, rhs); 7138b823408SWarner Losh } 7148b823408SWarner Losh return (buffer); 7158b823408SWarner Losh } 7168b823408SWarner Losh 7178b823408SWarner Losh void 7188b823408SWarner Losh config::find_and_execute(char type) 7198b823408SWarner Losh { 7208b823408SWarner Losh vector<event_proc *> *l; 7218b823408SWarner Losh vector<event_proc *>::const_iterator i; 72294d2d4ebSBrooks Davis const char *s; 7238b823408SWarner Losh 7248b823408SWarner Losh switch (type) { 7258b823408SWarner Losh default: 7268b823408SWarner Losh return; 727842ccec5SWarner Losh case notify: 728842ccec5SWarner Losh l = &_notify_list; 729842ccec5SWarner Losh s = "notify"; 730842ccec5SWarner Losh break; 7318b823408SWarner Losh case nomatch: 7328b823408SWarner Losh l = &_nomatch_list; 7338b823408SWarner Losh s = "nomatch"; 7348b823408SWarner Losh break; 7358b823408SWarner Losh case attach: 7368b823408SWarner Losh l = &_attach_list; 7378b823408SWarner Losh s = "attach"; 7388b823408SWarner Losh break; 7398b823408SWarner Losh case detach: 7408b823408SWarner Losh l = &_detach_list; 7418b823408SWarner Losh s = "detach"; 7428b823408SWarner Losh break; 7438b823408SWarner Losh } 7446d1014a3SAlan Somers devdlog(LOG_DEBUG, "Processing %s event\n", s); 745edee691dSEitan Adler for (i = l->begin(); i != l->end(); ++i) { 7468b823408SWarner Losh if ((*i)->matches(*this)) { 7478b823408SWarner Losh (*i)->run(*this); 7488b823408SWarner Losh break; 7498b823408SWarner Losh } 7508b823408SWarner Losh } 7518b823408SWarner Losh 7528b823408SWarner Losh } 7538b823408SWarner Losh 7540285e9b1SAlan Somers 7553054f218SWarner Losh static void 7568b823408SWarner Losh process_event(char *buffer) 7573054f218SWarner Losh { 7583054f218SWarner Losh char type; 7593054f218SWarner Losh char *sp; 7603054f218SWarner Losh 7618b823408SWarner Losh sp = buffer + 1; 7626d1014a3SAlan Somers devdlog(LOG_DEBUG, "Processing event '%s'\n", buffer); 7633054f218SWarner Losh type = *buffer++; 7648b823408SWarner Losh cfg.push_var_table(); 7658b823408SWarner Losh // No match doesn't have a device, and the format is a little 7668b823408SWarner Losh // different, so handle it separately. 767842ccec5SWarner Losh switch (type) { 768842ccec5SWarner Losh case notify: 769842ccec5SWarner Losh sp = cfg.set_vars(sp); 770842ccec5SWarner Losh break; 771842ccec5SWarner Losh case nomatch: 772528d3f79SWarner Losh //? at location pnp-info on bus 773528d3f79SWarner Losh sp = strchr(sp, ' '); 774528d3f79SWarner Losh if (sp == NULL) 775528d3f79SWarner Losh return; /* Can't happen? */ 776528d3f79SWarner Losh *sp++ = '\0'; 7779b6cb520SWarner Losh while (isspace(*sp)) 7789b6cb520SWarner Losh sp++; 779842ccec5SWarner Losh if (strncmp(sp, "at ", 3) == 0) 780842ccec5SWarner Losh sp += 3; 781842ccec5SWarner Losh sp = cfg.set_vars(sp); 7829b6cb520SWarner Losh while (isspace(*sp)) 7839b6cb520SWarner Losh sp++; 784842ccec5SWarner Losh if (strncmp(sp, "on ", 3) == 0) 785842ccec5SWarner Losh cfg.set_variable("bus", sp + 3); 786842ccec5SWarner Losh break; 787842ccec5SWarner Losh case attach: /*FALLTHROUGH*/ 788842ccec5SWarner Losh case detach: 7898b823408SWarner Losh sp = strchr(sp, ' '); 7903054f218SWarner Losh if (sp == NULL) 7913054f218SWarner Losh return; /* Can't happen? */ 7928b823408SWarner Losh *sp++ = '\0'; 7938b823408SWarner Losh cfg.set_variable("device-name", buffer); 7949b6cb520SWarner Losh while (isspace(*sp)) 7959b6cb520SWarner Losh sp++; 7968b823408SWarner Losh if (strncmp(sp, "at ", 3) == 0) 7978b823408SWarner Losh sp += 3; 7988b823408SWarner Losh sp = cfg.set_vars(sp); 7999b6cb520SWarner Losh while (isspace(*sp)) 8009b6cb520SWarner Losh sp++; 8018b823408SWarner Losh if (strncmp(sp, "on ", 3) == 0) 8028b823408SWarner Losh cfg.set_variable("bus", sp + 3); 803842ccec5SWarner Losh break; 804b1ee04b1SWarner Losh } 805b1ee04b1SWarner Losh 8068b823408SWarner Losh cfg.find_and_execute(type); 8078b823408SWarner Losh cfg.pop_var_table(); 8083054f218SWarner Losh } 8093054f218SWarner Losh 810251bedd1SWarner Losh int 811251bedd1SWarner Losh create_socket(const char *name) 812251bedd1SWarner Losh { 813251bedd1SWarner Losh int fd, slen; 814251bedd1SWarner Losh struct sockaddr_un sun; 815251bedd1SWarner Losh 816251bedd1SWarner Losh if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0) 817251bedd1SWarner Losh err(1, "socket"); 818251bedd1SWarner Losh bzero(&sun, sizeof(sun)); 819251bedd1SWarner Losh sun.sun_family = AF_UNIX; 820251bedd1SWarner Losh strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 821251bedd1SWarner Losh slen = SUN_LEN(&sun); 822251bedd1SWarner Losh unlink(name); 823e60fa014SJoe Marcus Clarke if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 824e60fa014SJoe Marcus Clarke err(1, "fcntl"); 8255d586d29SDimitry Andric if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 826251bedd1SWarner Losh err(1, "bind"); 827251bedd1SWarner Losh listen(fd, 4); 82883818929SJoe Marcus Clarke chown(name, 0, 0); /* XXX - root.wheel */ 829e60fa014SJoe Marcus Clarke chmod(name, 0666); 830251bedd1SWarner Losh return (fd); 831251bedd1SWarner Losh } 832251bedd1SWarner Losh 833e1334f93SIan Lepore unsigned int max_clients = 10; /* Default, can be overriden on cmdline. */ 834e1334f93SIan Lepore unsigned int num_clients; 835251bedd1SWarner Losh list<int> clients; 836251bedd1SWarner Losh 837251bedd1SWarner Losh void 838251bedd1SWarner Losh notify_clients(const char *data, int len) 839251bedd1SWarner Losh { 840e1334f93SIan Lepore list<int>::iterator i; 841251bedd1SWarner Losh 842e1334f93SIan Lepore /* 843e1334f93SIan Lepore * Deliver the data to all clients. Throw clients overboard at the 844e1334f93SIan Lepore * first sign of trouble. This reaps clients who've died or closed 845e1334f93SIan Lepore * their sockets, and also clients who are alive but failing to keep up 846e1334f93SIan Lepore * (or who are maliciously not reading, to consume buffer space in 847e1334f93SIan Lepore * kernel memory or tie up the limited number of available connections). 848e1334f93SIan Lepore */ 849e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 850e1334f93SIan Lepore if (write(*i, data, len) != len) { 851e1334f93SIan Lepore --num_clients; 852251bedd1SWarner Losh close(*i); 853e1334f93SIan Lepore i = clients.erase(i); 8546d1014a3SAlan Somers devdlog(LOG_WARNING, "notify_clients: write() failed; " 8556d1014a3SAlan Somers "dropping unresponsive client\n"); 856e1334f93SIan Lepore } else 857e1334f93SIan Lepore ++i; 858251bedd1SWarner Losh } 859251bedd1SWarner Losh } 860251bedd1SWarner Losh 861e1334f93SIan Lepore void 862e1334f93SIan Lepore check_clients(void) 863e1334f93SIan Lepore { 864e1334f93SIan Lepore int s; 865e1334f93SIan Lepore struct pollfd pfd; 866e1334f93SIan Lepore list<int>::iterator i; 867e1334f93SIan Lepore 868e1334f93SIan Lepore /* 869e1334f93SIan Lepore * Check all existing clients to see if any of them have disappeared. 870e1334f93SIan Lepore * Normally we reap clients when we get an error trying to send them an 871e1334f93SIan Lepore * event. This check eliminates the problem of an ever-growing list of 872e1334f93SIan Lepore * zombie clients because we're never writing to them on a system 873e1334f93SIan Lepore * without frequent device-change activity. 874e1334f93SIan Lepore */ 875e1334f93SIan Lepore pfd.events = 0; 876e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 877e1334f93SIan Lepore pfd.fd = *i; 878e1334f93SIan Lepore s = poll(&pfd, 1, 0); 879e1334f93SIan Lepore if ((s < 0 && s != EINTR ) || 880e1334f93SIan Lepore (s > 0 && (pfd.revents & POLLHUP))) { 881e1334f93SIan Lepore --num_clients; 882e1334f93SIan Lepore close(*i); 883e1334f93SIan Lepore i = clients.erase(i); 8846d1014a3SAlan Somers devdlog(LOG_NOTICE, "check_clients: " 8856d1014a3SAlan Somers "dropping disconnected client\n"); 886e1334f93SIan Lepore } else 887e1334f93SIan Lepore ++i; 888e1334f93SIan Lepore } 889251bedd1SWarner Losh } 890251bedd1SWarner Losh 891251bedd1SWarner Losh void 892251bedd1SWarner Losh new_client(int fd) 893251bedd1SWarner Losh { 894251bedd1SWarner Losh int s; 895251bedd1SWarner Losh 896e1334f93SIan Lepore /* 897e1334f93SIan Lepore * First go reap any zombie clients, then accept the connection, and 898e1334f93SIan Lepore * shut down the read side to stop clients from consuming kernel memory 899e1334f93SIan Lepore * by sending large buffers full of data we'll never read. 900e1334f93SIan Lepore */ 901e1334f93SIan Lepore check_clients(); 902251bedd1SWarner Losh s = accept(fd, NULL, NULL); 903e1334f93SIan Lepore if (s != -1) { 904e1334f93SIan Lepore shutdown(s, SHUT_RD); 905251bedd1SWarner Losh clients.push_back(s); 906e1334f93SIan Lepore ++num_clients; 907e1334f93SIan Lepore } 908251bedd1SWarner Losh } 909251bedd1SWarner Losh 9103054f218SWarner Losh static void 9113054f218SWarner Losh event_loop(void) 9123054f218SWarner Losh { 9133054f218SWarner Losh int rv; 9143054f218SWarner Losh int fd; 9153054f218SWarner Losh char buffer[DEVCTL_MAXBUF]; 9165a882d77SWarner Losh int once = 0; 917251bedd1SWarner Losh int server_fd, max_fd; 918e1334f93SIan Lepore int accepting; 9195a882d77SWarner Losh timeval tv; 9205a882d77SWarner Losh fd_set fds; 9213054f218SWarner Losh 922901610f4SPawel Jakub Dawidek fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 9233054f218SWarner Losh if (fd == -1) 924251bedd1SWarner Losh err(1, "Can't open devctl device %s", PATH_DEVCTL); 925251bedd1SWarner Losh server_fd = create_socket(PIPE); 926e1334f93SIan Lepore accepting = 1; 927251bedd1SWarner Losh max_fd = max(fd, server_fd) + 1; 92867ae3bf1SEitan Adler while (!romeo_must_die) { 9295a882d77SWarner Losh if (!once && !dflag && !nflag) { 9305a882d77SWarner Losh // Check to see if we have any events pending. 9315a882d77SWarner Losh tv.tv_sec = 0; 9325a882d77SWarner Losh tv.tv_usec = 0; 9335a882d77SWarner Losh FD_ZERO(&fds); 9345a882d77SWarner Losh FD_SET(fd, &fds); 9355a882d77SWarner Losh rv = select(fd + 1, &fds, &fds, &fds, &tv); 9365a882d77SWarner Losh // No events -> we've processed all pending events 937f3059505SWarner Losh if (rv == 0) { 9386d1014a3SAlan Somers devdlog(LOG_DEBUG, "Calling daemon\n"); 9391a0cc6b1SPawel Jakub Dawidek cfg.remove_pidfile(); 9401a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 9415a882d77SWarner Losh daemon(0, 0); 9421a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 9435a882d77SWarner Losh once++; 9445a882d77SWarner Losh } 9455a882d77SWarner Losh } 946e1334f93SIan Lepore /* 947e1334f93SIan Lepore * When we've already got the max number of clients, stop 948e1334f93SIan Lepore * accepting new connections (don't put server_fd in the set), 949e1334f93SIan Lepore * shrink the accept() queue to reject connections quickly, and 950e1334f93SIan Lepore * poll the existing clients more often, so that we notice more 951e1334f93SIan Lepore * quickly when any of them disappear to free up client slots. 952e1334f93SIan Lepore */ 953251bedd1SWarner Losh FD_ZERO(&fds); 954251bedd1SWarner Losh FD_SET(fd, &fds); 955e1334f93SIan Lepore if (num_clients < max_clients) { 956e1334f93SIan Lepore if (!accepting) { 957e1334f93SIan Lepore listen(server_fd, max_clients); 958e1334f93SIan Lepore accepting = 1; 959e1334f93SIan Lepore } 960251bedd1SWarner Losh FD_SET(server_fd, &fds); 961e1334f93SIan Lepore tv.tv_sec = 60; 962e1334f93SIan Lepore tv.tv_usec = 0; 963e1334f93SIan Lepore } else { 964e1334f93SIan Lepore if (accepting) { 965e1334f93SIan Lepore listen(server_fd, 0); 966e1334f93SIan Lepore accepting = 0; 967e1334f93SIan Lepore } 968e1334f93SIan Lepore tv.tv_sec = 2; 969e1334f93SIan Lepore tv.tv_usec = 0; 970e1334f93SIan Lepore } 971e1334f93SIan Lepore rv = select(max_fd, &fds, NULL, NULL, &tv); 972be685e01SAlan Somers if (got_siginfo) { 9733449b15aSAlan Somers devdlog(LOG_INFO, "Events received so far=%u\n", 974be685e01SAlan Somers total_events); 975be685e01SAlan Somers got_siginfo = 0; 976be685e01SAlan Somers } 977251bedd1SWarner Losh if (rv == -1) { 978251bedd1SWarner Losh if (errno == EINTR) 979251bedd1SWarner Losh continue; 980251bedd1SWarner Losh err(1, "select"); 981e1334f93SIan Lepore } else if (rv == 0) 982e1334f93SIan Lepore check_clients(); 983251bedd1SWarner Losh if (FD_ISSET(fd, &fds)) { 9843054f218SWarner Losh rv = read(fd, buffer, sizeof(buffer) - 1); 9853054f218SWarner Losh if (rv > 0) { 986be685e01SAlan Somers total_events++; 9876d1014a3SAlan Somers if (rv == sizeof(buffer) - 1) { 9886d1014a3SAlan Somers devdlog(LOG_WARNING, "Warning: " 9896d1014a3SAlan Somers "available event data exceeded " 9906d1014a3SAlan Somers "buffer space\n"); 9916d1014a3SAlan Somers } 992251bedd1SWarner Losh notify_clients(buffer, rv); 9933054f218SWarner Losh buffer[rv] = '\0'; 9943054f218SWarner Losh while (buffer[--rv] == '\n') 9953054f218SWarner Losh buffer[rv] = '\0'; 9963054f218SWarner Losh process_event(buffer); 9973054f218SWarner Losh } else if (rv < 0) { 9983054f218SWarner Losh if (errno != EINTR) 9993054f218SWarner Losh break; 10003054f218SWarner Losh } else { 10013054f218SWarner Losh /* EOF */ 10023054f218SWarner Losh break; 10033054f218SWarner Losh } 10043054f218SWarner Losh } 1005251bedd1SWarner Losh if (FD_ISSET(server_fd, &fds)) 1006251bedd1SWarner Losh new_client(server_fd); 1007251bedd1SWarner Losh } 10083054f218SWarner Losh close(fd); 10093054f218SWarner Losh } 10100285e9b1SAlan Somers 10113054f218SWarner Losh /* 10123054f218SWarner Losh * functions that the parser uses. 10133054f218SWarner Losh */ 10143054f218SWarner Losh void 10153054f218SWarner Losh add_attach(int prio, event_proc *p) 10163054f218SWarner Losh { 10173054f218SWarner Losh cfg.add_attach(prio, p); 10183054f218SWarner Losh } 10193054f218SWarner Losh 10203054f218SWarner Losh void 10213054f218SWarner Losh add_detach(int prio, event_proc *p) 10223054f218SWarner Losh { 10233054f218SWarner Losh cfg.add_detach(prio, p); 10243054f218SWarner Losh } 10253054f218SWarner Losh 10263054f218SWarner Losh void 10273054f218SWarner Losh add_directory(const char *dir) 10283054f218SWarner Losh { 10293054f218SWarner Losh cfg.add_directory(dir); 10303054f218SWarner Losh free(const_cast<char *>(dir)); 10313054f218SWarner Losh } 10323054f218SWarner Losh 10333054f218SWarner Losh void 10343054f218SWarner Losh add_nomatch(int prio, event_proc *p) 10353054f218SWarner Losh { 10363054f218SWarner Losh cfg.add_nomatch(prio, p); 10373054f218SWarner Losh } 10383054f218SWarner Losh 1039842ccec5SWarner Losh void 1040842ccec5SWarner Losh add_notify(int prio, event_proc *p) 1041842ccec5SWarner Losh { 1042842ccec5SWarner Losh cfg.add_notify(prio, p); 1043842ccec5SWarner Losh } 1044842ccec5SWarner Losh 10453054f218SWarner Losh event_proc * 10463054f218SWarner Losh add_to_event_proc(event_proc *ep, eps *eps) 10473054f218SWarner Losh { 10483054f218SWarner Losh if (ep == NULL) 10493054f218SWarner Losh ep = new event_proc(); 10503054f218SWarner Losh ep->add(eps); 10513054f218SWarner Losh return (ep); 10523054f218SWarner Losh } 10533054f218SWarner Losh 10543054f218SWarner Losh eps * 10553054f218SWarner Losh new_action(const char *cmd) 10563054f218SWarner Losh { 10573054f218SWarner Losh eps *e = new action(cmd); 10583054f218SWarner Losh free(const_cast<char *>(cmd)); 10593054f218SWarner Losh return (e); 10603054f218SWarner Losh } 10613054f218SWarner Losh 10623054f218SWarner Losh eps * 10633054f218SWarner Losh new_match(const char *var, const char *re) 10643054f218SWarner Losh { 10655102ef84SWarner Losh eps *e = new match(cfg, var, re); 10663054f218SWarner Losh free(const_cast<char *>(var)); 10673054f218SWarner Losh free(const_cast<char *>(re)); 10683054f218SWarner Losh return (e); 10693054f218SWarner Losh } 10703054f218SWarner Losh 1071cd70782bSWarner Losh eps * 1072cd70782bSWarner Losh new_media(const char *var, const char *re) 1073cd70782bSWarner Losh { 1074cd70782bSWarner Losh eps *e = new media(cfg, var, re); 1075cd70782bSWarner Losh free(const_cast<char *>(var)); 1076cd70782bSWarner Losh free(const_cast<char *>(re)); 1077cd70782bSWarner Losh return (e); 1078cd70782bSWarner Losh } 1079cd70782bSWarner Losh 10803054f218SWarner Losh void 10813054f218SWarner Losh set_pidfile(const char *name) 10823054f218SWarner Losh { 10833054f218SWarner Losh cfg.set_pidfile(name); 10843054f218SWarner Losh free(const_cast<char *>(name)); 10853054f218SWarner Losh } 10863054f218SWarner Losh 10873054f218SWarner Losh void 10883054f218SWarner Losh set_variable(const char *var, const char *val) 10893054f218SWarner Losh { 10903054f218SWarner Losh cfg.set_variable(var, val); 10913054f218SWarner Losh free(const_cast<char *>(var)); 10923054f218SWarner Losh free(const_cast<char *>(val)); 10933054f218SWarner Losh } 10943054f218SWarner Losh 10950285e9b1SAlan Somers 10963054f218SWarner Losh 10973054f218SWarner Losh static void 10983054f218SWarner Losh gensighand(int) 10993054f218SWarner Losh { 1100fcdcaa88SEitan Adler romeo_must_die = 1; 11013054f218SWarner Losh } 11023054f218SWarner Losh 11036d1014a3SAlan Somers /* 1104be685e01SAlan Somers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1105be685e01SAlan Somers * as appropriate 1106be685e01SAlan Somers */ 1107be685e01SAlan Somers static void 1108be685e01SAlan Somers siginfohand(int) 1109be685e01SAlan Somers { 1110be685e01SAlan Somers got_siginfo = 1; 1111be685e01SAlan Somers } 1112be685e01SAlan Somers 1113be685e01SAlan Somers /* 11142e024bc2SAndriy Gapon * Local logging function. Prints to syslog if we're daemonized; stderr 11156d1014a3SAlan Somers * otherwise. 11166d1014a3SAlan Somers */ 11176d1014a3SAlan Somers static void 11186d1014a3SAlan Somers devdlog(int priority, const char* fmt, ...) 11196d1014a3SAlan Somers { 11206d1014a3SAlan Somers va_list argp; 11216d1014a3SAlan Somers 11226d1014a3SAlan Somers va_start(argp, fmt); 11236d1014a3SAlan Somers if (dflag) 11246d1014a3SAlan Somers vfprintf(stderr, fmt, argp); 11256d1014a3SAlan Somers else 11266d1014a3SAlan Somers vsyslog(priority, fmt, argp); 11276d1014a3SAlan Somers va_end(argp); 11286d1014a3SAlan Somers } 11296d1014a3SAlan Somers 11303054f218SWarner Losh static void 11313054f218SWarner Losh usage() 11323054f218SWarner Losh { 11336d1014a3SAlan Somers fprintf(stderr, "usage: %s [-dn] [-l connlimit] [-f file]\n", 1134e1334f93SIan Lepore getprogname()); 11353054f218SWarner Losh exit(1); 11363054f218SWarner Losh } 11373054f218SWarner Losh 11389c180898SWarner Losh static void 11399c180898SWarner Losh check_devd_enabled() 11409c180898SWarner Losh { 11419c180898SWarner Losh int val = 0; 11429c180898SWarner Losh size_t len; 11439c180898SWarner Losh 11449c180898SWarner Losh len = sizeof(val); 114579f92e25SWarner Losh if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 11469c180898SWarner Losh errx(1, "devctl sysctl missing from kernel!"); 11479c180898SWarner Losh if (val) { 11489c180898SWarner Losh warnx("Setting " SYSCTL " to 0"); 11499c180898SWarner Losh val = 0; 11509c180898SWarner Losh sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 11519c180898SWarner Losh } 11529c180898SWarner Losh } 11539c180898SWarner Losh 11543054f218SWarner Losh /* 11553054f218SWarner Losh * main 11563054f218SWarner Losh */ 11573054f218SWarner Losh int 11583054f218SWarner Losh main(int argc, char **argv) 11593054f218SWarner Losh { 11603054f218SWarner Losh int ch; 11613054f218SWarner Losh 11629c180898SWarner Losh check_devd_enabled(); 11636d1014a3SAlan Somers while ((ch = getopt(argc, argv, "df:l:n")) != -1) { 11643054f218SWarner Losh switch (ch) { 11653054f218SWarner Losh case 'd': 11663054f218SWarner Losh dflag++; 11673054f218SWarner Losh break; 11688334958aSJoseph Koshy case 'f': 11698334958aSJoseph Koshy configfile = optarg; 11708334958aSJoseph Koshy break; 1171e1334f93SIan Lepore case 'l': 1172e1334f93SIan Lepore max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1173e1334f93SIan Lepore break; 11745a882d77SWarner Losh case 'n': 11755a882d77SWarner Losh nflag++; 11765a882d77SWarner Losh break; 11773054f218SWarner Losh default: 11783054f218SWarner Losh usage(); 11793054f218SWarner Losh } 11803054f218SWarner Losh } 11813054f218SWarner Losh 11823054f218SWarner Losh cfg.parse(); 1183fd6a8f23SWarner Losh if (!dflag && nflag) { 11841a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 11853054f218SWarner Losh daemon(0, 0); 11861a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 1187fd6a8f23SWarner Losh } 1188f32a7686SWarner Losh signal(SIGPIPE, SIG_IGN); 11893054f218SWarner Losh signal(SIGHUP, gensighand); 11903054f218SWarner Losh signal(SIGINT, gensighand); 11913054f218SWarner Losh signal(SIGTERM, gensighand); 1192be685e01SAlan Somers signal(SIGINFO, siginfohand); 11933054f218SWarner Losh event_loop(); 11943054f218SWarner Losh return (0); 11953054f218SWarner Losh } 1196