1/*****************************************************************************\ 2* _ _ _ _ ___ * 3* | || | ___ | |_ _ __ | | _ _ __ _ |_ ) * 4* | __ |/ _ \| _|| '_ \| || || |/ _` | / / * 5* |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| * 6* |_| |___/ * 7\*****************************************************************************/ 8 9#include <fcntl.h> 10#include <stdio.h> 11#include <unistd.h> 12#include <regex.h> 13#include <ctype.h> 14#include <stdlib.h> 15#include <string.h> 16#include <errno.h> 17#include <libgen.h> 18#include <pwd.h> 19#include <grp.h> 20#include <sys/wait.h> 21#include <sys/types.h> 22#include <sys/stat.h> 23 24#include "mem_utils.h" 25#include "hotplug2.h" 26#include "rules.h" 27 28#define last_rule return_rules->rules[return_rules->rules_c - 1] 29 30static void mkdir_p(char *path) { 31 char *ptr; 32 struct stat statbuf; 33 34 path = strdup(path); 35 path = dirname(path); 36 stat(path, &statbuf); 37 /* All is well... */ 38 if (S_ISDIR(statbuf.st_mode)) { 39 free(path); 40 return; 41 } 42 43 for (ptr = path; ptr != NULL; ptr = strchr(ptr, '/')) { 44 if (ptr == path) { 45 ptr++; 46 continue; 47 } 48 49 errno = 0; 50 *ptr='\0'; 51 mkdir(path, 0755); 52 *ptr='/'; 53 if (errno != 0 && errno != EEXIST) 54 break; 55 56 ptr++; 57 } 58 mkdir(path, 0755); 59 free(path); 60} 61 62static char *replace_str(char *hay, char *needle, char *replacement) { 63 char *ptr, *start, *bptr, *buf; 64 int occurences, j; 65 size_t needle_len; 66 size_t replacement_len; 67 size_t haystack_len; 68 69 if (replacement == NULL || *replacement=='\0') 70 return hay; 71 72 if (needle == NULL || *needle=='\0') 73 return hay; 74 75 occurences = 0; 76 j = 0; 77 for (ptr = hay; *ptr != '\0'; ++ptr) { 78 if (needle[j] == *ptr) { 79 ++j; 80 if (needle[j] == '\0') { 81 *(ptr-j+1) = '\0'; // mark occurence 82 occurences++; 83 j = 0; 84 } 85 } else { 86 j=0; 87 } 88 } 89 90 if (occurences <= 0) 91 return hay; 92 93 haystack_len = (size_t)(ptr - hay); 94 replacement_len = strlen(replacement); 95 needle_len = strlen(needle); 96 97 buf = xmalloc(haystack_len + (replacement_len - needle_len) * occurences + 1); 98 start = hay; 99 ptr = hay; 100 101 bptr = buf; 102 while (occurences-- > 0) { 103 while (*ptr != '\0') 104 ++ptr; 105 106 if (ptr-start > 0) { 107 memcpy(bptr, start, ptr - start); 108 bptr +=ptr - start; 109 } 110 111 memcpy(bptr, replacement, replacement_len); 112 bptr+=replacement_len; 113 ptr += needle_len; 114 start = ptr; 115 } 116 117 while (*ptr != '\0') 118 ++ptr; 119 120 if (ptr-start > 0) { 121 memcpy(bptr, start, ptr - start); 122 bptr +=ptr - start; 123 } 124 *bptr='\0'; 125 126 free(hay); 127 128 return buf; 129} 130 131inline int isescaped(char *hay, char *ptr) { 132 if (ptr <= hay) 133 return 0; 134 135 if (*(ptr-1) != '\\') 136 return 0; 137 138 return 1; 139} 140 141static char *replace_key_by_value(char *hay, struct hotplug2_event_t *event) { 142 char *sptr = hay, *ptr = hay; 143 char *buf, *replacement; 144 145 while ((sptr = strchr(sptr, '%')) != NULL) { 146 ptr = strchr(sptr+1, '%'); 147 if (ptr != NULL) { 148 buf = xmalloc(ptr - sptr + 2); 149 buf[ptr - sptr + 1] = '\0'; 150 memcpy(buf, sptr, ptr - sptr + 1); 151 152 buf[ptr - sptr] = '\0'; 153 replacement = get_hotplug2_value_by_key(event, &buf[1]); 154 buf[ptr - sptr] = '%'; 155 156 if (replacement != NULL) { 157 hay = replace_str(hay, buf, replacement); 158 sptr = hay; 159 } else { 160 sptr++; 161 } 162 163 free(buf); 164 } else { 165 sptr++; 166 } 167 } 168 169 hay = replace_str(hay, "%\\", "%"); 170 171 return hay; 172} 173 174static int make_dev_from_event(struct hotplug2_event_t *event, char *path, mode_t devmode) { 175 char *subsystem, *major, *minor, *devpath; 176 int rv = 1; 177 178 major = get_hotplug2_value_by_key(event, "MAJOR"); 179 if (major == NULL) 180 goto return_value; 181 182 minor = get_hotplug2_value_by_key(event, "MINOR"); 183 if (minor == NULL) 184 goto return_value; 185 186 devpath = get_hotplug2_value_by_key(event, "DEVPATH"); 187 if (devpath == NULL) 188 goto return_value; 189 190 subsystem = get_hotplug2_value_by_key(event, "SUBSYSTEM"); 191 if (!strcmp(subsystem, "block")) 192 devmode |= S_IFBLK; 193 else 194 devmode |= S_IFCHR; 195 196 path = replace_key_by_value(path, event); 197 mkdir_p(path); 198 rv = mknod(path, devmode, makedev(atoi(major), atoi(minor))); 199 free(path); 200 201return_value: 202 return rv; 203} 204 205static int exec_noshell(struct hotplug2_event_t *event, char *application, char **argv) { 206 pid_t p; 207 int i, status; 208 209 p = fork(); 210 switch (p) { 211 case -1: 212 return -1; 213 case 0: 214 for (i = 0; argv[i] != NULL; i++) { 215 argv[i] = replace_key_by_value(argv[i], event); 216 } 217 execvp(application, argv); 218 exit(0); 219 break; 220 default: 221 if (waitpid(p, &status, 0) == -1) 222 return -1; 223 224 return WEXITSTATUS(status); 225 break; 226 } 227} 228 229static int exec_shell(struct hotplug2_event_t *event, char *application) { 230 int rv; 231 232 application = replace_key_by_value(strdup(application), event); 233 rv = WEXITSTATUS(system(application)); 234 free(application); 235 return rv; 236} 237 238static int make_symlink(struct hotplug2_event_t *event, char *target, char *linkname) { 239 int rv; 240 241 target = replace_key_by_value(strdup(target), event); 242 linkname = replace_key_by_value(strdup(linkname), event); 243 244 mkdir_p(linkname); 245 rv = symlink(target, linkname); 246 247 free(target); 248 free(linkname); 249 250 return rv; 251} 252 253static int chown_chgrp(int action, char *file, char *param) { 254 struct group *grp; 255 struct passwd *pwd; 256 int rv; 257 258 switch (action) { 259 case ACT_CHOWN: 260 pwd = getpwnam(param); 261 rv = chown(file, pwd->pw_uid, -1); 262 break; 263 case ACT_CHGRP: 264 grp = getgrnam(param); 265 rv = chown(file, -1, grp->gr_gid); 266 break; 267 } 268 269 return -1; 270} 271 272static int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) { 273 int rv; 274 char *event_value = NULL; 275 regex_t preg; 276 277 event_value = get_hotplug2_value_by_key(event, condition->key); 278 279 switch (condition->type) { 280 case COND_MATCH_CMP: 281 case COND_NMATCH_CMP: 282 if (event_value == NULL) 283 return EVAL_NOT_AVAILABLE; 284 285 rv = strcmp(condition->value, event_value) ? EVAL_NOT_MATCH : EVAL_MATCH; 286 if (condition->type == COND_NMATCH_CMP) 287 rv = !rv; 288 289 return rv; 290 291 case COND_MATCH_RE: 292 case COND_NMATCH_RE: 293 if (event_value == NULL) 294 return EVAL_NOT_AVAILABLE; 295 296 regcomp(&preg, condition->value, REG_EXTENDED | REG_NOSUB); 297 298 rv = regexec(&preg, event_value, 0, NULL, 0) ? EVAL_NOT_MATCH : EVAL_MATCH; 299 if (condition->type == COND_NMATCH_RE) 300 rv = !rv; 301 302 regfree(&preg); 303 304 return rv; 305 306 case COND_MATCH_IS: 307 if (!strcasecmp(condition->value, "set")) 308 return event_value != NULL; 309 310 if (!strcasecmp(condition->value, "unset")) 311 return event_value == NULL; 312 } 313 314 return EVAL_NOT_AVAILABLE; 315} 316 317int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) { 318 int i, last_rv; 319 320 for (i = 0; i < rule->conditions_c; i++) { 321 if (rule_condition_eval(event, &(rule->conditions[i])) != EVAL_MATCH) 322 return 0; 323 } 324 325 last_rv = 0; 326 327 for (i = 0; i < event->env_vars_c; i++) 328 setenv(event->env_vars[i].key, event->env_vars[i].value, 1); 329 330 for (i = 0; i < rule->actions_c; i++) { 331 switch (rule->actions[i].type) { 332 case ACT_STOP_PROCESSING: 333 return 1; 334 break; 335 case ACT_STOP_IF_FAILED: 336 if (last_rv != 0) 337 return 1; 338 break; 339 case ACT_NEXT_EVENT: 340 return -1; 341 break; 342 case ACT_NEXT_IF_FAILED: 343 if (last_rv != 0) 344 return -1; 345 break; 346 case ACT_MAKE_DEVICE: 347 last_rv = make_dev_from_event(event, rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0)); 348 break; 349 case ACT_CHMOD: 350 last_rv = chmod(rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0)); 351 break; 352 case ACT_CHOWN: 353 case ACT_CHGRP: 354 last_rv = chown_chgrp(rule->actions[i].type, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); 355 break; 356 case ACT_SYMLINK: 357 last_rv = make_symlink(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); 358 break; 359 case ACT_RUN_SHELL: 360 last_rv = exec_shell(event, rule->actions[i].parameter[0]); 361 break; 362 case ACT_RUN_NOSHELL: 363 last_rv = exec_noshell(event, rule->actions[i].parameter[0], rule->actions[i].parameter); 364 break; 365 case ACT_SETENV: 366 last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1); 367 break; 368 } 369 } 370 371 return 0; 372} 373 374static inline int isinitiator(int c) { 375 switch (c) { 376 case ',': 377 case ';': 378 case '{': 379 case '}': 380 return 1; 381 } 382 383 return 0; 384} 385 386static inline void add_buffer(char **buf, int *blen, int *slen, char c) { 387 if (*slen + 1 >= *blen) { 388 *blen = *blen + 64; 389 *buf = xrealloc(*buf, *blen); 390 } 391 392 (*buf)[*slen] = c; 393 (*buf)[*slen+1] = '\0'; 394 *slen += 1; 395} 396 397static char *rules_get_value(char *input, char **nptr) { 398 int quotes = QUOTES_NONE; 399 char *ptr = input; 400 401 int blen, slen; 402 char *buf; 403 404 blen = slen = 0; 405 buf = NULL; 406 407 if (isinitiator(*ptr)) { 408 add_buffer(&buf, &blen, &slen, *ptr); 409 ptr++; 410 goto return_value; 411 } 412 413 while (isspace(*ptr) && *ptr != '\0') 414 ptr++; 415 416 if (*ptr == '\0') 417 return NULL; 418 419 switch (*ptr) { 420 case '"': 421 quotes = QUOTES_DOUBLE; 422 ptr++; 423 break; 424 case '\'': 425 quotes = QUOTES_SINGLE; 426 ptr++; 427 break; 428 } 429 430 if (quotes != QUOTES_NONE) { 431 while (quotes != QUOTES_NONE) { 432 switch (*ptr) { 433 case '\\': 434 ptr++; 435 add_buffer(&buf, &blen, &slen, *ptr); 436 break; 437 case '"': 438 if (quotes == QUOTES_DOUBLE) 439 quotes = QUOTES_NONE; 440 break; 441 case '\'': 442 if (quotes == QUOTES_SINGLE) 443 quotes = QUOTES_NONE; 444 break; 445 default: 446 add_buffer(&buf, &blen, &slen, *ptr); 447 break; 448 } 449 ptr++; 450 } 451 } else { 452 while (!isspace(*ptr) && *ptr != '\0') { 453 if (isinitiator(*ptr)) 454 break; 455 456 if (*ptr == '\\') 457 ptr++; 458 459 add_buffer(&buf, &blen, &slen, *ptr); 460 ptr++; 461 } 462 } 463 464return_value: 465 while (isspace(*ptr) && *ptr != '\0') 466 ptr++; 467 468 if (nptr != NULL) 469 *nptr = ptr; 470 471 return buf; 472} 473 474void rules_free(struct rules_t *rules) { 475 int i, j, k; 476 477 for (i = 0; i < rules->rules_c; i++) { 478 for (j = 0; j < rules->rules[i].actions_c; j++) { 479 if (rules->rules[i].actions[j].parameter != NULL) { 480 for (k = 0; rules->rules[i].actions[j].parameter[k] != NULL; k++) 481 free(rules->rules[i].actions[j].parameter[k]); 482 free(rules->rules[i].actions[j].parameter); 483 } 484 } 485 for (j = 0; j < rules->rules[i].conditions_c; j++) { 486 free(rules->rules[i].conditions[j].key); 487 free(rules->rules[i].conditions[j].value); 488 } 489 free(rules->rules[i].actions); 490 free(rules->rules[i].conditions); 491 } 492 free(rules->rules); 493} 494 495struct rules_t *rules_from_config(char *input) { 496 int status = STATUS_KEY, terminate; 497 char *buf; 498 struct rules_t *return_rules; 499 500 int i, j; 501 struct key_rec_t conditions[] = { /*NOTE: We never have parameters for conditions. */ 502 {"is", 0, COND_MATCH_IS}, 503 {"==", 0, COND_MATCH_CMP}, 504 {"!=", 0, COND_NMATCH_CMP}, 505 {"~~", 0, COND_MATCH_RE}, 506 {"!~", 0, COND_NMATCH_RE}, 507 {NULL, 0, -1} 508 }; 509 struct key_rec_t actions[] = { 510 /*one line / one command*/ 511 {"run", 1, ACT_RUN_SHELL}, 512 {"exec", -1, ACT_RUN_NOSHELL}, 513 {"break", 0, ACT_STOP_PROCESSING}, 514 {"break_if_failed", 0, ACT_STOP_IF_FAILED}, 515 {"next", 0, ACT_NEXT_EVENT}, 516 {"next_if_failed", 0, ACT_NEXT_IF_FAILED}, 517 {"chown", 2, ACT_CHOWN}, 518 {"chmod", 2, ACT_CHMOD}, 519 {"chgrp", 2, ACT_CHGRP}, 520 {"setenv", 2, ACT_SETENV}, 521 /*symlink*/ 522 {"symlink", 2, ACT_SYMLINK}, 523 {"softlink", 2, ACT_SYMLINK}, 524 /*makedev*/ 525 {"mknod", 2, ACT_MAKE_DEVICE}, 526 {"makedev", 2, ACT_MAKE_DEVICE}, 527 {NULL, 0, -1} 528 }; 529 530 return_rules = xmalloc(sizeof(struct rules_t)); 531 return_rules->rules_c = 1; 532 return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c); 533 534 last_rule.actions = NULL; 535 last_rule.actions_c = 0; 536 last_rule.conditions = NULL; 537 last_rule.conditions_c = 0; 538 539 terminate = 0; 540 do { 541 buf = rules_get_value(input, &input); 542 if (buf == NULL) { 543 ERROR("rules_get_value", "Malformed rule - unable to read!"); 544 terminate = 1; 545 break; 546 } 547 548 if (buf[0] == '#') { 549 /* Skip to next line */ 550 while (*input != '\0' && *input != '\n') 551 input++; 552 continue; 553 } 554 555 switch (status) { 556 case STATUS_KEY: 557 last_rule.conditions_c++; 558 last_rule.conditions = xrealloc(last_rule.conditions, sizeof(struct condition_t) * last_rule.conditions_c); 559 last_rule.conditions[last_rule.conditions_c-1].key = strdup(buf); 560 561 status = STATUS_CONDTYPE; 562 break; 563 case STATUS_CONDTYPE: 564 last_rule.conditions[last_rule.conditions_c-1].type = -1; 565 566 for (i = 0; conditions[i].key != NULL; i++) { 567 if (!strcmp(conditions[i].key, buf)) { 568 last_rule.conditions[last_rule.conditions_c-1].type = conditions[i].type; 569 break; 570 } 571 } 572 573 if (last_rule.conditions[last_rule.conditions_c-1].type == -1) { 574 ERROR("rules_get_value / status / condtype", "Malformed rule - unknown condition type."); 575 terminate = 1; 576 } 577 578 status = STATUS_VALUE; 579 break; 580 case STATUS_VALUE: 581 last_rule.conditions[last_rule.conditions_c-1].value = strdup(buf); 582 583 status = STATUS_INITIATOR; 584 break; 585 case STATUS_INITIATOR: 586 if (!strcmp(buf, ",") || !strcmp(buf, ";")) { 587 status = STATUS_KEY; 588 } else if (!strcmp(buf, "{")) { 589 status = STATUS_ACTION; 590 } else { 591 ERROR("rules_get_value / status / initiator", "Malformed rule - unknown initiator."); 592 terminate = 1; 593 } 594 break; 595 case STATUS_ACTION: 596 if (!strcmp(buf, "}")) { 597 status = STATUS_KEY; 598 return_rules->rules_c++; 599 return_rules->rules = xrealloc(return_rules->rules, sizeof(struct rule_t) * return_rules->rules_c); 600 601 last_rule.actions = NULL; 602 last_rule.actions_c = 0; 603 last_rule.conditions = NULL; 604 last_rule.conditions_c = 0; 605 break; 606 } 607 608 last_rule.actions_c++; 609 last_rule.actions = xrealloc(last_rule.actions, sizeof(struct action_t) * last_rule.actions_c); 610 last_rule.actions[last_rule.actions_c-1].parameter = NULL; 611 last_rule.actions[last_rule.actions_c-1].type = -1; 612 613 for (i = 0; actions[i].key != NULL; i++) { 614 if (!strcmp(actions[i].key, buf)) { 615 last_rule.actions[last_rule.actions_c-1].type = actions[i].type; 616 break; 617 } 618 } 619 620 if (last_rule.actions[last_rule.actions_c-1].type == -1) { 621 ERROR("rules_get_value / status / action", "Malformed rule - unknown action: %s.", buf); 622 terminate = 1; 623 } 624 625 if (actions[i].param > 0) { 626 last_rule.actions[last_rule.actions_c-1].parameter = xmalloc(sizeof(char*) * (actions[i].param + 1)); 627 last_rule.actions[last_rule.actions_c-1].parameter[actions[i].param] = NULL; 628 629 for (j = 0; j < actions[i].param; j++) { 630 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 631 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], "}")) { 632 ERROR("rules_get_value / status / action", "Malformed rule - not enough parameters passed."); 633 status = STATUS_KEY; 634 break; 635 } 636 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\}", "}"); 637 } 638 } else if (actions[i].param == -1) { 639 j = 0; 640 last_rule.actions[last_rule.actions_c-1].parameter = xmalloc(sizeof(char*) * (j + 1)); 641 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 642 while (last_rule.actions[last_rule.actions_c-1].parameter[j] != NULL) { 643 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], ";")) { 644 break; 645 } 646 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], "}")) { 647 ERROR("rules_get_value / status / action", "Malformed rule - missing parameter terminator ';'."); 648 status = STATUS_KEY; 649 break; 650 } 651 if (last_rule.actions[last_rule.actions_c-1].parameter[j][0] == '\0') { 652 ERROR("rules_get_value / status / action", "Malformed rule - missing parameter terminator ';'."); 653 status = STATUS_KEY; 654 break; 655 } 656 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\}", "}"); 657 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\;", ";"); 658 659 j++; 660 last_rule.actions[last_rule.actions_c-1].parameter = xrealloc(last_rule.actions[last_rule.actions_c-1].parameter, sizeof(char*) * (j + 1)); 661 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 662 } 663 free(last_rule.actions[last_rule.actions_c-1].parameter[j]); 664 last_rule.actions[last_rule.actions_c-1].parameter[j] = NULL; 665 } 666 667 if (status == STATUS_KEY) { 668 return_rules->rules_c++; 669 return_rules->rules = xrealloc(return_rules->rules, sizeof(struct rule_t) * return_rules->rules_c); 670 671 last_rule.actions = NULL; 672 last_rule.actions_c = 0; 673 last_rule.conditions = NULL; 674 last_rule.conditions_c = 0; 675 } 676 break; 677 } 678 679 free(buf); 680 } while (*input != '\0' && !terminate); 681 682 if (!terminate) { 683 /* A little bit hacky cleanup */ 684 return_rules->rules_c--; 685 return return_rules; 686 } else { 687 rules_free(return_rules); 688 free(return_rules); 689 return NULL; 690 } 691} 692