devd.cc revision 253046
1107665Simp/*-
2209583Simp * Copyright (c) 2002-2010 M. Warner Losh.
3107665Simp * All rights reserved.
4107665Simp *
5107665Simp * Redistribution and use in source and binary forms, with or without
6107665Simp * modification, are permitted provided that the following conditions
7107665Simp * are met:
8107665Simp * 1. Redistributions of source code must retain the above copyright
9107665Simp *    notice, this list of conditions and the following disclaimer.
10107665Simp * 2. Redistributions in binary form must reproduce the above copyright
11107665Simp *    notice, this list of conditions and the following disclaimer in the
12107665Simp *    documentation and/or other materials provided with the distribution.
13107665Simp *
14107665Simp * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15107665Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16107665Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17107665Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18107665Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19107665Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20107665Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21107665Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22107665Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23107665Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24107665Simp * SUCH DAMAGE.
25209583Simp *
26209583Simp * my_system is a variation on lib/libc/stdlib/system.c:
27209583Simp *
28209583Simp * Copyright (c) 1988, 1993
29209583Simp *	The Regents of the University of California.  All rights reserved.
30209583Simp *
31209583Simp * Redistribution and use in source and binary forms, with or without
32209583Simp * modification, are permitted provided that the following conditions
33209583Simp * are met:
34209583Simp * 1. Redistributions of source code must retain the above copyright
35209583Simp *    notice, this list of conditions and the following disclaimer.
36209583Simp * 2. Redistributions in binary form must reproduce the above copyright
37209583Simp *    notice, this list of conditions and the following disclaimer in the
38209583Simp *    documentation and/or other materials provided with the distribution.
39209583Simp * 4. Neither the name of the University nor the names of its contributors
40209583Simp *    may be used to endorse or promote products derived from this software
41209583Simp *    without specific prior written permission.
42209583Simp *
43209583Simp * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44209583Simp * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45209583Simp * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46209583Simp * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47209583Simp * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48209583Simp * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49209583Simp * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50209583Simp * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51209583Simp * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52209583Simp * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53209583Simp * SUCH DAMAGE.
54107665Simp */
55107665Simp
56107665Simp/*
57107665Simp * DEVD control daemon.
58107665Simp */
59107665Simp
60107665Simp// TODO list:
61107665Simp//	o devd.conf and devd man pages need a lot of help:
62131397Simp//	  - devd needs to document the unix domain socket
63107665Simp//	  - devd.conf needs more details on the supported statements.
64107665Simp
65107665Simp#include <sys/cdefs.h>
66107665Simp__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 253046 2013-07-08 21:10:30Z asomers $");
67107665Simp
68107665Simp#include <sys/param.h>
69131397Simp#include <sys/socket.h>
70131397Simp#include <sys/stat.h>
71131397Simp#include <sys/sysctl.h>
72107665Simp#include <sys/types.h>
73209583Simp#include <sys/wait.h>
74131397Simp#include <sys/un.h>
75107665Simp
76250186Seadler#include <cctype>
77250186Seadler#include <cerrno>
78250186Seadler#include <cstdlib>
79250186Seadler#include <cstdio>
80250186Seadler#include <csignal>
81250186Seadler#include <cstring>
82252508Sasomers#include <cstdarg>
83250186Seadler
84107665Simp#include <dirent.h>
85107665Simp#include <err.h>
86107665Simp#include <fcntl.h>
87155073Spjd#include <libutil.h>
88209583Simp#include <paths.h>
89246121Sian#include <poll.h>
90108014Simp#include <regex.h>
91252481Sasomers#include <syslog.h>
92107665Simp#include <unistd.h>
93107665Simp
94108783Simp#include <algorithm>
95107665Simp#include <map>
96107665Simp#include <string>
97131397Simp#include <list>
98107665Simp#include <vector>
99107665Simp
100114086Simp#include "devd.h"		/* C compatible definitions */
101114086Simp#include "devd.hh"		/* C++ class definitions */
102107665Simp
103131397Simp#define PIPE "/var/run/devd.pipe"
104107665Simp#define CF "/etc/devd.conf"
105113787Simp#define SYSCTL "hw.bus.devctl_disable"
106107665Simp
107107665Simpusing namespace std;
108107665Simp
109107665Simpextern FILE *yyin;
110107665Simpextern int lineno;
111107665Simp
112121487Simpstatic const char notify = '!';
113108783Simpstatic const char nomatch = '?';
114108783Simpstatic const char attach = '+';
115108783Simpstatic const char detach = '-';
116108783Simp
117155073Spjdstatic struct pidfh *pfh;
118155073Spjd
119107665Simpint dflag;
120114000Simpint nflag;
121252482Sasomersstatic unsigned total_events = 0;
122252482Sasomersstatic volatile sig_atomic_t got_siginfo = 0;
123247754Seadlerstatic volatile sig_atomic_t romeo_must_die = 0;
124107665Simp
125152770Sjkoshystatic const char *configfile = CF;
126152770Sjkoshy
127253046Sasomersstatic void devdlog(int priority, const char* message, ...)
128253046Sasomers	__printflike(2, 3);
129107665Simpstatic void event_loop(void);
130107665Simpstatic void usage(void);
131107665Simp
132108783Simptemplate <class T> void
133108783Simpdelete_and_clear(vector<T *> &v)
134108783Simp{
135108783Simp	typename vector<T *>::const_iterator i;
136108783Simp
137243931Seadler	for (i = v.begin(); i != v.end(); ++i)
138108783Simp		delete *i;
139108783Simp	v.clear();
140108783Simp}
141108783Simp
142107665Simpconfig cfg;
143107665Simp
144107665Simpevent_proc::event_proc() : _prio(-1)
145107665Simp{
146246134Sian	_epsvec.reserve(4);
147107665Simp}
148107665Simp
149107665Simpevent_proc::~event_proc()
150107665Simp{
151152406Sbland	delete_and_clear(_epsvec);
152107665Simp}
153107665Simp
154107665Simpvoid
155107665Simpevent_proc::add(eps *eps)
156107665Simp{
157107665Simp	_epsvec.push_back(eps);
158107665Simp}
159107665Simp
160107665Simpbool
161243930Seadlerevent_proc::matches(config &c) const
162107665Simp{
163107665Simp	vector<eps *>::const_iterator i;
164107665Simp
165243931Seadler	for (i = _epsvec.begin(); i != _epsvec.end(); ++i)
166107665Simp		if (!(*i)->do_match(c))
167107665Simp			return (false);
168107665Simp	return (true);
169107665Simp}
170107665Simp
171107665Simpbool
172243930Seadlerevent_proc::run(config &c) const
173107665Simp{
174107665Simp	vector<eps *>::const_iterator i;
175252485Sasomers
176243931Seadler	for (i = _epsvec.begin(); i != _epsvec.end(); ++i)
177107665Simp		if (!(*i)->do_action(c))
178107665Simp			return (false);
179107665Simp	return (true);
180107665Simp}
181107665Simp
182107665Simpaction::action(const char *cmd)
183252485Sasomers	: _cmd(cmd)
184107665Simp{
185107665Simp	// nothing
186107665Simp}
187107665Simp
188107665Simpaction::~action()
189107665Simp{
190107665Simp	// nothing
191107665Simp}
192107665Simp
193209583Simpstatic int
194209583Simpmy_system(const char *command)
195209583Simp{
196209583Simp	pid_t pid, savedpid;
197209583Simp	int pstat;
198209583Simp	struct sigaction ign, intact, quitact;
199209583Simp	sigset_t newsigblock, oldsigblock;
200209583Simp
201209583Simp	if (!command)		/* just checking... */
202252485Sasomers		return (1);
203209583Simp
204209583Simp	/*
205209583Simp	 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
206209583Simp	 * existing signal dispositions.
207209583Simp	 */
208209583Simp	ign.sa_handler = SIG_IGN;
209209583Simp	::sigemptyset(&ign.sa_mask);
210209583Simp	ign.sa_flags = 0;
211209583Simp	::sigaction(SIGINT, &ign, &intact);
212209583Simp	::sigaction(SIGQUIT, &ign, &quitact);
213209583Simp	::sigemptyset(&newsigblock);
214209583Simp	::sigaddset(&newsigblock, SIGCHLD);
215209583Simp	::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
216209583Simp	switch (pid = ::fork()) {
217209583Simp	case -1:			/* error */
218209583Simp		break;
219209583Simp	case 0:				/* child */
220209583Simp		/*
221209583Simp		 * Restore original signal dispositions and exec the command.
222209583Simp		 */
223209583Simp		::sigaction(SIGINT, &intact, NULL);
224209583Simp		::sigaction(SIGQUIT,  &quitact, NULL);
225209583Simp		::sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
226209583Simp		/*
227209583Simp		 * Close the PID file, and all other open descriptors.
228209583Simp		 * Inherit std{in,out,err} only.
229209583Simp		 */
230209583Simp		cfg.close_pidfile();
231209583Simp		::closefrom(3);
232209583Simp		::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL);
233209583Simp		::_exit(127);
234209583Simp	default:			/* parent */
235209583Simp		savedpid = pid;
236209583Simp		do {
237209583Simp			pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0);
238209583Simp		} while (pid == -1 && errno == EINTR);
239209583Simp		break;
240209583Simp	}
241209583Simp	::sigaction(SIGINT, &intact, NULL);
242209583Simp	::sigaction(SIGQUIT,  &quitact, NULL);
243209583Simp	::sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
244209583Simp	return (pid == -1 ? -1 : pstat);
245209583Simp}
246209583Simp
247107665Simpbool
248108014Simpaction::do_action(config &c)
249107665Simp{
250246134Sian	string s = c.expand_string(_cmd.c_str());
251252481Sasomers	devdlog(LOG_NOTICE, "Executing '%s'\n", s.c_str());
252209583Simp	my_system(s.c_str());
253107665Simp	return (true);
254107665Simp}
255107665Simp
256246134Sianmatch::match(config &c, const char *var, const char *re) :
257246134Sian	_inv(re[0] == '!'),
258246134Sian	_var(var),
259246134Sian	_re(c.expand_string(_inv ? re + 1 : re, "^", "$"))
260107665Simp{
261154109Simp	regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
262107665Simp}
263107665Simp
264107665Simpmatch::~match()
265107665Simp{
266108014Simp	regfree(&_regex);
267107665Simp}
268107665Simp
269107665Simpbool
270108014Simpmatch::do_match(config &c)
271107665Simp{
272210610Slulf	const string &value = c.get_variable(_var);
273108014Simp	bool retval;
274108014Simp
275252485Sasomers	/*
276252481Sasomers	 * This function gets called WAY too often to justify calling syslog()
277252481Sasomers	 * each time, even at LOG_DEBUG.  Because if syslogd isn't running, it
278252481Sasomers	 * can consume excessive amounts of systime inside of connect().  Only
279252481Sasomers	 * log when we're in -d mode.
280252481Sasomers	 */
281252481Sasomers	if (dflag) {
282252481Sasomers		devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n",
283226775Shrs		    _var.c_str(), value.c_str(), _re.c_str(), _inv);
284252481Sasomers	}
285108783Simp
286108014Simp	retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
287226775Shrs	if (_inv == 1)
288226775Shrs		retval = (retval == 0) ? 1 : 0;
289226775Shrs
290252485Sasomers	return (retval);
291107665Simp}
292107665Simp
293147874Simp#include <sys/sockio.h>
294147874Simp#include <net/if.h>
295147874Simp#include <net/if_media.h>
296147874Simp
297151486Sbrooksmedia::media(config &, const char *var, const char *type)
298147874Simp	: _var(var), _type(-1)
299147874Simp{
300147874Simp	static struct ifmedia_description media_types[] = {
301147874Simp		{ IFM_ETHER,		"Ethernet" },
302147874Simp		{ IFM_TOKEN,		"Tokenring" },
303147874Simp		{ IFM_FDDI,		"FDDI" },
304147874Simp		{ IFM_IEEE80211,	"802.11" },
305147874Simp		{ IFM_ATM,		"ATM" },
306147874Simp		{ -1,			"unknown" },
307147874Simp		{ 0, NULL },
308147874Simp	};
309243931Seadler	for (int i = 0; media_types[i].ifmt_string != NULL; ++i)
310147874Simp		if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
311147874Simp			_type = media_types[i].ifmt_word;
312147874Simp			break;
313147874Simp		}
314147874Simp}
315147874Simp
316147874Simpmedia::~media()
317147874Simp{
318147874Simp}
319147874Simp
320147874Simpbool
321147874Simpmedia::do_match(config &c)
322147874Simp{
323150949Simp	string value;
324147874Simp	struct ifmediareq ifmr;
325147874Simp	bool retval;
326147874Simp	int s;
327147874Simp
328150949Simp	// Since we can be called from both a device attach/detach
329150949Simp	// context where device-name is defined and what we want,
330150949Simp	// as well as from a link status context, where subsystem is
331150949Simp	// the name of interest, first try device-name and fall back
332150949Simp	// to subsystem if none exists.
333150949Simp	value = c.get_variable("device-name");
334247761Seadler	if (value.empty())
335151480Simp		value = c.get_variable("subsystem");
336252481Sasomers	devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n",
337147874Simp		    value.c_str(), _type);
338147874Simp
339147874Simp	retval = false;
340147874Simp
341147874Simp	s = socket(PF_INET, SOCK_DGRAM, 0);
342147874Simp	if (s >= 0) {
343147874Simp		memset(&ifmr, 0, sizeof(ifmr));
344147874Simp		strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
345147874Simp
346147874Simp		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
347147874Simp		    ifmr.ifm_status & IFM_AVALID) {
348252481Sasomers			devdlog(LOG_DEBUG, "%s has media type 0x%x\n",
349147874Simp				    value.c_str(), IFM_TYPE(ifmr.ifm_active));
350147874Simp			retval = (IFM_TYPE(ifmr.ifm_active) == _type);
351147874Simp		} else if (_type == -1) {
352252481Sasomers			devdlog(LOG_DEBUG, "%s has unknown media type\n",
353147874Simp				    value.c_str());
354147874Simp			retval = true;
355147874Simp		}
356147874Simp		close(s);
357147874Simp	}
358147874Simp
359252485Sasomers	return (retval);
360147874Simp}
361147874Simp
362107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
363107665Simpconst string var_list::nothing = "";
364107665Simp
365107665Simpconst string &
366107665Simpvar_list::get_variable(const string &var) const
367107665Simp{
368107665Simp	map<string, string>::const_iterator i;
369107665Simp
370107665Simp	i = _vars.find(var);
371107665Simp	if (i == _vars.end())
372108783Simp		return (var_list::bogus);
373107665Simp	return (i->second);
374107665Simp}
375107665Simp
376107665Simpbool
377107665Simpvar_list::is_set(const string &var) const
378107665Simp{
379107665Simp	return (_vars.find(var) != _vars.end());
380107665Simp}
381107665Simp
382107665Simpvoid
383107665Simpvar_list::set_variable(const string &var, const string &val)
384107665Simp{
385252481Sasomers	/*
386252481Sasomers	 * This function gets called WAY too often to justify calling syslog()
387252481Sasomers	 * each time, even at LOG_DEBUG.  Because if syslogd isn't running, it
388252481Sasomers	 * can consume excessive amounts of systime inside of connect().  Only
389252481Sasomers	 * log when we're in -d mode.
390252481Sasomers	 */
391252481Sasomers	if (dflag)
392252481Sasomers		devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str());
393107665Simp	_vars[var] = val;
394107665Simp}
395107665Simp
396107665Simpvoid
397107665Simpconfig::reset(void)
398107665Simp{
399107665Simp	_dir_list.clear();
400108783Simp	delete_and_clear(_var_list_table);
401108783Simp	delete_and_clear(_attach_list);
402108783Simp	delete_and_clear(_detach_list);
403108783Simp	delete_and_clear(_nomatch_list);
404121487Simp	delete_and_clear(_notify_list);
405107665Simp}
406107665Simp
407107665Simpvoid
408107665Simpconfig::parse_one_file(const char *fn)
409107665Simp{
410252481Sasomers	devdlog(LOG_DEBUG, "Parsing %s\n", fn);
411107665Simp	yyin = fopen(fn, "r");
412107665Simp	if (yyin == NULL)
413107665Simp		err(1, "Cannot open config file %s", fn);
414157746Smaxim	lineno = 1;
415107665Simp	if (yyparse() != 0)
416107665Simp		errx(1, "Cannot parse %s at line %d", fn, lineno);
417107665Simp	fclose(yyin);
418107665Simp}
419107665Simp
420107665Simpvoid
421107665Simpconfig::parse_files_in_dir(const char *dirname)
422107665Simp{
423107665Simp	DIR *dirp;
424107665Simp	struct dirent *dp;
425107665Simp	char path[PATH_MAX];
426107665Simp
427252481Sasomers	devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname);
428107665Simp	dirp = opendir(dirname);
429107665Simp	if (dirp == NULL)
430107665Simp		return;
431107665Simp	readdir(dirp);		/* Skip . */
432107665Simp	readdir(dirp);		/* Skip .. */
433107665Simp	while ((dp = readdir(dirp)) != NULL) {
434107665Simp		if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
435107665Simp			snprintf(path, sizeof(path), "%s/%s",
436107665Simp			    dirname, dp->d_name);
437107665Simp			parse_one_file(path);
438107665Simp		}
439107665Simp	}
440215607Skevlo	closedir(dirp);
441107665Simp}
442107665Simp
443108783Simpclass epv_greater {
444108783Simppublic:
445243930Seadler	int operator()(event_proc *const&l1, event_proc *const&l2) const
446108783Simp	{
447108783Simp		return (l1->get_priority() > l2->get_priority());
448108783Simp	}
449108783Simp};
450108783Simp
451107665Simpvoid
452108783Simpconfig::sort_vector(vector<event_proc *> &v)
453108783Simp{
454243907Sdim	stable_sort(v.begin(), v.end(), epv_greater());
455108783Simp}
456108783Simp
457108783Simpvoid
458107665Simpconfig::parse(void)
459107665Simp{
460107665Simp	vector<string>::const_iterator i;
461107665Simp
462152770Sjkoshy	parse_one_file(configfile);
463243931Seadler	for (i = _dir_list.begin(); i != _dir_list.end(); ++i)
464107665Simp		parse_files_in_dir((*i).c_str());
465108783Simp	sort_vector(_attach_list);
466108783Simp	sort_vector(_detach_list);
467108783Simp	sort_vector(_nomatch_list);
468121487Simp	sort_vector(_notify_list);
469107665Simp}
470107665Simp
471107665Simpvoid
472155073Spjdconfig::open_pidfile()
473107665Simp{
474155073Spjd	pid_t otherpid;
475252485Sasomers
476247758Seadler	if (_pidfile.empty())
477107665Simp		return;
478155073Spjd	pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid);
479155073Spjd	if (pfh == NULL) {
480155073Spjd		if (errno == EEXIST)
481155073Spjd			errx(1, "devd already running, pid: %d", (int)otherpid);
482155073Spjd		warn("cannot open pid file");
483155073Spjd	}
484107665Simp}
485107665Simp
486107665Simpvoid
487155073Spjdconfig::write_pidfile()
488155073Spjd{
489252485Sasomers
490155073Spjd	pidfile_write(pfh);
491155073Spjd}
492155073Spjd
493155073Spjdvoid
494209583Simpconfig::close_pidfile()
495209583Simp{
496252485Sasomers
497209583Simp	pidfile_close(pfh);
498209583Simp}
499209583Simp
500209583Simpvoid
501155073Spjdconfig::remove_pidfile()
502155073Spjd{
503252485Sasomers
504155073Spjd	pidfile_remove(pfh);
505155073Spjd}
506155073Spjd
507155073Spjdvoid
508107665Simpconfig::add_attach(int prio, event_proc *p)
509107665Simp{
510107665Simp	p->set_priority(prio);
511107665Simp	_attach_list.push_back(p);
512107665Simp}
513107665Simp
514107665Simpvoid
515107665Simpconfig::add_detach(int prio, event_proc *p)
516107665Simp{
517107665Simp	p->set_priority(prio);
518107665Simp	_detach_list.push_back(p);
519107665Simp}
520107665Simp
521107665Simpvoid
522107665Simpconfig::add_directory(const char *dir)
523107665Simp{
524107665Simp	_dir_list.push_back(string(dir));
525107665Simp}
526107665Simp
527107665Simpvoid
528107665Simpconfig::add_nomatch(int prio, event_proc *p)
529107665Simp{
530107665Simp	p->set_priority(prio);
531107665Simp	_nomatch_list.push_back(p);
532107665Simp}
533107665Simp
534107665Simpvoid
535121487Simpconfig::add_notify(int prio, event_proc *p)
536121487Simp{
537121487Simp	p->set_priority(prio);
538121487Simp	_notify_list.push_back(p);
539121487Simp}
540121487Simp
541121487Simpvoid
542107665Simpconfig::set_pidfile(const char *fn)
543107665Simp{
544247758Seadler	_pidfile = fn;
545107665Simp}
546107665Simp
547107665Simpvoid
548107665Simpconfig::push_var_table()
549107665Simp{
550107665Simp	var_list *vl;
551252485Sasomers
552107665Simp	vl = new var_list();
553107665Simp	_var_list_table.push_back(vl);
554252481Sasomers	devdlog(LOG_DEBUG, "Pushing table\n");
555107665Simp}
556107665Simp
557107665Simpvoid
558107665Simpconfig::pop_var_table()
559107665Simp{
560107665Simp	delete _var_list_table.back();
561107665Simp	_var_list_table.pop_back();
562252481Sasomers	devdlog(LOG_DEBUG, "Popping table\n");
563107665Simp}
564107665Simp
565107665Simpvoid
566107665Simpconfig::set_variable(const char *var, const char *val)
567107665Simp{
568107665Simp	_var_list_table.back()->set_variable(var, val);
569107665Simp}
570107665Simp
571107665Simpconst string &
572107665Simpconfig::get_variable(const string &var)
573107665Simp{
574107665Simp	vector<var_list *>::reverse_iterator i;
575107665Simp
576243931Seadler	for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) {
577107665Simp		if ((*i)->is_set(var))
578108783Simp			return ((*i)->get_variable(var));
579107665Simp	}
580107665Simp	return (var_list::nothing);
581107665Simp}
582107665Simp
583108783Simpbool
584243930Seadlerconfig::is_id_char(char ch) const
585108783Simp{
586252485Sasomers	return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
587108783Simp	    ch == '-'));
588108783Simp}
589108783Simp
590108014Simpvoid
591114081Simpconfig::expand_one(const char *&src, string &dst)
592107665Simp{
593108014Simp	int count;
594210610Slulf	string buffer;
595108014Simp
596108783Simp	src++;
597108014Simp	// $$ -> $
598108014Simp	if (*src == '$') {
599247762Seadler		dst += *src++;
600108014Simp		return;
601108014Simp	}
602252485Sasomers
603108014Simp	// $(foo) -> $(foo)
604108783Simp	// Not sure if I want to support this or not, so for now we just pass
605108783Simp	// it through.
606108014Simp	if (*src == '(') {
607247762Seadler		dst += '$';
608108014Simp		count = 1;
609114081Simp		/* If the string ends before ) is matched , return. */
610114081Simp		while (count > 0 && *src) {
611108014Simp			if (*src == ')')
612108014Simp				count--;
613108014Simp			else if (*src == '(')
614108014Simp				count++;
615247762Seadler			dst += *src++;
616108014Simp		}
617108014Simp		return;
618108014Simp	}
619252485Sasomers
620247763Seadler	// $[^A-Za-z] -> $\1
621108014Simp	if (!isalpha(*src)) {
622247762Seadler		dst += '$';
623247762Seadler		dst += *src++;
624108014Simp		return;
625108014Simp	}
626108014Simp
627108014Simp	// $var -> replace with value
628114081Simp	do {
629247762Seadler		buffer += *src++;
630114084Simp	} while (is_id_char(*src));
631247758Seadler	dst.append(get_variable(buffer));
632107665Simp}
633107665Simp
634108014Simpconst string
635246134Sianconfig::expand_string(const char *src, const char *prepend, const char *append)
636108014Simp{
637246134Sian	const char *var_at;
638114081Simp	string dst;
639108014Simp
640246134Sian	/*
641246134Sian	 * 128 bytes is enough for 2427 of 2438 expansions that happen
642246134Sian	 * while parsing config files, as tested on 2013-01-30.
643246134Sian	 */
644246134Sian	dst.reserve(128);
645246134Sian
646246134Sian	if (prepend != NULL)
647246134Sian		dst = prepend;
648246134Sian
649246134Sian	for (;;) {
650246134Sian		var_at = strchr(src, '$');
651246134Sian		if (var_at == NULL) {
652246134Sian			dst.append(src);
653246134Sian			break;
654246134Sian		}
655246134Sian		dst.append(src, var_at - src);
656246134Sian		src = var_at;
657246134Sian		expand_one(src, dst);
658108014Simp	}
659108014Simp
660246134Sian	if (append != NULL)
661246134Sian		dst.append(append);
662246134Sian
663114081Simp	return (dst);
664108014Simp}
665108014Simp
666108783Simpbool
667247751Seadlerconfig::chop_var(char *&buffer, char *&lhs, char *&rhs) const
668108783Simp{
669108783Simp	char *walker;
670252485Sasomers
671108783Simp	if (*buffer == '\0')
672108783Simp		return (false);
673108783Simp	walker = lhs = buffer;
674108783Simp	while (is_id_char(*walker))
675108783Simp		walker++;
676108783Simp	if (*walker != '=')
677108783Simp		return (false);
678108783Simp	walker++;		// skip =
679108783Simp	if (*walker == '"') {
680108783Simp		walker++;	// skip "
681108783Simp		rhs = walker;
682108783Simp		while (*walker && *walker != '"')
683108783Simp			walker++;
684108783Simp		if (*walker != '"')
685108783Simp			return (false);
686108783Simp		rhs[-2] = '\0';
687108783Simp		*walker++ = '\0';
688108783Simp	} else {
689108783Simp		rhs = walker;
690108783Simp		while (*walker && !isspace(*walker))
691108783Simp			walker++;
692108783Simp		if (*walker != '\0')
693108783Simp			*walker++ = '\0';
694108783Simp		rhs[-1] = '\0';
695108783Simp	}
696113785Simp	while (isspace(*walker))
697113785Simp		walker++;
698108783Simp	buffer = walker;
699108783Simp	return (true);
700108783Simp}
701108783Simp
702108783Simp
703108783Simpchar *
704108783Simpconfig::set_vars(char *buffer)
705108783Simp{
706108783Simp	char *lhs;
707108783Simp	char *rhs;
708108783Simp
709108783Simp	while (1) {
710108783Simp		if (!chop_var(buffer, lhs, rhs))
711108783Simp			break;
712108783Simp		set_variable(lhs, rhs);
713108783Simp	}
714108783Simp	return (buffer);
715108783Simp}
716108783Simp
717108783Simpvoid
718108783Simpconfig::find_and_execute(char type)
719108783Simp{
720108783Simp	vector<event_proc *> *l;
721108783Simp	vector<event_proc *>::const_iterator i;
722151486Sbrooks	const char *s;
723108783Simp
724108783Simp	switch (type) {
725108783Simp	default:
726108783Simp		return;
727121487Simp	case notify:
728121487Simp		l = &_notify_list;
729121487Simp		s = "notify";
730121487Simp		break;
731108783Simp	case nomatch:
732108783Simp		l = &_nomatch_list;
733108783Simp		s = "nomatch";
734108783Simp		break;
735108783Simp	case attach:
736108783Simp		l = &_attach_list;
737108783Simp		s = "attach";
738108783Simp		break;
739108783Simp	case detach:
740108783Simp		l = &_detach_list;
741108783Simp		s = "detach";
742108783Simp		break;
743108783Simp	}
744252481Sasomers	devdlog(LOG_DEBUG, "Processing %s event\n", s);
745243931Seadler	for (i = l->begin(); i != l->end(); ++i) {
746108783Simp		if ((*i)->matches(*this)) {
747108783Simp			(*i)->run(*this);
748108783Simp			break;
749108783Simp		}
750108783Simp	}
751108783Simp
752108783Simp}
753108783Simp
754252485Sasomers
755107665Simpstatic void
756108783Simpprocess_event(char *buffer)
757107665Simp{
758107665Simp	char type;
759107665Simp	char *sp;
760107665Simp
761108783Simp	sp = buffer + 1;
762252481Sasomers	devdlog(LOG_DEBUG, "Processing event '%s'\n", buffer);
763107665Simp	type = *buffer++;
764108783Simp	cfg.push_var_table();
765108783Simp	// No match doesn't have a device, and the format is a little
766108783Simp	// different, so handle it separately.
767121487Simp	switch (type) {
768121487Simp	case notify:
769121487Simp		sp = cfg.set_vars(sp);
770121487Simp		break;
771121487Simp	case nomatch:
772145218Simp		//? at location pnp-info on bus
773145218Simp		sp = strchr(sp, ' ');
774145218Simp		if (sp == NULL)
775145218Simp			return;	/* Can't happen? */
776145218Simp		*sp++ = '\0';
777213646Simp		while (isspace(*sp))
778213646Simp			sp++;
779121487Simp		if (strncmp(sp, "at ", 3) == 0)
780121487Simp			sp += 3;
781121487Simp		sp = cfg.set_vars(sp);
782213646Simp		while (isspace(*sp))
783213646Simp			sp++;
784121487Simp		if (strncmp(sp, "on ", 3) == 0)
785121487Simp			cfg.set_variable("bus", sp + 3);
786121487Simp		break;
787121487Simp	case attach:	/*FALLTHROUGH*/
788121487Simp	case detach:
789108783Simp		sp = strchr(sp, ' ');
790108783Simp		if (sp == NULL)
791108783Simp			return;	/* Can't happen? */
792108783Simp		*sp++ = '\0';
793108783Simp		cfg.set_variable("device-name", buffer);
794213646Simp		while (isspace(*sp))
795213646Simp			sp++;
796113785Simp		if (strncmp(sp, "at ", 3) == 0)
797113785Simp			sp += 3;
798113785Simp		sp = cfg.set_vars(sp);
799213646Simp		while (isspace(*sp))
800213646Simp			sp++;
801113785Simp		if (strncmp(sp, "on ", 3) == 0)
802113785Simp			cfg.set_variable("bus", sp + 3);
803121487Simp		break;
804108783Simp	}
805252485Sasomers
806108783Simp	cfg.find_and_execute(type);
807108783Simp	cfg.pop_var_table();
808107665Simp}
809107665Simp
810131397Simpint
811131397Simpcreate_socket(const char *name)
812131397Simp{
813131397Simp	int fd, slen;
814131397Simp	struct sockaddr_un sun;
815131397Simp
816131397Simp	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
817131397Simp		err(1, "socket");
818131397Simp	bzero(&sun, sizeof(sun));
819131397Simp	sun.sun_family = AF_UNIX;
820131397Simp	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
821131397Simp	slen = SUN_LEN(&sun);
822131397Simp	unlink(name);
823147973Smarcus	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
824147973Smarcus	    	err(1, "fcntl");
825236388Sdim	if (::bind(fd, (struct sockaddr *) & sun, slen) < 0)
826131397Simp		err(1, "bind");
827131397Simp	listen(fd, 4);
828147972Smarcus	chown(name, 0, 0);	/* XXX - root.wheel */
829147973Smarcus	chmod(name, 0666);
830131397Simp	return (fd);
831131397Simp}
832131397Simp
833246121Sianunsigned int max_clients = 10;	/* Default, can be overriden on cmdline. */
834246121Sianunsigned int num_clients;
835131397Simplist<int> clients;
836131397Simp
837131397Simpvoid
838131397Simpnotify_clients(const char *data, int len)
839131397Simp{
840246121Sian	list<int>::iterator i;
841131397Simp
842246121Sian	/*
843246121Sian	 * Deliver the data to all clients.  Throw clients overboard at the
844246121Sian	 * first sign of trouble.  This reaps clients who've died or closed
845246121Sian	 * their sockets, and also clients who are alive but failing to keep up
846246121Sian	 * (or who are maliciously not reading, to consume buffer space in
847246121Sian	 * kernel memory or tie up the limited number of available connections).
848246121Sian	 */
849246121Sian	for (i = clients.begin(); i != clients.end(); ) {
850246121Sian		if (write(*i, data, len) != len) {
851246121Sian			--num_clients;
852131397Simp			close(*i);
853246121Sian			i = clients.erase(i);
854252481Sasomers			devdlog(LOG_WARNING, "notify_clients: write() failed; "
855252481Sasomers			    "dropping unresponsive client\n");
856246121Sian		} else
857246121Sian			++i;
858131397Simp	}
859246121Sian}
860131397Simp
861246121Sianvoid
862246121Siancheck_clients(void)
863246121Sian{
864246121Sian	int s;
865246121Sian	struct pollfd pfd;
866246121Sian	list<int>::iterator i;
867246121Sian
868246121Sian	/*
869246121Sian	 * Check all existing clients to see if any of them have disappeared.
870246121Sian	 * Normally we reap clients when we get an error trying to send them an
871246121Sian	 * event.  This check eliminates the problem of an ever-growing list of
872246121Sian	 * zombie clients because we're never writing to them on a system
873246121Sian	 * without frequent device-change activity.
874246121Sian	 */
875246121Sian	pfd.events = 0;
876246121Sian	for (i = clients.begin(); i != clients.end(); ) {
877246121Sian		pfd.fd = *i;
878246121Sian		s = poll(&pfd, 1, 0);
879246121Sian		if ((s < 0 && s != EINTR ) ||
880246121Sian		    (s > 0 && (pfd.revents & POLLHUP))) {
881246121Sian			--num_clients;
882246121Sian			close(*i);
883246121Sian			i = clients.erase(i);
884252481Sasomers			devdlog(LOG_NOTICE, "check_clients:  "
885252481Sasomers			    "dropping disconnected client\n");
886246121Sian		} else
887246121Sian			++i;
888246121Sian	}
889131397Simp}
890131397Simp
891131397Simpvoid
892131397Simpnew_client(int fd)
893131397Simp{
894131397Simp	int s;
895131397Simp
896246121Sian	/*
897246121Sian	 * First go reap any zombie clients, then accept the connection, and
898246121Sian	 * shut down the read side to stop clients from consuming kernel memory
899246121Sian	 * by sending large buffers full of data we'll never read.
900246121Sian	 */
901246121Sian	check_clients();
902131397Simp	s = accept(fd, NULL, NULL);
903246121Sian	if (s != -1) {
904246121Sian		shutdown(s, SHUT_RD);
905131397Simp		clients.push_back(s);
906246121Sian		++num_clients;
907246121Sian	}
908131397Simp}
909131397Simp
910107665Simpstatic void
911107665Simpevent_loop(void)
912107665Simp{
913107665Simp	int rv;
914107665Simp	int fd;
915107665Simp	char buffer[DEVCTL_MAXBUF];
916113790Simp	int once = 0;
917131397Simp	int server_fd, max_fd;
918246121Sian	int accepting;
919113790Simp	timeval tv;
920113790Simp	fd_set fds;
921107665Simp
922240823Spjd	fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC);
923107665Simp	if (fd == -1)
924131397Simp		err(1, "Can't open devctl device %s", PATH_DEVCTL);
925131397Simp	server_fd = create_socket(PIPE);
926246121Sian	accepting = 1;
927131397Simp	max_fd = max(fd, server_fd) + 1;
928247756Seadler	while (!romeo_must_die) {
929113790Simp		if (!once && !dflag && !nflag) {
930113790Simp			// Check to see if we have any events pending.
931113790Simp			tv.tv_sec = 0;
932113790Simp			tv.tv_usec = 0;
933113790Simp			FD_ZERO(&fds);
934113790Simp			FD_SET(fd, &fds);
935113790Simp			rv = select(fd + 1, &fds, &fds, &fds, &tv);
936113790Simp			// No events -> we've processed all pending events
937117944Simp			if (rv == 0) {
938252481Sasomers				devdlog(LOG_DEBUG, "Calling daemon\n");
939155073Spjd				cfg.remove_pidfile();
940155073Spjd				cfg.open_pidfile();
941113790Simp				daemon(0, 0);
942155073Spjd				cfg.write_pidfile();
943113790Simp				once++;
944113790Simp			}
945113790Simp		}
946246121Sian		/*
947246121Sian		 * When we've already got the max number of clients, stop
948246121Sian		 * accepting new connections (don't put server_fd in the set),
949246121Sian		 * shrink the accept() queue to reject connections quickly, and
950246121Sian		 * poll the existing clients more often, so that we notice more
951246121Sian		 * quickly when any of them disappear to free up client slots.
952246121Sian		 */
953131397Simp		FD_ZERO(&fds);
954131397Simp		FD_SET(fd, &fds);
955246121Sian		if (num_clients < max_clients) {
956246121Sian			if (!accepting) {
957246121Sian				listen(server_fd, max_clients);
958246121Sian				accepting = 1;
959246121Sian			}
960246121Sian			FD_SET(server_fd, &fds);
961246121Sian			tv.tv_sec = 60;
962246121Sian			tv.tv_usec = 0;
963246121Sian		} else {
964246121Sian			if (accepting) {
965246121Sian				listen(server_fd, 0);
966246121Sian				accepting = 0;
967246121Sian			}
968246121Sian			tv.tv_sec = 2;
969246121Sian			tv.tv_usec = 0;
970246121Sian		}
971246121Sian		rv = select(max_fd, &fds, NULL, NULL, &tv);
972252482Sasomers		if (got_siginfo) {
973253046Sasomers			devdlog(LOG_INFO, "Events received so far=%u\n",
974252482Sasomers			    total_events);
975252482Sasomers			got_siginfo = 0;
976252482Sasomers		}
977131397Simp		if (rv == -1) {
978131397Simp			if (errno == EINTR)
979131397Simp				continue;
980131397Simp			err(1, "select");
981246121Sian		} else if (rv == 0)
982246121Sian			check_clients();
983131397Simp		if (FD_ISSET(fd, &fds)) {
984131397Simp			rv = read(fd, buffer, sizeof(buffer) - 1);
985131397Simp			if (rv > 0) {
986252482Sasomers				total_events++;
987252481Sasomers				if (rv == sizeof(buffer) - 1) {
988252481Sasomers					devdlog(LOG_WARNING, "Warning: "
989252481Sasomers					    "available event data exceeded "
990252481Sasomers					    "buffer space\n");
991252481Sasomers				}
992131397Simp				notify_clients(buffer, rv);
993107665Simp				buffer[rv] = '\0';
994131397Simp				while (buffer[--rv] == '\n')
995131397Simp					buffer[rv] = '\0';
996131397Simp				process_event(buffer);
997131397Simp			} else if (rv < 0) {
998131397Simp				if (errno != EINTR)
999131397Simp					break;
1000131397Simp			} else {
1001131397Simp				/* EOF */
1002107665Simp				break;
1003131397Simp			}
1004107665Simp		}
1005131397Simp		if (FD_ISSET(server_fd, &fds))
1006131397Simp			new_client(server_fd);
1007107665Simp	}
1008107665Simp	close(fd);
1009107665Simp}
1010252485Sasomers
1011107665Simp/*
1012107665Simp * functions that the parser uses.
1013107665Simp */
1014107665Simpvoid
1015107665Simpadd_attach(int prio, event_proc *p)
1016107665Simp{
1017107665Simp	cfg.add_attach(prio, p);
1018107665Simp}
1019107665Simp
1020107665Simpvoid
1021107665Simpadd_detach(int prio, event_proc *p)
1022107665Simp{
1023107665Simp	cfg.add_detach(prio, p);
1024107665Simp}
1025107665Simp
1026107665Simpvoid
1027107665Simpadd_directory(const char *dir)
1028107665Simp{
1029107665Simp	cfg.add_directory(dir);
1030107665Simp	free(const_cast<char *>(dir));
1031107665Simp}
1032107665Simp
1033107665Simpvoid
1034107665Simpadd_nomatch(int prio, event_proc *p)
1035107665Simp{
1036107665Simp	cfg.add_nomatch(prio, p);
1037107665Simp}
1038107665Simp
1039121487Simpvoid
1040121487Simpadd_notify(int prio, event_proc *p)
1041121487Simp{
1042121487Simp	cfg.add_notify(prio, p);
1043121487Simp}
1044121487Simp
1045107665Simpevent_proc *
1046107665Simpadd_to_event_proc(event_proc *ep, eps *eps)
1047107665Simp{
1048107665Simp	if (ep == NULL)
1049107665Simp		ep = new event_proc();
1050107665Simp	ep->add(eps);
1051107665Simp	return (ep);
1052107665Simp}
1053107665Simp
1054107665Simpeps *
1055107665Simpnew_action(const char *cmd)
1056107665Simp{
1057107665Simp	eps *e = new action(cmd);
1058107665Simp	free(const_cast<char *>(cmd));
1059107665Simp	return (e);
1060107665Simp}
1061107665Simp
1062107665Simpeps *
1063107665Simpnew_match(const char *var, const char *re)
1064107665Simp{
1065108014Simp	eps *e = new match(cfg, var, re);
1066107665Simp	free(const_cast<char *>(var));
1067107665Simp	free(const_cast<char *>(re));
1068107665Simp	return (e);
1069107665Simp}
1070107665Simp
1071147874Simpeps *
1072147874Simpnew_media(const char *var, const char *re)
1073147874Simp{
1074147874Simp	eps *e = new media(cfg, var, re);
1075147874Simp	free(const_cast<char *>(var));
1076147874Simp	free(const_cast<char *>(re));
1077147874Simp	return (e);
1078147874Simp}
1079147874Simp
1080107665Simpvoid
1081107665Simpset_pidfile(const char *name)
1082107665Simp{
1083107665Simp	cfg.set_pidfile(name);
1084107665Simp	free(const_cast<char *>(name));
1085107665Simp}
1086107665Simp
1087107665Simpvoid
1088107665Simpset_variable(const char *var, const char *val)
1089107665Simp{
1090107665Simp	cfg.set_variable(var, val);
1091107665Simp	free(const_cast<char *>(var));
1092107665Simp	free(const_cast<char *>(val));
1093107665Simp}
1094107665Simp
1095107665Simp
1096252485Sasomers
1097107665Simpstatic void
1098107665Simpgensighand(int)
1099107665Simp{
1100247754Seadler	romeo_must_die = 1;
1101107665Simp}
1102107665Simp
1103252481Sasomers/*
1104252482Sasomers * SIGINFO handler.  Will print useful statistics to the syslog or stderr
1105252482Sasomers * as appropriate
1106252482Sasomers */
1107252482Sasomersstatic void
1108252482Sasomerssiginfohand(int)
1109252482Sasomers{
1110252482Sasomers	got_siginfo = 1;
1111252482Sasomers}
1112252482Sasomers
1113252482Sasomers/*
1114252481Sasomers * Local logging function.  Prints to syslog if we're daemonized; syslog
1115252481Sasomers * otherwise.
1116252481Sasomers */
1117107665Simpstatic void
1118252481Sasomersdevdlog(int priority, const char* fmt, ...)
1119252481Sasomers{
1120252481Sasomers	va_list argp;
1121252481Sasomers
1122252481Sasomers	va_start(argp, fmt);
1123252481Sasomers	if (dflag)
1124252481Sasomers		vfprintf(stderr, fmt, argp);
1125252481Sasomers	else
1126252481Sasomers		vsyslog(priority, fmt, argp);
1127252481Sasomers	va_end(argp);
1128252481Sasomers}
1129252481Sasomers
1130252481Sasomersstatic void
1131107665Simpusage()
1132107665Simp{
1133252481Sasomers	fprintf(stderr, "usage: %s [-dn] [-l connlimit] [-f file]\n",
1134246121Sian	    getprogname());
1135107665Simp	exit(1);
1136107665Simp}
1137107665Simp
1138113787Simpstatic void
1139113787Simpcheck_devd_enabled()
1140113787Simp{
1141113787Simp	int val = 0;
1142113787Simp	size_t len;
1143113787Simp
1144113787Simp	len = sizeof(val);
1145114541Simp	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
1146113787Simp		errx(1, "devctl sysctl missing from kernel!");
1147113787Simp	if (val) {
1148113787Simp		warnx("Setting " SYSCTL " to 0");
1149113787Simp		val = 0;
1150113787Simp		sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
1151113787Simp	}
1152113787Simp}
1153113787Simp
1154107665Simp/*
1155107665Simp * main
1156107665Simp */
1157107665Simpint
1158107665Simpmain(int argc, char **argv)
1159107665Simp{
1160107665Simp	int ch;
1161107665Simp
1162113787Simp	check_devd_enabled();
1163252481Sasomers	while ((ch = getopt(argc, argv, "df:l:n")) != -1) {
1164107665Simp		switch (ch) {
1165107665Simp		case 'd':
1166107665Simp			dflag++;
1167107665Simp			break;
1168152770Sjkoshy		case 'f':
1169152770Sjkoshy			configfile = optarg;
1170152770Sjkoshy			break;
1171246121Sian		case 'l':
1172246121Sian			max_clients = MAX(1, strtoul(optarg, NULL, 0));
1173246121Sian			break;
1174113790Simp		case 'n':
1175113790Simp			nflag++;
1176113790Simp			break;
1177107665Simp		default:
1178107665Simp			usage();
1179107665Simp		}
1180107665Simp	}
1181107665Simp
1182107665Simp	cfg.parse();
1183117246Simp	if (!dflag && nflag) {
1184155073Spjd		cfg.open_pidfile();
1185107665Simp		daemon(0, 0);
1186155073Spjd		cfg.write_pidfile();
1187117246Simp	}
1188146306Simp	signal(SIGPIPE, SIG_IGN);
1189107665Simp	signal(SIGHUP, gensighand);
1190107665Simp	signal(SIGINT, gensighand);
1191107665Simp	signal(SIGTERM, gensighand);
1192252482Sasomers	signal(SIGINFO, siginfohand);
1193107665Simp	event_loop();
1194107665Simp	return (0);
1195107665Simp}
1196