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