devd.cc revision 147972
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 147972 2005-07-13 17:10:47Z marcus $");
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>
51108014Simp#include <regex.h>
52146306Simp#include <signal.h>
53107665Simp#include <stdlib.h>
54107665Simp#include <stdio.h>
55107665Simp#include <string.h>
56107665Simp#include <unistd.h>
57107665Simp
58108783Simp#include <algorithm>
59107665Simp#include <map>
60107665Simp#include <string>
61131397Simp#include <list>
62107665Simp#include <vector>
63107665Simp
64114086Simp#include "devd.h"		/* C compatible definitions */
65114086Simp#include "devd.hh"		/* C++ class definitions */
66107665Simp
67131397Simp#define PIPE "/var/run/devd.pipe"
68107665Simp#define CF "/etc/devd.conf"
69113787Simp#define SYSCTL "hw.bus.devctl_disable"
70107665Simp
71107665Simpusing namespace std;
72107665Simp
73107665Simpextern FILE *yyin;
74107665Simpextern int lineno;
75107665Simp
76121487Simpstatic const char notify = '!';
77108783Simpstatic const char nomatch = '?';
78108783Simpstatic const char attach = '+';
79108783Simpstatic const char detach = '-';
80108783Simp
81113790Simpint Dflag;
82107665Simpint dflag;
83114000Simpint nflag;
84107665Simpint romeo_must_die = 0;
85107665Simp
86107665Simpstatic void event_loop(void);
87107665Simpstatic void usage(void);
88107665Simp
89108783Simptemplate <class T> void
90108783Simpdelete_and_clear(vector<T *> &v)
91108783Simp{
92108783Simp	typename vector<T *>::const_iterator i;
93108783Simp
94108783Simp	for (i = v.begin(); i != v.end(); i++)
95108783Simp		delete *i;
96108783Simp	v.clear();
97108783Simp}
98108783Simp
99107665Simpconfig cfg;
100107665Simp
101107665Simpevent_proc::event_proc() : _prio(-1)
102107665Simp{
103107665Simp	// nothing
104107665Simp}
105107665Simp
106107665Simpevent_proc::~event_proc()
107107665Simp{
108107665Simp	vector<eps *>::const_iterator i;
109107665Simp
110107665Simp	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
111107665Simp		delete *i;
112107665Simp	_epsvec.clear();
113107665Simp}
114107665Simp
115107665Simpvoid
116107665Simpevent_proc::add(eps *eps)
117107665Simp{
118107665Simp	_epsvec.push_back(eps);
119107665Simp}
120107665Simp
121107665Simpbool
122107665Simpevent_proc::matches(config &c)
123107665Simp{
124107665Simp	vector<eps *>::const_iterator i;
125107665Simp
126107665Simp	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
127107665Simp		if (!(*i)->do_match(c))
128107665Simp			return (false);
129107665Simp	return (true);
130107665Simp}
131107665Simp
132107665Simpbool
133107665Simpevent_proc::run(config &c)
134107665Simp{
135107665Simp	vector<eps *>::const_iterator i;
136107665Simp
137107665Simp	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
138107665Simp		if (!(*i)->do_action(c))
139107665Simp			return (false);
140107665Simp	return (true);
141107665Simp}
142107665Simp
143107665Simpaction::action(const char *cmd)
144107665Simp	: _cmd(cmd)
145107665Simp{
146107665Simp	// nothing
147107665Simp}
148107665Simp
149107665Simpaction::~action()
150107665Simp{
151107665Simp	// nothing
152107665Simp}
153107665Simp
154107665Simpbool
155108014Simpaction::do_action(config &c)
156107665Simp{
157108783Simp	string s = c.expand_string(_cmd);
158113790Simp	if (Dflag)
159108783Simp		fprintf(stderr, "Executing '%s'\n", s.c_str());
160108783Simp	::system(s.c_str());
161107665Simp	return (true);
162107665Simp}
163107665Simp
164108014Simpmatch::match(config &c, const char *var, const char *re)
165108783Simp	: _var(var)
166107665Simp{
167108783Simp	string pattern = re;
168108783Simp	_re = "^";
169108783Simp	_re.append(c.expand_string(string(re)));
170108783Simp	_re.append("$");
171108783Simp	regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB);
172107665Simp}
173107665Simp
174107665Simpmatch::~match()
175107665Simp{
176108014Simp	regfree(&_regex);
177107665Simp}
178107665Simp
179107665Simpbool
180108014Simpmatch::do_match(config &c)
181107665Simp{
182108014Simp	string value = c.get_variable(_var);
183108014Simp	bool retval;
184108014Simp
185113790Simp	if (Dflag)
186108783Simp		fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(),
187108783Simp		    value.c_str(), _re.c_str());
188108783Simp
189108014Simp	retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
190108014Simp	return retval;
191107665Simp}
192107665Simp
193147874Simp#include <sys/sockio.h>
194147874Simp#include <net/if.h>
195147874Simp#include <net/if_media.h>
196147874Simp
197147874Simpmedia::media(config &c, const char *var, const char *type)
198147874Simp	: _var(var), _type(-1)
199147874Simp{
200147874Simp	static struct ifmedia_description media_types[] = {
201147874Simp		{ IFM_ETHER,		"Ethernet" },
202147874Simp		{ IFM_TOKEN,		"Tokenring" },
203147874Simp		{ IFM_FDDI,		"FDDI" },
204147874Simp		{ IFM_IEEE80211,	"802.11" },
205147874Simp		{ IFM_ATM,		"ATM" },
206147874Simp		{ IFM_CARP,		"CARP" },
207147874Simp		{ -1,			"unknown" },
208147874Simp		{ 0, NULL },
209147874Simp	};
210147874Simp	for (int i = 0; media_types[i].ifmt_string != NULL; i++)
211147874Simp		if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
212147874Simp			_type = media_types[i].ifmt_word;
213147874Simp			break;
214147874Simp		}
215147874Simp}
216147874Simp
217147874Simpmedia::~media()
218147874Simp{
219147874Simp}
220147874Simp
221147874Simpbool
222147874Simpmedia::do_match(config &c)
223147874Simp{
224147874Simp	string value = c.get_variable("device-name");
225147874Simp	struct ifmediareq ifmr;
226147874Simp	bool retval;
227147874Simp	int s;
228147874Simp
229147874Simp	if (Dflag)
230147874Simp		fprintf(stderr, "Testing media type of %s against 0x%x\n",
231147874Simp		    value.c_str(), _type);
232147874Simp
233147874Simp	retval = false;
234147874Simp
235147874Simp	s = socket(PF_INET, SOCK_DGRAM, 0);
236147874Simp	if (s >= 0) {
237147874Simp		memset(&ifmr, 0, sizeof(ifmr));
238147874Simp		strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
239147874Simp
240147874Simp		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
241147874Simp		    ifmr.ifm_status & IFM_AVALID) {
242147874Simp			if (Dflag)
243147874Simp				fprintf(stderr, "%s has media type 0x%x\n",
244147874Simp				    value.c_str(), IFM_TYPE(ifmr.ifm_active));
245147874Simp			retval = (IFM_TYPE(ifmr.ifm_active) == _type);
246147874Simp		} else if (_type == -1) {
247147874Simp			if (Dflag)
248147874Simp				fprintf(stderr, "%s has unknown media type\n",
249147874Simp				    value.c_str());
250147874Simp			retval = true;
251147874Simp		}
252147874Simp		close(s);
253147874Simp	}
254147874Simp
255147874Simp	return retval;
256147874Simp}
257147874Simp
258107665Simpconst string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
259107665Simpconst string var_list::nothing = "";
260107665Simp
261107665Simpconst string &
262107665Simpvar_list::get_variable(const string &var) const
263107665Simp{
264107665Simp	map<string, string>::const_iterator i;
265107665Simp
266107665Simp	i = _vars.find(var);
267107665Simp	if (i == _vars.end())
268108783Simp		return (var_list::bogus);
269107665Simp	return (i->second);
270107665Simp}
271107665Simp
272107665Simpbool
273107665Simpvar_list::is_set(const string &var) const
274107665Simp{
275107665Simp	return (_vars.find(var) != _vars.end());
276107665Simp}
277107665Simp
278107665Simpvoid
279107665Simpvar_list::set_variable(const string &var, const string &val)
280107665Simp{
281113790Simp	if (Dflag)
282145218Simp		fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
283107665Simp	_vars[var] = val;
284107665Simp}
285107665Simp
286107665Simpvoid
287107665Simpconfig::reset(void)
288107665Simp{
289107665Simp	_dir_list.clear();
290108783Simp	delete_and_clear(_var_list_table);
291108783Simp	delete_and_clear(_attach_list);
292108783Simp	delete_and_clear(_detach_list);
293108783Simp	delete_and_clear(_nomatch_list);
294121487Simp	delete_and_clear(_notify_list);
295107665Simp}
296107665Simp
297107665Simpvoid
298107665Simpconfig::parse_one_file(const char *fn)
299107665Simp{
300113790Simp	if (Dflag)
301107665Simp		printf("Parsing %s\n", fn);
302107665Simp	yyin = fopen(fn, "r");
303107665Simp	if (yyin == NULL)
304107665Simp		err(1, "Cannot open config file %s", fn);
305107665Simp	if (yyparse() != 0)
306107665Simp		errx(1, "Cannot parse %s at line %d", fn, lineno);
307107665Simp	fclose(yyin);
308107665Simp}
309107665Simp
310107665Simpvoid
311107665Simpconfig::parse_files_in_dir(const char *dirname)
312107665Simp{
313107665Simp	DIR *dirp;
314107665Simp	struct dirent *dp;
315107665Simp	char path[PATH_MAX];
316107665Simp
317113790Simp	if (Dflag)
318107665Simp		printf("Parsing files in %s\n", dirname);
319107665Simp	dirp = opendir(dirname);
320107665Simp	if (dirp == NULL)
321107665Simp		return;
322107665Simp	readdir(dirp);		/* Skip . */
323107665Simp	readdir(dirp);		/* Skip .. */
324107665Simp	while ((dp = readdir(dirp)) != NULL) {
325107665Simp		if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
326107665Simp			snprintf(path, sizeof(path), "%s/%s",
327107665Simp			    dirname, dp->d_name);
328107665Simp			parse_one_file(path);
329107665Simp		}
330107665Simp	}
331107665Simp}
332107665Simp
333108783Simpclass epv_greater {
334108783Simppublic:
335108783Simp	int operator()(event_proc *const&l1, event_proc *const&l2)
336108783Simp	{
337108783Simp		return (l1->get_priority() > l2->get_priority());
338108783Simp	}
339108783Simp};
340108783Simp
341107665Simpvoid
342108783Simpconfig::sort_vector(vector<event_proc *> &v)
343108783Simp{
344108783Simp	sort(v.begin(), v.end(), epv_greater());
345108783Simp}
346108783Simp
347108783Simpvoid
348107665Simpconfig::parse(void)
349107665Simp{
350107665Simp	vector<string>::const_iterator i;
351107665Simp
352107665Simp	parse_one_file(CF);
353107665Simp	for (i = _dir_list.begin(); i != _dir_list.end(); i++)
354107665Simp		parse_files_in_dir((*i).c_str());
355108783Simp	sort_vector(_attach_list);
356108783Simp	sort_vector(_detach_list);
357108783Simp	sort_vector(_nomatch_list);
358121487Simp	sort_vector(_notify_list);
359107665Simp}
360107665Simp
361107665Simpvoid
362107665Simpconfig::drop_pidfile()
363107665Simp{
364107665Simp	FILE *fp;
365107665Simp
366107665Simp	if (_pidfile == "")
367107665Simp		return;
368107665Simp	fp = fopen(_pidfile.c_str(), "w");
369107665Simp	if (fp == NULL)
370107665Simp		return;
371107665Simp	fprintf(fp, "%d\n", getpid());
372107665Simp	fclose(fp);
373107665Simp}
374107665Simp
375107665Simpvoid
376107665Simpconfig::add_attach(int prio, event_proc *p)
377107665Simp{
378107665Simp	p->set_priority(prio);
379107665Simp	_attach_list.push_back(p);
380107665Simp}
381107665Simp
382107665Simpvoid
383107665Simpconfig::add_detach(int prio, event_proc *p)
384107665Simp{
385107665Simp	p->set_priority(prio);
386107665Simp	_detach_list.push_back(p);
387107665Simp}
388107665Simp
389107665Simpvoid
390107665Simpconfig::add_directory(const char *dir)
391107665Simp{
392107665Simp	_dir_list.push_back(string(dir));
393107665Simp}
394107665Simp
395107665Simpvoid
396107665Simpconfig::add_nomatch(int prio, event_proc *p)
397107665Simp{
398107665Simp	p->set_priority(prio);
399107665Simp	_nomatch_list.push_back(p);
400107665Simp}
401107665Simp
402107665Simpvoid
403121487Simpconfig::add_notify(int prio, event_proc *p)
404121487Simp{
405121487Simp	p->set_priority(prio);
406121487Simp	_notify_list.push_back(p);
407121487Simp}
408121487Simp
409121487Simpvoid
410107665Simpconfig::set_pidfile(const char *fn)
411107665Simp{
412107665Simp	_pidfile = string(fn);
413107665Simp}
414107665Simp
415107665Simpvoid
416107665Simpconfig::push_var_table()
417107665Simp{
418107665Simp	var_list *vl;
419107665Simp
420107665Simp	vl = new var_list();
421107665Simp	_var_list_table.push_back(vl);
422113790Simp	if (Dflag)
423108783Simp		fprintf(stderr, "Pushing table\n");
424107665Simp}
425107665Simp
426107665Simpvoid
427107665Simpconfig::pop_var_table()
428107665Simp{
429107665Simp	delete _var_list_table.back();
430107665Simp	_var_list_table.pop_back();
431113790Simp	if (Dflag)
432108783Simp		fprintf(stderr, "Popping table\n");
433107665Simp}
434107665Simp
435107665Simpvoid
436107665Simpconfig::set_variable(const char *var, const char *val)
437107665Simp{
438107665Simp	_var_list_table.back()->set_variable(var, val);
439107665Simp}
440107665Simp
441107665Simpconst string &
442107665Simpconfig::get_variable(const string &var)
443107665Simp{
444107665Simp	vector<var_list *>::reverse_iterator i;
445107665Simp
446107665Simp	for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) {
447107665Simp		if ((*i)->is_set(var))
448108783Simp			return ((*i)->get_variable(var));
449107665Simp	}
450107665Simp	return (var_list::nothing);
451107665Simp}
452107665Simp
453108783Simpbool
454108783Simpconfig::is_id_char(char ch)
455108783Simp{
456108783Simp	return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
457108783Simp	    ch == '-'));
458108783Simp}
459108783Simp
460108014Simpvoid
461114081Simpconfig::expand_one(const char *&src, string &dst)
462107665Simp{
463108014Simp	int count;
464114081Simp	string buffer, varstr;
465108014Simp
466108783Simp	src++;
467108014Simp	// $$ -> $
468108014Simp	if (*src == '$') {
469114081Simp		dst.append(src++, 1);
470108014Simp		return;
471108014Simp	}
472108014Simp
473108014Simp	// $(foo) -> $(foo)
474108783Simp	// Not sure if I want to support this or not, so for now we just pass
475108783Simp	// it through.
476108014Simp	if (*src == '(') {
477114081Simp		dst.append("$");
478108014Simp		count = 1;
479114081Simp		/* If the string ends before ) is matched , return. */
480114081Simp		while (count > 0 && *src) {
481108014Simp			if (*src == ')')
482108014Simp				count--;
483108014Simp			else if (*src == '(')
484108014Simp				count++;
485114081Simp			dst.append(src++, 1);
486108014Simp		}
487108014Simp		return;
488108014Simp	}
489108014Simp
490108014Simp	// ${^A-Za-z] -> $\1
491108014Simp	if (!isalpha(*src)) {
492114081Simp		dst.append("$");
493114081Simp		dst.append(src++, 1);
494108014Simp		return;
495108014Simp	}
496108014Simp
497108014Simp	// $var -> replace with value
498114081Simp	do {
499114081Simp		buffer.append(src++, 1);
500114084Simp	} while (is_id_char(*src));
501114081Simp	buffer.append("", 1);
502114081Simp	varstr = get_variable(buffer.c_str());
503114081Simp	dst.append(varstr);
504107665Simp}
505107665Simp
506108014Simpconst string
507108014Simpconfig::expand_string(const string &s)
508108014Simp{
509108014Simp	const char *src;
510114081Simp	string dst;
511108014Simp
512108014Simp	src = s.c_str();
513108014Simp	while (*src) {
514108014Simp		if (*src == '$')
515114081Simp			expand_one(src, dst);
516108014Simp		else
517114081Simp			dst.append(src++, 1);
518108014Simp	}
519114081Simp	dst.append("", 1);
520108014Simp
521114081Simp	return (dst);
522108014Simp}
523108014Simp
524108783Simpbool
525108783Simpconfig::chop_var(char *&buffer, char *&lhs, char *&rhs)
526108783Simp{
527108783Simp	char *walker;
528108783Simp
529108783Simp	if (*buffer == '\0')
530108783Simp		return (false);
531108783Simp	walker = lhs = buffer;
532108783Simp	while (is_id_char(*walker))
533108783Simp		walker++;
534108783Simp	if (*walker != '=')
535108783Simp		return (false);
536108783Simp	walker++;		// skip =
537108783Simp	if (*walker == '"') {
538108783Simp		walker++;	// skip "
539108783Simp		rhs = walker;
540108783Simp		while (*walker && *walker != '"')
541108783Simp			walker++;
542108783Simp		if (*walker != '"')
543108783Simp			return (false);
544108783Simp		rhs[-2] = '\0';
545108783Simp		*walker++ = '\0';
546108783Simp	} else {
547108783Simp		rhs = walker;
548108783Simp		while (*walker && !isspace(*walker))
549108783Simp			walker++;
550108783Simp		if (*walker != '\0')
551108783Simp			*walker++ = '\0';
552108783Simp		rhs[-1] = '\0';
553108783Simp	}
554113785Simp	while (isspace(*walker))
555113785Simp		walker++;
556108783Simp	buffer = walker;
557108783Simp	return (true);
558108783Simp}
559108783Simp
560108783Simp
561108783Simpchar *
562108783Simpconfig::set_vars(char *buffer)
563108783Simp{
564108783Simp	char *lhs;
565108783Simp	char *rhs;
566108783Simp
567108783Simp	while (1) {
568108783Simp		if (!chop_var(buffer, lhs, rhs))
569108783Simp			break;
570108783Simp		set_variable(lhs, rhs);
571108783Simp	}
572108783Simp	return (buffer);
573108783Simp}
574108783Simp
575108783Simpvoid
576108783Simpconfig::find_and_execute(char type)
577108783Simp{
578108783Simp	vector<event_proc *> *l;
579108783Simp	vector<event_proc *>::const_iterator i;
580108783Simp	char *s;
581108783Simp
582108783Simp	switch (type) {
583108783Simp	default:
584108783Simp		return;
585121487Simp	case notify:
586121487Simp		l = &_notify_list;
587121487Simp		s = "notify";
588121487Simp		break;
589108783Simp	case nomatch:
590108783Simp		l = &_nomatch_list;
591108783Simp		s = "nomatch";
592108783Simp		break;
593108783Simp	case attach:
594108783Simp		l = &_attach_list;
595108783Simp		s = "attach";
596108783Simp		break;
597108783Simp	case detach:
598108783Simp		l = &_detach_list;
599108783Simp		s = "detach";
600108783Simp		break;
601108783Simp	}
602113790Simp	if (Dflag)
603108783Simp		fprintf(stderr, "Processing %s event\n", s);
604108783Simp	for (i = l->begin(); i != l->end(); i++) {
605108783Simp		if ((*i)->matches(*this)) {
606108783Simp			(*i)->run(*this);
607108783Simp			break;
608108783Simp		}
609108783Simp	}
610108783Simp
611108783Simp}
612108783Simp
613107665Simp
614107665Simpstatic void
615108783Simpprocess_event(char *buffer)
616107665Simp{
617107665Simp	char type;
618107665Simp	char *sp;
619107665Simp
620108783Simp	sp = buffer + 1;
621113790Simp	if (Dflag)
622108783Simp		fprintf(stderr, "Processing event '%s'\n", buffer);
623107665Simp	type = *buffer++;
624108783Simp	cfg.push_var_table();
625108783Simp	// No match doesn't have a device, and the format is a little
626108783Simp	// different, so handle it separately.
627121487Simp	switch (type) {
628121487Simp	case notify:
629121487Simp		sp = cfg.set_vars(sp);
630121487Simp		break;
631121487Simp	case nomatch:
632145218Simp		//? at location pnp-info on bus
633145218Simp		sp = strchr(sp, ' ');
634145218Simp		if (sp == NULL)
635145218Simp			return;	/* Can't happen? */
636145218Simp		*sp++ = '\0';
637121487Simp		if (strncmp(sp, "at ", 3) == 0)
638121487Simp			sp += 3;
639121487Simp		sp = cfg.set_vars(sp);
640121487Simp		if (strncmp(sp, "on ", 3) == 0)
641121487Simp			cfg.set_variable("bus", sp + 3);
642121487Simp		break;
643121487Simp	case attach:	/*FALLTHROUGH*/
644121487Simp	case detach:
645108783Simp		sp = strchr(sp, ' ');
646108783Simp		if (sp == NULL)
647108783Simp			return;	/* Can't happen? */
648108783Simp		*sp++ = '\0';
649108783Simp		cfg.set_variable("device-name", buffer);
650113785Simp		if (strncmp(sp, "at ", 3) == 0)
651113785Simp			sp += 3;
652113785Simp		sp = cfg.set_vars(sp);
653113785Simp		if (strncmp(sp, "on ", 3) == 0)
654113785Simp			cfg.set_variable("bus", sp + 3);
655121487Simp		break;
656108783Simp	}
657113785Simp
658108783Simp	cfg.find_and_execute(type);
659108783Simp	cfg.pop_var_table();
660107665Simp}
661107665Simp
662131397Simpint
663131397Simpcreate_socket(const char *name)
664131397Simp{
665131397Simp	int fd, slen;
666131397Simp	struct sockaddr_un sun;
667131397Simp
668131397Simp	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
669131397Simp		err(1, "socket");
670131397Simp	bzero(&sun, sizeof(sun));
671131397Simp	sun.sun_family = AF_UNIX;
672131397Simp	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
673131397Simp	slen = SUN_LEN(&sun);
674131397Simp	unlink(name);
675131397Simp	if (bind(fd, (struct sockaddr *) & sun, slen) < 0)
676131397Simp		err(1, "bind");
677131397Simp	listen(fd, 4);
678147972Smarcus	chown(name, 0, 0);	/* XXX - root.wheel */
679147972Smarcus	chmod(name, 0660);
680131397Simp	return (fd);
681131397Simp}
682131397Simp
683131397Simplist<int> clients;
684131397Simp
685131397Simpvoid
686131397Simpnotify_clients(const char *data, int len)
687131397Simp{
688131397Simp	list<int> bad;
689131397Simp	list<int>::const_iterator i;
690131397Simp
691131397Simp	for (i = clients.begin(); i != clients.end(); i++) {
692131397Simp		if (write(*i, data, len) <= 0) {
693131397Simp			bad.push_back(*i);
694131397Simp			close(*i);
695131397Simp		}
696131397Simp	}
697131397Simp
698131397Simp	for (i = bad.begin(); i != bad.end(); i++)
699131397Simp		clients.erase(find(clients.begin(), clients.end(), *i));
700131397Simp}
701131397Simp
702131397Simpvoid
703131397Simpnew_client(int fd)
704131397Simp{
705131397Simp	int s;
706131397Simp
707131397Simp	s = accept(fd, NULL, NULL);
708131397Simp	if (s != -1)
709131397Simp		clients.push_back(s);
710131397Simp}
711131397Simp
712107665Simpstatic void
713107665Simpevent_loop(void)
714107665Simp{
715107665Simp	int rv;
716107665Simp	int fd;
717107665Simp	char buffer[DEVCTL_MAXBUF];
718113790Simp	int once = 0;
719131397Simp	int server_fd, max_fd;
720113790Simp	timeval tv;
721113790Simp	fd_set fds;
722107665Simp
723107665Simp	fd = open(PATH_DEVCTL, O_RDONLY);
724107665Simp	if (fd == -1)
725131397Simp		err(1, "Can't open devctl device %s", PATH_DEVCTL);
726107665Simp	if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
727131397Simp		err(1, "Can't set close-on-exec flag on devctl");
728131397Simp	server_fd = create_socket(PIPE);
729131397Simp	max_fd = max(fd, server_fd) + 1;
730107665Simp	while (1) {
731107665Simp		if (romeo_must_die)
732107665Simp			break;
733113790Simp		if (!once && !dflag && !nflag) {
734113790Simp			// Check to see if we have any events pending.
735113790Simp			tv.tv_sec = 0;
736113790Simp			tv.tv_usec = 0;
737113790Simp			FD_ZERO(&fds);
738113790Simp			FD_SET(fd, &fds);
739113790Simp			rv = select(fd + 1, &fds, &fds, &fds, &tv);
740113790Simp			// No events -> we've processed all pending events
741117944Simp			if (rv == 0) {
742113790Simp				if (Dflag)
743113790Simp					fprintf(stderr, "Calling daemon\n");
744113790Simp				daemon(0, 0);
745117246Simp				cfg.drop_pidfile();
746113790Simp				once++;
747113790Simp			}
748113790Simp		}
749131397Simp		FD_ZERO(&fds);
750131397Simp		FD_SET(fd, &fds);
751131397Simp		FD_SET(server_fd, &fds);
752131397Simp		rv = select(max_fd, &fds, NULL, NULL, NULL);
753131397Simp		if (rv == -1) {
754131397Simp			if (errno == EINTR)
755131397Simp				continue;
756131397Simp			err(1, "select");
757131397Simp		}
758131397Simp		if (FD_ISSET(fd, &fds)) {
759131397Simp			rv = read(fd, buffer, sizeof(buffer) - 1);
760131397Simp			if (rv > 0) {
761131397Simp				notify_clients(buffer, rv);
762107665Simp				buffer[rv] = '\0';
763131397Simp				while (buffer[--rv] == '\n')
764131397Simp					buffer[rv] = '\0';
765131397Simp				process_event(buffer);
766131397Simp			} else if (rv < 0) {
767131397Simp				if (errno != EINTR)
768131397Simp					break;
769131397Simp			} else {
770131397Simp				/* EOF */
771107665Simp				break;
772131397Simp			}
773107665Simp		}
774131397Simp		if (FD_ISSET(server_fd, &fds))
775131397Simp			new_client(server_fd);
776107665Simp	}
777107665Simp	close(fd);
778107665Simp}
779107665Simp
780107665Simp/*
781107665Simp * functions that the parser uses.
782107665Simp */
783107665Simpvoid
784107665Simpadd_attach(int prio, event_proc *p)
785107665Simp{
786107665Simp	cfg.add_attach(prio, p);
787107665Simp}
788107665Simp
789107665Simpvoid
790107665Simpadd_detach(int prio, event_proc *p)
791107665Simp{
792107665Simp	cfg.add_detach(prio, p);
793107665Simp}
794107665Simp
795107665Simpvoid
796107665Simpadd_directory(const char *dir)
797107665Simp{
798107665Simp	cfg.add_directory(dir);
799107665Simp	free(const_cast<char *>(dir));
800107665Simp}
801107665Simp
802107665Simpvoid
803107665Simpadd_nomatch(int prio, event_proc *p)
804107665Simp{
805107665Simp	cfg.add_nomatch(prio, p);
806107665Simp}
807107665Simp
808121487Simpvoid
809121487Simpadd_notify(int prio, event_proc *p)
810121487Simp{
811121487Simp	cfg.add_notify(prio, p);
812121487Simp}
813121487Simp
814107665Simpevent_proc *
815107665Simpadd_to_event_proc(event_proc *ep, eps *eps)
816107665Simp{
817107665Simp	if (ep == NULL)
818107665Simp		ep = new event_proc();
819107665Simp	ep->add(eps);
820107665Simp	return (ep);
821107665Simp}
822107665Simp
823107665Simpeps *
824107665Simpnew_action(const char *cmd)
825107665Simp{
826107665Simp	eps *e = new action(cmd);
827107665Simp	free(const_cast<char *>(cmd));
828107665Simp	return (e);
829107665Simp}
830107665Simp
831107665Simpeps *
832107665Simpnew_match(const char *var, const char *re)
833107665Simp{
834108014Simp	eps *e = new match(cfg, var, re);
835107665Simp	free(const_cast<char *>(var));
836107665Simp	free(const_cast<char *>(re));
837107665Simp	return (e);
838107665Simp}
839107665Simp
840147874Simpeps *
841147874Simpnew_media(const char *var, const char *re)
842147874Simp{
843147874Simp	eps *e = new media(cfg, var, re);
844147874Simp	free(const_cast<char *>(var));
845147874Simp	free(const_cast<char *>(re));
846147874Simp	return (e);
847147874Simp}
848147874Simp
849107665Simpvoid
850107665Simpset_pidfile(const char *name)
851107665Simp{
852107665Simp	cfg.set_pidfile(name);
853107665Simp	free(const_cast<char *>(name));
854107665Simp}
855107665Simp
856107665Simpvoid
857107665Simpset_variable(const char *var, const char *val)
858107665Simp{
859107665Simp	cfg.set_variable(var, val);
860107665Simp	free(const_cast<char *>(var));
861107665Simp	free(const_cast<char *>(val));
862107665Simp}
863107665Simp
864107665Simp
865107665Simp
866107665Simpstatic void
867107665Simpgensighand(int)
868107665Simp{
869107665Simp	romeo_must_die++;
870107665Simp	_exit(0);
871107665Simp}
872107665Simp
873107665Simpstatic void
874107665Simpusage()
875107665Simp{
876141611Sru	fprintf(stderr, "usage: %s [-Ddn]\n", getprogname());
877107665Simp	exit(1);
878107665Simp}
879107665Simp
880113787Simpstatic void
881113787Simpcheck_devd_enabled()
882113787Simp{
883113787Simp	int val = 0;
884113787Simp	size_t len;
885113787Simp
886113787Simp	len = sizeof(val);
887114541Simp	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
888113787Simp		errx(1, "devctl sysctl missing from kernel!");
889113787Simp	if (val) {
890113787Simp		warnx("Setting " SYSCTL " to 0");
891113787Simp		val = 0;
892113787Simp		sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
893113787Simp	}
894113787Simp}
895113787Simp
896107665Simp/*
897107665Simp * main
898107665Simp */
899107665Simpint
900107665Simpmain(int argc, char **argv)
901107665Simp{
902107665Simp	int ch;
903107665Simp
904113787Simp	check_devd_enabled();
905113790Simp	while ((ch = getopt(argc, argv, "Ddn")) != -1) {
906107665Simp		switch (ch) {
907113790Simp		case 'D':
908113790Simp			Dflag++;
909113790Simp			break;
910107665Simp		case 'd':
911107665Simp			dflag++;
912107665Simp			break;
913113790Simp		case 'n':
914113790Simp			nflag++;
915113790Simp			break;
916107665Simp		default:
917107665Simp			usage();
918107665Simp		}
919107665Simp	}
920107665Simp
921107665Simp	cfg.parse();
922117246Simp	if (!dflag && nflag) {
923107665Simp		daemon(0, 0);
924117246Simp		cfg.drop_pidfile();
925117246Simp	}
926146306Simp	signal(SIGPIPE, SIG_IGN);
927107665Simp	signal(SIGHUP, gensighand);
928107665Simp	signal(SIGINT, gensighand);
929107665Simp	signal(SIGTERM, gensighand);
930107665Simp	event_loop();
931107665Simp	return (0);
932107665Simp}
933