13054f218SWarner Losh /*- 28a16b7a1SPedro F. Giffuni * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause-FreeBSD 38a16b7a1SPedro F. Giffuni * 4f86e6000SWarner Losh * Copyright (c) 2002-2010 M. Warner Losh <[email protected]> 53054f218SWarner Losh * 63054f218SWarner Losh * Redistribution and use in source and binary forms, with or without 73054f218SWarner Losh * modification, are permitted provided that the following conditions 83054f218SWarner Losh * are met: 93054f218SWarner Losh * 1. Redistributions of source code must retain the above copyright 103054f218SWarner Losh * notice, this list of conditions and the following disclaimer. 113054f218SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 123054f218SWarner Losh * notice, this list of conditions and the following disclaimer in the 133054f218SWarner Losh * documentation and/or other materials provided with the distribution. 143054f218SWarner Losh * 153054f218SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 163054f218SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 173054f218SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 183054f218SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 193054f218SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 203054f218SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 213054f218SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 223054f218SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 233054f218SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 243054f218SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 253054f218SWarner Losh * SUCH DAMAGE. 26b8f92ce4SWarner Losh * 27b8f92ce4SWarner Losh * my_system is a variation on lib/libc/stdlib/system.c: 28b8f92ce4SWarner Losh * 29b8f92ce4SWarner Losh * Copyright (c) 1988, 1993 30b8f92ce4SWarner Losh * The Regents of the University of California. All rights reserved. 31b8f92ce4SWarner Losh * 32b8f92ce4SWarner Losh * Redistribution and use in source and binary forms, with or without 33b8f92ce4SWarner Losh * modification, are permitted provided that the following conditions 34b8f92ce4SWarner Losh * are met: 35b8f92ce4SWarner Losh * 1. Redistributions of source code must retain the above copyright 36b8f92ce4SWarner Losh * notice, this list of conditions and the following disclaimer. 37b8f92ce4SWarner Losh * 2. Redistributions in binary form must reproduce the above copyright 38b8f92ce4SWarner Losh * notice, this list of conditions and the following disclaimer in the 39b8f92ce4SWarner Losh * documentation and/or other materials provided with the distribution. 40fbbd9655SWarner Losh * 3. Neither the name of the University nor the names of its contributors 41b8f92ce4SWarner Losh * may be used to endorse or promote products derived from this software 42b8f92ce4SWarner Losh * without specific prior written permission. 43b8f92ce4SWarner Losh * 44b8f92ce4SWarner Losh * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 45b8f92ce4SWarner Losh * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46b8f92ce4SWarner Losh * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47b8f92ce4SWarner Losh * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 48b8f92ce4SWarner Losh * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49b8f92ce4SWarner Losh * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50b8f92ce4SWarner Losh * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51b8f92ce4SWarner Losh * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52b8f92ce4SWarner Losh * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53b8f92ce4SWarner Losh * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54b8f92ce4SWarner Losh * SUCH DAMAGE. 553054f218SWarner Losh */ 563054f218SWarner Losh 573054f218SWarner Losh /* 583054f218SWarner Losh * DEVD control daemon. 593054f218SWarner Losh */ 603054f218SWarner Losh 613054f218SWarner Losh // TODO list: 623054f218SWarner Losh // o devd.conf and devd man pages need a lot of help: 63251bedd1SWarner Losh // - devd needs to document the unix domain socket 643054f218SWarner Losh // - devd.conf needs more details on the supported statements. 653054f218SWarner Losh 663054f218SWarner Losh #include <sys/cdefs.h> 673054f218SWarner Losh __FBSDID("$FreeBSD$"); 683054f218SWarner Losh 693054f218SWarner Losh #include <sys/param.h> 70251bedd1SWarner Losh #include <sys/socket.h> 71251bedd1SWarner Losh #include <sys/stat.h> 729c180898SWarner Losh #include <sys/sysctl.h> 73251bedd1SWarner Losh #include <sys/types.h> 74b8f92ce4SWarner Losh #include <sys/wait.h> 75251bedd1SWarner Losh #include <sys/un.h> 763054f218SWarner Losh 77d005340fSEitan Adler #include <cctype> 78d005340fSEitan Adler #include <cerrno> 79d005340fSEitan Adler #include <cstdlib> 80d005340fSEitan Adler #include <cstdio> 81d005340fSEitan Adler #include <csignal> 82d005340fSEitan Adler #include <cstring> 83967ecf4aSAlan Somers #include <cstdarg> 84d005340fSEitan Adler 853054f218SWarner Losh #include <dirent.h> 863054f218SWarner Losh #include <err.h> 873054f218SWarner Losh #include <fcntl.h> 881a0cc6b1SPawel Jakub Dawidek #include <libutil.h> 89b8f92ce4SWarner Losh #include <paths.h> 90e1334f93SIan Lepore #include <poll.h> 915102ef84SWarner Losh #include <regex.h> 926d1014a3SAlan Somers #include <syslog.h> 933054f218SWarner Losh #include <unistd.h> 943054f218SWarner Losh 958b823408SWarner Losh #include <algorithm> 963054f218SWarner Losh #include <map> 973054f218SWarner Losh #include <string> 98251bedd1SWarner Losh #include <list> 993e9662dcSAlan Somers #include <stdexcept> 1003054f218SWarner Losh #include <vector> 1013054f218SWarner Losh 1026aeeca8eSWarner Losh #include "devd.h" /* C compatible definitions */ 1036aeeca8eSWarner Losh #include "devd.hh" /* C++ class definitions */ 1043054f218SWarner Losh 1059102691eSAlan Somers #define STREAMPIPE "/var/run/devd.pipe" 1069102691eSAlan Somers #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 1073054f218SWarner Losh #define CF "/etc/devd.conf" 108ee38f2e0SMateusz Guzik #define SYSCTL "hw.bus.devctl_queue" 1093054f218SWarner Losh 110b026eddfSAlan Somers /* 111b026eddfSAlan Somers * Since the client socket is nonblocking, we must increase its send buffer to 112b026eddfSAlan Somers * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 113d9ca81daSAlan Somers * buffer, so the client can't increase the buffersize by itself. 114b026eddfSAlan Somers * 115b026eddfSAlan Somers * For example, when creating a ZFS pool, devd emits one 165 character 116d9ca81daSAlan Somers * resource.fs.zfs.statechange message for each vdev in the pool. The kernel 117d9ca81daSAlan Somers * allocates a 4608B mbuf for each message. Modern technology places a limit of 118d9ca81daSAlan Somers * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger 119d9ca81daSAlan Somers * than that. 120d9ca81daSAlan Somers * 121d9ca81daSAlan Somers * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf 122d9ca81daSAlan Somers * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf 123d9ca81daSAlan Somers * 124d9ca81daSAlan Somers * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. 125d9ca81daSAlan Somers * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * 126d9ca81daSAlan Somers * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by 127d9ca81daSAlan Somers * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we 128d9ca81daSAlan Somers * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the 129d9ca81daSAlan Somers * kernel will set the mbuf limit to 2MB, which is just large enough for 450 130d9ca81daSAlan Somers * drives. It also happens to be the same as the hardcoded maximum value. 131b026eddfSAlan Somers */ 132d9ca81daSAlan Somers #define CLIENT_BUFSIZE 262144 133b026eddfSAlan Somers 1343054f218SWarner Losh using namespace std; 1353054f218SWarner Losh 1369102691eSAlan Somers typedef struct client { 1379102691eSAlan Somers int fd; 1389102691eSAlan Somers int socktype; 1399102691eSAlan Somers } client_t; 1409102691eSAlan Somers 1413054f218SWarner Losh extern FILE *yyin; 1423054f218SWarner Losh 143842ccec5SWarner Losh static const char notify = '!'; 1448b823408SWarner Losh static const char nomatch = '?'; 1458b823408SWarner Losh static const char attach = '+'; 1468b823408SWarner Losh static const char detach = '-'; 1478b823408SWarner Losh 1481a0cc6b1SPawel Jakub Dawidek static struct pidfh *pfh; 1491a0cc6b1SPawel Jakub Dawidek 1506a2ae0ebSAlan Somers static int no_daemon = 0; 1516a2ae0ebSAlan Somers static int daemonize_quick = 0; 1526a2ae0ebSAlan Somers static int quiet_mode = 0; 153be685e01SAlan Somers static unsigned total_events = 0; 154be685e01SAlan Somers static volatile sig_atomic_t got_siginfo = 0; 155fcdcaa88SEitan Adler static volatile sig_atomic_t romeo_must_die = 0; 1563054f218SWarner Losh 1578334958aSJoseph Koshy static const char *configfile = CF; 1588334958aSJoseph Koshy 1593449b15aSAlan Somers static void devdlog(int priority, const char* message, ...) 1603449b15aSAlan Somers __printflike(2, 3); 1613054f218SWarner Losh static void event_loop(void); 162510a8c88SEitan Adler static void usage(void) __dead2; 1633054f218SWarner Losh 1648b823408SWarner Losh template <class T> void 1658b823408SWarner Losh delete_and_clear(vector<T *> &v) 1668b823408SWarner Losh { 1678b823408SWarner Losh typename vector<T *>::const_iterator i; 1683054f218SWarner Losh 169edee691dSEitan Adler for (i = v.begin(); i != v.end(); ++i) 1708b823408SWarner Losh delete *i; 1718b823408SWarner Losh v.clear(); 1728b823408SWarner Losh } 1738b823408SWarner Losh 1740bc60783SEitan Adler static config cfg; 1753054f218SWarner Losh 176*76527435SWarner Losh static const char *curr_cf = NULL; 177*76527435SWarner Losh 1783054f218SWarner Losh event_proc::event_proc() : _prio(-1) 1793054f218SWarner Losh { 1805dfc0f6cSIan Lepore _epsvec.reserve(4); 1813054f218SWarner Losh } 1823054f218SWarner Losh 1833054f218SWarner Losh event_proc::~event_proc() 1843054f218SWarner Losh { 18513f8bb71SAlexander Nedotsukov delete_and_clear(_epsvec); 1863054f218SWarner Losh } 1873054f218SWarner Losh 1883054f218SWarner Losh void 1893054f218SWarner Losh event_proc::add(eps *eps) 1903054f218SWarner Losh { 1913054f218SWarner Losh _epsvec.push_back(eps); 1923054f218SWarner Losh } 1933054f218SWarner Losh 1943054f218SWarner Losh bool 195ef370346SEitan Adler event_proc::matches(config &c) const 1963054f218SWarner Losh { 1973054f218SWarner Losh vector<eps *>::const_iterator i; 1983054f218SWarner Losh 199edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 2003054f218SWarner Losh if (!(*i)->do_match(c)) 2013054f218SWarner Losh return (false); 2023054f218SWarner Losh return (true); 2033054f218SWarner Losh } 2043054f218SWarner Losh 2053054f218SWarner Losh bool 206ef370346SEitan Adler event_proc::run(config &c) const 2073054f218SWarner Losh { 2083054f218SWarner Losh vector<eps *>::const_iterator i; 2093054f218SWarner Losh 210edee691dSEitan Adler for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 2113054f218SWarner Losh if (!(*i)->do_action(c)) 2123054f218SWarner Losh return (false); 2133054f218SWarner Losh return (true); 2143054f218SWarner Losh } 2153054f218SWarner Losh 2163054f218SWarner Losh action::action(const char *cmd) 2173054f218SWarner Losh : _cmd(cmd) 2183054f218SWarner Losh { 2193054f218SWarner Losh // nothing 2203054f218SWarner Losh } 2213054f218SWarner Losh 2223054f218SWarner Losh action::~action() 2233054f218SWarner Losh { 2243054f218SWarner Losh // nothing 2253054f218SWarner Losh } 2263054f218SWarner Losh 227b8f92ce4SWarner Losh static int 228b8f92ce4SWarner Losh my_system(const char *command) 229b8f92ce4SWarner Losh { 230b8f92ce4SWarner Losh pid_t pid, savedpid; 231b8f92ce4SWarner Losh int pstat; 232b8f92ce4SWarner Losh struct sigaction ign, intact, quitact; 233b8f92ce4SWarner Losh sigset_t newsigblock, oldsigblock; 234b8f92ce4SWarner Losh 235b8f92ce4SWarner Losh if (!command) /* just checking... */ 236b8f92ce4SWarner Losh return (1); 237b8f92ce4SWarner Losh 238b8f92ce4SWarner Losh /* 239b8f92ce4SWarner Losh * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 240b8f92ce4SWarner Losh * existing signal dispositions. 241b8f92ce4SWarner Losh */ 242b8f92ce4SWarner Losh ign.sa_handler = SIG_IGN; 243b8f92ce4SWarner Losh ::sigemptyset(&ign.sa_mask); 244b8f92ce4SWarner Losh ign.sa_flags = 0; 245b8f92ce4SWarner Losh ::sigaction(SIGINT, &ign, &intact); 246b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &ign, &quitact); 247b8f92ce4SWarner Losh ::sigemptyset(&newsigblock); 248b8f92ce4SWarner Losh ::sigaddset(&newsigblock, SIGCHLD); 249b8f92ce4SWarner Losh ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 250b8f92ce4SWarner Losh switch (pid = ::fork()) { 251b8f92ce4SWarner Losh case -1: /* error */ 252b8f92ce4SWarner Losh break; 253b8f92ce4SWarner Losh case 0: /* child */ 254b8f92ce4SWarner Losh /* 255b8f92ce4SWarner Losh * Restore original signal dispositions and exec the command. 256b8f92ce4SWarner Losh */ 257b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 258b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 259b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 260b8f92ce4SWarner Losh /* 261b8f92ce4SWarner Losh * Close the PID file, and all other open descriptors. 262b8f92ce4SWarner Losh * Inherit std{in,out,err} only. 263b8f92ce4SWarner Losh */ 264b8f92ce4SWarner Losh cfg.close_pidfile(); 265b8f92ce4SWarner Losh ::closefrom(3); 26627744ca7SEitan Adler ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 267b8f92ce4SWarner Losh ::_exit(127); 268b8f92ce4SWarner Losh default: /* parent */ 269b8f92ce4SWarner Losh savedpid = pid; 270b8f92ce4SWarner Losh do { 27127744ca7SEitan Adler pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 272b8f92ce4SWarner Losh } while (pid == -1 && errno == EINTR); 273b8f92ce4SWarner Losh break; 274b8f92ce4SWarner Losh } 275b8f92ce4SWarner Losh ::sigaction(SIGINT, &intact, NULL); 276b8f92ce4SWarner Losh ::sigaction(SIGQUIT, &quitact, NULL); 277b8f92ce4SWarner Losh ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 278b8f92ce4SWarner Losh return (pid == -1 ? -1 : pstat); 279b8f92ce4SWarner Losh } 280b8f92ce4SWarner Losh 2813054f218SWarner Losh bool 2825102ef84SWarner Losh action::do_action(config &c) 2833054f218SWarner Losh { 2845dfc0f6cSIan Lepore string s = c.expand_string(_cmd.c_str()); 2851bb0777eSAndriy Gapon devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 286b8f92ce4SWarner Losh my_system(s.c_str()); 2873054f218SWarner Losh return (true); 2883054f218SWarner Losh } 2893054f218SWarner Losh 2905dfc0f6cSIan Lepore match::match(config &c, const char *var, const char *re) : 2915dfc0f6cSIan Lepore _inv(re[0] == '!'), 2925dfc0f6cSIan Lepore _var(var), 2935dfc0f6cSIan Lepore _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 2943054f218SWarner Losh { 2959cb4a7bdSWarner Losh regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 2963054f218SWarner Losh } 2973054f218SWarner Losh 2983054f218SWarner Losh match::~match() 2993054f218SWarner Losh { 3005102ef84SWarner Losh regfree(&_regex); 3013054f218SWarner Losh } 3023054f218SWarner Losh 3033054f218SWarner Losh bool 3045102ef84SWarner Losh match::do_match(config &c) 3053054f218SWarner Losh { 3065bf08422SUlf Lilleengen const string &value = c.get_variable(_var); 3075102ef84SWarner Losh bool retval; 3085102ef84SWarner Losh 3096d1014a3SAlan Somers /* 3106d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 3116d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 3126d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 3136d1014a3SAlan Somers * log when we're in -d mode. 3146d1014a3SAlan Somers */ 3156a2ae0ebSAlan Somers if (no_daemon) { 3166d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 3170321b694SHiroki Sato _var.c_str(), value.c_str(), _re.c_str(), _inv); 3186d1014a3SAlan Somers } 3198b823408SWarner Losh 3205102ef84SWarner Losh retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 3210321b694SHiroki Sato if (_inv == 1) 3220321b694SHiroki Sato retval = (retval == 0) ? 1 : 0; 3230321b694SHiroki Sato 3240285e9b1SAlan Somers return (retval); 3253054f218SWarner Losh } 3263054f218SWarner Losh 327cd70782bSWarner Losh #include <sys/sockio.h> 328cd70782bSWarner Losh #include <net/if.h> 329cd70782bSWarner Losh #include <net/if_media.h> 330cd70782bSWarner Losh 33194d2d4ebSBrooks Davis media::media(config &, const char *var, const char *type) 332cd70782bSWarner Losh : _var(var), _type(-1) 333cd70782bSWarner Losh { 334cd70782bSWarner Losh static struct ifmedia_description media_types[] = { 335cd70782bSWarner Losh { IFM_ETHER, "Ethernet" }, 336cd70782bSWarner Losh { IFM_IEEE80211, "802.11" }, 337cd70782bSWarner Losh { IFM_ATM, "ATM" }, 338cd70782bSWarner Losh { -1, "unknown" }, 339cd70782bSWarner Losh { 0, NULL }, 340cd70782bSWarner Losh }; 341edee691dSEitan Adler for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 342cd70782bSWarner Losh if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 343cd70782bSWarner Losh _type = media_types[i].ifmt_word; 344cd70782bSWarner Losh break; 345cd70782bSWarner Losh } 346cd70782bSWarner Losh } 347cd70782bSWarner Losh 348cd70782bSWarner Losh media::~media() 349cd70782bSWarner Losh { 350cd70782bSWarner Losh } 351cd70782bSWarner Losh 352cd70782bSWarner Losh bool 353cd70782bSWarner Losh media::do_match(config &c) 354cd70782bSWarner Losh { 35509c47e76SWarner Losh string value; 356cd70782bSWarner Losh struct ifmediareq ifmr; 357cd70782bSWarner Losh bool retval; 358cd70782bSWarner Losh int s; 359cd70782bSWarner Losh 36009c47e76SWarner Losh // Since we can be called from both a device attach/detach 36109c47e76SWarner Losh // context where device-name is defined and what we want, 36209c47e76SWarner Losh // as well as from a link status context, where subsystem is 36309c47e76SWarner Losh // the name of interest, first try device-name and fall back 36409c47e76SWarner Losh // to subsystem if none exists. 36509c47e76SWarner Losh value = c.get_variable("device-name"); 366a6393aa1SEitan Adler if (value.empty()) 367de02eec8SWarner Losh value = c.get_variable("subsystem"); 3686d1014a3SAlan Somers devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 369cd70782bSWarner Losh value.c_str(), _type); 370cd70782bSWarner Losh 371cd70782bSWarner Losh retval = false; 372cd70782bSWarner Losh 373cd70782bSWarner Losh s = socket(PF_INET, SOCK_DGRAM, 0); 374cd70782bSWarner Losh if (s >= 0) { 375cd70782bSWarner Losh memset(&ifmr, 0, sizeof(ifmr)); 376daa0d9ddSAlan Somers strlcpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 377cd70782bSWarner Losh 37827744ca7SEitan Adler if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 379cd70782bSWarner Losh ifmr.ifm_status & IFM_AVALID) { 3806d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 381cd70782bSWarner Losh value.c_str(), IFM_TYPE(ifmr.ifm_active)); 382cd70782bSWarner Losh retval = (IFM_TYPE(ifmr.ifm_active) == _type); 383cd70782bSWarner Losh } else if (_type == -1) { 3846d1014a3SAlan Somers devdlog(LOG_DEBUG, "%s has unknown media type\n", 385cd70782bSWarner Losh value.c_str()); 386cd70782bSWarner Losh retval = true; 387cd70782bSWarner Losh } 388cd70782bSWarner Losh close(s); 389cd70782bSWarner Losh } 390cd70782bSWarner Losh 3910285e9b1SAlan Somers return (retval); 392cd70782bSWarner Losh } 393cd70782bSWarner Losh 3943054f218SWarner Losh const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 3953054f218SWarner Losh const string var_list::nothing = ""; 3963054f218SWarner Losh 3973054f218SWarner Losh const string & 3983054f218SWarner Losh var_list::get_variable(const string &var) const 3993054f218SWarner Losh { 4003054f218SWarner Losh map<string, string>::const_iterator i; 4013054f218SWarner Losh 4023054f218SWarner Losh i = _vars.find(var); 4033054f218SWarner Losh if (i == _vars.end()) 4048b823408SWarner Losh return (var_list::bogus); 4053054f218SWarner Losh return (i->second); 4063054f218SWarner Losh } 4073054f218SWarner Losh 4083054f218SWarner Losh bool 4093054f218SWarner Losh var_list::is_set(const string &var) const 4103054f218SWarner Losh { 4113054f218SWarner Losh return (_vars.find(var) != _vars.end()); 4123054f218SWarner Losh } 4133054f218SWarner Losh 414192af3b7SWarner Losh /** fix_value 415192af3b7SWarner Losh * 416192af3b7SWarner Losh * Removes quoted characters that have made it this far. \" are 417192af3b7SWarner Losh * converted to ". For all other characters, both \ and following 418192af3b7SWarner Losh * character. So the string 'fre\:\"' is translated to 'fred\:"'. 419192af3b7SWarner Losh */ 420416823b1SWarner Losh std::string 421192af3b7SWarner Losh var_list::fix_value(const std::string &val) const 422192af3b7SWarner Losh { 423416823b1SWarner Losh std::string rv(val); 424416823b1SWarner Losh std::string::size_type pos(0); 425192af3b7SWarner Losh 426416823b1SWarner Losh while ((pos = rv.find("\\\"", pos)) != rv.npos) { 427416823b1SWarner Losh rv.erase(pos, 1); 428192af3b7SWarner Losh } 429416823b1SWarner Losh return (rv); 430192af3b7SWarner Losh } 431192af3b7SWarner Losh 4323054f218SWarner Losh void 4333054f218SWarner Losh var_list::set_variable(const string &var, const string &val) 4343054f218SWarner Losh { 4356d1014a3SAlan Somers /* 4366d1014a3SAlan Somers * This function gets called WAY too often to justify calling syslog() 4376d1014a3SAlan Somers * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 4386d1014a3SAlan Somers * can consume excessive amounts of systime inside of connect(). Only 4396d1014a3SAlan Somers * log when we're in -d mode. 4406d1014a3SAlan Somers */ 441192af3b7SWarner Losh _vars[var] = fix_value(val); 4426a2ae0ebSAlan Somers if (no_daemon) 4436d1014a3SAlan Somers devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 4443054f218SWarner Losh } 4453054f218SWarner Losh 4463054f218SWarner Losh void 4473054f218SWarner Losh config::reset(void) 4483054f218SWarner Losh { 4493054f218SWarner Losh _dir_list.clear(); 4508b823408SWarner Losh delete_and_clear(_var_list_table); 4518b823408SWarner Losh delete_and_clear(_attach_list); 4528b823408SWarner Losh delete_and_clear(_detach_list); 4538b823408SWarner Losh delete_and_clear(_nomatch_list); 454842ccec5SWarner Losh delete_and_clear(_notify_list); 4553054f218SWarner Losh } 4563054f218SWarner Losh 457*76527435SWarner Losh /* 458*76527435SWarner Losh * Called recursively as new files are included, so current stack of old names 459*76527435SWarner Losh * saved in each instance of 'old' on the call stack. Called single threaded 460*76527435SWarner Losh * so global varaibles curr_cf and lineno (and all of yacc's parser state) 461*76527435SWarner Losh * are safe to access w/o a lock. 462*76527435SWarner Losh */ 4633054f218SWarner Losh void 4643054f218SWarner Losh config::parse_one_file(const char *fn) 4653054f218SWarner Losh { 466*76527435SWarner Losh const char *old; 467*76527435SWarner Losh 4686d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing %s\n", fn); 4693054f218SWarner Losh yyin = fopen(fn, "r"); 470*76527435SWarner Losh old = curr_cf; 471*76527435SWarner Losh curr_cf = fn; 4723054f218SWarner Losh if (yyin == NULL) 4733054f218SWarner Losh err(1, "Cannot open config file %s", fn); 4744a3050fcSMaxim Konovalov lineno = 1; 4753054f218SWarner Losh if (yyparse() != 0) 4763054f218SWarner Losh errx(1, "Cannot parse %s at line %d", fn, lineno); 4773054f218SWarner Losh fclose(yyin); 478*76527435SWarner Losh curr_cf = old; 4793054f218SWarner Losh } 4803054f218SWarner Losh 4813054f218SWarner Losh void 4823054f218SWarner Losh config::parse_files_in_dir(const char *dirname) 4833054f218SWarner Losh { 4843054f218SWarner Losh DIR *dirp; 4853054f218SWarner Losh struct dirent *dp; 4863054f218SWarner Losh char path[PATH_MAX]; 4873054f218SWarner Losh 4886d1014a3SAlan Somers devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 4893054f218SWarner Losh dirp = opendir(dirname); 4903054f218SWarner Losh if (dirp == NULL) 4913054f218SWarner Losh return; 4923054f218SWarner Losh readdir(dirp); /* Skip . */ 4933054f218SWarner Losh readdir(dirp); /* Skip .. */ 4943054f218SWarner Losh while ((dp = readdir(dirp)) != NULL) { 4953054f218SWarner Losh if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 4963054f218SWarner Losh snprintf(path, sizeof(path), "%s/%s", 4973054f218SWarner Losh dirname, dp->d_name); 4983054f218SWarner Losh parse_one_file(path); 4993054f218SWarner Losh } 5003054f218SWarner Losh } 5015983e891SKevin Lo closedir(dirp); 5023054f218SWarner Losh } 5033054f218SWarner Losh 5048b823408SWarner Losh class epv_greater { 5058b823408SWarner Losh public: 506ef370346SEitan Adler int operator()(event_proc *const&l1, event_proc *const&l2) const 5078b823408SWarner Losh { 5088b823408SWarner Losh return (l1->get_priority() > l2->get_priority()); 5098b823408SWarner Losh } 5108b823408SWarner Losh }; 5118b823408SWarner Losh 5128b823408SWarner Losh void 5138b823408SWarner Losh config::sort_vector(vector<event_proc *> &v) 5148b823408SWarner Losh { 5150c04d185SDimitry Andric stable_sort(v.begin(), v.end(), epv_greater()); 5168b823408SWarner Losh } 5178b823408SWarner Losh 5183054f218SWarner Losh void 5193054f218SWarner Losh config::parse(void) 5203054f218SWarner Losh { 5213054f218SWarner Losh vector<string>::const_iterator i; 5223054f218SWarner Losh 5238334958aSJoseph Koshy parse_one_file(configfile); 524edee691dSEitan Adler for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 5253054f218SWarner Losh parse_files_in_dir((*i).c_str()); 5268b823408SWarner Losh sort_vector(_attach_list); 5278b823408SWarner Losh sort_vector(_detach_list); 5288b823408SWarner Losh sort_vector(_nomatch_list); 529842ccec5SWarner Losh sort_vector(_notify_list); 5303054f218SWarner Losh } 5313054f218SWarner Losh 5323054f218SWarner Losh void 5331a0cc6b1SPawel Jakub Dawidek config::open_pidfile() 5343054f218SWarner Losh { 5351a0cc6b1SPawel Jakub Dawidek pid_t otherpid; 5363054f218SWarner Losh 5375a3b1a3dSEitan Adler if (_pidfile.empty()) 5383054f218SWarner Losh return; 5391a0cc6b1SPawel Jakub Dawidek pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 5401a0cc6b1SPawel Jakub Dawidek if (pfh == NULL) { 5411a0cc6b1SPawel Jakub Dawidek if (errno == EEXIST) 54227744ca7SEitan Adler errx(1, "devd already running, pid: %d", (int)otherpid); 5431a0cc6b1SPawel Jakub Dawidek warn("cannot open pid file"); 5441a0cc6b1SPawel Jakub Dawidek } 5451a0cc6b1SPawel Jakub Dawidek } 5461a0cc6b1SPawel Jakub Dawidek 5471a0cc6b1SPawel Jakub Dawidek void 5481a0cc6b1SPawel Jakub Dawidek config::write_pidfile() 5491a0cc6b1SPawel Jakub Dawidek { 5501a0cc6b1SPawel Jakub Dawidek 5511a0cc6b1SPawel Jakub Dawidek pidfile_write(pfh); 5521a0cc6b1SPawel Jakub Dawidek } 5531a0cc6b1SPawel Jakub Dawidek 5541a0cc6b1SPawel Jakub Dawidek void 555b8f92ce4SWarner Losh config::close_pidfile() 556b8f92ce4SWarner Losh { 557b8f92ce4SWarner Losh 558b8f92ce4SWarner Losh pidfile_close(pfh); 559b8f92ce4SWarner Losh } 560b8f92ce4SWarner Losh 561b8f92ce4SWarner Losh void 5621a0cc6b1SPawel Jakub Dawidek config::remove_pidfile() 5631a0cc6b1SPawel Jakub Dawidek { 5641a0cc6b1SPawel Jakub Dawidek 5651a0cc6b1SPawel Jakub Dawidek pidfile_remove(pfh); 5663054f218SWarner Losh } 5673054f218SWarner Losh 5683054f218SWarner Losh void 5693054f218SWarner Losh config::add_attach(int prio, event_proc *p) 5703054f218SWarner Losh { 5713054f218SWarner Losh p->set_priority(prio); 5723054f218SWarner Losh _attach_list.push_back(p); 5733054f218SWarner Losh } 5743054f218SWarner Losh 5753054f218SWarner Losh void 5763054f218SWarner Losh config::add_detach(int prio, event_proc *p) 5773054f218SWarner Losh { 5783054f218SWarner Losh p->set_priority(prio); 5793054f218SWarner Losh _detach_list.push_back(p); 5803054f218SWarner Losh } 5813054f218SWarner Losh 5823054f218SWarner Losh void 5833054f218SWarner Losh config::add_directory(const char *dir) 5843054f218SWarner Losh { 5853054f218SWarner Losh _dir_list.push_back(string(dir)); 5863054f218SWarner Losh } 5873054f218SWarner Losh 5883054f218SWarner Losh void 5893054f218SWarner Losh config::add_nomatch(int prio, event_proc *p) 5903054f218SWarner Losh { 5913054f218SWarner Losh p->set_priority(prio); 5923054f218SWarner Losh _nomatch_list.push_back(p); 5933054f218SWarner Losh } 5943054f218SWarner Losh 5953054f218SWarner Losh void 596842ccec5SWarner Losh config::add_notify(int prio, event_proc *p) 597842ccec5SWarner Losh { 598842ccec5SWarner Losh p->set_priority(prio); 599842ccec5SWarner Losh _notify_list.push_back(p); 600842ccec5SWarner Losh } 601842ccec5SWarner Losh 602842ccec5SWarner Losh void 6033054f218SWarner Losh config::set_pidfile(const char *fn) 6043054f218SWarner Losh { 6055a3b1a3dSEitan Adler _pidfile = fn; 6063054f218SWarner Losh } 6073054f218SWarner Losh 6083054f218SWarner Losh void 6093054f218SWarner Losh config::push_var_table() 6103054f218SWarner Losh { 6113054f218SWarner Losh var_list *vl; 6123054f218SWarner Losh 6133054f218SWarner Losh vl = new var_list(); 6143054f218SWarner Losh _var_list_table.push_back(vl); 6156d1014a3SAlan Somers devdlog(LOG_DEBUG, "Pushing table\n"); 6163054f218SWarner Losh } 6173054f218SWarner Losh 6183054f218SWarner Losh void 6193054f218SWarner Losh config::pop_var_table() 6203054f218SWarner Losh { 6213054f218SWarner Losh delete _var_list_table.back(); 6223054f218SWarner Losh _var_list_table.pop_back(); 6236d1014a3SAlan Somers devdlog(LOG_DEBUG, "Popping table\n"); 6243054f218SWarner Losh } 6253054f218SWarner Losh 6263054f218SWarner Losh void 6273054f218SWarner Losh config::set_variable(const char *var, const char *val) 6283054f218SWarner Losh { 6293054f218SWarner Losh _var_list_table.back()->set_variable(var, val); 6303054f218SWarner Losh } 6313054f218SWarner Losh 6323054f218SWarner Losh const string & 6333054f218SWarner Losh config::get_variable(const string &var) 6343054f218SWarner Losh { 6353054f218SWarner Losh vector<var_list *>::reverse_iterator i; 6363054f218SWarner Losh 637edee691dSEitan Adler for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 6383054f218SWarner Losh if ((*i)->is_set(var)) 6398b823408SWarner Losh return ((*i)->get_variable(var)); 6403054f218SWarner Losh } 6413054f218SWarner Losh return (var_list::nothing); 6423054f218SWarner Losh } 6433054f218SWarner Losh 6448b823408SWarner Losh bool 645ef370346SEitan Adler config::is_id_char(char ch) const 6468b823408SWarner Losh { 6478b823408SWarner Losh return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 6488b823408SWarner Losh ch == '-')); 6498b823408SWarner Losh } 6508b823408SWarner Losh 65154aa4076SWarner Losh string 65254aa4076SWarner Losh config::shell_quote(const string &s) 65354aa4076SWarner Losh { 65454aa4076SWarner Losh string buffer; 65595cbefb3SWarner Losh const char *cs, *ce; 65695cbefb3SWarner Losh char c; 65754aa4076SWarner Losh 65854aa4076SWarner Losh /* 65954aa4076SWarner Losh * Enclose the string in $' ' with escapes for ' and / characters making 66054aa4076SWarner Losh * it one argument and ensuring the shell won't be affected by its 66154aa4076SWarner Losh * usual list of candidates. 66254aa4076SWarner Losh */ 66354aa4076SWarner Losh buffer.reserve(s.length() * 3 / 2); 66454aa4076SWarner Losh buffer += '$'; 66554aa4076SWarner Losh buffer += '\''; 66695cbefb3SWarner Losh cs = s.c_str(); 66795cbefb3SWarner Losh ce = cs + strlen(cs); 66895cbefb3SWarner Losh for (; cs < ce; cs++) { 66995cbefb3SWarner Losh c = *cs; 67054aa4076SWarner Losh if (c == '\'' || c == '\\') { 67154aa4076SWarner Losh buffer += '\\'; 67254aa4076SWarner Losh } 67354aa4076SWarner Losh buffer += c; 67454aa4076SWarner Losh } 67554aa4076SWarner Losh buffer += '\''; 67654aa4076SWarner Losh 67754aa4076SWarner Losh return buffer; 67854aa4076SWarner Losh } 67954aa4076SWarner Losh 6805102ef84SWarner Losh void 6816577e8c4SWarner Losh config::expand_one(const char *&src, string &dst, bool is_shell) 6823054f218SWarner Losh { 6835102ef84SWarner Losh int count; 6845bf08422SUlf Lilleengen string buffer; 6855102ef84SWarner Losh 6868b823408SWarner Losh src++; 6875102ef84SWarner Losh // $$ -> $ 6885102ef84SWarner Losh if (*src == '$') { 6897d9e9c60SEitan Adler dst += *src++; 6905102ef84SWarner Losh return; 6915102ef84SWarner Losh } 6925102ef84SWarner Losh 6935102ef84SWarner Losh // $(foo) -> $(foo) 69454aa4076SWarner Losh // This is the escape hatch for passing down shell subcommands 6955102ef84SWarner Losh if (*src == '(') { 6967d9e9c60SEitan Adler dst += '$'; 697de579766SAlexander Motin count = 0; 6986cabd675SWarner Losh /* If the string ends before ) is matched , return. */ 699de579766SAlexander Motin do { 7005102ef84SWarner Losh if (*src == ')') 7015102ef84SWarner Losh count--; 7025102ef84SWarner Losh else if (*src == '(') 7035102ef84SWarner Losh count++; 7047d9e9c60SEitan Adler dst += *src++; 705de579766SAlexander Motin } while (count > 0 && *src); 7065102ef84SWarner Losh return; 7075102ef84SWarner Losh } 7085102ef84SWarner Losh 709b3d32292SWarner Losh // $[^-A-Za-z_*] -> $\1 710b3d32292SWarner Losh if (!isalpha(*src) && *src != '_' && *src != '-' && *src != '*') { 7117d9e9c60SEitan Adler dst += '$'; 7127d9e9c60SEitan Adler dst += *src++; 7135102ef84SWarner Losh return; 7145102ef84SWarner Losh } 7155102ef84SWarner Losh 7165102ef84SWarner Losh // $var -> replace with value 7176cabd675SWarner Losh do { 7187d9e9c60SEitan Adler buffer += *src++; 71966ff6821SWarner Losh } while (is_id_char(*src)); 7206577e8c4SWarner Losh dst.append(is_shell ? shell_quote(get_variable(buffer)) : get_variable(buffer)); 7215102ef84SWarner Losh } 7225102ef84SWarner Losh 7235102ef84SWarner Losh const string 7245dfc0f6cSIan Lepore config::expand_string(const char *src, const char *prepend, const char *append) 7255102ef84SWarner Losh { 7265dfc0f6cSIan Lepore const char *var_at; 7276cabd675SWarner Losh string dst; 7285102ef84SWarner Losh 7295dfc0f6cSIan Lepore /* 7305dfc0f6cSIan Lepore * 128 bytes is enough for 2427 of 2438 expansions that happen 7315dfc0f6cSIan Lepore * while parsing config files, as tested on 2013-01-30. 7325dfc0f6cSIan Lepore */ 7335dfc0f6cSIan Lepore dst.reserve(128); 7345dfc0f6cSIan Lepore 7355dfc0f6cSIan Lepore if (prepend != NULL) 7365dfc0f6cSIan Lepore dst = prepend; 7375dfc0f6cSIan Lepore 7385dfc0f6cSIan Lepore for (;;) { 7395dfc0f6cSIan Lepore var_at = strchr(src, '$'); 7405dfc0f6cSIan Lepore if (var_at == NULL) { 7415dfc0f6cSIan Lepore dst.append(src); 7425dfc0f6cSIan Lepore break; 7435102ef84SWarner Losh } 7445dfc0f6cSIan Lepore dst.append(src, var_at - src); 7455dfc0f6cSIan Lepore src = var_at; 7466577e8c4SWarner Losh expand_one(src, dst, prepend == NULL); 7475dfc0f6cSIan Lepore } 7485dfc0f6cSIan Lepore 7495dfc0f6cSIan Lepore if (append != NULL) 7505dfc0f6cSIan Lepore dst.append(append); 7515102ef84SWarner Losh 7526cabd675SWarner Losh return (dst); 7533054f218SWarner Losh } 7543054f218SWarner Losh 7558b823408SWarner Losh bool 75611fd1366SEitan Adler config::chop_var(char *&buffer, char *&lhs, char *&rhs) const 7578b823408SWarner Losh { 7588b823408SWarner Losh char *walker; 7598b823408SWarner Losh 7608b823408SWarner Losh if (*buffer == '\0') 7618b823408SWarner Losh return (false); 7628b823408SWarner Losh walker = lhs = buffer; 7638b823408SWarner Losh while (is_id_char(*walker)) 7648b823408SWarner Losh walker++; 7658b823408SWarner Losh if (*walker != '=') 7668b823408SWarner Losh return (false); 7678b823408SWarner Losh walker++; // skip = 7688b823408SWarner Losh if (*walker == '"') { 7698b823408SWarner Losh walker++; // skip " 7708b823408SWarner Losh rhs = walker; 771192af3b7SWarner Losh while (*walker && *walker != '"') { 772192af3b7SWarner Losh // Skip \" ... We leave it in the string and strip the \ later. 773192af3b7SWarner Losh // due to the super simplistic parser that we have here. 774192af3b7SWarner Losh if (*walker == '\\' && walker[1] == '"') 7758b823408SWarner Losh walker++; 776192af3b7SWarner Losh walker++; 777192af3b7SWarner Losh } 7788b823408SWarner Losh if (*walker != '"') 7798b823408SWarner Losh return (false); 7808b823408SWarner Losh rhs[-2] = '\0'; 7818b823408SWarner Losh *walker++ = '\0'; 7828b823408SWarner Losh } else { 7838b823408SWarner Losh rhs = walker; 7848b823408SWarner Losh while (*walker && !isspace(*walker)) 7858b823408SWarner Losh walker++; 7868b823408SWarner Losh if (*walker != '\0') 7878b823408SWarner Losh *walker++ = '\0'; 7888b823408SWarner Losh rhs[-1] = '\0'; 7898b823408SWarner Losh } 790b1ee04b1SWarner Losh while (isspace(*walker)) 791b1ee04b1SWarner Losh walker++; 7928b823408SWarner Losh buffer = walker; 7938b823408SWarner Losh return (true); 7948b823408SWarner Losh } 7958b823408SWarner Losh 7968b823408SWarner Losh 7978b823408SWarner Losh char * 7988b823408SWarner Losh config::set_vars(char *buffer) 7998b823408SWarner Losh { 8008b823408SWarner Losh char *lhs; 8018b823408SWarner Losh char *rhs; 8028b823408SWarner Losh 8038b823408SWarner Losh while (1) { 8048b823408SWarner Losh if (!chop_var(buffer, lhs, rhs)) 8058b823408SWarner Losh break; 8068b823408SWarner Losh set_variable(lhs, rhs); 8078b823408SWarner Losh } 8088b823408SWarner Losh return (buffer); 8098b823408SWarner Losh } 8108b823408SWarner Losh 8118b823408SWarner Losh void 8128b823408SWarner Losh config::find_and_execute(char type) 8138b823408SWarner Losh { 8148b823408SWarner Losh vector<event_proc *> *l; 8158b823408SWarner Losh vector<event_proc *>::const_iterator i; 81694d2d4ebSBrooks Davis const char *s; 8178b823408SWarner Losh 8188b823408SWarner Losh switch (type) { 8198b823408SWarner Losh default: 8208b823408SWarner Losh return; 821842ccec5SWarner Losh case notify: 822842ccec5SWarner Losh l = &_notify_list; 823842ccec5SWarner Losh s = "notify"; 824842ccec5SWarner Losh break; 8258b823408SWarner Losh case nomatch: 8268b823408SWarner Losh l = &_nomatch_list; 8278b823408SWarner Losh s = "nomatch"; 8288b823408SWarner Losh break; 8298b823408SWarner Losh case attach: 8308b823408SWarner Losh l = &_attach_list; 8318b823408SWarner Losh s = "attach"; 8328b823408SWarner Losh break; 8338b823408SWarner Losh case detach: 8348b823408SWarner Losh l = &_detach_list; 8358b823408SWarner Losh s = "detach"; 8368b823408SWarner Losh break; 8378b823408SWarner Losh } 8386d1014a3SAlan Somers devdlog(LOG_DEBUG, "Processing %s event\n", s); 839edee691dSEitan Adler for (i = l->begin(); i != l->end(); ++i) { 8408b823408SWarner Losh if ((*i)->matches(*this)) { 8418b823408SWarner Losh (*i)->run(*this); 8428b823408SWarner Losh break; 8438b823408SWarner Losh } 8448b823408SWarner Losh } 8458b823408SWarner Losh 8468b823408SWarner Losh } 8478b823408SWarner Losh 8480285e9b1SAlan Somers 8493054f218SWarner Losh static void 8508b823408SWarner Losh process_event(char *buffer) 8513054f218SWarner Losh { 8523054f218SWarner Losh char type; 8533054f218SWarner Losh char *sp; 8543b336ac3SWarner Losh struct timeval tv; 8553b336ac3SWarner Losh char *timestr; 8563054f218SWarner Losh 8578b823408SWarner Losh sp = buffer + 1; 858f0038a8eSAlan Somers devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 8593054f218SWarner Losh type = *buffer++; 8608b823408SWarner Losh cfg.push_var_table(); 861b3d32292SWarner Losh // $* is the entire line 862b3d32292SWarner Losh cfg.set_variable("*", buffer - 1); 863b3d32292SWarner Losh // $_ is the entire line without the initial character 864535595dbSWarner Losh cfg.set_variable("_", buffer); 8653b336ac3SWarner Losh 8663b336ac3SWarner Losh // Save the time this happened (as approximated by when we got 8673b336ac3SWarner Losh // around to processing it). 8683b336ac3SWarner Losh gettimeofday(&tv, NULL); 86927744ca7SEitan Adler asprintf(×tr, "%jd.%06ld", (uintmax_t)tv.tv_sec, tv.tv_usec); 8703b336ac3SWarner Losh cfg.set_variable("timestamp", timestr); 8713b336ac3SWarner Losh free(timestr); 8723b336ac3SWarner Losh 8733b336ac3SWarner Losh // Match doesn't have a device, and the format is a little 8748b823408SWarner Losh // different, so handle it separately. 875842ccec5SWarner Losh switch (type) { 876842ccec5SWarner Losh case notify: 877b3d32292SWarner Losh //! (k=v)* 878842ccec5SWarner Losh sp = cfg.set_vars(sp); 879842ccec5SWarner Losh break; 880842ccec5SWarner Losh case nomatch: 881528d3f79SWarner Losh //? at location pnp-info on bus 882528d3f79SWarner Losh sp = strchr(sp, ' '); 883528d3f79SWarner Losh if (sp == NULL) 884528d3f79SWarner Losh return; /* Can't happen? */ 885528d3f79SWarner Losh *sp++ = '\0'; 8869b6cb520SWarner Losh while (isspace(*sp)) 8879b6cb520SWarner Losh sp++; 888842ccec5SWarner Losh if (strncmp(sp, "at ", 3) == 0) 889842ccec5SWarner Losh sp += 3; 890842ccec5SWarner Losh sp = cfg.set_vars(sp); 8919b6cb520SWarner Losh while (isspace(*sp)) 8929b6cb520SWarner Losh sp++; 893842ccec5SWarner Losh if (strncmp(sp, "on ", 3) == 0) 894842ccec5SWarner Losh cfg.set_variable("bus", sp + 3); 895842ccec5SWarner Losh break; 896842ccec5SWarner Losh case attach: /*FALLTHROUGH*/ 897842ccec5SWarner Losh case detach: 8988b823408SWarner Losh sp = strchr(sp, ' '); 8993054f218SWarner Losh if (sp == NULL) 9003054f218SWarner Losh return; /* Can't happen? */ 9018b823408SWarner Losh *sp++ = '\0'; 9028b823408SWarner Losh cfg.set_variable("device-name", buffer); 9039b6cb520SWarner Losh while (isspace(*sp)) 9049b6cb520SWarner Losh sp++; 9058b823408SWarner Losh if (strncmp(sp, "at ", 3) == 0) 9068b823408SWarner Losh sp += 3; 9078b823408SWarner Losh sp = cfg.set_vars(sp); 9089b6cb520SWarner Losh while (isspace(*sp)) 9099b6cb520SWarner Losh sp++; 9108b823408SWarner Losh if (strncmp(sp, "on ", 3) == 0) 9118b823408SWarner Losh cfg.set_variable("bus", sp + 3); 912842ccec5SWarner Losh break; 913b1ee04b1SWarner Losh } 914b1ee04b1SWarner Losh 9158b823408SWarner Losh cfg.find_and_execute(type); 9168b823408SWarner Losh cfg.pop_var_table(); 9173054f218SWarner Losh } 9183054f218SWarner Losh 919514e2dbfSEitan Adler static int 9209102691eSAlan Somers create_socket(const char *name, int socktype) 921251bedd1SWarner Losh { 922251bedd1SWarner Losh int fd, slen; 923251bedd1SWarner Losh struct sockaddr_un sun; 924251bedd1SWarner Losh 9259102691eSAlan Somers if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 926251bedd1SWarner Losh err(1, "socket"); 927251bedd1SWarner Losh bzero(&sun, sizeof(sun)); 928251bedd1SWarner Losh sun.sun_family = AF_UNIX; 929251bedd1SWarner Losh strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 930251bedd1SWarner Losh slen = SUN_LEN(&sun); 931251bedd1SWarner Losh unlink(name); 932e60fa014SJoe Marcus Clarke if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 933e60fa014SJoe Marcus Clarke err(1, "fcntl"); 93427744ca7SEitan Adler if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 935251bedd1SWarner Losh err(1, "bind"); 936251bedd1SWarner Losh listen(fd, 4); 937daa0d9ddSAlan Somers if (chown(name, 0, 0)) /* XXX - root.wheel */ 938daa0d9ddSAlan Somers err(1, "chown"); 939daa0d9ddSAlan Somers if (chmod(name, 0666)) 940daa0d9ddSAlan Somers err(1, "chmod"); 941251bedd1SWarner Losh return (fd); 942251bedd1SWarner Losh } 943251bedd1SWarner Losh 9440bc60783SEitan Adler static unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ 9450bc60783SEitan Adler static unsigned int num_clients; 9469102691eSAlan Somers 9470bc60783SEitan Adler static list<client_t> clients; 948251bedd1SWarner Losh 949514e2dbfSEitan Adler static void 950251bedd1SWarner Losh notify_clients(const char *data, int len) 951251bedd1SWarner Losh { 9529102691eSAlan Somers list<client_t>::iterator i; 953251bedd1SWarner Losh 954e1334f93SIan Lepore /* 955e1334f93SIan Lepore * Deliver the data to all clients. Throw clients overboard at the 956e1334f93SIan Lepore * first sign of trouble. This reaps clients who've died or closed 957e1334f93SIan Lepore * their sockets, and also clients who are alive but failing to keep up 958e1334f93SIan Lepore * (or who are maliciously not reading, to consume buffer space in 959e1334f93SIan Lepore * kernel memory or tie up the limited number of available connections). 960e1334f93SIan Lepore */ 961e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 9629102691eSAlan Somers int flags; 9639102691eSAlan Somers if (i->socktype == SOCK_SEQPACKET) 9649102691eSAlan Somers flags = MSG_EOR; 9659102691eSAlan Somers else 9669102691eSAlan Somers flags = 0; 9679102691eSAlan Somers 9689102691eSAlan Somers if (send(i->fd, data, len, flags) != len) { 969e1334f93SIan Lepore --num_clients; 9709102691eSAlan Somers close(i->fd); 971e1334f93SIan Lepore i = clients.erase(i); 9729102691eSAlan Somers devdlog(LOG_WARNING, "notify_clients: send() failed; " 9736d1014a3SAlan Somers "dropping unresponsive client\n"); 974e1334f93SIan Lepore } else 975e1334f93SIan Lepore ++i; 976251bedd1SWarner Losh } 977251bedd1SWarner Losh } 978251bedd1SWarner Losh 979514e2dbfSEitan Adler static void 980e1334f93SIan Lepore check_clients(void) 981e1334f93SIan Lepore { 982e1334f93SIan Lepore int s; 983e1334f93SIan Lepore struct pollfd pfd; 9849102691eSAlan Somers list<client_t>::iterator i; 985e1334f93SIan Lepore 986e1334f93SIan Lepore /* 987e1334f93SIan Lepore * Check all existing clients to see if any of them have disappeared. 988e1334f93SIan Lepore * Normally we reap clients when we get an error trying to send them an 989e1334f93SIan Lepore * event. This check eliminates the problem of an ever-growing list of 990e1334f93SIan Lepore * zombie clients because we're never writing to them on a system 991e1334f93SIan Lepore * without frequent device-change activity. 992e1334f93SIan Lepore */ 993e1334f93SIan Lepore pfd.events = 0; 994e1334f93SIan Lepore for (i = clients.begin(); i != clients.end(); ) { 9959102691eSAlan Somers pfd.fd = i->fd; 996e1334f93SIan Lepore s = poll(&pfd, 1, 0); 997e1334f93SIan Lepore if ((s < 0 && s != EINTR ) || 998e1334f93SIan Lepore (s > 0 && (pfd.revents & POLLHUP))) { 999e1334f93SIan Lepore --num_clients; 10009102691eSAlan Somers close(i->fd); 1001e1334f93SIan Lepore i = clients.erase(i); 10026d1014a3SAlan Somers devdlog(LOG_NOTICE, "check_clients: " 10036d1014a3SAlan Somers "dropping disconnected client\n"); 1004e1334f93SIan Lepore } else 1005e1334f93SIan Lepore ++i; 1006e1334f93SIan Lepore } 1007251bedd1SWarner Losh } 1008251bedd1SWarner Losh 1009514e2dbfSEitan Adler static void 10109102691eSAlan Somers new_client(int fd, int socktype) 1011251bedd1SWarner Losh { 10129102691eSAlan Somers client_t s; 1013b026eddfSAlan Somers int sndbuf_size; 1014251bedd1SWarner Losh 1015e1334f93SIan Lepore /* 1016e1334f93SIan Lepore * First go reap any zombie clients, then accept the connection, and 1017e1334f93SIan Lepore * shut down the read side to stop clients from consuming kernel memory 1018e1334f93SIan Lepore * by sending large buffers full of data we'll never read. 1019e1334f93SIan Lepore */ 1020e1334f93SIan Lepore check_clients(); 10219102691eSAlan Somers s.socktype = socktype; 10229102691eSAlan Somers s.fd = accept(fd, NULL, NULL); 10239102691eSAlan Somers if (s.fd != -1) { 1024b026eddfSAlan Somers sndbuf_size = CLIENT_BUFSIZE; 10259102691eSAlan Somers if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 1026b026eddfSAlan Somers sizeof(sndbuf_size))) 1027b026eddfSAlan Somers err(1, "setsockopt"); 10289102691eSAlan Somers shutdown(s.fd, SHUT_RD); 1029251bedd1SWarner Losh clients.push_back(s); 1030e1334f93SIan Lepore ++num_clients; 1031b026eddfSAlan Somers } else 1032b026eddfSAlan Somers err(1, "accept"); 1033251bedd1SWarner Losh } 1034251bedd1SWarner Losh 10353054f218SWarner Losh static void 10363054f218SWarner Losh event_loop(void) 10373054f218SWarner Losh { 10383054f218SWarner Losh int rv; 10393054f218SWarner Losh int fd; 10403054f218SWarner Losh char buffer[DEVCTL_MAXBUF]; 10415a882d77SWarner Losh int once = 0; 10429102691eSAlan Somers int stream_fd, seqpacket_fd, max_fd; 1043e1334f93SIan Lepore int accepting; 10445a882d77SWarner Losh timeval tv; 10455a882d77SWarner Losh fd_set fds; 10463054f218SWarner Losh 1047901610f4SPawel Jakub Dawidek fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 10483054f218SWarner Losh if (fd == -1) 1049251bedd1SWarner Losh err(1, "Can't open devctl device %s", PATH_DEVCTL); 10509102691eSAlan Somers stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 10519102691eSAlan Somers seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 1052e1334f93SIan Lepore accepting = 1; 10539102691eSAlan Somers max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 105467ae3bf1SEitan Adler while (!romeo_must_die) { 10556a2ae0ebSAlan Somers if (!once && !no_daemon && !daemonize_quick) { 10565a882d77SWarner Losh // Check to see if we have any events pending. 10575a882d77SWarner Losh tv.tv_sec = 0; 10585a882d77SWarner Losh tv.tv_usec = 0; 10595a882d77SWarner Losh FD_ZERO(&fds); 10605a882d77SWarner Losh FD_SET(fd, &fds); 1061dacbef68SEitan Adler rv = select(fd + 1, &fds, NULL, NULL, &tv); 10625a882d77SWarner Losh // No events -> we've processed all pending events 1063f3059505SWarner Losh if (rv == 0) { 10646d1014a3SAlan Somers devdlog(LOG_DEBUG, "Calling daemon\n"); 10651a0cc6b1SPawel Jakub Dawidek cfg.remove_pidfile(); 10661a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 10675a882d77SWarner Losh daemon(0, 0); 10681a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 10695a882d77SWarner Losh once++; 10705a882d77SWarner Losh } 10715a882d77SWarner Losh } 1072e1334f93SIan Lepore /* 1073e1334f93SIan Lepore * When we've already got the max number of clients, stop 10749102691eSAlan Somers * accepting new connections (don't put the listening sockets in 10759102691eSAlan Somers * the set), shrink the accept() queue to reject connections 10769102691eSAlan Somers * quickly, and poll the existing clients more often, so that we 10779102691eSAlan Somers * notice more quickly when any of them disappear to free up 10789102691eSAlan Somers * client slots. 1079e1334f93SIan Lepore */ 1080251bedd1SWarner Losh FD_ZERO(&fds); 1081251bedd1SWarner Losh FD_SET(fd, &fds); 1082e1334f93SIan Lepore if (num_clients < max_clients) { 1083e1334f93SIan Lepore if (!accepting) { 10849102691eSAlan Somers listen(stream_fd, max_clients); 10859102691eSAlan Somers listen(seqpacket_fd, max_clients); 1086e1334f93SIan Lepore accepting = 1; 1087e1334f93SIan Lepore } 10889102691eSAlan Somers FD_SET(stream_fd, &fds); 10899102691eSAlan Somers FD_SET(seqpacket_fd, &fds); 1090e1334f93SIan Lepore tv.tv_sec = 60; 1091e1334f93SIan Lepore tv.tv_usec = 0; 1092e1334f93SIan Lepore } else { 1093e1334f93SIan Lepore if (accepting) { 10949102691eSAlan Somers listen(stream_fd, 0); 10959102691eSAlan Somers listen(seqpacket_fd, 0); 1096e1334f93SIan Lepore accepting = 0; 1097e1334f93SIan Lepore } 1098e1334f93SIan Lepore tv.tv_sec = 2; 1099e1334f93SIan Lepore tv.tv_usec = 0; 1100e1334f93SIan Lepore } 1101e1334f93SIan Lepore rv = select(max_fd, &fds, NULL, NULL, &tv); 1102be685e01SAlan Somers if (got_siginfo) { 1103f0038a8eSAlan Somers devdlog(LOG_NOTICE, "Events received so far=%u\n", 1104be685e01SAlan Somers total_events); 1105be685e01SAlan Somers got_siginfo = 0; 1106be685e01SAlan Somers } 1107251bedd1SWarner Losh if (rv == -1) { 1108251bedd1SWarner Losh if (errno == EINTR) 1109251bedd1SWarner Losh continue; 1110251bedd1SWarner Losh err(1, "select"); 1111e1334f93SIan Lepore } else if (rv == 0) 1112e1334f93SIan Lepore check_clients(); 1113251bedd1SWarner Losh if (FD_ISSET(fd, &fds)) { 11143054f218SWarner Losh rv = read(fd, buffer, sizeof(buffer) - 1); 11153054f218SWarner Losh if (rv > 0) { 1116be685e01SAlan Somers total_events++; 11176d1014a3SAlan Somers if (rv == sizeof(buffer) - 1) { 11186d1014a3SAlan Somers devdlog(LOG_WARNING, "Warning: " 11196d1014a3SAlan Somers "available event data exceeded " 11206d1014a3SAlan Somers "buffer space\n"); 11216d1014a3SAlan Somers } 1122251bedd1SWarner Losh notify_clients(buffer, rv); 11233054f218SWarner Losh buffer[rv] = '\0'; 11243054f218SWarner Losh while (buffer[--rv] == '\n') 11253054f218SWarner Losh buffer[rv] = '\0'; 1126daa0d9ddSAlan Somers try { 11273054f218SWarner Losh process_event(buffer); 1128daa0d9ddSAlan Somers } 1129958160f3SEitan Adler catch (const std::length_error& e) { 1130daa0d9ddSAlan Somers devdlog(LOG_ERR, "Dropping event %s " 1131daa0d9ddSAlan Somers "due to low memory", buffer); 1132daa0d9ddSAlan Somers } 11333054f218SWarner Losh } else if (rv < 0) { 11343054f218SWarner Losh if (errno != EINTR) 11353054f218SWarner Losh break; 11363054f218SWarner Losh } else { 11373054f218SWarner Losh /* EOF */ 11383054f218SWarner Losh break; 11393054f218SWarner Losh } 11403054f218SWarner Losh } 11419102691eSAlan Somers if (FD_ISSET(stream_fd, &fds)) 11429102691eSAlan Somers new_client(stream_fd, SOCK_STREAM); 11439102691eSAlan Somers /* 11449102691eSAlan Somers * Aside from the socket type, both sockets use the same 11459102691eSAlan Somers * protocol, so we can process clients the same way. 11469102691eSAlan Somers */ 11479102691eSAlan Somers if (FD_ISSET(seqpacket_fd, &fds)) 11489102691eSAlan Somers new_client(seqpacket_fd, SOCK_SEQPACKET); 1149251bedd1SWarner Losh } 115091cfa6b8SAlan Somers cfg.remove_pidfile(); 1151daa0d9ddSAlan Somers close(seqpacket_fd); 1152daa0d9ddSAlan Somers close(stream_fd); 11533054f218SWarner Losh close(fd); 11543054f218SWarner Losh } 11550285e9b1SAlan Somers 11563054f218SWarner Losh /* 11573054f218SWarner Losh * functions that the parser uses. 11583054f218SWarner Losh */ 11593054f218SWarner Losh void 11603054f218SWarner Losh add_attach(int prio, event_proc *p) 11613054f218SWarner Losh { 11623054f218SWarner Losh cfg.add_attach(prio, p); 11633054f218SWarner Losh } 11643054f218SWarner Losh 11653054f218SWarner Losh void 11663054f218SWarner Losh add_detach(int prio, event_proc *p) 11673054f218SWarner Losh { 11683054f218SWarner Losh cfg.add_detach(prio, p); 11693054f218SWarner Losh } 11703054f218SWarner Losh 11713054f218SWarner Losh void 11723054f218SWarner Losh add_directory(const char *dir) 11733054f218SWarner Losh { 11743054f218SWarner Losh cfg.add_directory(dir); 11753054f218SWarner Losh free(const_cast<char *>(dir)); 11763054f218SWarner Losh } 11773054f218SWarner Losh 11783054f218SWarner Losh void 11793054f218SWarner Losh add_nomatch(int prio, event_proc *p) 11803054f218SWarner Losh { 11813054f218SWarner Losh cfg.add_nomatch(prio, p); 11823054f218SWarner Losh } 11833054f218SWarner Losh 1184842ccec5SWarner Losh void 1185842ccec5SWarner Losh add_notify(int prio, event_proc *p) 1186842ccec5SWarner Losh { 1187842ccec5SWarner Losh cfg.add_notify(prio, p); 1188842ccec5SWarner Losh } 1189842ccec5SWarner Losh 11903054f218SWarner Losh event_proc * 11913054f218SWarner Losh add_to_event_proc(event_proc *ep, eps *eps) 11923054f218SWarner Losh { 11933054f218SWarner Losh if (ep == NULL) 11943054f218SWarner Losh ep = new event_proc(); 11953054f218SWarner Losh ep->add(eps); 11963054f218SWarner Losh return (ep); 11973054f218SWarner Losh } 11983054f218SWarner Losh 11993054f218SWarner Losh eps * 12003054f218SWarner Losh new_action(const char *cmd) 12013054f218SWarner Losh { 12023054f218SWarner Losh eps *e = new action(cmd); 12033054f218SWarner Losh free(const_cast<char *>(cmd)); 12043054f218SWarner Losh return (e); 12053054f218SWarner Losh } 12063054f218SWarner Losh 12073054f218SWarner Losh eps * 12083054f218SWarner Losh new_match(const char *var, const char *re) 12093054f218SWarner Losh { 1210*76527435SWarner Losh /* 1211*76527435SWarner Losh * In FreeBSD 14, we changed the system=kern to system=kernel for the 1212*76527435SWarner Losh * resume message to match all the other 'kernel' messages. Generate a 1213*76527435SWarner Losh * warning for the life of 14.x that we've 'fixed' the file on the fly, 1214*76527435SWarner Losh * but make it a fatal error in 15.x and newer. 1215*76527435SWarner Losh */ 1216*76527435SWarner Losh if (strcmp(var, "kern") == 0) { 1217*76527435SWarner Losh #if __FreeBSD_version < 1500000 1218*76527435SWarner Losh devdlog(LOG_WARNING, 1219*76527435SWarner Losh "Changing deprecated system='kern' to new name 'kernel' in %s line %d.", 1220*76527435SWarner Losh curr_cf, lineno); 1221*76527435SWarner Losh free(const_cast<char *>(var)); 1222*76527435SWarner Losh var = strdup("kernel"); 1223*76527435SWarner Losh #elif __FreeBSD_version < 1600000 1224*76527435SWarner Losh errx(1, "Encountered deprecated system=\"kern\" rule in %s line %d", 1225*76527435SWarner Losh curr_cf, lineno); 1226*76527435SWarner Losh #else 1227*76527435SWarner Losh #error "Remove this gross hack" 1228*76527435SWarner Losh #endif 1229*76527435SWarner Losh } 1230*76527435SWarner Losh 12315102ef84SWarner Losh eps *e = new match(cfg, var, re); 12323054f218SWarner Losh free(const_cast<char *>(var)); 12333054f218SWarner Losh free(const_cast<char *>(re)); 12343054f218SWarner Losh return (e); 12353054f218SWarner Losh } 12363054f218SWarner Losh 1237cd70782bSWarner Losh eps * 1238cd70782bSWarner Losh new_media(const char *var, const char *re) 1239cd70782bSWarner Losh { 1240cd70782bSWarner Losh eps *e = new media(cfg, var, re); 1241cd70782bSWarner Losh free(const_cast<char *>(var)); 1242cd70782bSWarner Losh free(const_cast<char *>(re)); 1243cd70782bSWarner Losh return (e); 1244cd70782bSWarner Losh } 1245cd70782bSWarner Losh 12463054f218SWarner Losh void 12473054f218SWarner Losh set_pidfile(const char *name) 12483054f218SWarner Losh { 12493054f218SWarner Losh cfg.set_pidfile(name); 12503054f218SWarner Losh free(const_cast<char *>(name)); 12513054f218SWarner Losh } 12523054f218SWarner Losh 12533054f218SWarner Losh void 12543054f218SWarner Losh set_variable(const char *var, const char *val) 12553054f218SWarner Losh { 12563054f218SWarner Losh cfg.set_variable(var, val); 12573054f218SWarner Losh free(const_cast<char *>(var)); 12583054f218SWarner Losh free(const_cast<char *>(val)); 12593054f218SWarner Losh } 12603054f218SWarner Losh 12610285e9b1SAlan Somers 12623054f218SWarner Losh 12633054f218SWarner Losh static void 12643054f218SWarner Losh gensighand(int) 12653054f218SWarner Losh { 1266fcdcaa88SEitan Adler romeo_must_die = 1; 12673054f218SWarner Losh } 12683054f218SWarner Losh 12696d1014a3SAlan Somers /* 1270be685e01SAlan Somers * SIGINFO handler. Will print useful statistics to the syslog or stderr 1271be685e01SAlan Somers * as appropriate 1272be685e01SAlan Somers */ 1273be685e01SAlan Somers static void 1274be685e01SAlan Somers siginfohand(int) 1275be685e01SAlan Somers { 1276be685e01SAlan Somers got_siginfo = 1; 1277be685e01SAlan Somers } 1278be685e01SAlan Somers 1279be685e01SAlan Somers /* 12802e024bc2SAndriy Gapon * Local logging function. Prints to syslog if we're daemonized; stderr 12816d1014a3SAlan Somers * otherwise. 12826d1014a3SAlan Somers */ 12836d1014a3SAlan Somers static void 12846d1014a3SAlan Somers devdlog(int priority, const char* fmt, ...) 12856d1014a3SAlan Somers { 12866d1014a3SAlan Somers va_list argp; 12876d1014a3SAlan Somers 12886d1014a3SAlan Somers va_start(argp, fmt); 12896a2ae0ebSAlan Somers if (no_daemon) 12906d1014a3SAlan Somers vfprintf(stderr, fmt, argp); 12914d00d13cSWarner Losh else if (quiet_mode == 0 || priority <= LOG_WARNING) 12926d1014a3SAlan Somers vsyslog(priority, fmt, argp); 12936d1014a3SAlan Somers va_end(argp); 12946d1014a3SAlan Somers } 12956d1014a3SAlan Somers 12963054f218SWarner Losh static void 12973054f218SWarner Losh usage() 12983054f218SWarner Losh { 12996a2ae0ebSAlan Somers fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1300e1334f93SIan Lepore getprogname()); 13013054f218SWarner Losh exit(1); 13023054f218SWarner Losh } 13033054f218SWarner Losh 13049c180898SWarner Losh static void 13059c180898SWarner Losh check_devd_enabled() 13069c180898SWarner Losh { 13079c180898SWarner Losh int val = 0; 13089c180898SWarner Losh size_t len; 13099c180898SWarner Losh 13109c180898SWarner Losh len = sizeof(val); 131179f92e25SWarner Losh if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 13129c180898SWarner Losh errx(1, "devctl sysctl missing from kernel!"); 1313ee38f2e0SMateusz Guzik if (val == 0) { 1314ee38f2e0SMateusz Guzik warnx("Setting " SYSCTL " to 1000"); 1315ee38f2e0SMateusz Guzik val = 1000; 1316daa0d9ddSAlan Somers if (sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val))) 1317daa0d9ddSAlan Somers err(1, "sysctlbyname"); 13189c180898SWarner Losh } 13199c180898SWarner Losh } 13209c180898SWarner Losh 13213054f218SWarner Losh /* 13223054f218SWarner Losh * main 13233054f218SWarner Losh */ 13243054f218SWarner Losh int 13253054f218SWarner Losh main(int argc, char **argv) 13263054f218SWarner Losh { 13273054f218SWarner Losh int ch; 13283054f218SWarner Losh 13299c180898SWarner Losh check_devd_enabled(); 13306a2ae0ebSAlan Somers while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 13313054f218SWarner Losh switch (ch) { 13323054f218SWarner Losh case 'd': 13336a2ae0ebSAlan Somers no_daemon = 1; 13343054f218SWarner Losh break; 13358334958aSJoseph Koshy case 'f': 13368334958aSJoseph Koshy configfile = optarg; 13378334958aSJoseph Koshy break; 1338e1334f93SIan Lepore case 'l': 1339e1334f93SIan Lepore max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1340e1334f93SIan Lepore break; 13415a882d77SWarner Losh case 'n': 13426a2ae0ebSAlan Somers daemonize_quick = 1; 13436a2ae0ebSAlan Somers break; 13446a2ae0ebSAlan Somers case 'q': 13456a2ae0ebSAlan Somers quiet_mode = 1; 13465a882d77SWarner Losh break; 13473054f218SWarner Losh default: 13483054f218SWarner Losh usage(); 13493054f218SWarner Losh } 13503054f218SWarner Losh } 13513054f218SWarner Losh 13523054f218SWarner Losh cfg.parse(); 13536a2ae0ebSAlan Somers if (!no_daemon && daemonize_quick) { 13541a0cc6b1SPawel Jakub Dawidek cfg.open_pidfile(); 13553054f218SWarner Losh daemon(0, 0); 13561a0cc6b1SPawel Jakub Dawidek cfg.write_pidfile(); 1357fd6a8f23SWarner Losh } 1358f32a7686SWarner Losh signal(SIGPIPE, SIG_IGN); 13593054f218SWarner Losh signal(SIGHUP, gensighand); 13603054f218SWarner Losh signal(SIGINT, gensighand); 13613054f218SWarner Losh signal(SIGTERM, gensighand); 1362be685e01SAlan Somers signal(SIGINFO, siginfohand); 13633054f218SWarner Losh event_loop(); 13643054f218SWarner Losh return (0); 13653054f218SWarner Losh } 1366