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