xref: /freebsd/sbin/devd/devd.cc (revision 2a3e3873a1e4cd958f2b0f85d3b10cfa40575d30)
1 /*-
2  * Copyright (c) 2002-2010 M. Warner Losh.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * my_system is a variation on lib/libc/stdlib/system.c:
27  *
28  * Copyright (c) 1988, 1993
29  *	The Regents of the University of California.  All rights reserved.
30  *
31  * Redistribution and use in source and binary forms, with or without
32  * modification, are permitted provided that the following conditions
33  * are met:
34  * 1. Redistributions of source code must retain the above copyright
35  *    notice, this list of conditions and the following disclaimer.
36  * 2. Redistributions in binary form must reproduce the above copyright
37  *    notice, this list of conditions and the following disclaimer in the
38  *    documentation and/or other materials provided with the distribution.
39  * 4. Neither the name of the University nor the names of its contributors
40  *    may be used to endorse or promote products derived from this software
41  *    without specific prior written permission.
42  *
43  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
44  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
45  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
46  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
47  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
48  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
49  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
50  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
51  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
52  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
53  * SUCH DAMAGE.
54  */
55 
56 /*
57  * DEVD control daemon.
58  */
59 
60 // TODO list:
61 //	o devd.conf and devd man pages need a lot of help:
62 //	  - devd needs to document the unix domain socket
63 //	  - devd.conf needs more details on the supported statements.
64 
65 #include <sys/cdefs.h>
66 __FBSDID("$FreeBSD$");
67 
68 #include <sys/param.h>
69 #include <sys/socket.h>
70 #include <sys/stat.h>
71 #include <sys/sysctl.h>
72 #include <sys/types.h>
73 #include <sys/wait.h>
74 #include <sys/un.h>
75 
76 #include <cctype>
77 #include <cerrno>
78 #include <cstdlib>
79 #include <cstdio>
80 #include <csignal>
81 #include <cstring>
82 
83 #include <dirent.h>
84 #include <err.h>
85 #include <fcntl.h>
86 #include <libutil.h>
87 #include <paths.h>
88 #include <poll.h>
89 #include <regex.h>
90 #include <unistd.h>
91 
92 #include <algorithm>
93 #include <map>
94 #include <string>
95 #include <list>
96 #include <vector>
97 
98 #include "devd.h"		/* C compatible definitions */
99 #include "devd.hh"		/* C++ class definitions */
100 
101 #define PIPE "/var/run/devd.pipe"
102 #define CF "/etc/devd.conf"
103 #define SYSCTL "hw.bus.devctl_disable"
104 
105 using namespace std;
106 
107 extern FILE *yyin;
108 extern int lineno;
109 
110 static const char notify = '!';
111 static const char nomatch = '?';
112 static const char attach = '+';
113 static const char detach = '-';
114 
115 static struct pidfh *pfh;
116 
117 int Dflag;
118 int dflag;
119 int nflag;
120 static volatile sig_atomic_t romeo_must_die = 0;
121 
122 static const char *configfile = CF;
123 
124 static void event_loop(void);
125 static void usage(void);
126 
127 template <class T> void
128 delete_and_clear(vector<T *> &v)
129 {
130 	typename vector<T *>::const_iterator i;
131 
132 	for (i = v.begin(); i != v.end(); ++i)
133 		delete *i;
134 	v.clear();
135 }
136 
137 config cfg;
138 
139 event_proc::event_proc() : _prio(-1)
140 {
141 	_epsvec.reserve(4);
142 }
143 
144 event_proc::~event_proc()
145 {
146 	delete_and_clear(_epsvec);
147 }
148 
149 void
150 event_proc::add(eps *eps)
151 {
152 	_epsvec.push_back(eps);
153 }
154 
155 bool
156 event_proc::matches(config &c) const
157 {
158 	vector<eps *>::const_iterator i;
159 
160 	for (i = _epsvec.begin(); i != _epsvec.end(); ++i)
161 		if (!(*i)->do_match(c))
162 			return (false);
163 	return (true);
164 }
165 
166 bool
167 event_proc::run(config &c) const
168 {
169 	vector<eps *>::const_iterator i;
170 
171 	for (i = _epsvec.begin(); i != _epsvec.end(); ++i)
172 		if (!(*i)->do_action(c))
173 			return (false);
174 	return (true);
175 }
176 
177 action::action(const char *cmd)
178 	: _cmd(cmd)
179 {
180 	// nothing
181 }
182 
183 action::~action()
184 {
185 	// nothing
186 }
187 
188 static int
189 my_system(const char *command)
190 {
191 	pid_t pid, savedpid;
192 	int pstat;
193 	struct sigaction ign, intact, quitact;
194 	sigset_t newsigblock, oldsigblock;
195 
196 	if (!command)		/* just checking... */
197 		return(1);
198 
199 	/*
200 	 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save
201 	 * existing signal dispositions.
202 	 */
203 	ign.sa_handler = SIG_IGN;
204 	::sigemptyset(&ign.sa_mask);
205 	ign.sa_flags = 0;
206 	::sigaction(SIGINT, &ign, &intact);
207 	::sigaction(SIGQUIT, &ign, &quitact);
208 	::sigemptyset(&newsigblock);
209 	::sigaddset(&newsigblock, SIGCHLD);
210 	::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock);
211 	switch (pid = ::fork()) {
212 	case -1:			/* error */
213 		break;
214 	case 0:				/* child */
215 		/*
216 		 * Restore original signal dispositions and exec the command.
217 		 */
218 		::sigaction(SIGINT, &intact, NULL);
219 		::sigaction(SIGQUIT,  &quitact, NULL);
220 		::sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
221 		/*
222 		 * Close the PID file, and all other open descriptors.
223 		 * Inherit std{in,out,err} only.
224 		 */
225 		cfg.close_pidfile();
226 		::closefrom(3);
227 		::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL);
228 		::_exit(127);
229 	default:			/* parent */
230 		savedpid = pid;
231 		do {
232 			pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0);
233 		} while (pid == -1 && errno == EINTR);
234 		break;
235 	}
236 	::sigaction(SIGINT, &intact, NULL);
237 	::sigaction(SIGQUIT,  &quitact, NULL);
238 	::sigprocmask(SIG_SETMASK, &oldsigblock, NULL);
239 	return (pid == -1 ? -1 : pstat);
240 }
241 
242 bool
243 action::do_action(config &c)
244 {
245 	string s = c.expand_string(_cmd.c_str());
246 	if (Dflag)
247 		fprintf(stderr, "Executing '%s'\n", s.c_str());
248 	my_system(s.c_str());
249 	return (true);
250 }
251 
252 match::match(config &c, const char *var, const char *re) :
253 	_inv(re[0] == '!'),
254 	_var(var),
255 	_re(c.expand_string(_inv ? re + 1 : re, "^", "$"))
256 {
257 	regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
258 }
259 
260 match::~match()
261 {
262 	regfree(&_regex);
263 }
264 
265 bool
266 match::do_match(config &c)
267 {
268 	const string &value = c.get_variable(_var);
269 	bool retval;
270 
271 	if (Dflag)
272 		fprintf(stderr, "Testing %s=%s against %s, invert=%d\n",
273 		    _var.c_str(), value.c_str(), _re.c_str(), _inv);
274 
275 	retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
276 	if (_inv == 1)
277 		retval = (retval == 0) ? 1 : 0;
278 
279 	return retval;
280 }
281 
282 #include <sys/sockio.h>
283 #include <net/if.h>
284 #include <net/if_media.h>
285 
286 media::media(config &, const char *var, const char *type)
287 	: _var(var), _type(-1)
288 {
289 	static struct ifmedia_description media_types[] = {
290 		{ IFM_ETHER,		"Ethernet" },
291 		{ IFM_TOKEN,		"Tokenring" },
292 		{ IFM_FDDI,		"FDDI" },
293 		{ IFM_IEEE80211,	"802.11" },
294 		{ IFM_ATM,		"ATM" },
295 		{ -1,			"unknown" },
296 		{ 0, NULL },
297 	};
298 	for (int i = 0; media_types[i].ifmt_string != NULL; ++i)
299 		if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
300 			_type = media_types[i].ifmt_word;
301 			break;
302 		}
303 }
304 
305 media::~media()
306 {
307 }
308 
309 bool
310 media::do_match(config &c)
311 {
312 	string value;
313 	struct ifmediareq ifmr;
314 	bool retval;
315 	int s;
316 
317 	// Since we can be called from both a device attach/detach
318 	// context where device-name is defined and what we want,
319 	// as well as from a link status context, where subsystem is
320 	// the name of interest, first try device-name and fall back
321 	// to subsystem if none exists.
322 	value = c.get_variable("device-name");
323 	if (value.empty())
324 		value = c.get_variable("subsystem");
325 	if (Dflag)
326 		fprintf(stderr, "Testing media type of %s against 0x%x\n",
327 		    value.c_str(), _type);
328 
329 	retval = false;
330 
331 	s = socket(PF_INET, SOCK_DGRAM, 0);
332 	if (s >= 0) {
333 		memset(&ifmr, 0, sizeof(ifmr));
334 		strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
335 
336 		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
337 		    ifmr.ifm_status & IFM_AVALID) {
338 			if (Dflag)
339 				fprintf(stderr, "%s has media type 0x%x\n",
340 				    value.c_str(), IFM_TYPE(ifmr.ifm_active));
341 			retval = (IFM_TYPE(ifmr.ifm_active) == _type);
342 		} else if (_type == -1) {
343 			if (Dflag)
344 				fprintf(stderr, "%s has unknown media type\n",
345 				    value.c_str());
346 			retval = true;
347 		}
348 		close(s);
349 	}
350 
351 	return retval;
352 }
353 
354 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
355 const string var_list::nothing = "";
356 
357 const string &
358 var_list::get_variable(const string &var) const
359 {
360 	map<string, string>::const_iterator i;
361 
362 	i = _vars.find(var);
363 	if (i == _vars.end())
364 		return (var_list::bogus);
365 	return (i->second);
366 }
367 
368 bool
369 var_list::is_set(const string &var) const
370 {
371 	return (_vars.find(var) != _vars.end());
372 }
373 
374 void
375 var_list::set_variable(const string &var, const string &val)
376 {
377 	if (Dflag)
378 		fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
379 	_vars[var] = val;
380 }
381 
382 void
383 config::reset(void)
384 {
385 	_dir_list.clear();
386 	delete_and_clear(_var_list_table);
387 	delete_and_clear(_attach_list);
388 	delete_and_clear(_detach_list);
389 	delete_and_clear(_nomatch_list);
390 	delete_and_clear(_notify_list);
391 }
392 
393 void
394 config::parse_one_file(const char *fn)
395 {
396 	if (Dflag)
397 		fprintf(stderr, "Parsing %s\n", fn);
398 	yyin = fopen(fn, "r");
399 	if (yyin == NULL)
400 		err(1, "Cannot open config file %s", fn);
401 	lineno = 1;
402 	if (yyparse() != 0)
403 		errx(1, "Cannot parse %s at line %d", fn, lineno);
404 	fclose(yyin);
405 }
406 
407 void
408 config::parse_files_in_dir(const char *dirname)
409 {
410 	DIR *dirp;
411 	struct dirent *dp;
412 	char path[PATH_MAX];
413 
414 	if (Dflag)
415 		fprintf(stderr, "Parsing files in %s\n", dirname);
416 	dirp = opendir(dirname);
417 	if (dirp == NULL)
418 		return;
419 	readdir(dirp);		/* Skip . */
420 	readdir(dirp);		/* Skip .. */
421 	while ((dp = readdir(dirp)) != NULL) {
422 		if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
423 			snprintf(path, sizeof(path), "%s/%s",
424 			    dirname, dp->d_name);
425 			parse_one_file(path);
426 		}
427 	}
428 	closedir(dirp);
429 }
430 
431 class epv_greater {
432 public:
433 	int operator()(event_proc *const&l1, event_proc *const&l2) const
434 	{
435 		return (l1->get_priority() > l2->get_priority());
436 	}
437 };
438 
439 void
440 config::sort_vector(vector<event_proc *> &v)
441 {
442 	stable_sort(v.begin(), v.end(), epv_greater());
443 }
444 
445 void
446 config::parse(void)
447 {
448 	vector<string>::const_iterator i;
449 
450 	parse_one_file(configfile);
451 	for (i = _dir_list.begin(); i != _dir_list.end(); ++i)
452 		parse_files_in_dir((*i).c_str());
453 	sort_vector(_attach_list);
454 	sort_vector(_detach_list);
455 	sort_vector(_nomatch_list);
456 	sort_vector(_notify_list);
457 }
458 
459 void
460 config::open_pidfile()
461 {
462 	pid_t otherpid;
463 
464 	if (_pidfile.empty())
465 		return;
466 	pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid);
467 	if (pfh == NULL) {
468 		if (errno == EEXIST)
469 			errx(1, "devd already running, pid: %d", (int)otherpid);
470 		warn("cannot open pid file");
471 	}
472 }
473 
474 void
475 config::write_pidfile()
476 {
477 
478 	pidfile_write(pfh);
479 }
480 
481 void
482 config::close_pidfile()
483 {
484 
485 	pidfile_close(pfh);
486 }
487 
488 void
489 config::remove_pidfile()
490 {
491 
492 	pidfile_remove(pfh);
493 }
494 
495 void
496 config::add_attach(int prio, event_proc *p)
497 {
498 	p->set_priority(prio);
499 	_attach_list.push_back(p);
500 }
501 
502 void
503 config::add_detach(int prio, event_proc *p)
504 {
505 	p->set_priority(prio);
506 	_detach_list.push_back(p);
507 }
508 
509 void
510 config::add_directory(const char *dir)
511 {
512 	_dir_list.push_back(string(dir));
513 }
514 
515 void
516 config::add_nomatch(int prio, event_proc *p)
517 {
518 	p->set_priority(prio);
519 	_nomatch_list.push_back(p);
520 }
521 
522 void
523 config::add_notify(int prio, event_proc *p)
524 {
525 	p->set_priority(prio);
526 	_notify_list.push_back(p);
527 }
528 
529 void
530 config::set_pidfile(const char *fn)
531 {
532 	_pidfile = fn;
533 }
534 
535 void
536 config::push_var_table()
537 {
538 	var_list *vl;
539 
540 	vl = new var_list();
541 	_var_list_table.push_back(vl);
542 	if (Dflag)
543 		fprintf(stderr, "Pushing table\n");
544 }
545 
546 void
547 config::pop_var_table()
548 {
549 	delete _var_list_table.back();
550 	_var_list_table.pop_back();
551 	if (Dflag)
552 		fprintf(stderr, "Popping table\n");
553 }
554 
555 void
556 config::set_variable(const char *var, const char *val)
557 {
558 	_var_list_table.back()->set_variable(var, val);
559 }
560 
561 const string &
562 config::get_variable(const string &var)
563 {
564 	vector<var_list *>::reverse_iterator i;
565 
566 	for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) {
567 		if ((*i)->is_set(var))
568 			return ((*i)->get_variable(var));
569 	}
570 	return (var_list::nothing);
571 }
572 
573 bool
574 config::is_id_char(char ch) const
575 {
576 	return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
577 	    ch == '-'));
578 }
579 
580 void
581 config::expand_one(const char *&src, string &dst)
582 {
583 	int count;
584 	string buffer;
585 
586 	src++;
587 	// $$ -> $
588 	if (*src == '$') {
589 		dst += *src++;
590 		return;
591 	}
592 
593 	// $(foo) -> $(foo)
594 	// Not sure if I want to support this or not, so for now we just pass
595 	// it through.
596 	if (*src == '(') {
597 		dst += '$';
598 		count = 1;
599 		/* If the string ends before ) is matched , return. */
600 		while (count > 0 && *src) {
601 			if (*src == ')')
602 				count--;
603 			else if (*src == '(')
604 				count++;
605 			dst += *src++;
606 		}
607 		return;
608 	}
609 
610 	// $[^A-Za-z] -> $\1
611 	if (!isalpha(*src)) {
612 		dst += '$';
613 		dst += *src++;
614 		return;
615 	}
616 
617 	// $var -> replace with value
618 	do {
619 		buffer += *src++;
620 	} while (is_id_char(*src));
621 	dst.append(get_variable(buffer));
622 }
623 
624 const string
625 config::expand_string(const char *src, const char *prepend, const char *append)
626 {
627 	const char *var_at;
628 	string dst;
629 
630 	/*
631 	 * 128 bytes is enough for 2427 of 2438 expansions that happen
632 	 * while parsing config files, as tested on 2013-01-30.
633 	 */
634 	dst.reserve(128);
635 
636 	if (prepend != NULL)
637 		dst = prepend;
638 
639 	for (;;) {
640 		var_at = strchr(src, '$');
641 		if (var_at == NULL) {
642 			dst.append(src);
643 			break;
644 		}
645 		dst.append(src, var_at - src);
646 		src = var_at;
647 		expand_one(src, dst);
648 	}
649 
650 	if (append != NULL)
651 		dst.append(append);
652 
653 	return (dst);
654 }
655 
656 bool
657 config::chop_var(char *&buffer, char *&lhs, char *&rhs) const
658 {
659 	char *walker;
660 
661 	if (*buffer == '\0')
662 		return (false);
663 	walker = lhs = buffer;
664 	while (is_id_char(*walker))
665 		walker++;
666 	if (*walker != '=')
667 		return (false);
668 	walker++;		// skip =
669 	if (*walker == '"') {
670 		walker++;	// skip "
671 		rhs = walker;
672 		while (*walker && *walker != '"')
673 			walker++;
674 		if (*walker != '"')
675 			return (false);
676 		rhs[-2] = '\0';
677 		*walker++ = '\0';
678 	} else {
679 		rhs = walker;
680 		while (*walker && !isspace(*walker))
681 			walker++;
682 		if (*walker != '\0')
683 			*walker++ = '\0';
684 		rhs[-1] = '\0';
685 	}
686 	while (isspace(*walker))
687 		walker++;
688 	buffer = walker;
689 	return (true);
690 }
691 
692 
693 char *
694 config::set_vars(char *buffer)
695 {
696 	char *lhs;
697 	char *rhs;
698 
699 	while (1) {
700 		if (!chop_var(buffer, lhs, rhs))
701 			break;
702 		set_variable(lhs, rhs);
703 	}
704 	return (buffer);
705 }
706 
707 void
708 config::find_and_execute(char type)
709 {
710 	vector<event_proc *> *l;
711 	vector<event_proc *>::const_iterator i;
712 	const char *s;
713 
714 	switch (type) {
715 	default:
716 		return;
717 	case notify:
718 		l = &_notify_list;
719 		s = "notify";
720 		break;
721 	case nomatch:
722 		l = &_nomatch_list;
723 		s = "nomatch";
724 		break;
725 	case attach:
726 		l = &_attach_list;
727 		s = "attach";
728 		break;
729 	case detach:
730 		l = &_detach_list;
731 		s = "detach";
732 		break;
733 	}
734 	if (Dflag)
735 		fprintf(stderr, "Processing %s event\n", s);
736 	for (i = l->begin(); i != l->end(); ++i) {
737 		if ((*i)->matches(*this)) {
738 			(*i)->run(*this);
739 			break;
740 		}
741 	}
742 
743 }
744 
745 
746 static void
747 process_event(char *buffer)
748 {
749 	char type;
750 	char *sp;
751 
752 	sp = buffer + 1;
753 	if (Dflag)
754 		fprintf(stderr, "Processing event '%s'\n", buffer);
755 	type = *buffer++;
756 	cfg.push_var_table();
757 	// No match doesn't have a device, and the format is a little
758 	// different, so handle it separately.
759 	switch (type) {
760 	case notify:
761 		sp = cfg.set_vars(sp);
762 		break;
763 	case nomatch:
764 		//? at location pnp-info on bus
765 		sp = strchr(sp, ' ');
766 		if (sp == NULL)
767 			return;	/* Can't happen? */
768 		*sp++ = '\0';
769 		while (isspace(*sp))
770 			sp++;
771 		if (strncmp(sp, "at ", 3) == 0)
772 			sp += 3;
773 		sp = cfg.set_vars(sp);
774 		while (isspace(*sp))
775 			sp++;
776 		if (strncmp(sp, "on ", 3) == 0)
777 			cfg.set_variable("bus", sp + 3);
778 		break;
779 	case attach:	/*FALLTHROUGH*/
780 	case detach:
781 		sp = strchr(sp, ' ');
782 		if (sp == NULL)
783 			return;	/* Can't happen? */
784 		*sp++ = '\0';
785 		cfg.set_variable("device-name", buffer);
786 		while (isspace(*sp))
787 			sp++;
788 		if (strncmp(sp, "at ", 3) == 0)
789 			sp += 3;
790 		sp = cfg.set_vars(sp);
791 		while (isspace(*sp))
792 			sp++;
793 		if (strncmp(sp, "on ", 3) == 0)
794 			cfg.set_variable("bus", sp + 3);
795 		break;
796 	}
797 
798 	cfg.find_and_execute(type);
799 	cfg.pop_var_table();
800 }
801 
802 int
803 create_socket(const char *name)
804 {
805 	int fd, slen;
806 	struct sockaddr_un sun;
807 
808 	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
809 		err(1, "socket");
810 	bzero(&sun, sizeof(sun));
811 	sun.sun_family = AF_UNIX;
812 	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
813 	slen = SUN_LEN(&sun);
814 	unlink(name);
815 	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
816 	    	err(1, "fcntl");
817 	if (::bind(fd, (struct sockaddr *) & sun, slen) < 0)
818 		err(1, "bind");
819 	listen(fd, 4);
820 	chown(name, 0, 0);	/* XXX - root.wheel */
821 	chmod(name, 0666);
822 	return (fd);
823 }
824 
825 unsigned int max_clients = 10;	/* Default, can be overriden on cmdline. */
826 unsigned int num_clients;
827 list<int> clients;
828 
829 void
830 notify_clients(const char *data, int len)
831 {
832 	list<int>::iterator i;
833 
834 	/*
835 	 * Deliver the data to all clients.  Throw clients overboard at the
836 	 * first sign of trouble.  This reaps clients who've died or closed
837 	 * their sockets, and also clients who are alive but failing to keep up
838 	 * (or who are maliciously not reading, to consume buffer space in
839 	 * kernel memory or tie up the limited number of available connections).
840 	 */
841 	for (i = clients.begin(); i != clients.end(); ) {
842 		if (write(*i, data, len) != len) {
843 			--num_clients;
844 			close(*i);
845 			i = clients.erase(i);
846 		} else
847 			++i;
848 	}
849 }
850 
851 void
852 check_clients(void)
853 {
854 	int s;
855 	struct pollfd pfd;
856 	list<int>::iterator i;
857 
858 	/*
859 	 * Check all existing clients to see if any of them have disappeared.
860 	 * Normally we reap clients when we get an error trying to send them an
861 	 * event.  This check eliminates the problem of an ever-growing list of
862 	 * zombie clients because we're never writing to them on a system
863 	 * without frequent device-change activity.
864 	 */
865 	pfd.events = 0;
866 	for (i = clients.begin(); i != clients.end(); ) {
867 		pfd.fd = *i;
868 		s = poll(&pfd, 1, 0);
869 		if ((s < 0 && s != EINTR ) ||
870 		    (s > 0 && (pfd.revents & POLLHUP))) {
871 			--num_clients;
872 			close(*i);
873 			i = clients.erase(i);
874 		} else
875 			++i;
876 	}
877 }
878 
879 void
880 new_client(int fd)
881 {
882 	int s;
883 
884 	/*
885 	 * First go reap any zombie clients, then accept the connection, and
886 	 * shut down the read side to stop clients from consuming kernel memory
887 	 * by sending large buffers full of data we'll never read.
888 	 */
889 	check_clients();
890 	s = accept(fd, NULL, NULL);
891 	if (s != -1) {
892 		shutdown(s, SHUT_RD);
893 		clients.push_back(s);
894 		++num_clients;
895 	}
896 }
897 
898 static void
899 event_loop(void)
900 {
901 	int rv;
902 	int fd;
903 	char buffer[DEVCTL_MAXBUF];
904 	int once = 0;
905 	int server_fd, max_fd;
906 	int accepting;
907 	timeval tv;
908 	fd_set fds;
909 
910 	fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC);
911 	if (fd == -1)
912 		err(1, "Can't open devctl device %s", PATH_DEVCTL);
913 	server_fd = create_socket(PIPE);
914 	accepting = 1;
915 	max_fd = max(fd, server_fd) + 1;
916 	while (!romeo_must_die) {
917 		if (!once && !dflag && !nflag) {
918 			// Check to see if we have any events pending.
919 			tv.tv_sec = 0;
920 			tv.tv_usec = 0;
921 			FD_ZERO(&fds);
922 			FD_SET(fd, &fds);
923 			rv = select(fd + 1, &fds, &fds, &fds, &tv);
924 			// No events -> we've processed all pending events
925 			if (rv == 0) {
926 				if (Dflag)
927 					fprintf(stderr, "Calling daemon\n");
928 				cfg.remove_pidfile();
929 				cfg.open_pidfile();
930 				daemon(0, 0);
931 				cfg.write_pidfile();
932 				once++;
933 			}
934 		}
935 		/*
936 		 * When we've already got the max number of clients, stop
937 		 * accepting new connections (don't put server_fd in the set),
938 		 * shrink the accept() queue to reject connections quickly, and
939 		 * poll the existing clients more often, so that we notice more
940 		 * quickly when any of them disappear to free up client slots.
941 		 */
942 		FD_ZERO(&fds);
943 		FD_SET(fd, &fds);
944 		if (num_clients < max_clients) {
945 			if (!accepting) {
946 				listen(server_fd, max_clients);
947 				accepting = 1;
948 			}
949 			FD_SET(server_fd, &fds);
950 			tv.tv_sec = 60;
951 			tv.tv_usec = 0;
952 		} else {
953 			if (accepting) {
954 				listen(server_fd, 0);
955 				accepting = 0;
956 			}
957 			tv.tv_sec = 2;
958 			tv.tv_usec = 0;
959 		}
960 		rv = select(max_fd, &fds, NULL, NULL, &tv);
961 		if (rv == -1) {
962 			if (errno == EINTR)
963 				continue;
964 			err(1, "select");
965 		} else if (rv == 0)
966 			check_clients();
967 		if (FD_ISSET(fd, &fds)) {
968 			rv = read(fd, buffer, sizeof(buffer) - 1);
969 			if (rv > 0) {
970 				notify_clients(buffer, rv);
971 				buffer[rv] = '\0';
972 				while (buffer[--rv] == '\n')
973 					buffer[rv] = '\0';
974 				process_event(buffer);
975 			} else if (rv < 0) {
976 				if (errno != EINTR)
977 					break;
978 			} else {
979 				/* EOF */
980 				break;
981 			}
982 		}
983 		if (FD_ISSET(server_fd, &fds))
984 			new_client(server_fd);
985 	}
986 	close(fd);
987 }
988 
989 /*
990  * functions that the parser uses.
991  */
992 void
993 add_attach(int prio, event_proc *p)
994 {
995 	cfg.add_attach(prio, p);
996 }
997 
998 void
999 add_detach(int prio, event_proc *p)
1000 {
1001 	cfg.add_detach(prio, p);
1002 }
1003 
1004 void
1005 add_directory(const char *dir)
1006 {
1007 	cfg.add_directory(dir);
1008 	free(const_cast<char *>(dir));
1009 }
1010 
1011 void
1012 add_nomatch(int prio, event_proc *p)
1013 {
1014 	cfg.add_nomatch(prio, p);
1015 }
1016 
1017 void
1018 add_notify(int prio, event_proc *p)
1019 {
1020 	cfg.add_notify(prio, p);
1021 }
1022 
1023 event_proc *
1024 add_to_event_proc(event_proc *ep, eps *eps)
1025 {
1026 	if (ep == NULL)
1027 		ep = new event_proc();
1028 	ep->add(eps);
1029 	return (ep);
1030 }
1031 
1032 eps *
1033 new_action(const char *cmd)
1034 {
1035 	eps *e = new action(cmd);
1036 	free(const_cast<char *>(cmd));
1037 	return (e);
1038 }
1039 
1040 eps *
1041 new_match(const char *var, const char *re)
1042 {
1043 	eps *e = new match(cfg, var, re);
1044 	free(const_cast<char *>(var));
1045 	free(const_cast<char *>(re));
1046 	return (e);
1047 }
1048 
1049 eps *
1050 new_media(const char *var, const char *re)
1051 {
1052 	eps *e = new media(cfg, var, re);
1053 	free(const_cast<char *>(var));
1054 	free(const_cast<char *>(re));
1055 	return (e);
1056 }
1057 
1058 void
1059 set_pidfile(const char *name)
1060 {
1061 	cfg.set_pidfile(name);
1062 	free(const_cast<char *>(name));
1063 }
1064 
1065 void
1066 set_variable(const char *var, const char *val)
1067 {
1068 	cfg.set_variable(var, val);
1069 	free(const_cast<char *>(var));
1070 	free(const_cast<char *>(val));
1071 }
1072 
1073 
1074 
1075 static void
1076 gensighand(int)
1077 {
1078 	romeo_must_die = 1;
1079 }
1080 
1081 static void
1082 usage()
1083 {
1084 	fprintf(stderr, "usage: %s [-Ddn] [-l connlimit] [-f file]\n",
1085 	    getprogname());
1086 	exit(1);
1087 }
1088 
1089 static void
1090 check_devd_enabled()
1091 {
1092 	int val = 0;
1093 	size_t len;
1094 
1095 	len = sizeof(val);
1096 	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
1097 		errx(1, "devctl sysctl missing from kernel!");
1098 	if (val) {
1099 		warnx("Setting " SYSCTL " to 0");
1100 		val = 0;
1101 		sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
1102 	}
1103 }
1104 
1105 /*
1106  * main
1107  */
1108 int
1109 main(int argc, char **argv)
1110 {
1111 	int ch;
1112 
1113 	check_devd_enabled();
1114 	while ((ch = getopt(argc, argv, "Ddf:l:n")) != -1) {
1115 		switch (ch) {
1116 		case 'D':
1117 			Dflag++;
1118 			break;
1119 		case 'd':
1120 			dflag++;
1121 			break;
1122 		case 'f':
1123 			configfile = optarg;
1124 			break;
1125 		case 'l':
1126 			max_clients = MAX(1, strtoul(optarg, NULL, 0));
1127 			break;
1128 		case 'n':
1129 			nflag++;
1130 			break;
1131 		default:
1132 			usage();
1133 		}
1134 	}
1135 
1136 	cfg.parse();
1137 	if (!dflag && nflag) {
1138 		cfg.open_pidfile();
1139 		daemon(0, 0);
1140 		cfg.write_pidfile();
1141 	}
1142 	signal(SIGPIPE, SIG_IGN);
1143 	signal(SIGHUP, gensighand);
1144 	signal(SIGINT, gensighand);
1145 	signal(SIGTERM, gensighand);
1146 	event_loop();
1147 	return (0);
1148 }
1149