1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause AND BSD-2-Clause 3 * 4 * Copyright (c) 2002-2010 M. Warner Losh <imp@FreeBSD.org> 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 1. Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * 2. Redistributions in binary form must reproduce the above copyright 12 * notice, this list of conditions and the following disclaimer in the 13 * documentation and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 16 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 18 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 21 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 22 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 23 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 25 * SUCH DAMAGE. 26 * 27 * my_system is a variation on lib/libc/stdlib/system.c: 28 * 29 * Copyright (c) 1988, 1993 30 * The Regents of the University of California. All rights reserved. 31 * 32 * Redistribution and use in source and binary forms, with or without 33 * modification, are permitted provided that the following conditions 34 * are met: 35 * 1. Redistributions of source code must retain the above copyright 36 * notice, this list of conditions and the following disclaimer. 37 * 2. Redistributions in binary form must reproduce the above copyright 38 * notice, this list of conditions and the following disclaimer in the 39 * documentation and/or other materials provided with the distribution. 40 * 3. Neither the name of the University nor the names of its contributors 41 * may be used to endorse or promote products derived from this software 42 * without specific prior written permission. 43 * 44 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 45 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 46 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 47 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 48 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 49 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 50 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 51 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 52 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 53 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 54 * SUCH DAMAGE. 55 */ 56 57 /* 58 * DEVD control daemon. 59 */ 60 61 // TODO list: 62 // o devd.conf and devd man pages need a lot of help: 63 // - devd needs to document the unix domain socket 64 // - devd.conf needs more details on the supported statements. 65 66 #include <sys/cdefs.h> 67 __FBSDID("$FreeBSD$"); 68 69 #include <sys/param.h> 70 #include <sys/socket.h> 71 #include <sys/stat.h> 72 #include <sys/sysctl.h> 73 #include <sys/types.h> 74 #include <sys/wait.h> 75 #include <sys/un.h> 76 77 #include <cctype> 78 #include <cerrno> 79 #include <cstdlib> 80 #include <cstdio> 81 #include <csignal> 82 #include <cstring> 83 #include <cstdarg> 84 85 #include <dirent.h> 86 #include <err.h> 87 #include <fcntl.h> 88 #include <libutil.h> 89 #include <paths.h> 90 #include <poll.h> 91 #include <regex.h> 92 #include <syslog.h> 93 #include <unistd.h> 94 95 #include <algorithm> 96 #include <map> 97 #include <string> 98 #include <list> 99 #include <stdexcept> 100 #include <vector> 101 102 #include "devd.h" /* C compatible definitions */ 103 #include "devd.hh" /* C++ class definitions */ 104 105 #define STREAMPIPE "/var/run/devd.pipe" 106 #define SEQPACKETPIPE "/var/run/devd.seqpacket.pipe" 107 #define CF "/etc/devd.conf" 108 #define SYSCTL "hw.bus.devctl_queue" 109 110 /* 111 * Since the client socket is nonblocking, we must increase its send buffer to 112 * handle brief event storms. On FreeBSD, AF_UNIX sockets don't have a receive 113 * buffer, so the client can't increase the buffersize by itself. 114 * 115 * For example, when creating a ZFS pool, devd emits one 165 character 116 * resource.fs.zfs.statechange message for each vdev in the pool. The kernel 117 * allocates a 4608B mbuf for each message. Modern technology places a limit of 118 * roughly 450 drives/rack, and it's unlikely that a zpool will ever be larger 119 * than that. 120 * 121 * 450 drives * 165 bytes / drive = 74250B of data in the sockbuf 122 * 450 drives * 4608B / drive = 2073600B of mbufs in the sockbuf 123 * 124 * We can't directly set the sockbuf's mbuf limit, but we can do it indirectly. 125 * The kernel sets it to the minimum of a hard-coded maximum value and sbcc * 126 * kern.ipc.sockbuf_waste_factor, where sbcc is the socket buffer size set by 127 * the user. The default value of kern.ipc.sockbuf_waste_factor is 8. If we 128 * set the bufsize to 256k and use the kern.ipc.sockbuf_waste_factor, then the 129 * kernel will set the mbuf limit to 2MB, which is just large enough for 450 130 * drives. It also happens to be the same as the hardcoded maximum value. 131 */ 132 #define CLIENT_BUFSIZE 262144 133 134 using namespace std; 135 136 typedef struct client { 137 int fd; 138 int socktype; 139 } client_t; 140 141 extern FILE *yyin; 142 143 static const char notify = '!'; 144 static const char nomatch = '?'; 145 static const char attach = '+'; 146 static const char detach = '-'; 147 148 static struct pidfh *pfh; 149 150 static int no_daemon = 0; 151 static int daemonize_quick = 0; 152 static int quiet_mode = 0; 153 static unsigned total_events = 0; 154 static volatile sig_atomic_t got_siginfo = 0; 155 static volatile sig_atomic_t romeo_must_die = 0; 156 157 static const char *configfile = CF; 158 159 static void devdlog(int priority, const char* message, ...) 160 __printflike(2, 3); 161 static void event_loop(void); 162 static void usage(void) __dead2; 163 164 template <class T> void 165 delete_and_clear(vector<T *> &v) 166 { 167 typename vector<T *>::const_iterator i; 168 169 for (i = v.begin(); i != v.end(); ++i) 170 delete *i; 171 v.clear(); 172 } 173 174 static config cfg; 175 176 static const char *curr_cf = NULL; 177 178 event_proc::event_proc() : _prio(-1) 179 { 180 _epsvec.reserve(4); 181 } 182 183 event_proc::~event_proc() 184 { 185 delete_and_clear(_epsvec); 186 } 187 188 void 189 event_proc::add(eps *eps) 190 { 191 _epsvec.push_back(eps); 192 } 193 194 bool 195 event_proc::matches(config &c) const 196 { 197 vector<eps *>::const_iterator i; 198 199 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 200 if (!(*i)->do_match(c)) 201 return (false); 202 return (true); 203 } 204 205 bool 206 event_proc::run(config &c) const 207 { 208 vector<eps *>::const_iterator i; 209 210 for (i = _epsvec.begin(); i != _epsvec.end(); ++i) 211 if (!(*i)->do_action(c)) 212 return (false); 213 return (true); 214 } 215 216 action::action(const char *cmd) 217 : _cmd(cmd) 218 { 219 // nothing 220 } 221 222 action::~action() 223 { 224 // nothing 225 } 226 227 static int 228 my_system(const char *command) 229 { 230 pid_t pid, savedpid; 231 int pstat; 232 struct sigaction ign, intact, quitact; 233 sigset_t newsigblock, oldsigblock; 234 235 if (!command) /* just checking... */ 236 return (1); 237 238 /* 239 * Ignore SIGINT and SIGQUIT, block SIGCHLD. Remember to save 240 * existing signal dispositions. 241 */ 242 ign.sa_handler = SIG_IGN; 243 ::sigemptyset(&ign.sa_mask); 244 ign.sa_flags = 0; 245 ::sigaction(SIGINT, &ign, &intact); 246 ::sigaction(SIGQUIT, &ign, &quitact); 247 ::sigemptyset(&newsigblock); 248 ::sigaddset(&newsigblock, SIGCHLD); 249 ::sigprocmask(SIG_BLOCK, &newsigblock, &oldsigblock); 250 switch (pid = ::fork()) { 251 case -1: /* error */ 252 break; 253 case 0: /* child */ 254 /* 255 * Restore original signal dispositions and exec the command. 256 */ 257 ::sigaction(SIGINT, &intact, NULL); 258 ::sigaction(SIGQUIT, &quitact, NULL); 259 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 260 /* 261 * Close the PID file, and all other open descriptors. 262 * Inherit std{in,out,err} only. 263 */ 264 cfg.close_pidfile(); 265 ::closefrom(3); 266 ::execl(_PATH_BSHELL, "sh", "-c", command, (char *)NULL); 267 ::_exit(127); 268 default: /* parent */ 269 savedpid = pid; 270 do { 271 pid = ::wait4(savedpid, &pstat, 0, (struct rusage *)0); 272 } while (pid == -1 && errno == EINTR); 273 break; 274 } 275 ::sigaction(SIGINT, &intact, NULL); 276 ::sigaction(SIGQUIT, &quitact, NULL); 277 ::sigprocmask(SIG_SETMASK, &oldsigblock, NULL); 278 return (pid == -1 ? -1 : pstat); 279 } 280 281 bool 282 action::do_action(config &c) 283 { 284 string s = c.expand_string(_cmd.c_str()); 285 devdlog(LOG_INFO, "Executing '%s'\n", s.c_str()); 286 my_system(s.c_str()); 287 return (true); 288 } 289 290 match::match(config &c, const char *var, const char *re) : 291 _inv(re[0] == '!'), 292 _var(var), 293 _re(c.expand_string(_inv ? re + 1 : re, "^", "$")) 294 { 295 regcomp(&_regex, _re.c_str(), REG_EXTENDED | REG_NOSUB | REG_ICASE); 296 } 297 298 match::~match() 299 { 300 regfree(&_regex); 301 } 302 303 bool 304 match::do_match(config &c) 305 { 306 const string &value = c.get_variable(_var); 307 bool retval; 308 309 /* 310 * This function gets called WAY too often to justify calling syslog() 311 * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 312 * can consume excessive amounts of systime inside of connect(). Only 313 * log when we're in -d mode. 314 */ 315 if (no_daemon) { 316 devdlog(LOG_DEBUG, "Testing %s=%s against %s, invert=%d\n", 317 _var.c_str(), value.c_str(), _re.c_str(), _inv); 318 } 319 320 retval = (regexec(&_regex, value.c_str(), 0, NULL, 0) == 0); 321 if (_inv == 1) 322 retval = (retval == 0) ? 1 : 0; 323 324 return (retval); 325 } 326 327 #include <sys/sockio.h> 328 #include <net/if.h> 329 #include <net/if_media.h> 330 331 media::media(config &, const char *var, const char *type) 332 : _var(var), _type(-1) 333 { 334 static struct ifmedia_description media_types[] = { 335 { IFM_ETHER, "Ethernet" }, 336 { IFM_IEEE80211, "802.11" }, 337 { IFM_ATM, "ATM" }, 338 { -1, "unknown" }, 339 { 0, NULL }, 340 }; 341 for (int i = 0; media_types[i].ifmt_string != NULL; ++i) 342 if (strcasecmp(type, media_types[i].ifmt_string) == 0) { 343 _type = media_types[i].ifmt_word; 344 break; 345 } 346 } 347 348 media::~media() 349 { 350 } 351 352 bool 353 media::do_match(config &c) 354 { 355 string value; 356 struct ifmediareq ifmr; 357 bool retval; 358 int s; 359 360 // Since we can be called from both a device attach/detach 361 // context where device-name is defined and what we want, 362 // as well as from a link status context, where subsystem is 363 // the name of interest, first try device-name and fall back 364 // to subsystem if none exists. 365 value = c.get_variable("device-name"); 366 if (value.empty()) 367 value = c.get_variable("subsystem"); 368 devdlog(LOG_DEBUG, "Testing media type of %s against 0x%x\n", 369 value.c_str(), _type); 370 371 retval = false; 372 373 s = socket(PF_INET, SOCK_DGRAM, 0); 374 if (s >= 0) { 375 memset(&ifmr, 0, sizeof(ifmr)); 376 strlcpy(ifmr.ifm_name, value.c_str(), sizeof(ifmr.ifm_name)); 377 378 if (ioctl(s, SIOCGIFMEDIA, (caddr_t)&ifmr) >= 0 && 379 ifmr.ifm_status & IFM_AVALID) { 380 devdlog(LOG_DEBUG, "%s has media type 0x%x\n", 381 value.c_str(), IFM_TYPE(ifmr.ifm_active)); 382 retval = (IFM_TYPE(ifmr.ifm_active) == _type); 383 } else if (_type == -1) { 384 devdlog(LOG_DEBUG, "%s has unknown media type\n", 385 value.c_str()); 386 retval = true; 387 } 388 close(s); 389 } 390 391 return (retval); 392 } 393 394 const string var_list::bogus = "_$_$_$_$_B_O_G_U_S_$_$_$_$_"; 395 const string var_list::nothing = ""; 396 397 const string & 398 var_list::get_variable(const string &var) const 399 { 400 map<string, string>::const_iterator i; 401 402 i = _vars.find(var); 403 if (i == _vars.end()) 404 return (var_list::bogus); 405 return (i->second); 406 } 407 408 bool 409 var_list::is_set(const string &var) const 410 { 411 return (_vars.find(var) != _vars.end()); 412 } 413 414 /** fix_value 415 * 416 * Removes quoted characters that have made it this far. \" are 417 * converted to ". For all other characters, both \ and following 418 * character. So the string 'fre\:\"' is translated to 'fred\:"'. 419 */ 420 std::string 421 var_list::fix_value(const std::string &val) const 422 { 423 std::string rv(val); 424 std::string::size_type pos(0); 425 426 while ((pos = rv.find("\\\"", pos)) != rv.npos) { 427 rv.erase(pos, 1); 428 } 429 return (rv); 430 } 431 432 void 433 var_list::set_variable(const string &var, const string &val) 434 { 435 /* 436 * This function gets called WAY too often to justify calling syslog() 437 * each time, even at LOG_DEBUG. Because if syslogd isn't running, it 438 * can consume excessive amounts of systime inside of connect(). Only 439 * log when we're in -d mode. 440 */ 441 _vars[var] = fix_value(val); 442 if (no_daemon) 443 devdlog(LOG_DEBUG, "setting %s=%s\n", var.c_str(), val.c_str()); 444 } 445 446 void 447 config::reset(void) 448 { 449 _dir_list.clear(); 450 delete_and_clear(_var_list_table); 451 delete_and_clear(_attach_list); 452 delete_and_clear(_detach_list); 453 delete_and_clear(_nomatch_list); 454 delete_and_clear(_notify_list); 455 } 456 457 /* 458 * Called recursively as new files are included, so current stack of old names 459 * saved in each instance of 'old' on the call stack. Called single threaded 460 * so global varaibles curr_cf and lineno (and all of yacc's parser state) 461 * are safe to access w/o a lock. 462 */ 463 void 464 config::parse_one_file(const char *fn) 465 { 466 const char *old; 467 468 devdlog(LOG_DEBUG, "Parsing %s\n", fn); 469 yyin = fopen(fn, "r"); 470 old = curr_cf; 471 curr_cf = fn; 472 if (yyin == NULL) 473 err(1, "Cannot open config file %s", fn); 474 lineno = 1; 475 if (yyparse() != 0) 476 errx(1, "Cannot parse %s at line %d", fn, lineno); 477 fclose(yyin); 478 curr_cf = old; 479 } 480 481 void 482 config::parse_files_in_dir(const char *dirname) 483 { 484 DIR *dirp; 485 struct dirent *dp; 486 char path[PATH_MAX]; 487 488 devdlog(LOG_DEBUG, "Parsing files in %s\n", dirname); 489 dirp = opendir(dirname); 490 if (dirp == NULL) 491 return; 492 readdir(dirp); /* Skip . */ 493 readdir(dirp); /* Skip .. */ 494 while ((dp = readdir(dirp)) != NULL) { 495 if (strcmp(dp->d_name + dp->d_namlen - 5, ".conf") == 0) { 496 snprintf(path, sizeof(path), "%s/%s", 497 dirname, dp->d_name); 498 parse_one_file(path); 499 } 500 } 501 closedir(dirp); 502 } 503 504 class epv_greater { 505 public: 506 int operator()(event_proc *const&l1, event_proc *const&l2) const 507 { 508 return (l1->get_priority() > l2->get_priority()); 509 } 510 }; 511 512 void 513 config::sort_vector(vector<event_proc *> &v) 514 { 515 stable_sort(v.begin(), v.end(), epv_greater()); 516 } 517 518 void 519 config::parse(void) 520 { 521 vector<string>::const_iterator i; 522 523 parse_one_file(configfile); 524 for (i = _dir_list.begin(); i != _dir_list.end(); ++i) 525 parse_files_in_dir((*i).c_str()); 526 sort_vector(_attach_list); 527 sort_vector(_detach_list); 528 sort_vector(_nomatch_list); 529 sort_vector(_notify_list); 530 } 531 532 void 533 config::open_pidfile() 534 { 535 pid_t otherpid; 536 537 if (_pidfile.empty()) 538 return; 539 pfh = pidfile_open(_pidfile.c_str(), 0600, &otherpid); 540 if (pfh == NULL) { 541 if (errno == EEXIST) 542 errx(1, "devd already running, pid: %d", (int)otherpid); 543 warn("cannot open pid file"); 544 } 545 } 546 547 void 548 config::write_pidfile() 549 { 550 551 pidfile_write(pfh); 552 } 553 554 void 555 config::close_pidfile() 556 { 557 558 pidfile_close(pfh); 559 } 560 561 void 562 config::remove_pidfile() 563 { 564 565 pidfile_remove(pfh); 566 } 567 568 void 569 config::add_attach(int prio, event_proc *p) 570 { 571 p->set_priority(prio); 572 _attach_list.push_back(p); 573 } 574 575 void 576 config::add_detach(int prio, event_proc *p) 577 { 578 p->set_priority(prio); 579 _detach_list.push_back(p); 580 } 581 582 void 583 config::add_directory(const char *dir) 584 { 585 _dir_list.push_back(string(dir)); 586 } 587 588 void 589 config::add_nomatch(int prio, event_proc *p) 590 { 591 p->set_priority(prio); 592 _nomatch_list.push_back(p); 593 } 594 595 void 596 config::add_notify(int prio, event_proc *p) 597 { 598 p->set_priority(prio); 599 _notify_list.push_back(p); 600 } 601 602 void 603 config::set_pidfile(const char *fn) 604 { 605 _pidfile = fn; 606 } 607 608 void 609 config::push_var_table() 610 { 611 var_list *vl; 612 613 vl = new var_list(); 614 _var_list_table.push_back(vl); 615 devdlog(LOG_DEBUG, "Pushing table\n"); 616 } 617 618 void 619 config::pop_var_table() 620 { 621 delete _var_list_table.back(); 622 _var_list_table.pop_back(); 623 devdlog(LOG_DEBUG, "Popping table\n"); 624 } 625 626 void 627 config::set_variable(const char *var, const char *val) 628 { 629 _var_list_table.back()->set_variable(var, val); 630 } 631 632 const string & 633 config::get_variable(const string &var) 634 { 635 vector<var_list *>::reverse_iterator i; 636 637 for (i = _var_list_table.rbegin(); i != _var_list_table.rend(); ++i) { 638 if ((*i)->is_set(var)) 639 return ((*i)->get_variable(var)); 640 } 641 return (var_list::nothing); 642 } 643 644 bool 645 config::is_id_char(char ch) const 646 { 647 return (ch != '\0' && (isalpha(ch) || isdigit(ch) || ch == '_' || 648 ch == '-')); 649 } 650 651 string 652 config::shell_quote(const string &s) 653 { 654 string buffer; 655 const char *cs, *ce; 656 char c; 657 658 /* 659 * Enclose the string in $' ' with escapes for ' and / characters making 660 * it one argument and ensuring the shell won't be affected by its 661 * usual list of candidates. 662 */ 663 buffer.reserve(s.length() * 3 / 2); 664 buffer += '$'; 665 buffer += '\''; 666 cs = s.c_str(); 667 ce = cs + strlen(cs); 668 for (; cs < ce; cs++) { 669 c = *cs; 670 if (c == '\'' || c == '\\') { 671 buffer += '\\'; 672 } 673 buffer += c; 674 } 675 buffer += '\''; 676 677 return buffer; 678 } 679 680 void 681 config::expand_one(const char *&src, string &dst, bool is_shell) 682 { 683 int count; 684 string buffer; 685 686 src++; 687 // $$ -> $ 688 if (*src == '$') { 689 dst += *src++; 690 return; 691 } 692 693 // $(foo) -> $(foo) 694 // This is the escape hatch for passing down shell subcommands 695 if (*src == '(') { 696 dst += '$'; 697 count = 0; 698 /* If the string ends before ) is matched , return. */ 699 do { 700 if (*src == ')') 701 count--; 702 else if (*src == '(') 703 count++; 704 dst += *src++; 705 } while (count > 0 && *src); 706 return; 707 } 708 709 // $[^-A-Za-z_*] -> $\1 710 if (!isalpha(*src) && *src != '_' && *src != '-' && *src != '*') { 711 dst += '$'; 712 dst += *src++; 713 return; 714 } 715 716 // $var -> replace with value 717 do { 718 buffer += *src++; 719 } while (is_id_char(*src)); 720 dst.append(is_shell ? shell_quote(get_variable(buffer)) : get_variable(buffer)); 721 } 722 723 const string 724 config::expand_string(const char *src, const char *prepend, const char *append) 725 { 726 const char *var_at; 727 string dst; 728 729 /* 730 * 128 bytes is enough for 2427 of 2438 expansions that happen 731 * while parsing config files, as tested on 2013-01-30. 732 */ 733 dst.reserve(128); 734 735 if (prepend != NULL) 736 dst = prepend; 737 738 for (;;) { 739 var_at = strchr(src, '$'); 740 if (var_at == NULL) { 741 dst.append(src); 742 break; 743 } 744 dst.append(src, var_at - src); 745 src = var_at; 746 expand_one(src, dst, prepend == NULL); 747 } 748 749 if (append != NULL) 750 dst.append(append); 751 752 return (dst); 753 } 754 755 bool 756 config::chop_var(char *&buffer, char *&lhs, char *&rhs) const 757 { 758 char *walker; 759 760 if (*buffer == '\0') 761 return (false); 762 walker = lhs = buffer; 763 while (is_id_char(*walker)) 764 walker++; 765 if (*walker != '=') 766 return (false); 767 walker++; // skip = 768 if (*walker == '"') { 769 walker++; // skip " 770 rhs = walker; 771 while (*walker && *walker != '"') { 772 // Skip \" ... We leave it in the string and strip the \ later. 773 // due to the super simplistic parser that we have here. 774 if (*walker == '\\' && walker[1] == '"') 775 walker++; 776 walker++; 777 } 778 if (*walker != '"') 779 return (false); 780 rhs[-2] = '\0'; 781 *walker++ = '\0'; 782 } else { 783 rhs = walker; 784 while (*walker && !isspace(*walker)) 785 walker++; 786 if (*walker != '\0') 787 *walker++ = '\0'; 788 rhs[-1] = '\0'; 789 } 790 while (isspace(*walker)) 791 walker++; 792 buffer = walker; 793 return (true); 794 } 795 796 797 char * 798 config::set_vars(char *buffer) 799 { 800 char *lhs; 801 char *rhs; 802 803 while (1) { 804 if (!chop_var(buffer, lhs, rhs)) 805 break; 806 set_variable(lhs, rhs); 807 } 808 return (buffer); 809 } 810 811 void 812 config::find_and_execute(char type) 813 { 814 vector<event_proc *> *l; 815 vector<event_proc *>::const_iterator i; 816 const char *s; 817 818 switch (type) { 819 default: 820 return; 821 case notify: 822 l = &_notify_list; 823 s = "notify"; 824 break; 825 case nomatch: 826 l = &_nomatch_list; 827 s = "nomatch"; 828 break; 829 case attach: 830 l = &_attach_list; 831 s = "attach"; 832 break; 833 case detach: 834 l = &_detach_list; 835 s = "detach"; 836 break; 837 } 838 devdlog(LOG_DEBUG, "Processing %s event\n", s); 839 for (i = l->begin(); i != l->end(); ++i) { 840 if ((*i)->matches(*this)) { 841 (*i)->run(*this); 842 break; 843 } 844 } 845 846 } 847 848 849 static void 850 process_event(char *buffer) 851 { 852 char type; 853 char *sp; 854 struct timeval tv; 855 char *timestr; 856 857 sp = buffer + 1; 858 devdlog(LOG_INFO, "Processing event '%s'\n", buffer); 859 type = *buffer++; 860 cfg.push_var_table(); 861 // $* is the entire line 862 cfg.set_variable("*", buffer - 1); 863 // $_ is the entire line without the initial character 864 cfg.set_variable("_", buffer); 865 866 // Save the time this happened (as approximated by when we got 867 // around to processing it). 868 gettimeofday(&tv, NULL); 869 asprintf(×tr, "%jd.%06ld", (uintmax_t)tv.tv_sec, tv.tv_usec); 870 cfg.set_variable("timestamp", timestr); 871 free(timestr); 872 873 // Match doesn't have a device, and the format is a little 874 // different, so handle it separately. 875 switch (type) { 876 case notify: 877 //! (k=v)* 878 sp = cfg.set_vars(sp); 879 break; 880 case nomatch: 881 //? at location pnp-info on bus 882 sp = strchr(sp, ' '); 883 if (sp == NULL) 884 return; /* Can't happen? */ 885 *sp++ = '\0'; 886 while (isspace(*sp)) 887 sp++; 888 if (strncmp(sp, "at ", 3) == 0) 889 sp += 3; 890 sp = cfg.set_vars(sp); 891 while (isspace(*sp)) 892 sp++; 893 if (strncmp(sp, "on ", 3) == 0) 894 cfg.set_variable("bus", sp + 3); 895 break; 896 case attach: /*FALLTHROUGH*/ 897 case detach: 898 sp = strchr(sp, ' '); 899 if (sp == NULL) 900 return; /* Can't happen? */ 901 *sp++ = '\0'; 902 cfg.set_variable("device-name", buffer); 903 while (isspace(*sp)) 904 sp++; 905 if (strncmp(sp, "at ", 3) == 0) 906 sp += 3; 907 sp = cfg.set_vars(sp); 908 while (isspace(*sp)) 909 sp++; 910 if (strncmp(sp, "on ", 3) == 0) 911 cfg.set_variable("bus", sp + 3); 912 break; 913 } 914 915 cfg.find_and_execute(type); 916 cfg.pop_var_table(); 917 } 918 919 static int 920 create_socket(const char *name, int socktype) 921 { 922 int fd, slen; 923 struct sockaddr_un sun; 924 925 if ((fd = socket(PF_LOCAL, socktype, 0)) < 0) 926 err(1, "socket"); 927 bzero(&sun, sizeof(sun)); 928 sun.sun_family = AF_UNIX; 929 strlcpy(sun.sun_path, name, sizeof(sun.sun_path)); 930 slen = SUN_LEN(&sun); 931 unlink(name); 932 if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) 933 err(1, "fcntl"); 934 if (::bind(fd, (struct sockaddr *) & sun, slen) < 0) 935 err(1, "bind"); 936 listen(fd, 4); 937 if (chown(name, 0, 0)) /* XXX - root.wheel */ 938 err(1, "chown"); 939 if (chmod(name, 0666)) 940 err(1, "chmod"); 941 return (fd); 942 } 943 944 static unsigned int max_clients = 10; /* Default, can be overridden on cmdline. */ 945 static unsigned int num_clients; 946 947 static list<client_t> clients; 948 949 static void 950 notify_clients(const char *data, int len) 951 { 952 list<client_t>::iterator i; 953 954 /* 955 * Deliver the data to all clients. Throw clients overboard at the 956 * first sign of trouble. This reaps clients who've died or closed 957 * their sockets, and also clients who are alive but failing to keep up 958 * (or who are maliciously not reading, to consume buffer space in 959 * kernel memory or tie up the limited number of available connections). 960 */ 961 for (i = clients.begin(); i != clients.end(); ) { 962 int flags; 963 if (i->socktype == SOCK_SEQPACKET) 964 flags = MSG_EOR; 965 else 966 flags = 0; 967 968 if (send(i->fd, data, len, flags) != len) { 969 --num_clients; 970 close(i->fd); 971 i = clients.erase(i); 972 devdlog(LOG_WARNING, "notify_clients: send() failed; " 973 "dropping unresponsive client\n"); 974 } else 975 ++i; 976 } 977 } 978 979 static void 980 check_clients(void) 981 { 982 int s; 983 struct pollfd pfd; 984 list<client_t>::iterator i; 985 986 /* 987 * Check all existing clients to see if any of them have disappeared. 988 * Normally we reap clients when we get an error trying to send them an 989 * event. This check eliminates the problem of an ever-growing list of 990 * zombie clients because we're never writing to them on a system 991 * without frequent device-change activity. 992 */ 993 pfd.events = 0; 994 for (i = clients.begin(); i != clients.end(); ) { 995 pfd.fd = i->fd; 996 s = poll(&pfd, 1, 0); 997 if ((s < 0 && s != EINTR ) || 998 (s > 0 && (pfd.revents & POLLHUP))) { 999 --num_clients; 1000 close(i->fd); 1001 i = clients.erase(i); 1002 devdlog(LOG_NOTICE, "check_clients: " 1003 "dropping disconnected client\n"); 1004 } else 1005 ++i; 1006 } 1007 } 1008 1009 static void 1010 new_client(int fd, int socktype) 1011 { 1012 client_t s; 1013 int sndbuf_size; 1014 1015 /* 1016 * First go reap any zombie clients, then accept the connection, and 1017 * shut down the read side to stop clients from consuming kernel memory 1018 * by sending large buffers full of data we'll never read. 1019 */ 1020 check_clients(); 1021 s.socktype = socktype; 1022 s.fd = accept(fd, NULL, NULL); 1023 if (s.fd != -1) { 1024 sndbuf_size = CLIENT_BUFSIZE; 1025 if (setsockopt(s.fd, SOL_SOCKET, SO_SNDBUF, &sndbuf_size, 1026 sizeof(sndbuf_size))) 1027 err(1, "setsockopt"); 1028 shutdown(s.fd, SHUT_RD); 1029 clients.push_back(s); 1030 ++num_clients; 1031 } else 1032 err(1, "accept"); 1033 } 1034 1035 static void 1036 event_loop(void) 1037 { 1038 int rv; 1039 int fd; 1040 char buffer[DEVCTL_MAXBUF]; 1041 int once = 0; 1042 int stream_fd, seqpacket_fd, max_fd; 1043 int accepting; 1044 timeval tv; 1045 fd_set fds; 1046 1047 fd = open(PATH_DEVCTL, O_RDONLY | O_CLOEXEC); 1048 if (fd == -1) 1049 err(1, "Can't open devctl device %s", PATH_DEVCTL); 1050 stream_fd = create_socket(STREAMPIPE, SOCK_STREAM); 1051 seqpacket_fd = create_socket(SEQPACKETPIPE, SOCK_SEQPACKET); 1052 accepting = 1; 1053 max_fd = max(fd, max(stream_fd, seqpacket_fd)) + 1; 1054 while (!romeo_must_die) { 1055 if (!once && !no_daemon && !daemonize_quick) { 1056 // Check to see if we have any events pending. 1057 tv.tv_sec = 0; 1058 tv.tv_usec = 0; 1059 FD_ZERO(&fds); 1060 FD_SET(fd, &fds); 1061 rv = select(fd + 1, &fds, NULL, NULL, &tv); 1062 // No events -> we've processed all pending events 1063 if (rv == 0) { 1064 devdlog(LOG_DEBUG, "Calling daemon\n"); 1065 cfg.remove_pidfile(); 1066 cfg.open_pidfile(); 1067 daemon(0, 0); 1068 cfg.write_pidfile(); 1069 once++; 1070 } 1071 } 1072 /* 1073 * When we've already got the max number of clients, stop 1074 * accepting new connections (don't put the listening sockets in 1075 * the set), shrink the accept() queue to reject connections 1076 * quickly, and poll the existing clients more often, so that we 1077 * notice more quickly when any of them disappear to free up 1078 * client slots. 1079 */ 1080 FD_ZERO(&fds); 1081 FD_SET(fd, &fds); 1082 if (num_clients < max_clients) { 1083 if (!accepting) { 1084 listen(stream_fd, max_clients); 1085 listen(seqpacket_fd, max_clients); 1086 accepting = 1; 1087 } 1088 FD_SET(stream_fd, &fds); 1089 FD_SET(seqpacket_fd, &fds); 1090 tv.tv_sec = 60; 1091 tv.tv_usec = 0; 1092 } else { 1093 if (accepting) { 1094 listen(stream_fd, 0); 1095 listen(seqpacket_fd, 0); 1096 accepting = 0; 1097 } 1098 tv.tv_sec = 2; 1099 tv.tv_usec = 0; 1100 } 1101 rv = select(max_fd, &fds, NULL, NULL, &tv); 1102 if (got_siginfo) { 1103 devdlog(LOG_NOTICE, "Events received so far=%u\n", 1104 total_events); 1105 got_siginfo = 0; 1106 } 1107 if (rv == -1) { 1108 if (errno == EINTR) 1109 continue; 1110 err(1, "select"); 1111 } else if (rv == 0) 1112 check_clients(); 1113 if (FD_ISSET(fd, &fds)) { 1114 rv = read(fd, buffer, sizeof(buffer) - 1); 1115 if (rv > 0) { 1116 total_events++; 1117 if (rv == sizeof(buffer) - 1) { 1118 devdlog(LOG_WARNING, "Warning: " 1119 "available event data exceeded " 1120 "buffer space\n"); 1121 } 1122 notify_clients(buffer, rv); 1123 buffer[rv] = '\0'; 1124 while (buffer[--rv] == '\n') 1125 buffer[rv] = '\0'; 1126 try { 1127 process_event(buffer); 1128 } 1129 catch (const std::length_error& e) { 1130 devdlog(LOG_ERR, "Dropping event %s " 1131 "due to low memory", buffer); 1132 } 1133 } else if (rv < 0) { 1134 if (errno != EINTR) 1135 break; 1136 } else { 1137 /* EOF */ 1138 break; 1139 } 1140 } 1141 if (FD_ISSET(stream_fd, &fds)) 1142 new_client(stream_fd, SOCK_STREAM); 1143 /* 1144 * Aside from the socket type, both sockets use the same 1145 * protocol, so we can process clients the same way. 1146 */ 1147 if (FD_ISSET(seqpacket_fd, &fds)) 1148 new_client(seqpacket_fd, SOCK_SEQPACKET); 1149 } 1150 cfg.remove_pidfile(); 1151 close(seqpacket_fd); 1152 close(stream_fd); 1153 close(fd); 1154 } 1155 1156 /* 1157 * functions that the parser uses. 1158 */ 1159 void 1160 add_attach(int prio, event_proc *p) 1161 { 1162 cfg.add_attach(prio, p); 1163 } 1164 1165 void 1166 add_detach(int prio, event_proc *p) 1167 { 1168 cfg.add_detach(prio, p); 1169 } 1170 1171 void 1172 add_directory(const char *dir) 1173 { 1174 cfg.add_directory(dir); 1175 free(const_cast<char *>(dir)); 1176 } 1177 1178 void 1179 add_nomatch(int prio, event_proc *p) 1180 { 1181 cfg.add_nomatch(prio, p); 1182 } 1183 1184 void 1185 add_notify(int prio, event_proc *p) 1186 { 1187 cfg.add_notify(prio, p); 1188 } 1189 1190 event_proc * 1191 add_to_event_proc(event_proc *ep, eps *eps) 1192 { 1193 if (ep == NULL) 1194 ep = new event_proc(); 1195 ep->add(eps); 1196 return (ep); 1197 } 1198 1199 eps * 1200 new_action(const char *cmd) 1201 { 1202 eps *e = new action(cmd); 1203 free(const_cast<char *>(cmd)); 1204 return (e); 1205 } 1206 1207 eps * 1208 new_match(const char *var, const char *re) 1209 { 1210 /* 1211 * In FreeBSD 14, we changed the system=kern to system=kernel for the 1212 * resume message to match all the other 'kernel' messages. Generate a 1213 * warning for the life of 14.x that we've 'fixed' the file on the fly, 1214 * but make it a fatal error in 15.x and newer. 1215 */ 1216 if (strcmp(var, "kern") == 0) { 1217 #if __FreeBSD_version < 1500000 1218 devdlog(LOG_WARNING, 1219 "Changing deprecated system='kern' to new name 'kernel' in %s line %d.", 1220 curr_cf, lineno); 1221 free(const_cast<char *>(var)); 1222 var = strdup("kernel"); 1223 #elif __FreeBSD_version < 1600000 1224 errx(1, "Encountered deprecated system=\"kern\" rule in %s line %d", 1225 curr_cf, lineno); 1226 #else 1227 #error "Remove this gross hack" 1228 #endif 1229 } 1230 1231 eps *e = new match(cfg, var, re); 1232 free(const_cast<char *>(var)); 1233 free(const_cast<char *>(re)); 1234 return (e); 1235 } 1236 1237 eps * 1238 new_media(const char *var, const char *re) 1239 { 1240 eps *e = new media(cfg, var, re); 1241 free(const_cast<char *>(var)); 1242 free(const_cast<char *>(re)); 1243 return (e); 1244 } 1245 1246 void 1247 set_pidfile(const char *name) 1248 { 1249 cfg.set_pidfile(name); 1250 free(const_cast<char *>(name)); 1251 } 1252 1253 void 1254 set_variable(const char *var, const char *val) 1255 { 1256 cfg.set_variable(var, val); 1257 free(const_cast<char *>(var)); 1258 free(const_cast<char *>(val)); 1259 } 1260 1261 1262 1263 static void 1264 gensighand(int) 1265 { 1266 romeo_must_die = 1; 1267 } 1268 1269 /* 1270 * SIGINFO handler. Will print useful statistics to the syslog or stderr 1271 * as appropriate 1272 */ 1273 static void 1274 siginfohand(int) 1275 { 1276 got_siginfo = 1; 1277 } 1278 1279 /* 1280 * Local logging function. Prints to syslog if we're daemonized; stderr 1281 * otherwise. 1282 */ 1283 static void 1284 devdlog(int priority, const char* fmt, ...) 1285 { 1286 va_list argp; 1287 1288 va_start(argp, fmt); 1289 if (no_daemon) 1290 vfprintf(stderr, fmt, argp); 1291 else if (quiet_mode == 0 || priority <= LOG_WARNING) 1292 vsyslog(priority, fmt, argp); 1293 va_end(argp); 1294 } 1295 1296 static void 1297 usage() 1298 { 1299 fprintf(stderr, "usage: %s [-dnq] [-l connlimit] [-f file]\n", 1300 getprogname()); 1301 exit(1); 1302 } 1303 1304 static void 1305 check_devd_enabled() 1306 { 1307 int val = 0; 1308 size_t len; 1309 1310 len = sizeof(val); 1311 if (sysctlbyname(SYSCTL, &val, &len, NULL, 0) != 0) 1312 errx(1, "devctl sysctl missing from kernel!"); 1313 if (val == 0) { 1314 warnx("Setting " SYSCTL " to 1000"); 1315 val = 1000; 1316 if (sysctlbyname(SYSCTL, NULL, NULL, &val, sizeof(val))) 1317 err(1, "sysctlbyname"); 1318 } 1319 } 1320 1321 /* 1322 * main 1323 */ 1324 int 1325 main(int argc, char **argv) 1326 { 1327 int ch; 1328 1329 check_devd_enabled(); 1330 while ((ch = getopt(argc, argv, "df:l:nq")) != -1) { 1331 switch (ch) { 1332 case 'd': 1333 no_daemon = 1; 1334 break; 1335 case 'f': 1336 configfile = optarg; 1337 break; 1338 case 'l': 1339 max_clients = MAX(1, strtoul(optarg, NULL, 0)); 1340 break; 1341 case 'n': 1342 daemonize_quick = 1; 1343 break; 1344 case 'q': 1345 quiet_mode = 1; 1346 break; 1347 default: 1348 usage(); 1349 } 1350 } 1351 1352 cfg.parse(); 1353 if (!no_daemon && daemonize_quick) { 1354 cfg.open_pidfile(); 1355 daemon(0, 0); 1356 cfg.write_pidfile(); 1357 } 1358 signal(SIGPIPE, SIG_IGN); 1359 signal(SIGHUP, gensighand); 1360 signal(SIGINT, gensighand); 1361 signal(SIGTERM, gensighand); 1362 signal(SIGINFO, siginfohand); 1363 event_loop(); 1364 return (0); 1365 } 1366