devd.cc revision 186078
1107665Simp/*-
2113785Simp * Copyright (c) 2002-2003 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.
25107665Simp */
26107665Simp
27107665Simp/*
28107665Simp * DEVD control daemon.
29107665Simp */
30107665Simp
31107665Simp// TODO list:
32107665Simp//	o devd.conf and devd man pages need a lot of help:
33131397Simp//	  - devd needs to document the unix domain socket
34107665Simp//	  - devd.conf needs more details on the supported statements.
35107665Simp
36107665Simp#include <sys/cdefs.h>
37107665Simp__FBSDID("$FreeBSD: head/sbin/devd/devd.cc 186078 2008-12-14 11:48:51Z phk $");
38107665Simp
39107665Simp#include <sys/param.h>
40131397Simp#include <sys/socket.h>
41131397Simp#include <sys/stat.h>
42131397Simp#include <sys/sysctl.h>
43107665Simp#include <sys/types.h>
44131397Simp#include <sys/un.h>
45107665Simp
46108014Simp#include <ctype.h>
47107665Simp#include <dirent.h>
48107665Simp#include <errno.h>
49107665Simp#include <err.h>
50107665Simp#include <fcntl.h>
51155073Spjd#include <libutil.h>
52108014Simp#include <regex.h>
53146306Simp#include <signal.h>
54107665Simp#include <stdlib.h>
55107665Simp#include <stdio.h>
56107665Simp#include <string.h>
57107665Simp#include <unistd.h>
58107665Simp
59108783Simp#include <algorithm>
60107665Simp#include <map>
61107665Simp#include <string>
62131397Simp#include <list>
63107665Simp#include <vector>
64107665Simp
65114086Simp#include "devd.h"		/* C compatible definitions */
66114086Simp#include "devd.hh"		/* C++ class definitions */
67107665Simp
68131397Simp#define PIPE "/var/run/devd.pipe"
69107665Simp#define CF "/etc/devd.conf"
70113787Simp#define SYSCTL "hw.bus.devctl_disable"
71107665Simp
72107665Simpusing namespace std;
73107665Simp
74107665Simpextern FILE *yyin;
75107665Simpextern int lineno;
76107665Simp
77121487Simpstatic const char notify = '!';
78108783Simpstatic const char nomatch = '?';
79108783Simpstatic const char attach = '+';
80108783Simpstatic const char detach = '-';
81108783Simp
82155073Spjdstatic struct pidfh *pfh;
83155073Spjd
84113790Simpint Dflag;
85107665Simpint dflag;
86114000Simpint nflag;
87107665Simpint romeo_must_die = 0;
88107665Simp
89152770Sjkoshystatic const char *configfile = CF;
90152770Sjkoshy
91107665Simpstatic void event_loop(void);
92107665Simpstatic void usage(void);
93107665Simp
94108783Simptemplate <class T> void
95108783Simpdelete_and_clear(vector<T *> &v)
96108783Simp{
97108783Simp	typename vector<T *>::const_iterator i;
98108783Simp
99108783Simp	for (i = v.begin(); i != v.end(); i++)
100108783Simp		delete *i;
101108783Simp	v.clear();
102108783Simp}
103108783Simp
104107665Simpconfig cfg;
105107665Simp
106107665Simpevent_proc::event_proc() : _prio(-1)
107107665Simp{
108107665Simp	// nothing
109107665Simp}
110107665Simp
111107665Simpevent_proc::~event_proc()
112107665Simp{
113152406Sbland	delete_and_clear(_epsvec);
114107665Simp}
115107665Simp
116107665Simpvoid
117107665Simpevent_proc::add(eps *eps)
118107665Simp{
119107665Simp	_epsvec.push_back(eps);
120107665Simp}
121107665Simp
122107665Simpbool
123107665Simpevent_proc::matches(config &c)
124107665Simp{
125107665Simp	vector<eps *>::const_iterator i;
126107665Simp
127107665Simp	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
128107665Simp		if (!(*i)->do_match(c))
129107665Simp			return (false);
130107665Simp	return (true);
131107665Simp}
132107665Simp
133107665Simpbool
134107665Simpevent_proc::run(config &c)
135107665Simp{
136107665Simp	vector<eps *>::const_iterator i;
137107665Simp
138107665Simp	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
139107665Simp		if (!(*i)->do_action(c))
140107665Simp			return (false);
141107665Simp	return (true);
142107665Simp}
143107665Simp
144107665Simpaction::action(const char *cmd)
145107665Simp	: _cmd(cmd)
146107665Simp{
147107665Simp	// nothing
148107665Simp}
149107665Simp
150107665Simpaction::~action()
151107665Simp{
152107665Simp	// nothing
153107665Simp}
154107665Simp
155107665Simpbool
156108014Simpaction::do_action(config &c)
157107665Simp{
158108783Simp	string s = c.expand_string(_cmd);
159113790Simp	if (Dflag)
160108783Simp		fprintf(stderr, "Executing '%s'\n", s.c_str());
161108783Simp	::system(s.c_str());
162107665Simp	return (true);
163107665Simp}
164107665Simp
165108014Simpmatch::match(config &c, const char *var, const char *re)
166108783Simp	: _var(var)
167107665Simp{
168108783Simp	string pattern = re;
169108783Simp	_re = "^";
170108783Simp	_re.append(c.expand_string(string(re)));
171108783Simp	_re.append("$");
172154109Simp	regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
173107665Simp}
174107665Simp
175107665Simpmatch::~match()
176107665Simp{
177108014Simp	regfree(&_regex);
178107665Simp}
179107665Simp
180107665Simpbool
181108014Simpmatch::do_match(config &c)
182107665Simp{
183108014Simp	string value = c.get_variable(_var);
184108014Simp	bool retval;
185108014Simp
186113790Simp	if (Dflag)
187108783Simp		fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(),
188108783Simp		    value.c_str(), _re.c_str());
189108783Simp
190108014Simp	retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
191108014Simp	return retval;
192107665Simp}
193107665Simp
194147874Simp#include <sys/sockio.h>
195147874Simp#include <net/if.h>
196147874Simp#include <net/if_media.h>
197147874Simp
198151486Sbrooksmedia::media(config &, const char *var, const char *type)
199147874Simp	: _var(var), _type(-1)
200147874Simp{
201147874Simp	static struct ifmedia_description media_types[] = {
202147874Simp		{ IFM_ETHER,		"Ethernet" },
203147874Simp		{ IFM_TOKEN,		"Tokenring" },
204147874Simp		{ IFM_FDDI,		"FDDI" },
205147874Simp		{ IFM_IEEE80211,	"802.11" },
206147874Simp		{ IFM_ATM,		"ATM" },
207147874Simp		{ IFM_CARP,		"CARP" },
208147874Simp		{ -1,			"unknown" },
209147874Simp		{ 0, NULL },
210147874Simp	};
211147874Simp	for (int i = 0; media_types[i].ifmt_string != NULL; i++)
212147874Simp		if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
213147874Simp			_type = media_types[i].ifmt_word;
214147874Simp			break;
215147874Simp		}
216147874Simp}
217147874Simp
218147874Simpmedia::~media()
219147874Simp{
220147874Simp}
221147874Simp
222147874Simpbool
223147874Simpmedia::do_match(config &c)
224147874Simp{
225150949Simp	string value;
226147874Simp	struct ifmediareq ifmr;
227147874Simp	bool retval;
228147874Simp	int s;
229147874Simp
230150949Simp	// Since we can be called from both a device attach/detach
231150949Simp	// context where device-name is defined and what we want,
232150949Simp	// as well as from a link status context, where subsystem is
233150949Simp	// the name of interest, first try device-name and fall back
234150949Simp	// to subsystem if none exists.
235150949Simp	value = c.get_variable("device-name");
236150949Simp	if (value.length() == 0)
237151480Simp		value = c.get_variable("subsystem");
238147874Simp	if (Dflag)
239147874Simp		fprintf(stderr, "Testing media type of %s against 0x%x\n",
240147874Simp		    value.c_str(), _type);
241147874Simp
242147874Simp	retval = false;
243147874Simp
244147874Simp	s = socket(PF_INET, SOCK_DGRAM, 0);
245147874Simp	if (s >= 0) {
246147874Simp		memset(&ifmr, 0, sizeof(ifmr));
247147874Simp		strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
248147874Simp
249147874Simp		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
250147874Simp		    ifmr.ifm_status & IFM_AVALID) {
251147874Simp			if (Dflag)
252147874Simp				fprintf(stderr, "%s has media type 0x%x\n",
253147874Simp				    value.c_str(), IFM_TYPE(ifmr.ifm_active));
254147874Simp			retval = (IFM_TYPE(ifmr.ifm_active) == _type);
255147874Simp		} else if (_type == -1) {
256147874Simp			if (Dflag)
257147874Simp				fprintf(stderr, "%s has unknown media type\n",
258147874Simp				    value.c_str());
259147874Simp			retval = true;
260147874Simp		}
261147874Simp		close(s);
262147874Simp	}
263147874Simp
264147874Simp	return retval;
265147874Simp}
266147874Simp
267107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
268107665Simpconst string var_list::nothing = "";
269107665Simp
270107665Simpconst string &
271107665Simpvar_list::get_variable(const string &var) const
272107665Simp{
273107665Simp	map<string, string>::const_iterator i;
274107665Simp
275107665Simp	i = _vars.find(var);
276107665Simp	if (i == _vars.end())
277108783Simp		return (var_list::bogus);
278107665Simp	return (i->second);
279107665Simp}
280107665Simp
281107665Simpbool
282107665Simpvar_list::is_set(const string &var) const
283107665Simp{
284107665Simp	return (_vars.find(var) != _vars.end());
285107665Simp}
286107665Simp
287107665Simpvoid
288107665Simpvar_list::set_variable(const string &var, const string &val)
289107665Simp{
290113790Simp	if (Dflag)
291145218Simp		fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
292107665Simp	_vars[var] = val;
293107665Simp}
294107665Simp
295107665Simpvoid
296107665Simpconfig::reset(void)
297107665Simp{
298107665Simp	_dir_list.clear();
299108783Simp	delete_and_clear(_var_list_table);
300108783Simp	delete_and_clear(_attach_list);
301108783Simp	delete_and_clear(_detach_list);
302108783Simp	delete_and_clear(_nomatch_list);
303121487Simp	delete_and_clear(_notify_list);
304107665Simp}
305107665Simp
306107665Simpvoid
307107665Simpconfig::parse_one_file(const char *fn)
308107665Simp{
309113790Simp	if (Dflag)
310186078Sphk		fprintf(stderr, "Parsing %s\n", fn);
311107665Simp	yyin = fopen(fn, "r");
312107665Simp	if (yyin == NULL)
313107665Simp		err(1, "Cannot open config file %s", fn);
314157746Smaxim	lineno = 1;
315107665Simp	if (yyparse() != 0)
316107665Simp		errx(1, "Cannot parse %s at line %d", fn, lineno);
317107665Simp	fclose(yyin);
318107665Simp}
319107665Simp
320107665Simpvoid
321107665Simpconfig::parse_files_in_dir(const char *dirname)
322107665Simp{
323107665Simp	DIR *dirp;
324107665Simp	struct dirent *dp;
325107665Simp	char path[PATH_MAX];
326107665Simp
327113790Simp	if (Dflag)
328186078Sphk		fprintf(stderr, "Parsing files in %s\n", dirname);
329107665Simp	dirp = opendir(dirname);
330107665Simp	if (dirp == NULL)
331107665Simp		return;
332107665Simp	readdir(dirp);		/* Skip . */
333107665Simp	readdir(dirp);		/* Skip .. */
334107665Simp	while ((dp = readdir(dirp)) != NULL) {
335107665Simp		if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
336107665Simp			snprintf(path, sizeof(path), "%s/%s",
337107665Simp			    dirname, dp->d_name);
338107665Simp			parse_one_file(path);
339107665Simp		}
340107665Simp	}
341107665Simp}
342107665Simp
343108783Simpclass epv_greater {
344108783Simppublic:
345108783Simp	int operator()(event_proc *const&l1, event_proc *const&l2)
346108783Simp	{
347108783Simp		return (l1->get_priority() > l2->get_priority());
348108783Simp	}
349108783Simp};
350108783Simp
351107665Simpvoid
352108783Simpconfig::sort_vector(vector<event_proc *> &v)
353108783Simp{
354108783Simp	sort(v.begin(), v.end(), epv_greater());
355108783Simp}
356108783Simp
357108783Simpvoid
358107665Simpconfig::parse(void)
359107665Simp{
360107665Simp	vector<string>::const_iterator i;
361107665Simp
362152770Sjkoshy	parse_one_file(configfile);
363107665Simp	for (i = _dir_list.begin(); i != _dir_list.end(); i++)
364107665Simp		parse_files_in_dir((*i).c_str());
365108783Simp	sort_vector(_attach_list);
366108783Simp	sort_vector(_detach_list);
367108783Simp	sort_vector(_nomatch_list);
368121487Simp	sort_vector(_notify_list);
369107665Simp}
370107665Simp
371107665Simpvoid
372155073Spjdconfig::open_pidfile()
373107665Simp{
374155073Spjd	pid_t otherpid;
375107665Simp
376107665Simp	if (_pidfile == "")
377107665Simp		return;
378155073Spjd	pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid);
379155073Spjd	if (pfh == NULL) {
380155073Spjd		if (errno == EEXIST)
381155073Spjd			errx(1, "devd already running, pid: %d", (int)otherpid);
382155073Spjd		warn("cannot open pid file");
383155073Spjd	}
384107665Simp}
385107665Simp
386107665Simpvoid
387155073Spjdconfig::write_pidfile()
388155073Spjd{
389155073Spjd
390155073Spjd	pidfile_write(pfh);
391155073Spjd}
392155073Spjd
393155073Spjdvoid
394155073Spjdconfig::remove_pidfile()
395155073Spjd{
396155073Spjd
397155073Spjd	pidfile_remove(pfh);
398155073Spjd}
399155073Spjd
400155073Spjdvoid
401107665Simpconfig::add_attach(int prio, event_proc *p)
402107665Simp{
403107665Simp	p->set_priority(prio);
404107665Simp	_attach_list.push_back(p);
405107665Simp}
406107665Simp
407107665Simpvoid
408107665Simpconfig::add_detach(int prio, event_proc *p)
409107665Simp{
410107665Simp	p->set_priority(prio);
411107665Simp	_detach_list.push_back(p);
412107665Simp}
413107665Simp
414107665Simpvoid
415107665Simpconfig::add_directory(const char *dir)
416107665Simp{
417107665Simp	_dir_list.push_back(string(dir));
418107665Simp}
419107665Simp
420107665Simpvoid
421107665Simpconfig::add_nomatch(int prio, event_proc *p)
422107665Simp{
423107665Simp	p->set_priority(prio);
424107665Simp	_nomatch_list.push_back(p);
425107665Simp}
426107665Simp
427107665Simpvoid
428121487Simpconfig::add_notify(int prio, event_proc *p)
429121487Simp{
430121487Simp	p->set_priority(prio);
431121487Simp	_notify_list.push_back(p);
432121487Simp}
433121487Simp
434121487Simpvoid
435107665Simpconfig::set_pidfile(const char *fn)
436107665Simp{
437107665Simp	_pidfile = string(fn);
438107665Simp}
439107665Simp
440107665Simpvoid
441107665Simpconfig::push_var_table()
442107665Simp{
443107665Simp	var_list *vl;
444107665Simp
445107665Simp	vl = new var_list();
446107665Simp	_var_list_table.push_back(vl);
447113790Simp	if (Dflag)
448108783Simp		fprintf(stderr, "Pushing table\n");
449107665Simp}
450107665Simp
451107665Simpvoid
452107665Simpconfig::pop_var_table()
453107665Simp{
454107665Simp	delete _var_list_table.back();
455107665Simp	_var_list_table.pop_back();
456113790Simp	if (Dflag)
457108783Simp		fprintf(stderr, "Popping table\n");
458107665Simp}
459107665Simp
460107665Simpvoid
461107665Simpconfig::set_variable(const char *var, const char *val)
462107665Simp{
463107665Simp	_var_list_table.back()->set_variable(var, val);
464107665Simp}
465107665Simp
466107665Simpconst string &
467107665Simpconfig::get_variable(const string &var)
468107665Simp{
469107665Simp	vector<var_list *>::reverse_iterator i;
470107665Simp
471107665Simp	for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) {
472107665Simp		if ((*i)->is_set(var))
473108783Simp			return ((*i)->get_variable(var));
474107665Simp	}
475107665Simp	return (var_list::nothing);
476107665Simp}
477107665Simp
478108783Simpbool
479108783Simpconfig::is_id_char(char ch)
480108783Simp{
481108783Simp	return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
482108783Simp	    ch == '-'));
483108783Simp}
484108783Simp
485108014Simpvoid
486114081Simpconfig::expand_one(const char *&src, string &dst)
487107665Simp{
488108014Simp	int count;
489114081Simp	string buffer, varstr;
490108014Simp
491108783Simp	src++;
492108014Simp	// $$ -> $
493108014Simp	if (*src == '$') {
494114081Simp		dst.append(src++, 1);
495108014Simp		return;
496108014Simp	}
497108014Simp
498108014Simp	// $(foo) -> $(foo)
499108783Simp	// Not sure if I want to support this or not, so for now we just pass
500108783Simp	// it through.
501108014Simp	if (*src == '(') {
502114081Simp		dst.append("$");
503108014Simp		count = 1;
504114081Simp		/* If the string ends before ) is matched , return. */
505114081Simp		while (count > 0 && *src) {
506108014Simp			if (*src == ')')
507108014Simp				count--;
508108014Simp			else if (*src == '(')
509108014Simp				count++;
510114081Simp			dst.append(src++, 1);
511108014Simp		}
512108014Simp		return;
513108014Simp	}
514108014Simp
515108014Simp	// ${^A-Za-z] -> $\1
516108014Simp	if (!isalpha(*src)) {
517114081Simp		dst.append("$");
518114081Simp		dst.append(src++, 1);
519108014Simp		return;
520108014Simp	}
521108014Simp
522108014Simp	// $var -> replace with value
523114081Simp	do {
524114081Simp		buffer.append(src++, 1);
525114084Simp	} while (is_id_char(*src));
526114081Simp	buffer.append("", 1);
527114081Simp	varstr = get_variable(buffer.c_str());
528114081Simp	dst.append(varstr);
529107665Simp}
530107665Simp
531108014Simpconst string
532108014Simpconfig::expand_string(const string &s)
533108014Simp{
534108014Simp	const char *src;
535114081Simp	string dst;
536108014Simp
537108014Simp	src = s.c_str();
538108014Simp	while (*src) {
539108014Simp		if (*src == '$')
540114081Simp			expand_one(src, dst);
541108014Simp		else
542114081Simp			dst.append(src++, 1);
543108014Simp	}
544114081Simp	dst.append("", 1);
545108014Simp
546114081Simp	return (dst);
547108014Simp}
548108014Simp
549108783Simpbool
550108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs)
551108783Simp{
552108783Simp	char *walker;
553108783Simp
554108783Simp	if (*buffer == '\0')
555108783Simp		return (false);
556108783Simp	walker = lhs = buffer;
557108783Simp	while (is_id_char(*walker))
558108783Simp		walker++;
559108783Simp	if (*walker != '=')
560108783Simp		return (false);
561108783Simp	walker++;		// skip =
562108783Simp	if (*walker == '"') {
563108783Simp		walker++;	// skip "
564108783Simp		rhs = walker;
565108783Simp		while (*walker && *walker != '"')
566108783Simp			walker++;
567108783Simp		if (*walker != '"')
568108783Simp			return (false);
569108783Simp		rhs[-2] = '\0';
570108783Simp		*walker++ = '\0';
571108783Simp	} else {
572108783Simp		rhs = walker;
573108783Simp		while (*walker && !isspace(*walker))
574108783Simp			walker++;
575108783Simp		if (*walker != '\0')
576108783Simp			*walker++ = '\0';
577108783Simp		rhs[-1] = '\0';
578108783Simp	}
579113785Simp	while (isspace(*walker))
580113785Simp		walker++;
581108783Simp	buffer = walker;
582108783Simp	return (true);
583108783Simp}
584108783Simp
585108783Simp
586108783Simpchar *
587108783Simpconfig::set_vars(char *buffer)
588108783Simp{
589108783Simp	char *lhs;
590108783Simp	char *rhs;
591108783Simp
592108783Simp	while (1) {
593108783Simp		if (!chop_var(buffer, lhs, rhs))
594108783Simp			break;
595108783Simp		set_variable(lhs, rhs);
596108783Simp	}
597108783Simp	return (buffer);
598108783Simp}
599108783Simp
600108783Simpvoid
601108783Simpconfig::find_and_execute(char type)
602108783Simp{
603108783Simp	vector<event_proc *> *l;
604108783Simp	vector<event_proc *>::const_iterator i;
605151486Sbrooks	const char *s;
606108783Simp
607108783Simp	switch (type) {
608108783Simp	default:
609108783Simp		return;
610121487Simp	case notify:
611121487Simp		l = &_notify_list;
612121487Simp		s = "notify";
613121487Simp		break;
614108783Simp	case nomatch:
615108783Simp		l = &_nomatch_list;
616108783Simp		s = "nomatch";
617108783Simp		break;
618108783Simp	case attach:
619108783Simp		l = &_attach_list;
620108783Simp		s = "attach";
621108783Simp		break;
622108783Simp	case detach:
623108783Simp		l = &_detach_list;
624108783Simp		s = "detach";
625108783Simp		break;
626108783Simp	}
627113790Simp	if (Dflag)
628108783Simp		fprintf(stderr, "Processing %s event\n", s);
629108783Simp	for (i = l->begin(); i != l->end(); i++) {
630108783Simp		if ((*i)->matches(*this)) {
631108783Simp			(*i)->run(*this);
632108783Simp			break;
633108783Simp		}
634108783Simp	}
635108783Simp
636108783Simp}
637108783Simp
638107665Simp
639107665Simpstatic void
640108783Simpprocess_event(char *buffer)
641107665Simp{
642107665Simp	char type;
643107665Simp	char *sp;
644107665Simp
645108783Simp	sp = buffer + 1;
646113790Simp	if (Dflag)
647108783Simp		fprintf(stderr, "Processing event '%s'\n", buffer);
648107665Simp	type = *buffer++;
649108783Simp	cfg.push_var_table();
650108783Simp	// No match doesn't have a device, and the format is a little
651108783Simp	// different, so handle it separately.
652121487Simp	switch (type) {
653121487Simp	case notify:
654121487Simp		sp = cfg.set_vars(sp);
655121487Simp		break;
656121487Simp	case nomatch:
657145218Simp		//? at location pnp-info on bus
658145218Simp		sp = strchr(sp, ' ');
659145218Simp		if (sp == NULL)
660145218Simp			return;	/* Can't happen? */
661145218Simp		*sp++ = '\0';
662121487Simp		if (strncmp(sp, "at ", 3) == 0)
663121487Simp			sp += 3;
664121487Simp		sp = cfg.set_vars(sp);
665121487Simp		if (strncmp(sp, "on ", 3) == 0)
666121487Simp			cfg.set_variable("bus", sp + 3);
667121487Simp		break;
668121487Simp	case attach:	/*FALLTHROUGH*/
669121487Simp	case detach:
670108783Simp		sp = strchr(sp, ' ');
671108783Simp		if (sp == NULL)
672108783Simp			return;	/* Can't happen? */
673108783Simp		*sp++ = '\0';
674108783Simp		cfg.set_variable("device-name", buffer);
675113785Simp		if (strncmp(sp, "at ", 3) == 0)
676113785Simp			sp += 3;
677113785Simp		sp = cfg.set_vars(sp);
678113785Simp		if (strncmp(sp, "on ", 3) == 0)
679113785Simp			cfg.set_variable("bus", sp + 3);
680121487Simp		break;
681108783Simp	}
682113785Simp
683108783Simp	cfg.find_and_execute(type);
684108783Simp	cfg.pop_var_table();
685107665Simp}
686107665Simp
687131397Simpint
688131397Simpcreate_socket(const char *name)
689131397Simp{
690131397Simp	int fd, slen;
691131397Simp	struct sockaddr_un sun;
692131397Simp
693131397Simp	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
694131397Simp		err(1, "socket");
695131397Simp	bzero(&sun, sizeof(sun));
696131397Simp	sun.sun_family = AF_UNIX;
697131397Simp	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
698131397Simp	slen = SUN_LEN(&sun);
699131397Simp	unlink(name);
700147973Smarcus	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
701147973Smarcus	    	err(1, "fcntl");
702131397Simp	if (bind(fd, (struct sockaddr *) & sun, slen) < 0)
703131397Simp		err(1, "bind");
704131397Simp	listen(fd, 4);
705147972Smarcus	chown(name, 0, 0);	/* XXX - root.wheel */
706147973Smarcus	chmod(name, 0666);
707131397Simp	return (fd);
708131397Simp}
709131397Simp
710131397Simplist<int> clients;
711131397Simp
712131397Simpvoid
713131397Simpnotify_clients(const char *data, int len)
714131397Simp{
715131397Simp	list<int> bad;
716131397Simp	list<int>::const_iterator i;
717131397Simp
718131397Simp	for (i = clients.begin(); i != clients.end(); i++) {
719131397Simp		if (write(*i, data, len) <= 0) {
720131397Simp			bad.push_back(*i);
721131397Simp			close(*i);
722131397Simp		}
723131397Simp	}
724131397Simp
725131397Simp	for (i = bad.begin(); i != bad.end(); i++)
726131397Simp		clients.erase(find(clients.begin(), clients.end(), *i));
727131397Simp}
728131397Simp
729131397Simpvoid
730131397Simpnew_client(int fd)
731131397Simp{
732131397Simp	int s;
733131397Simp
734131397Simp	s = accept(fd, NULL, NULL);
735131397Simp	if (s != -1)
736131397Simp		clients.push_back(s);
737131397Simp}
738131397Simp
739107665Simpstatic void
740107665Simpevent_loop(void)
741107665Simp{
742107665Simp	int rv;
743107665Simp	int fd;
744107665Simp	char buffer[DEVCTL_MAXBUF];
745113790Simp	int once = 0;
746131397Simp	int server_fd, max_fd;
747113790Simp	timeval tv;
748113790Simp	fd_set fds;
749107665Simp
750107665Simp	fd = open(PATH_DEVCTL, O_RDONLY);
751107665Simp	if (fd == -1)
752131397Simp		err(1, "Can't open devctl device %s", PATH_DEVCTL);
753107665Simp	if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
754131397Simp		err(1, "Can't set close-on-exec flag on devctl");
755131397Simp	server_fd = create_socket(PIPE);
756131397Simp	max_fd = max(fd, server_fd) + 1;
757107665Simp	while (1) {
758107665Simp		if (romeo_must_die)
759107665Simp			break;
760113790Simp		if (!once && !dflag && !nflag) {
761113790Simp			// Check to see if we have any events pending.
762113790Simp			tv.tv_sec = 0;
763113790Simp			tv.tv_usec = 0;
764113790Simp			FD_ZERO(&fds);
765113790Simp			FD_SET(fd, &fds);
766113790Simp			rv = select(fd + 1, &fds, &fds, &fds, &tv);
767113790Simp			// No events -> we've processed all pending events
768117944Simp			if (rv == 0) {
769113790Simp				if (Dflag)
770113790Simp					fprintf(stderr, "Calling daemon\n");
771155073Spjd				cfg.remove_pidfile();
772155073Spjd				cfg.open_pidfile();
773113790Simp				daemon(0, 0);
774155073Spjd				cfg.write_pidfile();
775113790Simp				once++;
776113790Simp			}
777113790Simp		}
778131397Simp		FD_ZERO(&fds);
779131397Simp		FD_SET(fd, &fds);
780131397Simp		FD_SET(server_fd, &fds);
781131397Simp		rv = select(max_fd, &fds, NULL, NULL, NULL);
782131397Simp		if (rv == -1) {
783131397Simp			if (errno == EINTR)
784131397Simp				continue;
785131397Simp			err(1, "select");
786131397Simp		}
787131397Simp		if (FD_ISSET(fd, &fds)) {
788131397Simp			rv = read(fd, buffer, sizeof(buffer) - 1);
789131397Simp			if (rv > 0) {
790131397Simp				notify_clients(buffer, rv);
791107665Simp				buffer[rv] = '\0';
792131397Simp				while (buffer[--rv] == '\n')
793131397Simp					buffer[rv] = '\0';
794131397Simp				process_event(buffer);
795131397Simp			} else if (rv < 0) {
796131397Simp				if (errno != EINTR)
797131397Simp					break;
798131397Simp			} else {
799131397Simp				/* EOF */
800107665Simp				break;
801131397Simp			}
802107665Simp		}
803131397Simp		if (FD_ISSET(server_fd, &fds))
804131397Simp			new_client(server_fd);
805107665Simp	}
806107665Simp	close(fd);
807107665Simp}
808107665Simp
809107665Simp/*
810107665Simp * functions that the parser uses.
811107665Simp */
812107665Simpvoid
813107665Simpadd_attach(int prio, event_proc *p)
814107665Simp{
815107665Simp	cfg.add_attach(prio, p);
816107665Simp}
817107665Simp
818107665Simpvoid
819107665Simpadd_detach(int prio, event_proc *p)
820107665Simp{
821107665Simp	cfg.add_detach(prio, p);
822107665Simp}
823107665Simp
824107665Simpvoid
825107665Simpadd_directory(const char *dir)
826107665Simp{
827107665Simp	cfg.add_directory(dir);
828107665Simp	free(const_cast<char *>(dir));
829107665Simp}
830107665Simp
831107665Simpvoid
832107665Simpadd_nomatch(int prio, event_proc *p)
833107665Simp{
834107665Simp	cfg.add_nomatch(prio, p);
835107665Simp}
836107665Simp
837121487Simpvoid
838121487Simpadd_notify(int prio, event_proc *p)
839121487Simp{
840121487Simp	cfg.add_notify(prio, p);
841121487Simp}
842121487Simp
843107665Simpevent_proc *
844107665Simpadd_to_event_proc(event_proc *ep, eps *eps)
845107665Simp{
846107665Simp	if (ep == NULL)
847107665Simp		ep = new event_proc();
848107665Simp	ep->add(eps);
849107665Simp	return (ep);
850107665Simp}
851107665Simp
852107665Simpeps *
853107665Simpnew_action(const char *cmd)
854107665Simp{
855107665Simp	eps *e = new action(cmd);
856107665Simp	free(const_cast<char *>(cmd));
857107665Simp	return (e);
858107665Simp}
859107665Simp
860107665Simpeps *
861107665Simpnew_match(const char *var, const char *re)
862107665Simp{
863108014Simp	eps *e = new match(cfg, var, re);
864107665Simp	free(const_cast<char *>(var));
865107665Simp	free(const_cast<char *>(re));
866107665Simp	return (e);
867107665Simp}
868107665Simp
869147874Simpeps *
870147874Simpnew_media(const char *var, const char *re)
871147874Simp{
872147874Simp	eps *e = new media(cfg, var, re);
873147874Simp	free(const_cast<char *>(var));
874147874Simp	free(const_cast<char *>(re));
875147874Simp	return (e);
876147874Simp}
877147874Simp
878107665Simpvoid
879107665Simpset_pidfile(const char *name)
880107665Simp{
881107665Simp	cfg.set_pidfile(name);
882107665Simp	free(const_cast<char *>(name));
883107665Simp}
884107665Simp
885107665Simpvoid
886107665Simpset_variable(const char *var, const char *val)
887107665Simp{
888107665Simp	cfg.set_variable(var, val);
889107665Simp	free(const_cast<char *>(var));
890107665Simp	free(const_cast<char *>(val));
891107665Simp}
892107665Simp
893107665Simp
894107665Simp
895107665Simpstatic void
896107665Simpgensighand(int)
897107665Simp{
898107665Simp	romeo_must_die++;
899107665Simp	_exit(0);
900107665Simp}
901107665Simp
902107665Simpstatic void
903107665Simpusage()
904107665Simp{
905162388Sru	fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname());
906107665Simp	exit(1);
907107665Simp}
908107665Simp
909113787Simpstatic void
910113787Simpcheck_devd_enabled()
911113787Simp{
912113787Simp	int val = 0;
913113787Simp	size_t len;
914113787Simp
915113787Simp	len = sizeof(val);
916114541Simp	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
917113787Simp		errx(1, "devctl sysctl missing from kernel!");
918113787Simp	if (val) {
919113787Simp		warnx("Setting " SYSCTL " to 0");
920113787Simp		val = 0;
921113787Simp		sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
922113787Simp	}
923113787Simp}
924113787Simp
925107665Simp/*
926107665Simp * main
927107665Simp */
928107665Simpint
929107665Simpmain(int argc, char **argv)
930107665Simp{
931107665Simp	int ch;
932107665Simp
933113787Simp	check_devd_enabled();
934152770Sjkoshy	while ((ch = getopt(argc, argv, "Ddf:n")) != -1) {
935107665Simp		switch (ch) {
936113790Simp		case 'D':
937113790Simp			Dflag++;
938113790Simp			break;
939107665Simp		case 'd':
940107665Simp			dflag++;
941107665Simp			break;
942152770Sjkoshy		case 'f':
943152770Sjkoshy			configfile = optarg;
944152770Sjkoshy			break;
945113790Simp		case 'n':
946113790Simp			nflag++;
947113790Simp			break;
948107665Simp		default:
949107665Simp			usage();
950107665Simp		}
951107665Simp	}
952107665Simp
953107665Simp	cfg.parse();
954117246Simp	if (!dflag && nflag) {
955155073Spjd		cfg.open_pidfile();
956107665Simp		daemon(0, 0);
957155073Spjd		cfg.write_pidfile();
958117246Simp	}
959146306Simp	signal(SIGPIPE, SIG_IGN);
960107665Simp	signal(SIGHUP, gensighand);
961107665Simp	signal(SIGINT, gensighand);
962107665Simp	signal(SIGTERM, gensighand);
963107665Simp	event_loop();
964107665Simp	return (0);
965107665Simp}
966