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