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 1039102691eSAlan Somers #define STREAMPIPE "/var/run/devd.pipe" 1049102691eSAlan Somers #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 1053054f218SWarner Losh #define CF "/etc/devd.conf" 106ee38f2e0SMateusz Guzik #define SYSCTL "hw.bus.devctl_queue" 1073054f218SWarner Losh 108b026eddfSAlan Somers /* 109b026eddfSAlan Somers * Since the client socket is nonblocking, we must increase its send buffer to 110b026eddfSAlan Somers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 111d9ca81daSAlan Somers * buffer, so the client can't increase the buffersize by itself. 112b026eddfSAlan Somers * 113b026eddfSAlan Somers * For example, when creating a ZFS pool, devd emits one 165 character 114d9ca81daSAlan Somers * resource.fs.zfs.statechange message for each vdev in the pool. The kernel 115d9ca81daSAlan Somers * allocates a 4608B mbuf for each message. Modern technology places a limit of 116d9ca81daSAlan Somers * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger 117d9ca81daSAlan Somers * than that. 118d9ca81daSAlan Somers * 119d9ca81daSAlan Somers * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf 120d9ca81daSAlan Somers * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf 121d9ca81daSAlan Somers * 122d9ca81daSAlan Somers * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. 123d9ca81daSAlan Somers * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * 124d9ca81daSAlan Somers * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by 125d9ca81daSAlan Somers * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we 126d9ca81daSAlan Somers * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the 127d9ca81daSAlan Somers * kernel will set the mbuf limit to 2MB, which is just large enough for 450 128d9ca81daSAlan Somers * drives. It also happens to be the same as the hardcoded maximum value. 129b026eddfSAlan Somers */ 130d9ca81daSAlan Somers #define CLIENT_BUFSIZE 262144 131b026eddfSAlan Somers 1323054f218SWarner Losh using namespace std; 1333054f218SWarner Losh 1349102691eSAlan Somers typedef struct client { 1359102691eSAlan Somers int fd; 1369102691eSAlan Somers int socktype; 1379102691eSAlan Somers } client_t; 1389102691eSAlan Somers 1393054f218SWarner Losh extern FILE *yyin; 1403054f218SWarner Losh extern int lineno; 1413054f218SWarner Losh 142842ccec5SWarner Losh static const char notify = '!'; 1438b823408SWarner Losh static const char nomatch = '?'; 1448b823408SWarner Losh static const char attach = '+'; 1458b823408SWarner Losh static const char detach = '-'; 1468b823408SWarner Losh 1471a0cc6b1SPawel Jakub Dawidek static struct pidfh *pfh; 1481a0cc6b1SPawel Jakub Dawidek 1496a2ae0ebSAlan Somers static int no_daemon = 0; 1506a2ae0ebSAlan Somers static int daemonize_quick = 0; 1516a2ae0ebSAlan Somers static int quiet_mode = 0; 152be685e01SAlan Somers static unsigned total_events = 0; 153be685e01SAlan Somers static volatile sig_atomic_t got_siginfo = 0; 154fcdcaa88SEitan Adler static volatile sig_atomic_t romeo_must_die = 0; 1553054f218SWarner Losh 1568334958aSJoseph Koshy static const char *configfile = CF; 1578334958aSJoseph Koshy 1583449b15aSAlan Somers static void devdlog(int priority, const char* message, ...) 1593449b15aSAlan Somers __printflike(2, 3); 1603054f218SWarner Losh static void event_loop(void); 1613054f218SWarner Losh static void usage(void); 1623054f218SWarner Losh 1638b823408SWarner Losh template <class T> void 1648b823408SWarner Losh delete_and_clear(vector<T *> &v) 1658b823408SWarner Losh { 1668b823408SWarner Losh typename vector<T *>::const_iterator i; 1673054f218SWarner Losh 168edee691dSEitan Adler for (i = v.begin(); i != v.end(); ++i) 1698b823408SWarner Losh delete *i; 1708b823408SWarner Losh v.clear(); 1718b823408SWarner Losh } 1728b823408SWarner Losh 1733054f218SWarner Losh config cfg; 1743054f218SWarner Losh 1753054f218SWarner Losh event_proc::event_proc() : _prio(-1) 1763054f218SWarner Losh { 1775dfc0f6cSIan Lepore _epsvec.reserve(4); 1783054f218SWarner Losh } 1793054f218SWarner Losh 1803054f218SWarner Losh event_proc::~event_proc() 1813054f218SWarner Losh { 18213f8bb71SAlexander Nedotsukov delete_and_clear(_epsvec); 1833054f218SWarner Losh } 1843054f218SWarner Losh 1853054f218SWarner Losh void 1863054f218SWarner Losh event_proc::add(eps *eps) 1873054f218SWarner Losh { 1883054f218SWarner Losh _epsvec.push_back(eps); 1893054f218SWarner Losh } 1903054f218SWarner Losh 1913054f218SWarner Losh bool 192ef370346SEitan Adler event_proc::matches(config &c) const 1933054f218SWarner Losh { 1943054f218SWarner Losh vector<eps *>::const_iterator i; 1953054f218SWarner Losh 196edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 1973054f218SWarner Losh if (!(*i)->do_match(c)) 1983054f218SWarner Losh return (false); 1993054f218SWarner Losh return (true); 2003054f218SWarner Losh } 2013054f218SWarner Losh 2023054f218SWarner Losh bool 203ef370346SEitan Adler event_proc::run(config &c) const 2043054f218SWarner Losh { 2053054f218SWarner Losh vector<eps *>::const_iterator i; 2063054f218SWarner Losh 207edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 2083054f218SWarner Losh if (!(*i)->do_action(c)) 2093054f218SWarner Losh return (false); 2103054f218SWarner Losh return (true); 2113054f218SWarner Losh } 2123054f218SWarner Losh 2133054f218SWarner Losh action::action(const char *cmd) 2143054f218SWarner Losh : _cmd(cmd) 2153054f218SWarner Losh { 2163054f218SWarner Losh // nothing 2173054f218SWarner Losh } 2183054f218SWarner Losh 2193054f218SWarner Losh action::~action() 2203054f218SWarner Losh { 2213054f218SWarner Losh // nothing 2223054f218SWarner Losh } 2233054f218SWarner Losh 224b8f92ce4SWarner Losh static int 225b8f92ce4SWarner Losh my_system(const char *command) 226b8f92ce4SWarner Losh { 227b8f92ce4SWarner Losh pid_t pid, savedpid; 228b8f92ce4SWarner Losh int pstat; 229b8f92ce4SWarner Losh struct sigaction ign, intact, quitact; 230b8f92ce4SWarner Losh sigset_t newsigblock, oldsigblock; 231b8f92ce4SWarner Losh 232b8f92ce4SWarner Losh if (!command) /* just checking... */ 233b8f92ce4SWarner Losh return (1); 234b8f92ce4SWarner Losh 235b8f92ce4SWarner Losh /* 236b8f92ce4SWarner Losh * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 237b8f92ce4SWarner Losh * existing signal dispositions. 238b8f92ce4SWarner Losh */ 239b8f92ce4SWarner Losh ign.sa_handler = SIG_IGN; 240b8f92ce4SWarner Losh ::sigemptyset(&ign.sa_mask); 241b8f92ce4SWarner Losh ign.sa_flags = 0; 242b8f92ce4SWarner Losh ::sigaction(SIGINT, &ign, &intact); 243b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &ign, &quitact); 244b8f92ce4SWarner Losh ::sigemptyset(&newsigblock); 245b8f92ce4SWarner Losh ::sigaddset(&newsigblock, SIGCHLD); 246b8f92ce4SWarner Losh ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 247b8f92ce4SWarner Losh switch (pid = ::fork()) { 248b8f92ce4SWarner Losh case -1: /* error */ 249b8f92ce4SWarner Losh break; 250b8f92ce4SWarner Losh case 0: /* child */ 251b8f92ce4SWarner Losh /* 252b8f92ce4SWarner Losh * Restore original signal dispositions and exec the command. 253b8f92ce4SWarner Losh */ 254b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 255b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 256b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 257b8f92ce4SWarner Losh /* 258b8f92ce4SWarner Losh * Close the PID file, and all other open descriptors. 259b8f92ce4SWarner Losh * Inherit std{in,out,err} only. 260b8f92ce4SWarner Losh */ 261b8f92ce4SWarner Losh cfg.close_pidfile(); 262b8f92ce4SWarner Losh ::closefrom(3); 263b8f92ce4SWarner Losh ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 264b8f92ce4SWarner Losh ::_exit(127); 265b8f92ce4SWarner Losh default: /* parent */ 266b8f92ce4SWarner Losh savedpid = pid; 267b8f92ce4SWarner Losh do { 268b8f92ce4SWarner Losh pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 269b8f92ce4SWarner Losh } while (pid == -1 && errno == EINTR); 270b8f92ce4SWarner Losh break; 271b8f92ce4SWarner Losh } 272b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 273b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 274b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 275b8f92ce4SWarner Losh return (pid == -1 ? -1 : pstat); 276b8f92ce4SWarner Losh } 277b8f92ce4SWarner Losh 2783054f218SWarner Losh bool 2795102ef84SWarner Losh action::do_action(config &c) 2803054f218SWarner Losh { 2815dfc0f6cSIan Lepore string s = c.expand_string(_cmd.c_str()); 2821bb0777eSAndriy Gapon devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 283b8f92ce4SWarner Losh my_system(s.c_str()); 2843054f218SWarner Losh return (true); 2853054f218SWarner Losh } 2863054f218SWarner Losh 2875dfc0f6cSIan Lepore match::match(config &c, const char *var, const char *re) : 2885dfc0f6cSIan Lepore _inv(re[0] == '!'), 2895dfc0f6cSIan Lepore _var(var), 2905dfc0f6cSIan Lepore _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 2913054f218SWarner Losh { 2929cb4a7bdSWarner Losh regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 2933054f218SWarner Losh } 2943054f218SWarner Losh 2953054f218SWarner Losh match::~match() 2963054f218SWarner Losh { 2975102ef84SWarner Losh regfree(&_regex); 2983054f218SWarner Losh } 2993054f218SWarner Losh 3003054f218SWarner Losh bool 3015102ef84SWarner Losh match::do_match(config &c) 3023054f218SWarner Losh { 3035bf08422SUlf Lilleengen const string &value = c.get_variable(_var); 3045102ef84SWarner Losh bool retval; 3055102ef84SWarner Losh 3066d1014a3SAlan Somers /* 3076d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 3086d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 3096d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 3106d1014a3SAlan Somers * log when we're in -d mode. 3116d1014a3SAlan Somers */ 3126a2ae0ebSAlan Somers if (no_daemon) { 3136d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 3140321b694SHiroki Sato _var.c_str(), value.c_str(), _re.c_str(), _inv); 3156d1014a3SAlan Somers } 3168b823408SWarner Losh 3175102ef84SWarner Losh retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 3180321b694SHiroki Sato if (_inv == 1) 3190321b694SHiroki Sato retval = (retval == 0) ? 1 : 0; 3200321b694SHiroki Sato 3210285e9b1SAlan Somers return (retval); 3223054f218SWarner Losh } 3233054f218SWarner Losh 324cd70782bSWarner Losh #include <sys/sockio.h> 325cd70782bSWarner Losh #include <net/if.h> 326cd70782bSWarner Losh #include <net/if_media.h> 327cd70782bSWarner Losh 32894d2d4ebSBrooks Davis media::media(config &, const char *var, const char *type) 329cd70782bSWarner Losh : _var(var), _type(-1) 330cd70782bSWarner Losh { 331cd70782bSWarner Losh static struct ifmedia_description media_types[] = { 332cd70782bSWarner Losh { IFM_ETHER, "Ethernet" }, 333cd70782bSWarner Losh { IFM_TOKEN, "Tokenring" }, 334cd70782bSWarner Losh { IFM_FDDI, "FDDI" }, 335cd70782bSWarner Losh { IFM_IEEE80211, "802.11" }, 336cd70782bSWarner Losh { IFM_ATM, "ATM" }, 337cd70782bSWarner Losh { -1, "unknown" }, 338cd70782bSWarner Losh { 0, NULL }, 339cd70782bSWarner Losh }; 340edee691dSEitan Adler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 341cd70782bSWarner Losh if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 342cd70782bSWarner Losh _type = media_types[i].ifmt_word; 343cd70782bSWarner Losh break; 344cd70782bSWarner Losh } 345cd70782bSWarner Losh } 346cd70782bSWarner Losh 347cd70782bSWarner Losh media::~media() 348cd70782bSWarner Losh { 349cd70782bSWarner Losh } 350cd70782bSWarner Losh 351cd70782bSWarner Losh bool 352cd70782bSWarner Losh media::do_match(config &c) 353cd70782bSWarner Losh { 35409c47e76SWarner Losh string value; 355cd70782bSWarner Losh struct ifmediareq ifmr; 356cd70782bSWarner Losh bool retval; 357cd70782bSWarner Losh int s; 358cd70782bSWarner Losh 35909c47e76SWarner Losh // Since we can be called from both a device attach/detach 36009c47e76SWarner Losh // context where device-name is defined and what we want, 36109c47e76SWarner Losh // as well as from a link status context, where subsystem is 36209c47e76SWarner Losh // the name of interest, first try device-name and fall back 36309c47e76SWarner Losh // to subsystem if none exists. 36409c47e76SWarner Losh value = c.get_variable("device-name"); 365a6393aa1SEitan Adler if (value.empty()) 366de02eec8SWarner Losh value = c.get_variable("subsystem"); 3676d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 368cd70782bSWarner Losh value.c_str(), _type); 369cd70782bSWarner Losh 370cd70782bSWarner Losh retval = false; 371cd70782bSWarner Losh 372cd70782bSWarner Losh s = socket(PF_INET, SOCK_DGRAM, 0); 373cd70782bSWarner Losh if (s >= 0) { 374cd70782bSWarner Losh memset(&ifmr, 0, sizeof(ifmr)); 375cd70782bSWarner Losh strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 376cd70782bSWarner Losh 377cd70782bSWarner Losh if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 378cd70782bSWarner Losh ifmr.ifm_status & IFM_AVALID) { 3796d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 380cd70782bSWarner Losh value.c_str(), IFM_TYPE(ifmr.ifm_active)); 381cd70782bSWarner Losh retval = (IFM_TYPE(ifmr.ifm_active) == _type); 382cd70782bSWarner Losh } else if (_type == -1) { 3836d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has unknown media type\n", 384cd70782bSWarner Losh value.c_str()); 385cd70782bSWarner Losh retval = true; 386cd70782bSWarner Losh } 387cd70782bSWarner Losh close(s); 388cd70782bSWarner Losh } 389cd70782bSWarner Losh 3900285e9b1SAlan Somers return (retval); 391cd70782bSWarner Losh } 392cd70782bSWarner Losh 3933054f218SWarner Losh const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 3943054f218SWarner Losh const string var_list::nothing = ""; 3953054f218SWarner Losh 3963054f218SWarner Losh const string & 3973054f218SWarner Losh var_list::get_variable(const string &var) const 3983054f218SWarner Losh { 3993054f218SWarner Losh map<string, string>::const_iterator i; 4003054f218SWarner Losh 4013054f218SWarner Losh i = _vars.find(var); 4023054f218SWarner Losh if (i == _vars.end()) 4038b823408SWarner Losh return (var_list::bogus); 4043054f218SWarner Losh return (i->second); 4053054f218SWarner Losh } 4063054f218SWarner Losh 4073054f218SWarner Losh bool 4083054f218SWarner Losh var_list::is_set(const string &var) const 4093054f218SWarner Losh { 4103054f218SWarner Losh return (_vars.find(var) != _vars.end()); 4113054f218SWarner Losh } 4123054f218SWarner Losh 4133054f218SWarner Losh void 4143054f218SWarner Losh var_list::set_variable(const string &var, const string &val) 4153054f218SWarner Losh { 4166d1014a3SAlan Somers /* 4176d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 4186d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 4196d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 4206d1014a3SAlan Somers * log when we're in -d mode. 4216d1014a3SAlan Somers */ 4226a2ae0ebSAlan Somers if (no_daemon) 4236d1014a3SAlan Somers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 4243054f218SWarner Losh _vars[var] = val; 4253054f218SWarner Losh } 4263054f218SWarner Losh 4273054f218SWarner Losh void 4283054f218SWarner Losh config::reset(void) 4293054f218SWarner Losh { 4303054f218SWarner Losh _dir_list.clear(); 4318b823408SWarner Losh delete_and_clear(_var_list_table); 4328b823408SWarner Losh delete_and_clear(_attach_list); 4338b823408SWarner Losh delete_and_clear(_detach_list); 4348b823408SWarner Losh delete_and_clear(_nomatch_list); 435842ccec5SWarner Losh delete_and_clear(_notify_list); 4363054f218SWarner Losh } 4373054f218SWarner Losh 4383054f218SWarner Losh void 4393054f218SWarner Losh config::parse_one_file(const char *fn) 4403054f218SWarner Losh { 4416d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 4423054f218SWarner Losh yyin = fopen(fn, "r"); 4433054f218SWarner Losh if (yyin == NULL) 4443054f218SWarner Losh err(1, "Cannot open config file %s", fn); 4454a3050fcSMaxim Konovalov lineno = 1; 4463054f218SWarner Losh if (yyparse() != 0) 4473054f218SWarner Losh errx(1, "Cannot parse %s at line %d", fn, lineno); 4483054f218SWarner Losh fclose(yyin); 4493054f218SWarner Losh } 4503054f218SWarner Losh 4513054f218SWarner Losh void 4523054f218SWarner Losh config::parse_files_in_dir(const char *dirname) 4533054f218SWarner Losh { 4543054f218SWarner Losh DIR *dirp; 4553054f218SWarner Losh struct dirent *dp; 4563054f218SWarner Losh char path[PATH_MAX]; 4573054f218SWarner Losh 4586d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 4593054f218SWarner Losh dirp = opendir(dirname); 4603054f218SWarner Losh if (dirp == NULL) 4613054f218SWarner Losh return; 4623054f218SWarner Losh readdir(dirp); /* Skip . */ 4633054f218SWarner Losh readdir(dirp); /* Skip .. */ 4643054f218SWarner Losh while ((dp = readdir(dirp)) != NULL) { 4653054f218SWarner Losh if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 4663054f218SWarner Losh snprintf(path, sizeof(path), "%s/%s", 4673054f218SWarner Losh dirname, dp->d_name); 4683054f218SWarner Losh parse_one_file(path); 4693054f218SWarner Losh } 4703054f218SWarner Losh } 4715983e891SKevin Lo closedir(dirp); 4723054f218SWarner Losh } 4733054f218SWarner Losh 4748b823408SWarner Losh class epv_greater { 4758b823408SWarner Losh public: 476ef370346SEitan Adler int operator()(event_proc *const&l1, event_proc *const&l2) const 4778b823408SWarner Losh { 4788b823408SWarner Losh return (l1->get_priority() > l2->get_priority()); 4798b823408SWarner Losh } 4808b823408SWarner Losh }; 4818b823408SWarner Losh 4828b823408SWarner Losh void 4838b823408SWarner Losh config::sort_vector(vector<event_proc *> &v) 4848b823408SWarner Losh { 4850c04d185SDimitry Andric stable_sort(v.begin(), v.end(), epv_greater()); 4868b823408SWarner Losh } 4878b823408SWarner Losh 4883054f218SWarner Losh void 4893054f218SWarner Losh config::parse(void) 4903054f218SWarner Losh { 4913054f218SWarner Losh vector<string>::const_iterator i; 4923054f218SWarner Losh 4938334958aSJoseph Koshy parse_one_file(configfile); 494edee691dSEitan Adler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 4953054f218SWarner Losh parse_files_in_dir((*i).c_str()); 4968b823408SWarner Losh sort_vector(_attach_list); 4978b823408SWarner Losh sort_vector(_detach_list); 4988b823408SWarner Losh sort_vector(_nomatch_list); 499842ccec5SWarner Losh sort_vector(_notify_list); 5003054f218SWarner Losh } 5013054f218SWarner Losh 5023054f218SWarner Losh void 5031a0cc6b1SPawel Jakub Dawidek config::open_pidfile() 5043054f218SWarner Losh { 5051a0cc6b1SPawel Jakub Dawidek pid_t otherpid; 5063054f218SWarner Losh 5075a3b1a3dSEitan Adler if (_pidfile.empty()) 5083054f218SWarner Losh return; 5091a0cc6b1SPawel Jakub Dawidek pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 5101a0cc6b1SPawel Jakub Dawidek if (pfh == NULL) { 5111a0cc6b1SPawel Jakub Dawidek if (errno == EEXIST) 5121a0cc6b1SPawel Jakub Dawidek errx(1, "devd already running, pid: %d", (int)otherpid); 5131a0cc6b1SPawel Jakub Dawidek warn("cannot open pid file"); 5141a0cc6b1SPawel Jakub Dawidek } 5151a0cc6b1SPawel Jakub Dawidek } 5161a0cc6b1SPawel Jakub Dawidek 5171a0cc6b1SPawel Jakub Dawidek void 5181a0cc6b1SPawel Jakub Dawidek config::write_pidfile() 5191a0cc6b1SPawel Jakub Dawidek { 5201a0cc6b1SPawel Jakub Dawidek 5211a0cc6b1SPawel Jakub Dawidek pidfile_write(pfh); 5221a0cc6b1SPawel Jakub Dawidek } 5231a0cc6b1SPawel Jakub Dawidek 5241a0cc6b1SPawel Jakub Dawidek void 525b8f92ce4SWarner Losh config::close_pidfile() 526b8f92ce4SWarner Losh { 527b8f92ce4SWarner Losh 528b8f92ce4SWarner Losh pidfile_close(pfh); 529b8f92ce4SWarner Losh } 530b8f92ce4SWarner Losh 531b8f92ce4SWarner Losh void 5321a0cc6b1SPawel Jakub Dawidek config::remove_pidfile() 5331a0cc6b1SPawel Jakub Dawidek { 5341a0cc6b1SPawel Jakub Dawidek 5351a0cc6b1SPawel Jakub Dawidek pidfile_remove(pfh); 5363054f218SWarner Losh } 5373054f218SWarner Losh 5383054f218SWarner Losh void 5393054f218SWarner Losh config::add_attach(int prio, event_proc *p) 5403054f218SWarner Losh { 5413054f218SWarner Losh p->set_priority(prio); 5423054f218SWarner Losh _attach_list.push_back(p); 5433054f218SWarner Losh } 5443054f218SWarner Losh 5453054f218SWarner Losh void 5463054f218SWarner Losh config::add_detach(int prio, event_proc *p) 5473054f218SWarner Losh { 5483054f218SWarner Losh p->set_priority(prio); 5493054f218SWarner Losh _detach_list.push_back(p); 5503054f218SWarner Losh } 5513054f218SWarner Losh 5523054f218SWarner Losh void 5533054f218SWarner Losh config::add_directory(const char *dir) 5543054f218SWarner Losh { 5553054f218SWarner Losh _dir_list.push_back(string(dir)); 5563054f218SWarner Losh } 5573054f218SWarner Losh 5583054f218SWarner Losh void 5593054f218SWarner Losh config::add_nomatch(int prio, event_proc *p) 5603054f218SWarner Losh { 5613054f218SWarner Losh p->set_priority(prio); 5623054f218SWarner Losh _nomatch_list.push_back(p); 5633054f218SWarner Losh } 5643054f218SWarner Losh 5653054f218SWarner Losh void 566842ccec5SWarner Losh config::add_notify(int prio, event_proc *p) 567842ccec5SWarner Losh { 568842ccec5SWarner Losh p->set_priority(prio); 569842ccec5SWarner Losh _notify_list.push_back(p); 570842ccec5SWarner Losh } 571842ccec5SWarner Losh 572842ccec5SWarner Losh void 5733054f218SWarner Losh config::set_pidfile(const char *fn) 5743054f218SWarner Losh { 5755a3b1a3dSEitan Adler _pidfile = fn; 5763054f218SWarner Losh } 5773054f218SWarner Losh 5783054f218SWarner Losh void 5793054f218SWarner Losh config::push_var_table() 5803054f218SWarner Losh { 5813054f218SWarner Losh var_list *vl; 5823054f218SWarner Losh 5833054f218SWarner Losh vl = new var_list(); 5843054f218SWarner Losh _var_list_table.push_back(vl); 5856d1014a3SAlan Somers devdlog(LOG_DEBUG, "Pushing table\n"); 5863054f218SWarner Losh } 5873054f218SWarner Losh 5883054f218SWarner Losh void 5893054f218SWarner Losh config::pop_var_table() 5903054f218SWarner Losh { 5913054f218SWarner Losh delete _var_list_table.back(); 5923054f218SWarner Losh _var_list_table.pop_back(); 5936d1014a3SAlan Somers devdlog(LOG_DEBUG, "Popping table\n"); 5943054f218SWarner Losh } 5953054f218SWarner Losh 5963054f218SWarner Losh void 5973054f218SWarner Losh config::set_variable(const char *var, const char *val) 5983054f218SWarner Losh { 5993054f218SWarner Losh _var_list_table.back()->set_variable(var, val); 6003054f218SWarner Losh } 6013054f218SWarner Losh 6023054f218SWarner Losh const string & 6033054f218SWarner Losh config::get_variable(const string &var) 6043054f218SWarner Losh { 6053054f218SWarner Losh vector<var_list *>::reverse_iterator i; 6063054f218SWarner Losh 607edee691dSEitan Adler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 6083054f218SWarner Losh if ((*i)->is_set(var)) 6098b823408SWarner Losh return ((*i)->get_variable(var)); 6103054f218SWarner Losh } 6113054f218SWarner Losh return (var_list::nothing); 6123054f218SWarner Losh } 6133054f218SWarner Losh 6148b823408SWarner Losh bool 615ef370346SEitan Adler config::is_id_char(char ch) const 6168b823408SWarner Losh { 6178b823408SWarner Losh return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 6188b823408SWarner Losh ch == '-')); 6198b823408SWarner Losh } 6208b823408SWarner Losh 6215102ef84SWarner Losh void 6226cabd675SWarner Losh config::expand_one(const char *&src, string &dst) 6233054f218SWarner Losh { 6245102ef84SWarner Losh int count; 6255bf08422SUlf Lilleengen string buffer; 6265102ef84SWarner Losh 6278b823408SWarner Losh src++; 6285102ef84SWarner Losh // $$ -> $ 6295102ef84SWarner Losh if (*src == '$') { 6307d9e9c60SEitan Adler dst += *src++; 6315102ef84SWarner Losh return; 6325102ef84SWarner Losh } 6335102ef84SWarner Losh 6345102ef84SWarner Losh // $(foo) -> $(foo) 6358b823408SWarner Losh // Not sure if I want to support this or not, so for now we just pass 6368b823408SWarner Losh // it through. 6375102ef84SWarner Losh if (*src == '(') { 6387d9e9c60SEitan Adler dst += '$'; 6395102ef84SWarner Losh count = 1; 6406cabd675SWarner Losh /* If the string ends before ) is matched , return. */ 6416cabd675SWarner Losh while (count > 0 && *src) { 6425102ef84SWarner Losh if (*src == ')') 6435102ef84SWarner Losh count--; 6445102ef84SWarner Losh else if (*src == '(') 6455102ef84SWarner Losh count++; 6467d9e9c60SEitan Adler dst += *src++; 6475102ef84SWarner Losh } 6485102ef84SWarner Losh return; 6495102ef84SWarner Losh } 6505102ef84SWarner Losh 651b3d32292SWarner Losh // $[^-A-Za-z_*] -> $\1 652b3d32292SWarner Losh if (!isalpha(*src) && *src != '_' && *src != '-' && *src != '*') { 6537d9e9c60SEitan Adler dst += '$'; 6547d9e9c60SEitan Adler dst += *src++; 6555102ef84SWarner Losh return; 6565102ef84SWarner Losh } 6575102ef84SWarner Losh 6585102ef84SWarner Losh // $var -> replace with value 6596cabd675SWarner Losh do { 6607d9e9c60SEitan Adler buffer += *src++; 66166ff6821SWarner Losh } while (is_id_char(*src)); 6625a3b1a3dSEitan Adler dst.append(get_variable(buffer)); 6635102ef84SWarner Losh } 6645102ef84SWarner Losh 6655102ef84SWarner Losh const string 6665dfc0f6cSIan Lepore config::expand_string(const char *src, const char *prepend, const char *append) 6675102ef84SWarner Losh { 6685dfc0f6cSIan Lepore const char *var_at; 6696cabd675SWarner Losh string dst; 6705102ef84SWarner Losh 6715dfc0f6cSIan Lepore /* 6725dfc0f6cSIan Lepore * 128 bytes is enough for 2427 of 2438 expansions that happen 6735dfc0f6cSIan Lepore * while parsing config files, as tested on 2013-01-30. 6745dfc0f6cSIan Lepore */ 6755dfc0f6cSIan Lepore dst.reserve(128); 6765dfc0f6cSIan Lepore 6775dfc0f6cSIan Lepore if (prepend != NULL) 6785dfc0f6cSIan Lepore dst = prepend; 6795dfc0f6cSIan Lepore 6805dfc0f6cSIan Lepore for (;;) { 6815dfc0f6cSIan Lepore var_at = strchr(src, '$'); 6825dfc0f6cSIan Lepore if (var_at == NULL) { 6835dfc0f6cSIan Lepore dst.append(src); 6845dfc0f6cSIan Lepore break; 6855102ef84SWarner Losh } 6865dfc0f6cSIan Lepore dst.append(src, var_at - src); 6875dfc0f6cSIan Lepore src = var_at; 6885dfc0f6cSIan Lepore expand_one(src, dst); 6895dfc0f6cSIan Lepore } 6905dfc0f6cSIan Lepore 6915dfc0f6cSIan Lepore if (append != NULL) 6925dfc0f6cSIan Lepore dst.append(append); 6935102ef84SWarner Losh 6946cabd675SWarner Losh return (dst); 6953054f218SWarner Losh } 6963054f218SWarner Losh 6978b823408SWarner Losh bool 69811fd1366SEitan Adler config::chop_var(char *&buffer, char *&lhs, char *&rhs) const 6998b823408SWarner Losh { 7008b823408SWarner Losh char *walker; 7018b823408SWarner Losh 7028b823408SWarner Losh if (*buffer == '\0') 7038b823408SWarner Losh return (false); 7048b823408SWarner Losh walker = lhs = buffer; 7058b823408SWarner Losh while (is_id_char(*walker)) 7068b823408SWarner Losh walker++; 7078b823408SWarner Losh if (*walker != '=') 7088b823408SWarner Losh return (false); 7098b823408SWarner Losh walker++; // skip = 7108b823408SWarner Losh if (*walker == '"') { 7118b823408SWarner Losh walker++; // skip " 7128b823408SWarner Losh rhs = walker; 7138b823408SWarner Losh while (*walker && *walker != '"') 7148b823408SWarner Losh walker++; 7158b823408SWarner Losh if (*walker != '"') 7168b823408SWarner Losh return (false); 7178b823408SWarner Losh rhs[-2] = '\0'; 7188b823408SWarner Losh *walker++ = '\0'; 7198b823408SWarner Losh } else { 7208b823408SWarner Losh rhs = walker; 7218b823408SWarner Losh while (*walker && !isspace(*walker)) 7228b823408SWarner Losh walker++; 7238b823408SWarner Losh if (*walker != '\0') 7248b823408SWarner Losh *walker++ = '\0'; 7258b823408SWarner Losh rhs[-1] = '\0'; 7268b823408SWarner Losh } 727b1ee04b1SWarner Losh while (isspace(*walker)) 728b1ee04b1SWarner Losh walker++; 7298b823408SWarner Losh buffer = walker; 7308b823408SWarner Losh return (true); 7318b823408SWarner Losh } 7328b823408SWarner Losh 7338b823408SWarner Losh 7348b823408SWarner Losh char * 7358b823408SWarner Losh config::set_vars(char *buffer) 7368b823408SWarner Losh { 7378b823408SWarner Losh char *lhs; 7388b823408SWarner Losh char *rhs; 7398b823408SWarner Losh 7408b823408SWarner Losh while (1) { 7418b823408SWarner Losh if (!chop_var(buffer, lhs, rhs)) 7428b823408SWarner Losh break; 7438b823408SWarner Losh set_variable(lhs, rhs); 7448b823408SWarner Losh } 7458b823408SWarner Losh return (buffer); 7468b823408SWarner Losh } 7478b823408SWarner Losh 7488b823408SWarner Losh void 7498b823408SWarner Losh config::find_and_execute(char type) 7508b823408SWarner Losh { 7518b823408SWarner Losh vector<event_proc *> *l; 7528b823408SWarner Losh vector<event_proc *>::const_iterator i; 75394d2d4ebSBrooks Davis const char *s; 7548b823408SWarner Losh 7558b823408SWarner Losh switch (type) { 7568b823408SWarner Losh default: 7578b823408SWarner Losh return; 758842ccec5SWarner Losh case notify: 759842ccec5SWarner Losh l = &_notify_list; 760842ccec5SWarner Losh s = "notify"; 761842ccec5SWarner Losh break; 7628b823408SWarner Losh case nomatch: 7638b823408SWarner Losh l = &_nomatch_list; 7648b823408SWarner Losh s = "nomatch"; 7658b823408SWarner Losh break; 7668b823408SWarner Losh case attach: 7678b823408SWarner Losh l = &_attach_list; 7688b823408SWarner Losh s = "attach"; 7698b823408SWarner Losh break; 7708b823408SWarner Losh case detach: 7718b823408SWarner Losh l = &_detach_list; 7728b823408SWarner Losh s = "detach"; 7738b823408SWarner Losh break; 7748b823408SWarner Losh } 7756d1014a3SAlan Somers devdlog(LOG_DEBUG, "Processing %s event\n", s); 776edee691dSEitan Adler for (i = l->begin(); i != l->end(); ++i) { 7778b823408SWarner Losh if ((*i)->matches(*this)) { 7788b823408SWarner Losh (*i)->run(*this); 7798b823408SWarner Losh break; 7808b823408SWarner Losh } 7818b823408SWarner Losh } 7828b823408SWarner Losh 7838b823408SWarner Losh } 7848b823408SWarner Losh 7850285e9b1SAlan Somers 7863054f218SWarner Losh static void 7878b823408SWarner Losh process_event(char *buffer) 7883054f218SWarner Losh { 7893054f218SWarner Losh char type; 7903054f218SWarner Losh char *sp; 791*3b336ac3SWarner Losh struct timeval tv; 792*3b336ac3SWarner Losh char *timestr; 7933054f218SWarner Losh 7948b823408SWarner Losh sp = buffer + 1; 795f0038a8eSAlan Somers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 7963054f218SWarner Losh type = *buffer++; 7978b823408SWarner Losh cfg.push_var_table(); 798b3d32292SWarner Losh // $* is the entire line 799b3d32292SWarner Losh cfg.set_variable("*", buffer - 1); 800b3d32292SWarner Losh // $_ is the entire line without the initial character 801535595dbSWarner Losh cfg.set_variable("_", buffer); 802*3b336ac3SWarner Losh 803*3b336ac3SWarner Losh // Save the time this happened (as approximated by when we got 804*3b336ac3SWarner Losh // around to processing it). 805*3b336ac3SWarner Losh gettimeofday(&tv, NULL); 806*3b336ac3SWarner Losh asprintf(×tr, "%jd.%06ld", (uintmax_t)tv.tv_sec, tv.tv_usec); 807*3b336ac3SWarner Losh cfg.set_variable("timestamp", timestr); 808*3b336ac3SWarner Losh free(timestr); 809*3b336ac3SWarner Losh 810*3b336ac3SWarner Losh // Match doesn't have a device, and the format is a little 8118b823408SWarner Losh // different, so handle it separately. 812842ccec5SWarner Losh switch (type) { 813842ccec5SWarner Losh case notify: 814b3d32292SWarner Losh //! (k=v)* 815842ccec5SWarner Losh sp = cfg.set_vars(sp); 816842ccec5SWarner Losh break; 817842ccec5SWarner Losh case nomatch: 818528d3f79SWarner Losh //? at location pnp-info on bus 819528d3f79SWarner Losh sp = strchr(sp, ' '); 820528d3f79SWarner Losh if (sp == NULL) 821528d3f79SWarner Losh return; /* Can't happen? */ 822528d3f79SWarner Losh *sp++ = '\0'; 8239b6cb520SWarner Losh while (isspace(*sp)) 8249b6cb520SWarner Losh sp++; 825842ccec5SWarner Losh if (strncmp(sp, "at ", 3) == 0) 826842ccec5SWarner Losh sp += 3; 827842ccec5SWarner Losh sp = cfg.set_vars(sp); 8289b6cb520SWarner Losh while (isspace(*sp)) 8299b6cb520SWarner Losh sp++; 830842ccec5SWarner Losh if (strncmp(sp, "on ", 3) == 0) 831842ccec5SWarner Losh cfg.set_variable("bus", sp + 3); 832842ccec5SWarner Losh break; 833842ccec5SWarner Losh case attach: /*FALLTHROUGH*/ 834842ccec5SWarner Losh case detach: 8358b823408SWarner Losh sp = strchr(sp, ' '); 8363054f218SWarner Losh if (sp == NULL) 8373054f218SWarner Losh return; /* Can't happen? */ 8388b823408SWarner Losh *sp++ = '\0'; 8398b823408SWarner Losh cfg.set_variable("device-name", buffer); 8409b6cb520SWarner Losh while (isspace(*sp)) 8419b6cb520SWarner Losh sp++; 8428b823408SWarner Losh if (strncmp(sp, "at ", 3) == 0) 8438b823408SWarner Losh sp += 3; 8448b823408SWarner Losh sp = cfg.set_vars(sp); 8459b6cb520SWarner Losh while (isspace(*sp)) 8469b6cb520SWarner Losh sp++; 8478b823408SWarner Losh if (strncmp(sp, "on ", 3) == 0) 8488b823408SWarner Losh cfg.set_variable("bus", sp + 3); 849842ccec5SWarner Losh break; 850b1ee04b1SWarner Losh } 851b1ee04b1SWarner Losh 8528b823408SWarner Losh cfg.find_and_execute(type); 8538b823408SWarner Losh cfg.pop_var_table(); 8543054f218SWarner Losh } 8553054f218SWarner Losh 856251bedd1SWarner Losh int 8579102691eSAlan Somers create_socket(const char *name, int socktype) 858251bedd1SWarner Losh { 859251bedd1SWarner Losh int fd, slen; 860251bedd1SWarner Losh struct sockaddr_un sun; 861251bedd1SWarner Losh 8629102691eSAlan Somers if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 863251bedd1SWarner Losh err(1, "socket"); 864251bedd1SWarner Losh bzero(&sun, sizeof(sun)); 865251bedd1SWarner Losh sun.sun_family = AF_UNIX; 866251bedd1SWarner Losh strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 867251bedd1SWarner Losh slen = SUN_LEN(&sun); 868251bedd1SWarner Losh unlink(name); 869e60fa014SJoe Marcus Clarke if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 870e60fa014SJoe Marcus Clarke err(1, "fcntl"); 8715d586d29SDimitry Andric if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 872251bedd1SWarner Losh err(1, "bind"); 873251bedd1SWarner Losh listen(fd, 4); 87483818929SJoe Marcus Clarke chown(name, 0, 0); /* XXX - root.wheel */ 875e60fa014SJoe Marcus Clarke chmod(name, 0666); 876251bedd1SWarner Losh return (fd); 877251bedd1SWarner Losh } 878251bedd1SWarner Losh 879463a577bSEitan Adler unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ 880e1334f93SIan Lepore unsigned int num_clients; 8819102691eSAlan Somers 8829102691eSAlan Somers list<client_t> clients; 883251bedd1SWarner Losh 884251bedd1SWarner Losh void 885251bedd1SWarner Losh notify_clients(const char *data, int len) 886251bedd1SWarner Losh { 8879102691eSAlan Somers list<client_t>::iterator i; 888251bedd1SWarner Losh 889e1334f93SIan Lepore /* 890e1334f93SIan Lepore * Deliver the data to all clients. Throw clients overboard at the 891e1334f93SIan Lepore * first sign of trouble. This reaps clients who've died or closed 892e1334f93SIan Lepore * their sockets, and also clients who are alive but failing to keep up 893e1334f93SIan Lepore * (or who are maliciously not reading, to consume buffer space in 894e1334f93SIan Lepore * kernel memory or tie up the limited number of available connections). 895e1334f93SIan Lepore */ 896e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 8979102691eSAlan Somers int flags; 8989102691eSAlan Somers if (i->socktype == SOCK_SEQPACKET) 8999102691eSAlan Somers flags = MSG_EOR; 9009102691eSAlan Somers else 9019102691eSAlan Somers flags = 0; 9029102691eSAlan Somers 9039102691eSAlan Somers if (send(i->fd, data, len, flags) != len) { 904e1334f93SIan Lepore --num_clients; 9059102691eSAlan Somers close(i->fd); 906e1334f93SIan Lepore i = clients.erase(i); 9079102691eSAlan Somers devdlog(LOG_WARNING, "notify_clients: send() failed; " 9086d1014a3SAlan Somers "dropping unresponsive client\n"); 909e1334f93SIan Lepore } else 910e1334f93SIan Lepore ++i; 911251bedd1SWarner Losh } 912251bedd1SWarner Losh } 913251bedd1SWarner Losh 914e1334f93SIan Lepore void 915e1334f93SIan Lepore check_clients(void) 916e1334f93SIan Lepore { 917e1334f93SIan Lepore int s; 918e1334f93SIan Lepore struct pollfd pfd; 9199102691eSAlan Somers list<client_t>::iterator i; 920e1334f93SIan Lepore 921e1334f93SIan Lepore /* 922e1334f93SIan Lepore * Check all existing clients to see if any of them have disappeared. 923e1334f93SIan Lepore * Normally we reap clients when we get an error trying to send them an 924e1334f93SIan Lepore * event. This check eliminates the problem of an ever-growing list of 925e1334f93SIan Lepore * zombie clients because we're never writing to them on a system 926e1334f93SIan Lepore * without frequent device-change activity. 927e1334f93SIan Lepore */ 928e1334f93SIan Lepore pfd.events = 0; 929e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 9309102691eSAlan Somers pfd.fd = i->fd; 931e1334f93SIan Lepore s = poll(&pfd, 1, 0); 932e1334f93SIan Lepore if ((s < 0 && s != EINTR ) || 933e1334f93SIan Lepore (s > 0 && (pfd.revents & POLLHUP))) { 934e1334f93SIan Lepore --num_clients; 9359102691eSAlan Somers close(i->fd); 936e1334f93SIan Lepore i = clients.erase(i); 9376d1014a3SAlan Somers devdlog(LOG_NOTICE, "check_clients: " 9386d1014a3SAlan Somers "dropping disconnected client\n"); 939e1334f93SIan Lepore } else 940e1334f93SIan Lepore ++i; 941e1334f93SIan Lepore } 942251bedd1SWarner Losh } 943251bedd1SWarner Losh 944251bedd1SWarner Losh void 9459102691eSAlan Somers new_client(int fd, int socktype) 946251bedd1SWarner Losh { 9479102691eSAlan Somers client_t s; 948b026eddfSAlan Somers int sndbuf_size; 949251bedd1SWarner Losh 950e1334f93SIan Lepore /* 951e1334f93SIan Lepore * First go reap any zombie clients, then accept the connection, and 952e1334f93SIan Lepore * shut down the read side to stop clients from consuming kernel memory 953e1334f93SIan Lepore * by sending large buffers full of data we'll never read. 954e1334f93SIan Lepore */ 955e1334f93SIan Lepore check_clients(); 9569102691eSAlan Somers s.socktype = socktype; 9579102691eSAlan Somers s.fd = accept(fd, NULL, NULL); 9589102691eSAlan Somers if (s.fd != -1) { 959b026eddfSAlan Somers sndbuf_size = CLIENT_BUFSIZE; 9609102691eSAlan Somers if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 961b026eddfSAlan Somers sizeof(sndbuf_size))) 962b026eddfSAlan Somers err(1, "setsockopt"); 9639102691eSAlan Somers shutdown(s.fd, SHUT_RD); 964251bedd1SWarner Losh clients.push_back(s); 965e1334f93SIan Lepore ++num_clients; 966b026eddfSAlan Somers } else 967b026eddfSAlan Somers err(1, "accept"); 968251bedd1SWarner Losh } 969251bedd1SWarner Losh 9703054f218SWarner Losh static void 9713054f218SWarner Losh event_loop(void) 9723054f218SWarner Losh { 9733054f218SWarner Losh int rv; 9743054f218SWarner Losh int fd; 9753054f218SWarner Losh char buffer[DEVCTL_MAXBUF]; 9765a882d77SWarner Losh int once = 0; 9779102691eSAlan Somers int stream_fd, seqpacket_fd, max_fd; 978e1334f93SIan Lepore int accepting; 9795a882d77SWarner Losh timeval tv; 9805a882d77SWarner Losh fd_set fds; 9813054f218SWarner Losh 982901610f4SPawel Jakub Dawidek fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 9833054f218SWarner Losh if (fd == -1) 984251bedd1SWarner Losh err(1, "Can't open devctl device %s", PATH_DEVCTL); 9859102691eSAlan Somers stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 9869102691eSAlan Somers seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 987e1334f93SIan Lepore accepting = 1; 9889102691eSAlan Somers max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 98967ae3bf1SEitan Adler while (!romeo_must_die) { 9906a2ae0ebSAlan Somers if (!once && !no_daemon && !daemonize_quick) { 9915a882d77SWarner Losh // Check to see if we have any events pending. 9925a882d77SWarner Losh tv.tv_sec = 0; 9935a882d77SWarner Losh tv.tv_usec = 0; 9945a882d77SWarner Losh FD_ZERO(&fds); 9955a882d77SWarner Losh FD_SET(fd, &fds); 9965a882d77SWarner Losh rv = select(fd + 1, &fds, &fds, &fds, &tv); 9975a882d77SWarner Losh // No events -> we've processed all pending events 998f3059505SWarner Losh if (rv == 0) { 9996d1014a3SAlan Somers devdlog(LOG_DEBUG, "Calling daemon\n"); 10001a0cc6b1SPawel Jakub Dawidek cfg.remove_pidfile(); 10011a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 10025a882d77SWarner Losh daemon(0, 0); 10031a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 10045a882d77SWarner Losh once++; 10055a882d77SWarner Losh } 10065a882d77SWarner Losh } 1007e1334f93SIan Lepore /* 1008e1334f93SIan Lepore * When we've already got the max number of clients, stop 10099102691eSAlan Somers * accepting new connections (don't put the listening sockets in 10109102691eSAlan Somers * the set), shrink the accept() queue to reject connections 10119102691eSAlan Somers * quickly, and poll the existing clients more often, so that we 10129102691eSAlan Somers * notice more quickly when any of them disappear to free up 10139102691eSAlan Somers * client slots. 1014e1334f93SIan Lepore */ 1015251bedd1SWarner Losh FD_ZERO(&fds); 1016251bedd1SWarner Losh FD_SET(fd, &fds); 1017e1334f93SIan Lepore if (num_clients < max_clients) { 1018e1334f93SIan Lepore if (!accepting) { 10199102691eSAlan Somers listen(stream_fd, max_clients); 10209102691eSAlan Somers listen(seqpacket_fd, max_clients); 1021e1334f93SIan Lepore accepting = 1; 1022e1334f93SIan Lepore } 10239102691eSAlan Somers FD_SET(stream_fd, &fds); 10249102691eSAlan Somers FD_SET(seqpacket_fd, &fds); 1025e1334f93SIan Lepore tv.tv_sec = 60; 1026e1334f93SIan Lepore tv.tv_usec = 0; 1027e1334f93SIan Lepore } else { 1028e1334f93SIan Lepore if (accepting) { 10299102691eSAlan Somers listen(stream_fd, 0); 10309102691eSAlan Somers listen(seqpacket_fd, 0); 1031e1334f93SIan Lepore accepting = 0; 1032e1334f93SIan Lepore } 1033e1334f93SIan Lepore tv.tv_sec = 2; 1034e1334f93SIan Lepore tv.tv_usec = 0; 1035e1334f93SIan Lepore } 1036e1334f93SIan Lepore rv = select(max_fd, &fds, NULL, NULL, &tv); 1037be685e01SAlan Somers if (got_siginfo) { 1038f0038a8eSAlan Somers devdlog(LOG_NOTICE, "Events received so far=%u\n", 1039be685e01SAlan Somers total_events); 1040be685e01SAlan Somers got_siginfo = 0; 1041be685e01SAlan Somers } 1042251bedd1SWarner Losh if (rv == -1) { 1043251bedd1SWarner Losh if (errno == EINTR) 1044251bedd1SWarner Losh continue; 1045251bedd1SWarner Losh err(1, "select"); 1046e1334f93SIan Lepore } else if (rv == 0) 1047e1334f93SIan Lepore check_clients(); 1048251bedd1SWarner Losh if (FD_ISSET(fd, &fds)) { 10493054f218SWarner Losh rv = read(fd, buffer, sizeof(buffer) - 1); 10503054f218SWarner Losh if (rv > 0) { 1051be685e01SAlan Somers total_events++; 10526d1014a3SAlan Somers if (rv == sizeof(buffer) - 1) { 10536d1014a3SAlan Somers devdlog(LOG_WARNING, "Warning: " 10546d1014a3SAlan Somers "available event data exceeded " 10556d1014a3SAlan Somers "buffer space\n"); 10566d1014a3SAlan Somers } 1057251bedd1SWarner Losh notify_clients(buffer, rv); 10583054f218SWarner Losh buffer[rv] = '\0'; 10593054f218SWarner Losh while (buffer[--rv] == '\n') 10603054f218SWarner Losh buffer[rv] = '\0'; 10613054f218SWarner Losh process_event(buffer); 10623054f218SWarner Losh } else if (rv < 0) { 10633054f218SWarner Losh if (errno != EINTR) 10643054f218SWarner Losh break; 10653054f218SWarner Losh } else { 10663054f218SWarner Losh /* EOF */ 10673054f218SWarner Losh break; 10683054f218SWarner Losh } 10693054f218SWarner Losh } 10709102691eSAlan Somers if (FD_ISSET(stream_fd, &fds)) 10719102691eSAlan Somers new_client(stream_fd, SOCK_STREAM); 10729102691eSAlan Somers /* 10739102691eSAlan Somers * Aside from the socket type, both sockets use the same 10749102691eSAlan Somers * protocol, so we can process clients the same way. 10759102691eSAlan Somers */ 10769102691eSAlan Somers if (FD_ISSET(seqpacket_fd, &fds)) 10779102691eSAlan Somers new_client(seqpacket_fd, SOCK_SEQPACKET); 1078251bedd1SWarner Losh } 10793054f218SWarner Losh close(fd); 10803054f218SWarner Losh } 10810285e9b1SAlan Somers 10823054f218SWarner Losh /* 10833054f218SWarner Losh * functions that the parser uses. 10843054f218SWarner Losh */ 10853054f218SWarner Losh void 10863054f218SWarner Losh add_attach(int prio, event_proc *p) 10873054f218SWarner Losh { 10883054f218SWarner Losh cfg.add_attach(prio, p); 10893054f218SWarner Losh } 10903054f218SWarner Losh 10913054f218SWarner Losh void 10923054f218SWarner Losh add_detach(int prio, event_proc *p) 10933054f218SWarner Losh { 10943054f218SWarner Losh cfg.add_detach(prio, p); 10953054f218SWarner Losh } 10963054f218SWarner Losh 10973054f218SWarner Losh void 10983054f218SWarner Losh add_directory(const char *dir) 10993054f218SWarner Losh { 11003054f218SWarner Losh cfg.add_directory(dir); 11013054f218SWarner Losh free(const_cast<char *>(dir)); 11023054f218SWarner Losh } 11033054f218SWarner Losh 11043054f218SWarner Losh void 11053054f218SWarner Losh add_nomatch(int prio, event_proc *p) 11063054f218SWarner Losh { 11073054f218SWarner Losh cfg.add_nomatch(prio, p); 11083054f218SWarner Losh } 11093054f218SWarner Losh 1110842ccec5SWarner Losh void 1111842ccec5SWarner Losh add_notify(int prio, event_proc *p) 1112842ccec5SWarner Losh { 1113842ccec5SWarner Losh cfg.add_notify(prio, p); 1114842ccec5SWarner Losh } 1115842ccec5SWarner Losh 11163054f218SWarner Losh event_proc * 11173054f218SWarner Losh add_to_event_proc(event_proc *ep, eps *eps) 11183054f218SWarner Losh { 11193054f218SWarner Losh if (ep == NULL) 11203054f218SWarner Losh ep = new event_proc(); 11213054f218SWarner Losh ep->add(eps); 11223054f218SWarner Losh return (ep); 11233054f218SWarner Losh } 11243054f218SWarner Losh 11253054f218SWarner Losh eps * 11263054f218SWarner Losh new_action(const char *cmd) 11273054f218SWarner Losh { 11283054f218SWarner Losh eps *e = new action(cmd); 11293054f218SWarner Losh free(const_cast<char *>(cmd)); 11303054f218SWarner Losh return (e); 11313054f218SWarner Losh } 11323054f218SWarner Losh 11333054f218SWarner Losh eps * 11343054f218SWarner Losh new_match(const char *var, const char *re) 11353054f218SWarner Losh { 11365102ef84SWarner Losh eps *e = new match(cfg, var, re); 11373054f218SWarner Losh free(const_cast<char *>(var)); 11383054f218SWarner Losh free(const_cast<char *>(re)); 11393054f218SWarner Losh return (e); 11403054f218SWarner Losh } 11413054f218SWarner Losh 1142cd70782bSWarner Losh eps * 1143cd70782bSWarner Losh new_media(const char *var, const char *re) 1144cd70782bSWarner Losh { 1145cd70782bSWarner Losh eps *e = new media(cfg, var, re); 1146cd70782bSWarner Losh free(const_cast<char *>(var)); 1147cd70782bSWarner Losh free(const_cast<char *>(re)); 1148cd70782bSWarner Losh return (e); 1149cd70782bSWarner Losh } 1150cd70782bSWarner Losh 11513054f218SWarner Losh void 11523054f218SWarner Losh set_pidfile(const char *name) 11533054f218SWarner Losh { 11543054f218SWarner Losh cfg.set_pidfile(name); 11553054f218SWarner Losh free(const_cast<char *>(name)); 11563054f218SWarner Losh } 11573054f218SWarner Losh 11583054f218SWarner Losh void 11593054f218SWarner Losh set_variable(const char *var, const char *val) 11603054f218SWarner Losh { 11613054f218SWarner Losh cfg.set_variable(var, val); 11623054f218SWarner Losh free(const_cast<char *>(var)); 11633054f218SWarner Losh free(const_cast<char *>(val)); 11643054f218SWarner Losh } 11653054f218SWarner Losh 11660285e9b1SAlan Somers 11673054f218SWarner Losh 11683054f218SWarner Losh static void 11693054f218SWarner Losh gensighand(int) 11703054f218SWarner Losh { 1171fcdcaa88SEitan Adler romeo_must_die = 1; 11723054f218SWarner Losh } 11733054f218SWarner Losh 11746d1014a3SAlan Somers /* 1175be685e01SAlan Somers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1176be685e01SAlan Somers * as appropriate 1177be685e01SAlan Somers */ 1178be685e01SAlan Somers static void 1179be685e01SAlan Somers siginfohand(int) 1180be685e01SAlan Somers { 1181be685e01SAlan Somers got_siginfo = 1; 1182be685e01SAlan Somers } 1183be685e01SAlan Somers 1184be685e01SAlan Somers /* 11852e024bc2SAndriy Gapon * Local logging function. Prints to syslog if we're daemonized; stderr 11866d1014a3SAlan Somers * otherwise. 11876d1014a3SAlan Somers */ 11886d1014a3SAlan Somers static void 11896d1014a3SAlan Somers devdlog(int priority, const char* fmt, ...) 11906d1014a3SAlan Somers { 11916d1014a3SAlan Somers va_list argp; 11926d1014a3SAlan Somers 11936d1014a3SAlan Somers va_start(argp, fmt); 11946a2ae0ebSAlan Somers if (no_daemon) 11956d1014a3SAlan Somers vfprintf(stderr, fmt, argp); 11966a2ae0ebSAlan Somers else if ((! quiet_mode) || (priority <= LOG_WARNING)) 11976d1014a3SAlan Somers vsyslog(priority, fmt, argp); 11986d1014a3SAlan Somers va_end(argp); 11996d1014a3SAlan Somers } 12006d1014a3SAlan Somers 12013054f218SWarner Losh static void 12023054f218SWarner Losh usage() 12033054f218SWarner Losh { 12046a2ae0ebSAlan Somers fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1205e1334f93SIan Lepore getprogname()); 12063054f218SWarner Losh exit(1); 12073054f218SWarner Losh } 12083054f218SWarner Losh 12099c180898SWarner Losh static void 12109c180898SWarner Losh check_devd_enabled() 12119c180898SWarner Losh { 12129c180898SWarner Losh int val = 0; 12139c180898SWarner Losh size_t len; 12149c180898SWarner Losh 12159c180898SWarner Losh len = sizeof(val); 121679f92e25SWarner Losh if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 12179c180898SWarner Losh errx(1, "devctl sysctl missing from kernel!"); 1218ee38f2e0SMateusz Guzik if (val == 0) { 1219ee38f2e0SMateusz Guzik warnx("Setting " SYSCTL " to 1000"); 1220ee38f2e0SMateusz Guzik val = 1000; 12219c180898SWarner Losh sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val)); 12229c180898SWarner Losh } 12239c180898SWarner Losh } 12249c180898SWarner Losh 12253054f218SWarner Losh /* 12263054f218SWarner Losh * main 12273054f218SWarner Losh */ 12283054f218SWarner Losh int 12293054f218SWarner Losh main(int argc, char **argv) 12303054f218SWarner Losh { 12313054f218SWarner Losh int ch; 12323054f218SWarner Losh 12339c180898SWarner Losh check_devd_enabled(); 12346a2ae0ebSAlan Somers while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 12353054f218SWarner Losh switch (ch) { 12363054f218SWarner Losh case 'd': 12376a2ae0ebSAlan Somers no_daemon = 1; 12383054f218SWarner Losh break; 12398334958aSJoseph Koshy case 'f': 12408334958aSJoseph Koshy configfile = optarg; 12418334958aSJoseph Koshy break; 1242e1334f93SIan Lepore case 'l': 1243e1334f93SIan Lepore max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1244e1334f93SIan Lepore break; 12455a882d77SWarner Losh case 'n': 12466a2ae0ebSAlan Somers daemonize_quick = 1; 12476a2ae0ebSAlan Somers break; 12486a2ae0ebSAlan Somers case 'q': 12496a2ae0ebSAlan Somers quiet_mode = 1; 12505a882d77SWarner Losh break; 12513054f218SWarner Losh default: 12523054f218SWarner Losh usage(); 12533054f218SWarner Losh } 12543054f218SWarner Losh } 12553054f218SWarner Losh 12563054f218SWarner Losh cfg.parse(); 12576a2ae0ebSAlan Somers if (!no_daemon && daemonize_quick) { 12581a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 12593054f218SWarner Losh daemon(0, 0); 12601a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 1261fd6a8f23SWarner Losh } 1262f32a7686SWarner Losh signal(SIGPIPE, SIG_IGN); 12633054f218SWarner Losh signal(SIGHUP, gensighand); 12643054f218SWarner Losh signal(SIGINT, gensighand); 12653054f218SWarner Losh signal(SIGTERM, gensighand); 1266be685e01SAlan Somers signal(SIGINFO, siginfohand); 12673054f218SWarner Losh event_loop(); 12683054f218SWarner Losh return (0); 12693054f218SWarner Losh } 1270