xref: /dragonfly/sbin/devd/devd.cc (revision fcf53d9b)
1 /*-
2  * Copyright (c) 2002-2003 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  * $FreeBSD: src/sbin/devd/devd.cc,v 1.33 2006/09/17 22:49:26 ru Exp $
27  * $DragonFly: src/sbin/devd/devd.cc,v 1.1 2008/10/03 00:26:21 hasso Exp $
28  */
29 
30 /*
31  * DEVD control daemon.
32  */
33 
34 // TODO list:
35 //	o devd.conf and devd man pages need a lot of help:
36 //	  - devd needs to document the unix domain socket
37 //	  - devd.conf needs more details on the supported statements.
38 
39 #include <sys/param.h>
40 #include <sys/socket.h>
41 #include <sys/stat.h>
42 #include <sys/sysctl.h>
43 #include <sys/types.h>
44 #include <sys/un.h>
45 
46 #include <ctype.h>
47 #include <dirent.h>
48 #include <errno.h>
49 #include <err.h>
50 #include <fcntl.h>
51 #include <libutil.h>
52 #include <regex.h>
53 #include <signal.h>
54 #include <stdlib.h>
55 #include <stdio.h>
56 #include <string.h>
57 #include <unistd.h>
58 
59 #include <algorithm>
60 #include <map>
61 #include <string>
62 #include <list>
63 #include <vector>
64 
65 #include "devd.h"		/* C compatible definitions */
66 #include "devd.hh"		/* C++ class definitions */
67 
68 #define PIPE "/var/run/devd.pipe"
69 #define CF "/etc/devd.conf"
70 #define SYSCTL "hw.bus.devctl_disable"
71 
72 using namespace std;
73 
74 extern FILE *yyin;
75 extern int lineno;
76 
77 static const char notify = '!';
78 static const char nomatch = '?';
79 static const char attach = '+';
80 static const char detach = '-';
81 
82 int Dflag;
83 int dflag;
84 int nflag;
85 int romeo_must_die = 0;
86 
87 static const char *configfile = CF;
88 
89 static void event_loop(void);
90 static void usage(void);
91 
92 template <class T> void
93 delete_and_clear(vector<T *> &v)
94 {
95 	typename vector<T *>::const_iterator i;
96 
97 	for (i = v.begin(); i != v.end(); i++)
98 		delete *i;
99 	v.clear();
100 }
101 
102 config cfg;
103 
104 event_proc::event_proc() : _prio(-1)
105 {
106 	// nothing
107 }
108 
109 event_proc::~event_proc()
110 {
111 	delete_and_clear(_epsvec);
112 }
113 
114 void
115 event_proc::add(eps *eps)
116 {
117 	_epsvec.push_back(eps);
118 }
119 
120 bool
121 event_proc::matches(config &c)
122 {
123 	vector<eps *>::const_iterator i;
124 
125 	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
126 		if (!(*i)->do_match(c))
127 			return (false);
128 	return (true);
129 }
130 
131 bool
132 event_proc::run(config &c)
133 {
134 	vector<eps *>::const_iterator i;
135 
136 	for (i = _epsvec.begin(); i != _epsvec.end(); i++)
137 		if (!(*i)->do_action(c))
138 			return (false);
139 	return (true);
140 }
141 
142 action::action(const char *cmd)
143 	: _cmd(cmd)
144 {
145 	// nothing
146 }
147 
148 action::~action()
149 {
150 	// nothing
151 }
152 
153 bool
154 action::do_action(config &c)
155 {
156 	string s = c.expand_string(_cmd);
157 	if (Dflag)
158 		fprintf(stderr, "Executing '%s'\n", s.c_str());
159 	::system(s.c_str());
160 	return (true);
161 }
162 
163 match::match(config &c, const char *var, const char *re)
164 	: _var(var)
165 {
166 	string pattern = re;
167 	_re = "^";
168 	_re.append(c.expand_string(string(re)));
169 	_re.append("$");
170 	regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE);
171 }
172 
173 match::~match()
174 {
175 	regfree(&_regex);
176 }
177 
178 bool
179 match::do_match(config &c)
180 {
181 	string value = c.get_variable(_var);
182 	bool retval;
183 
184 	if (Dflag)
185 		fprintf(stderr, "Testing %s=%s against %s\n", _var.c_str(),
186 		    value.c_str(), _re.c_str());
187 
188 	retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0);
189 	return retval;
190 }
191 
192 #include <sys/sockio.h>
193 #include <net/if.h>
194 #include <net/if_media.h>
195 
196 media::media(config &, const char *var, const char *type)
197 	: _var(var), _type(-1)
198 {
199 	static struct ifmedia_description media_types[] = {
200 		{ IFM_ETHER,		"Ethernet" },
201 		{ IFM_IEEE80211,	"802.11" },
202 		{ IFM_ATM,		"ATM" },
203 		{ IFM_CARP,		"CARP" },
204 		{ -1,			"unknown" },
205 		{ 0, NULL },
206 	};
207 	for (int i = 0; media_types[i].ifmt_string != NULL; i++)
208 		if (strcasecmp(type, media_types[i].ifmt_string) == 0) {
209 			_type = media_types[i].ifmt_word;
210 			break;
211 		}
212 }
213 
214 media::~media()
215 {
216 }
217 
218 bool
219 media::do_match(config &c)
220 {
221 	string value;
222 	struct ifmediareq ifmr;
223 	bool retval;
224 	int s;
225 
226 	// Since we can be called from both a device attach/detach
227 	// context where device-name is defined and what we want,
228 	// as well as from a link status context, where subsystem is
229 	// the name of interest, first try device-name and fall back
230 	// to subsystem if none exists.
231 	value = c.get_variable("device-name");
232 	if (value.length() == 0)
233 		value = c.get_variable("subsystem");
234 	if (Dflag)
235 		fprintf(stderr, "Testing media type of %s against 0x%x\n",
236 		    value.c_str(), _type);
237 
238 	retval = false;
239 
240 	s = socket(PF_INET, SOCK_DGRAM, 0);
241 	if (s >= 0) {
242 		memset(&ifmr, 0, sizeof(ifmr));
243 		strncpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name));
244 
245 		if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 &&
246 		    ifmr.ifm_status & IFM_AVALID) {
247 			if (Dflag)
248 				fprintf(stderr, "%s has media type 0x%x\n",
249 				    value.c_str(), IFM_TYPE(ifmr.ifm_active));
250 			retval = (IFM_TYPE(ifmr.ifm_active) == _type);
251 		} else if (_type == -1) {
252 			if (Dflag)
253 				fprintf(stderr, "%s has unknown media type\n",
254 				    value.c_str());
255 			retval = true;
256 		}
257 		close(s);
258 	}
259 
260 	return retval;
261 }
262 
263 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_";
264 const string var_list::nothing = "";
265 
266 const string &
267 var_list::get_variable(const string &var) const
268 {
269 	map<string, string>::const_iterator i;
270 
271 	i = _vars.find(var);
272 	if (i == _vars.end())
273 		return (var_list::bogus);
274 	return (i->second);
275 }
276 
277 bool
278 var_list::is_set(const string &var) const
279 {
280 	return (_vars.find(var) != _vars.end());
281 }
282 
283 void
284 var_list::set_variable(const string &var, const string &val)
285 {
286 	if (Dflag)
287 		fprintf(stderr, "setting %s=%s\n", var.c_str(), val.c_str());
288 	_vars[var] = val;
289 }
290 
291 void
292 config::reset(void)
293 {
294 	_dir_list.clear();
295 	delete_and_clear(_var_list_table);
296 	delete_and_clear(_attach_list);
297 	delete_and_clear(_detach_list);
298 	delete_and_clear(_nomatch_list);
299 	delete_and_clear(_notify_list);
300 }
301 
302 void
303 config::parse_one_file(const char *fn)
304 {
305 	if (Dflag)
306 		printf("Parsing %s\n", fn);
307 	yyin = fopen(fn, "r");
308 	if (yyin == NULL)
309 		err(1, "Cannot open config file %s", fn);
310 	lineno = 1;
311 	if (yyparse() != 0)
312 		errx(1, "Cannot parse %s at line %d", fn, lineno);
313 	fclose(yyin);
314 }
315 
316 void
317 config::parse_files_in_dir(const char *dirname)
318 {
319 	DIR *dirp;
320 	struct dirent *dp;
321 	char path[PATH_MAX];
322 
323 	if (Dflag)
324 		printf("Parsing files in %s\n", dirname);
325 	dirp = opendir(dirname);
326 	if (dirp == NULL)
327 		return;
328 	readdir(dirp);		/* Skip . */
329 	readdir(dirp);		/* Skip .. */
330 	while ((dp = readdir(dirp)) != NULL) {
331 		if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) {
332 			snprintf(path, sizeof(path), "%s/%s",
333 			    dirname, dp->d_name);
334 			parse_one_file(path);
335 		}
336 	}
337 }
338 
339 class epv_greater {
340 public:
341 	int operator()(event_proc *const&l1, event_proc *const&l2)
342 	{
343 		return (l1->get_priority() > l2->get_priority());
344 	}
345 };
346 
347 void
348 config::sort_vector(vector<event_proc *> &v)
349 {
350 	sort(v.begin(), v.end(), epv_greater());
351 }
352 
353 void
354 config::parse(void)
355 {
356 	vector<string>::const_iterator i;
357 
358 	parse_one_file(configfile);
359 	for (i = _dir_list.begin(); i != _dir_list.end(); i++)
360 		parse_files_in_dir((*i).c_str());
361 	sort_vector(_attach_list);
362 	sort_vector(_detach_list);
363 	sort_vector(_nomatch_list);
364 	sort_vector(_notify_list);
365 }
366 
367 void
368 config::open_pidfile()
369 {
370 	if (pidfile(NULL))
371 		errx(1, "devd already running");
372 }
373 
374 void
375 config::add_attach(int prio, event_proc *p)
376 {
377 	p->set_priority(prio);
378 	_attach_list.push_back(p);
379 }
380 
381 void
382 config::add_detach(int prio, event_proc *p)
383 {
384 	p->set_priority(prio);
385 	_detach_list.push_back(p);
386 }
387 
388 void
389 config::add_directory(const char *dir)
390 {
391 	_dir_list.push_back(string(dir));
392 }
393 
394 void
395 config::add_nomatch(int prio, event_proc *p)
396 {
397 	p->set_priority(prio);
398 	_nomatch_list.push_back(p);
399 }
400 
401 void
402 config::add_notify(int prio, event_proc *p)
403 {
404 	p->set_priority(prio);
405 	_notify_list.push_back(p);
406 }
407 
408 void
409 config::set_pidfile(const char *fn)
410 {
411 	_pidfile = string(fn);
412 }
413 
414 void
415 config::push_var_table()
416 {
417 	var_list *vl;
418 
419 	vl = new var_list();
420 	_var_list_table.push_back(vl);
421 	if (Dflag)
422 		fprintf(stderr, "Pushing table\n");
423 }
424 
425 void
426 config::pop_var_table()
427 {
428 	delete _var_list_table.back();
429 	_var_list_table.pop_back();
430 	if (Dflag)
431 		fprintf(stderr, "Popping table\n");
432 }
433 
434 void
435 config::set_variable(const char *var, const char *val)
436 {
437 	_var_list_table.back()->set_variable(var, val);
438 }
439 
440 const string &
441 config::get_variable(const string &var)
442 {
443 	vector<var_list *>::reverse_iterator i;
444 
445 	for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); i++) {
446 		if ((*i)->is_set(var))
447 			return ((*i)->get_variable(var));
448 	}
449 	return (var_list::nothing);
450 }
451 
452 bool
453 config::is_id_char(char ch)
454 {
455 	return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' ||
456 	    ch == '-'));
457 }
458 
459 void
460 config::expand_one(const char *&src, string &dst)
461 {
462 	int count;
463 	string buffer, varstr;
464 
465 	src++;
466 	// $$ -> $
467 	if (*src == '$') {
468 		dst.append(src++, 1);
469 		return;
470 	}
471 
472 	// $(foo) -> $(foo)
473 	// Not sure if I want to support this or not, so for now we just pass
474 	// it through.
475 	if (*src == '(') {
476 		dst.append("$");
477 		count = 1;
478 		/* If the string ends before ) is matched , return. */
479 		while (count > 0 && *src) {
480 			if (*src == ')')
481 				count--;
482 			else if (*src == '(')
483 				count++;
484 			dst.append(src++, 1);
485 		}
486 		return;
487 	}
488 
489 	// ${^A-Za-z] -> $\1
490 	if (!isalpha(*src)) {
491 		dst.append("$");
492 		dst.append(src++, 1);
493 		return;
494 	}
495 
496 	// $var -> replace with value
497 	do {
498 		buffer.append(src++, 1);
499 	} while (is_id_char(*src));
500 	buffer.append("", 1);
501 	varstr = get_variable(buffer.c_str());
502 	dst.append(varstr);
503 }
504 
505 const string
506 config::expand_string(const string &s)
507 {
508 	const char *src;
509 	string dst;
510 
511 	src = s.c_str();
512 	while (*src) {
513 		if (*src == '$')
514 			expand_one(src, dst);
515 		else
516 			dst.append(src++, 1);
517 	}
518 	dst.append("", 1);
519 
520 	return (dst);
521 }
522 
523 bool
524 config::chop_var(char *&buffer, char *&lhs, char *&rhs)
525 {
526 	char *walker;
527 
528 	if (*buffer == '\0')
529 		return (false);
530 	walker = lhs = buffer;
531 	while (is_id_char(*walker))
532 		walker++;
533 	if (*walker != '=')
534 		return (false);
535 	walker++;		// skip =
536 	if (*walker == '"') {
537 		walker++;	// skip "
538 		rhs = walker;
539 		while (*walker && *walker != '"')
540 			walker++;
541 		if (*walker != '"')
542 			return (false);
543 		rhs[-2] = '\0';
544 		*walker++ = '\0';
545 	} else {
546 		rhs = walker;
547 		while (*walker && !isspace(*walker))
548 			walker++;
549 		if (*walker != '\0')
550 			*walker++ = '\0';
551 		rhs[-1] = '\0';
552 	}
553 	while (isspace(*walker))
554 		walker++;
555 	buffer = walker;
556 	return (true);
557 }
558 
559 
560 char *
561 config::set_vars(char *buffer)
562 {
563 	char *lhs;
564 	char *rhs;
565 
566 	while (1) {
567 		if (!chop_var(buffer, lhs, rhs))
568 			break;
569 		set_variable(lhs, rhs);
570 	}
571 	return (buffer);
572 }
573 
574 void
575 config::find_and_execute(char type)
576 {
577 	vector<event_proc *> *l;
578 	vector<event_proc *>::const_iterator i;
579 	const char *s;
580 
581 	switch (type) {
582 	default:
583 		return;
584 	case notify:
585 		l = &_notify_list;
586 		s = "notify";
587 		break;
588 	case nomatch:
589 		l = &_nomatch_list;
590 		s = "nomatch";
591 		break;
592 	case attach:
593 		l = &_attach_list;
594 		s = "attach";
595 		break;
596 	case detach:
597 		l = &_detach_list;
598 		s = "detach";
599 		break;
600 	}
601 	if (Dflag)
602 		fprintf(stderr, "Processing %s event\n", s);
603 	for (i = l->begin(); i != l->end(); i++) {
604 		if ((*i)->matches(*this)) {
605 			(*i)->run(*this);
606 			break;
607 		}
608 	}
609 
610 }
611 
612 
613 static void
614 process_event(char *buffer)
615 {
616 	char type;
617 	char *sp;
618 
619 	sp = buffer + 1;
620 	if (Dflag)
621 		fprintf(stderr, "Processing event '%s'\n", buffer);
622 	type = *buffer++;
623 	cfg.push_var_table();
624 	// No match doesn't have a device, and the format is a little
625 	// different, so handle it separately.
626 	switch (type) {
627 	case notify:
628 		sp = cfg.set_vars(sp);
629 		break;
630 	case nomatch:
631 		//? at location pnp-info on bus
632 		sp = strchr(sp, ' ');
633 		if (sp == NULL)
634 			return;	/* Can't happen? */
635 		*sp++ = '\0';
636 		if (strncmp(sp, "at ", 3) == 0)
637 			sp += 3;
638 		sp = cfg.set_vars(sp);
639 		if (strncmp(sp, "on ", 3) == 0)
640 			cfg.set_variable("bus", sp + 3);
641 		break;
642 	case attach:	/*FALLTHROUGH*/
643 	case detach:
644 		sp = strchr(sp, ' ');
645 		if (sp == NULL)
646 			return;	/* Can't happen? */
647 		*sp++ = '\0';
648 		cfg.set_variable("device-name", buffer);
649 		if (strncmp(sp, "at ", 3) == 0)
650 			sp += 3;
651 		sp = cfg.set_vars(sp);
652 		if (strncmp(sp, "on ", 3) == 0)
653 			cfg.set_variable("bus", sp + 3);
654 		break;
655 	}
656 
657 	cfg.find_and_execute(type);
658 	cfg.pop_var_table();
659 }
660 
661 int
662 create_socket(const char *name)
663 {
664 	int fd, slen;
665 	struct sockaddr_un sun;
666 
667 	if ((fd = socket(PF_LOCAL, SOCK_STREAM, 0)) < 0)
668 		err(1, "socket");
669 	bzero(&sun, sizeof(sun));
670 	sun.sun_family = AF_UNIX;
671 	strlcpy(sun.sun_path, name, sizeof(sun.sun_path));
672 	slen = SUN_LEN(&sun);
673 	unlink(name);
674 	if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0)
675 	    	err(1, "fcntl");
676 	if (bind(fd, (struct sockaddr *) & sun, slen) < 0)
677 		err(1, "bind");
678 	listen(fd, 4);
679 	chown(name, 0, 0);	/* XXX - root.wheel */
680 	chmod(name, 0666);
681 	return (fd);
682 }
683 
684 list<int> clients;
685 
686 void
687 notify_clients(const char *data, int len)
688 {
689 	list<int> bad;
690 	list<int>::const_iterator i;
691 
692 	for (i = clients.begin(); i != clients.end(); i++) {
693 		if (write(*i, data, len) <= 0) {
694 			bad.push_back(*i);
695 			close(*i);
696 		}
697 	}
698 
699 	for (i = bad.begin(); i != bad.end(); i++)
700 		clients.erase(find(clients.begin(), clients.end(), *i));
701 }
702 
703 void
704 new_client(int fd)
705 {
706 	int s;
707 
708 	s = accept(fd, NULL, NULL);
709 	if (s != -1)
710 		clients.push_back(s);
711 }
712 
713 static void
714 event_loop(void)
715 {
716 	int rv;
717 	int fd;
718 	char buffer[DEVCTL_MAXBUF];
719 	int once = 0;
720 	int server_fd, max_fd;
721 	timeval tv;
722 	fd_set fds;
723 
724 	fd = open(PATH_DEVCTL, O_RDONLY);
725 	if (fd == -1)
726 		err(1, "Can't open devctl device %s", PATH_DEVCTL);
727 	if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0)
728 		err(1, "Can't set close-on-exec flag on devctl");
729 	server_fd = create_socket(PIPE);
730 	max_fd = max(fd, server_fd) + 1;
731 	while (1) {
732 		if (romeo_must_die)
733 			break;
734 		if (!once && !dflag && !nflag) {
735 			// Check to see if we have any events pending.
736 			tv.tv_sec = 0;
737 			tv.tv_usec = 0;
738 			FD_ZERO(&fds);
739 			FD_SET(fd, &fds);
740 			rv = select(fd + 1, &fds, NULL, NULL, &tv);
741 			// No events -> we've processed all pending events
742 			if (rv == 0) {
743 				if (Dflag)
744 					fprintf(stderr, "Calling daemon\n");
745 				daemon(0, 0);
746 				cfg.open_pidfile();
747 				once++;
748 			}
749 		}
750 		FD_ZERO(&fds);
751 		FD_SET(fd, &fds);
752 		FD_SET(server_fd, &fds);
753 		rv = select(max_fd, &fds, NULL, NULL, NULL);
754 		if (rv == -1) {
755 			if (errno == EINTR)
756 				continue;
757 			err(1, "select");
758 		}
759 		if (FD_ISSET(fd, &fds)) {
760 			rv = read(fd, buffer, sizeof(buffer) - 1);
761 			if (rv > 0) {
762 				notify_clients(buffer, rv);
763 				buffer[rv] = '\0';
764 				while (buffer[--rv] == '\n')
765 					buffer[rv] = '\0';
766 				process_event(buffer);
767 			} else if (rv < 0) {
768 				if (errno != EINTR)
769 					break;
770 			} else {
771 				/* EOF */
772 				break;
773 			}
774 		}
775 		if (FD_ISSET(server_fd, &fds))
776 			new_client(server_fd);
777 	}
778 	close(fd);
779 }
780 
781 /*
782  * functions that the parser uses.
783  */
784 void
785 add_attach(int prio, event_proc *p)
786 {
787 	cfg.add_attach(prio, p);
788 }
789 
790 void
791 add_detach(int prio, event_proc *p)
792 {
793 	cfg.add_detach(prio, p);
794 }
795 
796 void
797 add_directory(const char *dir)
798 {
799 	cfg.add_directory(dir);
800 	free(const_cast<char *>(dir));
801 }
802 
803 void
804 add_nomatch(int prio, event_proc *p)
805 {
806 	cfg.add_nomatch(prio, p);
807 }
808 
809 void
810 add_notify(int prio, event_proc *p)
811 {
812 	cfg.add_notify(prio, p);
813 }
814 
815 event_proc *
816 add_to_event_proc(event_proc *ep, eps *eps)
817 {
818 	if (ep == NULL)
819 		ep = new event_proc();
820 	ep->add(eps);
821 	return (ep);
822 }
823 
824 eps *
825 new_action(const char *cmd)
826 {
827 	eps *e = new action(cmd);
828 	free(const_cast<char *>(cmd));
829 	return (e);
830 }
831 
832 eps *
833 new_match(const char *var, const char *re)
834 {
835 	eps *e = new match(cfg, var, re);
836 	free(const_cast<char *>(var));
837 	free(const_cast<char *>(re));
838 	return (e);
839 }
840 
841 eps *
842 new_media(const char *var, const char *re)
843 {
844 	eps *e = new media(cfg, var, re);
845 	free(const_cast<char *>(var));
846 	free(const_cast<char *>(re));
847 	return (e);
848 }
849 
850 void
851 set_pidfile(const char *name)
852 {
853 	cfg.set_pidfile(name);
854 	free(const_cast<char *>(name));
855 }
856 
857 void
858 set_variable(const char *var, const char *val)
859 {
860 	cfg.set_variable(var, val);
861 	free(const_cast<char *>(var));
862 	free(const_cast<char *>(val));
863 }
864 
865 
866 
867 static void
868 gensighand(int)
869 {
870 	romeo_must_die++;
871 	unlink("/var/run/devd.pid");	/* XXX */
872 	_exit(0);
873 }
874 
875 static void
876 usage()
877 {
878 	fprintf(stderr, "usage: %s [-Ddn] [-f file]\n", getprogname());
879 	exit(1);
880 }
881 
882 static void
883 check_devd_enabled()
884 {
885 	int val = 0;
886 	size_t len;
887 
888 	len = sizeof(val);
889 	if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0)
890 		errx(1, "devctl sysctl missing from kernel!");
891 	if (val) {
892 		warnx("Setting " SYSCTL " to 0");
893 		val = 0;
894 		sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val));
895 	}
896 }
897 
898 /*
899  * main
900  */
901 int
902 main(int argc, char **argv)
903 {
904 	int ch;
905 
906 	check_devd_enabled();
907 	while ((ch = getopt(argc, argv, "Ddf:n")) != -1) {
908 		switch (ch) {
909 		case 'D':
910 			Dflag++;
911 			break;
912 		case 'd':
913 			dflag++;
914 			break;
915 		case 'f':
916 			configfile = optarg;
917 			break;
918 		case 'n':
919 			nflag++;
920 			break;
921 		default:
922 			usage();
923 		}
924 	}
925 
926 	cfg.parse();
927 	if (!dflag && nflag) {
928 		if (Dflag)
929 			fprintf(stderr, "Calling daemon\n");
930 		daemon(0, 0);
931 		cfg.open_pidfile();
932 	}
933 	signal(SIGPIPE, SIG_IGN);
934 	signal(SIGHUP, gensighand);
935 	signal(SIGINT, gensighand);
936 	signal(SIGTERM, gensighand);
937 	event_loop();
938 	return (0);
939 }
940