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 "filemap_utils.h" 26#include "hotplug2.h" 27#include "rules.h" 28 29 30/** 31 * Function supplementing 'mkdir -p'. 32 * 33 * @1 Path to be mkdir'd 34 * 35 * Returns: void 36 */ 37static void mkdir_p(char *path) { 38 char *ptr; 39 struct stat statbuf; 40 41 path = strdup(path); 42 path = dirname(path); 43 stat(path, &statbuf); 44 /* All is well... */ 45 if (S_ISDIR(statbuf.st_mode)) { 46 free(path); 47 return; 48 } 49 50 for (ptr = path; ptr != NULL; ptr = strchr(ptr, '/')) { 51 if (ptr == path) { 52 ptr++; 53 continue; 54 } 55 56 errno = 0; 57 *ptr='\0'; 58 mkdir(path, 0755); 59 *ptr='/'; 60 if (errno != 0 && errno != EEXIST) 61 break; 62 63 ptr++; 64 } 65 mkdir(path, 0755); 66 free(path); 67} 68 69/** 70 * Function supplementing 'rmdir -p'. 71 * 72 * @1 Path to be rmdir'd 73 * 74 * Returns: void 75 */ 76static void rmdir_p(char *path) { 77 char *ptr; 78 79 path = strdup(path); 80 ptr = path; 81 while (ptr != NULL) { 82 ptr = strrchr(path, '/'); 83 if (ptr == NULL) 84 break; 85 86 *ptr = '\0'; 87 88 if (rmdir(path)) 89 break; 90 } 91 free(path); 92} 93 94/** 95 * Replaces all needles by a given value. 96 * 97 * @1 Haystack (which gets free'd in the function) 98 * @2 Needle 99 * @3 Needle replacement 100 * 101 * Returns: Newly allocated haysteck after replacement. 102 */ 103static char *replace_str(char *hay, char *needle, char *replacement) { 104 char *ptr, *start, *bptr, *buf; 105 int occurences, j; 106 size_t needle_len; 107 size_t replacement_len; 108 size_t haystack_len; 109 110 if (replacement == NULL || *replacement=='\0') 111 return hay; 112 113 if (needle == NULL || *needle=='\0') 114 return hay; 115 116 occurences = 0; 117 j = 0; 118 for (ptr = hay; *ptr != '\0'; ++ptr) { 119 if (needle[j] == *ptr) { 120 ++j; 121 if (needle[j] == '\0') { 122 *(ptr-j+1) = '\0'; // mark occurence 123 occurences++; 124 j = 0; 125 } 126 } else { 127 j=0; 128 } 129 } 130 131 if (occurences <= 0) 132 return hay; 133 134 haystack_len = (size_t)(ptr - hay); 135 replacement_len = strlen(replacement); 136 needle_len = strlen(needle); 137 138 buf = xmalloc(haystack_len + (replacement_len - needle_len) * occurences + 1); 139 start = hay; 140 ptr = hay; 141 142 bptr = buf; 143 while (occurences-- > 0) { 144 while (*ptr != '\0') 145 ++ptr; 146 147 if (ptr-start > 0) { 148 memcpy(bptr, start, ptr - start); 149 bptr +=ptr - start; 150 } 151 152 memcpy(bptr, replacement, replacement_len); 153 bptr+=replacement_len; 154 ptr += needle_len; 155 start = ptr; 156 } 157 158 while (*ptr != '\0') 159 ++ptr; 160 161 if (ptr-start > 0) { 162 memcpy(bptr, start, ptr - start); 163 bptr +=ptr - start; 164 } 165 *bptr='\0'; 166 167 free(hay); 168 169 return buf; 170} 171 172/** 173 * Trivial utility, figuring out whether a character is escaped or not. 174 * 175 * @1 Haystack 176 * @2 Pointer to the character in question 177 * 178 * Returns: 1 if escaped, 0 otherwise 179 */ 180static inline int isescaped(char *hay, char *ptr) { 181 if (ptr <= hay) 182 return 0; 183 184 if (*(ptr-1) != '\\') 185 return 0; 186 187 return 1; 188} 189 190/** 191 * Performs replacement of all keys by their value based on the hotplug 192 * event structure. Keys are identified as strings %KEY%. 193 * 194 * @1 Haystack 195 * @2 Hotplug event structure 196 * 197 * Returns: Newly allocated haystack (old is freed) 198 */ 199static char *replace_key_by_value(char *hay, struct hotplug2_event_t *event) { 200 char *sptr = hay, *ptr = hay; 201 char *buf, *replacement; 202 203 while ((sptr = strchr(sptr, '%')) != NULL) { 204 ptr = strchr(sptr+1, '%'); 205 if (ptr != NULL) { 206 buf = xmalloc(ptr - sptr + 2); 207 buf[ptr - sptr + 1] = '\0'; 208 memcpy(buf, sptr, ptr - sptr + 1); 209 210 buf[ptr - sptr] = '\0'; 211 replacement = get_hotplug2_value_by_key(event, &buf[1]); 212 buf[ptr - sptr] = '%'; 213 214 if (replacement != NULL) { 215 hay = replace_str(hay, buf, replacement); 216 sptr = hay; 217 } else { 218 sptr++; 219 } 220 221 free(buf); 222 } else { 223 sptr++; 224 } 225 } 226 227 hay = replace_str(hay, "%\\", "%"); 228 229 return hay; 230} 231 232/** 233 * Obtains all information from hotplug event structure about a device node. 234 * Creates the device node at a given path (expandable by keys) and with 235 * given mode. 236 * 237 * @1 Hotplug event structure 238 * @2 Path (may contain keys) 239 * @3 Mode of the file 240 * 241 * Returns: 0 if success, non-zero otherwise 242 */ 243static int make_dev_from_event(struct hotplug2_event_t *event, char *path, mode_t devmode) { 244 char *subsystem, *major, *minor, *devpath; 245 int rv = 1; 246 247 major = get_hotplug2_value_by_key(event, "MAJOR"); 248 if (major == NULL) 249 goto return_value; 250 251 minor = get_hotplug2_value_by_key(event, "MINOR"); 252 if (minor == NULL) 253 goto return_value; 254 255 devpath = get_hotplug2_value_by_key(event, "DEVPATH"); 256 if (devpath == NULL) 257 goto return_value; 258 259 subsystem = get_hotplug2_value_by_key(event, "SUBSYSTEM"); 260 if (!strcmp(subsystem, "block")) 261 devmode |= S_IFBLK; 262 else 263 devmode |= S_IFCHR; 264 265 path = replace_key_by_value(path, event); 266 mkdir_p(path); 267 rv = mknod(path, devmode, makedev(atoi(major), atoi(minor))); 268 269 /* 270 * Fixes an issue caused by devmode being modified by umask. 271 */ 272 chmod(path, devmode); 273 274 free(path); 275 276return_value: 277 return rv; 278} 279 280/** 281 * Execute an application without invoking a shell. 282 * 283 * @1 Hotplug event structure 284 * @2 Path to the application, with expandable keys 285 * @3 Argv for the application, with expandable keys 286 * 287 * Returns: Exit status of the application. 288 */ 289static int exec_noshell(struct hotplug2_event_t *event, char *application, char **argv) { 290 pid_t p; 291 int i, status; 292 293 p = fork(); 294 switch (p) { 295 case -1: 296 return -1; 297 case 0: 298 application = replace_key_by_value(strdup(application), event); 299 for (i = 0; argv[i] != NULL; i++) { 300 argv[i] = replace_key_by_value(argv[i], event); 301 } 302 execvp(application, argv); 303 exit(127); 304 break; 305 default: 306 if (waitpid(p, &status, 0) == -1) 307 return -1; 308 309 return WEXITSTATUS(status); 310 break; 311 } 312} 313 314/** 315 * Execute an application while invoking a shell. 316 * 317 * @1 Hotplug event structure 318 * @2 The application and all its arguments, with expandable keys 319 * 320 * Returns: Exit status of the application. 321 */ 322static int exec_shell(struct hotplug2_event_t *event, char *application) { 323 int rv; 324 325 application = replace_key_by_value(strdup(application), event); 326 rv = WEXITSTATUS(system(application)); 327 free(application); 328 return rv; 329} 330 331/** 332 * Create a symlink, with necessary parent directories. 333 * 334 * @1 Hotplug event structure 335 * @2 Link target, with expandable keys 336 * @3 Link name, with expandable keys 337 * 338 * Returns: return value of symlink() 339 */ 340static int make_symlink(struct hotplug2_event_t *event, char *target, char *linkname) { 341 int rv; 342 343 target = replace_key_by_value(strdup(target), event); 344 linkname = replace_key_by_value(strdup(linkname), event); 345 346 mkdir_p(linkname); 347 rv = symlink(target, linkname); 348 349 free(target); 350 free(linkname); 351 352 return rv; 353} 354 355/** 356 * Chmod a given file. 357 * 358 * @1 Hotplug event structure 359 * @2 File name, with expandable keys 360 * @3 Chmod value, with expandable keys 361 * 362 * Returns: return value of chmod() 363 */ 364static int chmod_file(struct hotplug2_event_t *event, char *file, char *value) { 365 int rv; 366 367 file = replace_key_by_value(strdup(file), event); 368 value = replace_key_by_value(strdup(value), event); 369 370 rv = chmod(file, strtoul(value, 0, 8)); 371 372 free(file); 373 free(value); 374 375 return rv; 376} 377 378 379/** 380 * Change owner or group of a given file. 381 * 382 * @1 Hotplug event structure 383 * @2 Whether we chown or chgrp 384 * @3 Filename, with expandable keys 385 * @4 Group or user name, with expandable keys 386 * 387 * Returns: return value of chown() 388 */ 389static int chown_chgrp(struct hotplug2_event_t *event, int action, char *file, char *param) { 390 struct group *grp; 391 struct passwd *pwd; 392 int rv; 393 394 file = replace_key_by_value(strdup(file), event); 395 param = replace_key_by_value(strdup(param), event); 396 397 rv = -1; 398 399 switch (action) { 400 case ACT_CHOWN: 401 pwd = getpwnam(param); 402 rv = chown(file, pwd->pw_uid, -1); 403 break; 404 case ACT_CHGRP: 405 grp = getgrnam(param); 406 rv = chown(file, -1, grp->gr_gid); 407 break; 408 } 409 410 free(file); 411 free(param); 412 413 return rv; 414} 415 416/** 417 * Prints all uevent keys. 418 * 419 * @1 Hotplug event structure 420 * 421 * Returns: void 422 */ 423static void print_debug(struct hotplug2_event_t *event) { 424 int i; 425 426 for (i = 0; i < event->env_vars_c; i++) 427 printf("%s=%s\n", event->env_vars[i].key, event->env_vars[i].value); 428} 429 430/** 431 * Evaluates a condition according to a given hotplug event structure. 432 * 433 * @1 Hotplug event structure 434 * @2 Condition to be evaluated 435 * 436 * Returns: 1 if match, 0 if no match, EVAL_NOT_AVAILABLE if unable to 437 * perform evaluation 438 */ 439int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) { 440 int rv; 441 char *event_value = NULL; 442 regex_t preg; 443 444 event_value = get_hotplug2_value_by_key(event, condition->key); 445 446 switch (condition->type) { 447 case COND_MATCH_CMP: 448 case COND_NMATCH_CMP: 449 if (event_value == NULL) 450 return EVAL_NOT_AVAILABLE; 451 452 rv = strcmp(condition->value, event_value) ? EVAL_NOT_MATCH : EVAL_MATCH; 453 if (condition->type == COND_NMATCH_CMP) 454 rv = !rv; 455 456 return rv; 457 458 case COND_MATCH_RE: 459 case COND_NMATCH_RE: 460 if (event_value == NULL) 461 return EVAL_NOT_AVAILABLE; 462 463 regcomp(&preg, condition->value, REG_EXTENDED | REG_NOSUB); 464 465 rv = regexec(&preg, event_value, 0, NULL, 0) ? EVAL_NOT_MATCH : EVAL_MATCH; 466 if (condition->type == COND_NMATCH_RE) 467 rv = !rv; 468 469 regfree(&preg); 470 471 return rv; 472 473 case COND_MATCH_IS: 474 if (!strcasecmp(condition->value, "set")) 475 return event_value != NULL; 476 477 if (!strcasecmp(condition->value, "unset")) 478 return event_value == NULL; 479 } 480 481 return EVAL_NOT_AVAILABLE; 482} 483 484/** 485 * Creates a "key=value" string from the given key and value 486 * 487 * @1 Key 488 * @2 Value 489 * 490 * Returns: Newly allocated string in "key=value" form 491 * 492 */ 493static char* alloc_env(const char *key, const char *value) { 494 size_t keylen, vallen; 495 char *combined; 496 497 keylen = strlen(key); 498 vallen = strlen(value) + 1; 499 500 combined = xmalloc(keylen + vallen + 1); 501 memcpy(combined, key, keylen); 502 combined[keylen] = '='; 503 memcpy(&combined[keylen + 1], value, vallen); 504 505 return combined; 506} 507 508/** 509 * Executes a rule. Contains evaluation of all conditions prior 510 * to execution. 511 * 512 * @1 Hotplug event structure 513 * @2 The rule to be executed 514 * 515 * Returns: 0 if success, -1 if the whole event is to be 516 * discared, 1 if bail out of this particular rule was required 517 */ 518int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) { 519 int i, last_rv, res; 520 char **env; 521 522 for (i = 0; i < rule->conditions_c; i++) { 523 if (rule_condition_eval(event, &(rule->conditions[i])) != EVAL_MATCH) 524 return 0; 525 } 526 527 res = 0; 528 last_rv = 0; 529 530 env = xmalloc(sizeof(char *) * event->env_vars_c); 531 for (i = 0; i < event->env_vars_c; i++) { 532 env[i] = alloc_env(event->env_vars[i].key, event->env_vars[i].value); 533 putenv(env[i]); 534 } 535 536 for (i = 0; i < rule->actions_c; i++) { 537 switch (rule->actions[i].type) { 538 case ACT_STOP_PROCESSING: 539 res = 1; 540 break; 541 case ACT_STOP_IF_FAILED: 542 if (last_rv != 0) 543 res = 1; 544 break; 545 case ACT_NEXT_EVENT: 546 res = -1; 547 break; 548 case ACT_NEXT_IF_FAILED: 549 if (last_rv != 0) 550 res = -1; 551 break; 552 case ACT_MAKE_DEVICE: 553 last_rv = make_dev_from_event(event, rule->actions[i].parameter[0], strtoul(rule->actions[i].parameter[1], NULL, 0)); 554 break; 555 case ACT_CHMOD: 556 last_rv = chmod_file(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); 557 break; 558 case ACT_CHOWN: 559 case ACT_CHGRP: 560 last_rv = chown_chgrp(event, rule->actions[i].type, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); 561 break; 562 case ACT_SYMLINK: 563 last_rv = make_symlink(event, rule->actions[i].parameter[0], rule->actions[i].parameter[1]); 564 break; 565 case ACT_RUN_SHELL: 566 last_rv = exec_shell(event, rule->actions[i].parameter[0]); 567 break; 568 case ACT_RUN_NOSHELL: 569 last_rv = exec_noshell(event, rule->actions[i].parameter[0], rule->actions[i].parameter); 570 break; 571 case ACT_SETENV: 572 last_rv = setenv(rule->actions[i].parameter[0], rule->actions[i].parameter[1], 1); 573 break; 574 case ACT_REMOVE: 575 last_rv = unlink(rule->actions[i].parameter[0]); 576 rmdir_p(rule->actions[i].parameter[0]); 577 break; 578 case ACT_DEBUG: 579 print_debug(event); 580 last_rv = 0; 581 break; 582 } 583 if (res != 0) 584 break; 585 } 586 587 for (i = 0; i < event->env_vars_c; i++) { 588 unsetenv(event->env_vars[i].key); 589 free(env[i]); 590 } 591 free(env); 592 593 return res; 594} 595 596/** 597 * Sets the flags of the given rule. 598 * 599 * @1 Rule structure 600 * 601 * Returns: void 602 */ 603void rule_flags(struct rule_t *rule) { 604 int i; 605 606 for (i = 0; i < rule->actions_c; i++) { 607 switch (rule->actions[i].type) { 608 case ACT_FLAG_NOTHROTTLE: 609 rule->flags |= FLAG_NOTHROTTLE; 610 break; 611 } 612 } 613 614 return; 615} 616 617/** 618 * Checks whether the given character should initiate 619 * further parsing. 620 * 621 * @1 Character to examine 622 * 623 * Returns: 1 if it should, 0 otherwise 624 */ 625static inline int isinitiator(int c) { 626 switch (c) { 627 case ',': 628 case ';': 629 case '{': 630 case '}': 631 return 1; 632 } 633 634 return 0; 635} 636 637/** 638 * Appends a character to a buffer. Enlarges if necessary. 639 * 640 * @1 Pointer to the buffer 641 * @2 Pointer to buffer size 642 * @3 Pointer to last buffer character 643 * @4 Appended character 644 * 645 * Returns: void 646 */ 647static inline void add_buffer(char **buf, int *blen, int *slen, char c) { 648 if (*slen + 1 >= *blen) { 649 *blen = *blen + 64; 650 *buf = xrealloc(*buf, *blen); 651 } 652 653 (*buf)[*slen] = c; 654 (*buf)[*slen+1] = '\0'; 655 *slen += 1; 656} 657 658/** 659 * Parses a string into a syntactically acceptable value. 660 * 661 * @1 Input string 662 * @2 Pointer to the new position 663 * 664 * Returns: Newly allocated string. 665 */ 666static char *rules_get_value(char *input, char **nptr) { 667 int quotes = QUOTES_NONE; 668 char *ptr = input; 669 670 int blen, slen; 671 char *buf; 672 673 blen = slen = 0; 674 buf = NULL; 675 676 if (isinitiator(*ptr)) { 677 add_buffer(&buf, &blen, &slen, *ptr); 678 ptr++; 679 goto return_value; 680 } 681 682 while (isspace(*ptr) && *ptr != '\0') 683 ptr++; 684 685 if (*ptr == '\0') 686 return NULL; 687 688 switch (*ptr) { 689 case '"': 690 quotes = QUOTES_DOUBLE; 691 ptr++; 692 break; 693 case '\'': 694 quotes = QUOTES_SINGLE; 695 ptr++; 696 break; 697 } 698 699 if (quotes != QUOTES_NONE) { 700 while (quotes != QUOTES_NONE) { 701 switch (*ptr) { 702 case '\\': 703 ptr++; 704 add_buffer(&buf, &blen, &slen, *ptr); 705 break; 706 case '"': 707 if (quotes == QUOTES_DOUBLE) 708 quotes = QUOTES_NONE; 709 break; 710 case '\'': 711 if (quotes == QUOTES_SINGLE) 712 quotes = QUOTES_NONE; 713 break; 714 default: 715 add_buffer(&buf, &blen, &slen, *ptr); 716 break; 717 } 718 ptr++; 719 } 720 } else { 721 while (!isspace(*ptr) && *ptr != '\0') { 722 if (isinitiator(*ptr)) 723 break; 724 725 if (*ptr == '\\') 726 ptr++; 727 728 add_buffer(&buf, &blen, &slen, *ptr); 729 ptr++; 730 } 731 } 732 733return_value: 734 while (isspace(*ptr) && *ptr != '\0') 735 ptr++; 736 737 if (nptr != NULL) 738 *nptr = ptr; 739 740 return buf; 741} 742 743/** 744 * Releases all memory associated with the ruleset. TODO: Make 745 * the behavior same for all _free() functions, ie. either 746 * release the given pointer itself or keep it, but do it 747 * in all functions! 748 * 749 * @1 The ruleset to be freed 750 * 751 * Returns: void 752 */ 753void rules_free(struct rules_t *rules) { 754 int i, j, k; 755 756 for (i = 0; i < rules->rules_c; i++) { 757 for (j = 0; j < rules->rules[i].actions_c; j++) { 758 if (rules->rules[i].actions[j].parameter != NULL) { 759 for (k = 0; rules->rules[i].actions[j].parameter[k] != NULL; k++) 760 free(rules->rules[i].actions[j].parameter[k]); 761 free(rules->rules[i].actions[j].parameter); 762 } 763 } 764 for (j = 0; j < rules->rules[i].conditions_c; j++) { 765 free(rules->rules[i].conditions[j].key); 766 free(rules->rules[i].conditions[j].value); 767 } 768 free(rules->rules[i].actions); 769 free(rules->rules[i].conditions); 770 } 771 free(rules->rules); 772} 773 774/** 775 * Includes a rule file. 776 * 777 * @1 Filename 778 * @2 The ruleset structure 779 * 780 * Returns: 0 if success, -1 otherwise 781 */ 782int rules_include(const char *filename, struct rules_t **return_rules) { 783 struct filemap_t filemap; 784 struct rules_t *rules; 785 786 if (map_file(filename, &filemap)) { 787 ERROR("rules parse","Unable to open/mmap rules file."); 788 return -1; 789 } 790 791 rules = rules_from_config((char*)(filemap.map), *return_rules); 792 if (rules == NULL) { 793 ERROR("rules parse","Unable to parse rules file."); 794 return -1; 795 } 796 *return_rules = rules; 797 798 unmap_file(&filemap); 799 800 return 0; 801} 802 803/** 804 * Parses an entire file of rules. 805 * 806 * @1 The whole file in memory or mmap'd 807 * 808 * Returns: A newly allocated ruleset. 809 */ 810struct rules_t *rules_from_config(char *input, struct rules_t *return_rules) { 811 #define last_rule return_rules->rules[return_rules->rules_c - 1] 812 int nested; 813 int status; 814 int terminate; 815 char *buf; 816 817 /* 818 * TODO: cleanup 819 * 820 * BIIIG cleanup... Use callbacks for actions and for internal actions. 821 */ 822 823 int i, j; 824 struct key_rec_t conditions[] = { /*NOTE: We never have parameters for conditions. */ 825 {"is", 0, COND_MATCH_IS}, 826 {"==", 0, COND_MATCH_CMP}, 827 {"!=", 0, COND_NMATCH_CMP}, 828 {"~~", 0, COND_MATCH_RE}, 829 {"!~", 0, COND_NMATCH_RE}, 830 {NULL, 0, -1} 831 }; 832 833 struct key_rec_t actions[] = { 834 /*one line / one command*/ 835 {"run", 1, ACT_RUN_SHELL}, 836 {"exec", -1, ACT_RUN_NOSHELL}, 837 {"break", 0, ACT_STOP_PROCESSING}, 838 {"break_if_failed", 0, ACT_STOP_IF_FAILED}, 839 {"next", 0, ACT_NEXT_EVENT}, 840 {"next_if_failed", 0, ACT_NEXT_IF_FAILED}, 841 {"chown", 2, ACT_CHOWN}, 842 {"chmod", 2, ACT_CHMOD}, 843 {"chgrp", 2, ACT_CHGRP}, 844 {"setenv", 2, ACT_SETENV}, 845 {"remove", 1, ACT_REMOVE}, 846 {"nothrottle", 0, ACT_FLAG_NOTHROTTLE}, 847 {"printdebug", 0, ACT_DEBUG}, 848 /*symlink*/ 849 {"symlink", 2, ACT_SYMLINK}, 850 {"softlink", 2, ACT_SYMLINK}, 851 /*makedev*/ 852 {"mknod", 2, ACT_MAKE_DEVICE}, 853 {"makedev", 2, ACT_MAKE_DEVICE}, 854 {NULL, 0, -1} 855 }; 856 857 /* 858 * A little trick for inclusion. 859 */ 860 if (return_rules == NULL) { 861 return_rules = xmalloc(sizeof(struct rules_t)); 862 return_rules->rules_c = 1; 863 return_rules->rules = xmalloc(sizeof(struct rule_t) * return_rules->rules_c); 864 nested = 0; 865 } else { 866 nested = 1; 867 } 868 869 status = STATUS_KEY; 870 871 last_rule.actions = NULL; 872 last_rule.actions_c = 0; 873 last_rule.conditions = NULL; 874 last_rule.conditions_c = 0; 875 876 terminate = 0; 877 do { 878 buf = rules_get_value(input, &input); 879 if (buf == NULL) { 880 ERROR("rules_get_value", "Malformed rule - unable to read!"); 881 terminate = 1; 882 break; 883 } 884 885 if (buf[0] == '#') { 886 /* Skip to next line */ 887 while (*input != '\0' && *input != '\n') 888 input++; 889 890 free(buf); 891 continue; 892 } else if (buf[0] == '$') { 893 buf++; 894 895 /* 896 * Warning, hack ahead... 897 */ 898 if (!strcmp("include", buf)) { 899 buf = rules_get_value(input, &input); 900 if (rules_include(buf, &return_rules)) { 901 ERROR("rules_include", "Unable to include ruleset '%s'!", buf); 902 } 903 } 904 905 free(buf); 906 continue; 907 } 908 909 switch (status) { 910 case STATUS_KEY: 911 last_rule.conditions_c++; 912 last_rule.conditions = xrealloc(last_rule.conditions, sizeof(struct condition_t) * last_rule.conditions_c); 913 last_rule.conditions[last_rule.conditions_c-1].key = strdup(buf); 914 915 status = STATUS_CONDTYPE; 916 break; 917 case STATUS_CONDTYPE: 918 last_rule.conditions[last_rule.conditions_c-1].type = -1; 919 920 for (i = 0; conditions[i].key != NULL; i++) { 921 if (!strcmp(conditions[i].key, buf)) { 922 last_rule.conditions[last_rule.conditions_c-1].type = conditions[i].type; 923 break; 924 } 925 } 926 927 if (last_rule.conditions[last_rule.conditions_c-1].type == -1) { 928 ERROR("rules_get_value / status / condtype", "Malformed rule - unknown condition type."); 929 terminate = 1; 930 } 931 932 status = STATUS_VALUE; 933 break; 934 case STATUS_VALUE: 935 last_rule.conditions[last_rule.conditions_c-1].value = strdup(buf); 936 937 status = STATUS_INITIATOR; 938 break; 939 case STATUS_INITIATOR: 940 if (!strcmp(buf, ",") || !strcmp(buf, ";")) { 941 status = STATUS_KEY; 942 } else if (!strcmp(buf, "{")) { 943 status = STATUS_ACTION; 944 } else { 945 ERROR("rules_get_value / status / initiator", "Malformed rule - unknown initiator."); 946 terminate = 1; 947 } 948 break; 949 case STATUS_ACTION: 950 if (!strcmp(buf, "}")) { 951 status = STATUS_KEY; 952 return_rules->rules_c++; 953 return_rules->rules = xrealloc(return_rules->rules, sizeof(struct rule_t) * return_rules->rules_c); 954 955 last_rule.actions = NULL; 956 last_rule.actions_c = 0; 957 last_rule.conditions = NULL; 958 last_rule.conditions_c = 0; 959 break; 960 } 961 962 last_rule.actions_c++; 963 last_rule.actions = xrealloc(last_rule.actions, sizeof(struct action_t) * last_rule.actions_c); 964 last_rule.actions[last_rule.actions_c-1].parameter = NULL; 965 last_rule.actions[last_rule.actions_c-1].type = -1; 966 967 for (i = 0; actions[i].key != NULL; i++) { 968 if (!strcmp(actions[i].key, buf)) { 969 last_rule.actions[last_rule.actions_c-1].type = actions[i].type; 970 break; 971 } 972 } 973 974 if (last_rule.actions[last_rule.actions_c-1].type == -1) { 975 ERROR("rules_get_value / status / action", "Malformed rule - unknown action: %s.", buf); 976 terminate = 1; 977 } 978 979 if (actions[i].param > 0) { 980 last_rule.actions[last_rule.actions_c-1].parameter = xmalloc(sizeof(char*) * (actions[i].param + 1)); 981 last_rule.actions[last_rule.actions_c-1].parameter[actions[i].param] = NULL; 982 983 for (j = 0; j < actions[i].param; j++) { 984 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 985 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], "}")) { 986 ERROR("rules_get_value / status / action", "Malformed rule - not enough parameters passed."); 987 status = STATUS_KEY; 988 break; 989 } 990 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\}", "}"); 991 } 992 } else if (actions[i].param == -1) { 993 j = 0; 994 last_rule.actions[last_rule.actions_c-1].parameter = xmalloc(sizeof(char*) * (j + 1)); 995 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 996 while (last_rule.actions[last_rule.actions_c-1].parameter[j] != NULL) { 997 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], ";")) { 998 break; 999 } 1000 if (!strcmp(last_rule.actions[last_rule.actions_c-1].parameter[j], "}")) { 1001 ERROR("rules_get_value / status / action", "Malformed rule - missing parameter terminator ';'."); 1002 status = STATUS_KEY; 1003 break; 1004 } 1005 if (last_rule.actions[last_rule.actions_c-1].parameter[j][0] == '\0') { 1006 ERROR("rules_get_value / status / action", "Malformed rule - missing parameter terminator ';'."); 1007 status = STATUS_KEY; 1008 break; 1009 } 1010 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\}", "}"); 1011 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\;", ";"); 1012 1013 j++; 1014 last_rule.actions[last_rule.actions_c-1].parameter = xrealloc(last_rule.actions[last_rule.actions_c-1].parameter, sizeof(char*) * (j + 1)); 1015 last_rule.actions[last_rule.actions_c-1].parameter[j] = rules_get_value(input, &input); 1016 } 1017 free(last_rule.actions[last_rule.actions_c-1].parameter[j]); 1018 last_rule.actions[last_rule.actions_c-1].parameter[j] = NULL; 1019 } 1020 1021 if (status == STATUS_KEY) { 1022 return_rules->rules_c++; 1023 return_rules->rules = xrealloc(return_rules->rules, sizeof(struct rule_t) * return_rules->rules_c); 1024 1025 last_rule.actions = NULL; 1026 last_rule.actions_c = 0; 1027 last_rule.conditions = NULL; 1028 last_rule.conditions_c = 0; 1029 } 1030 break; 1031 } 1032 1033 free(buf); 1034 } while (*input != '\0' && !terminate); 1035 1036 if (!terminate) { 1037 /* A little bit hacky cleanup */ 1038 if (!nested) 1039 return_rules->rules_c--; 1040 return return_rules; 1041 } else { 1042 /* 1043 * We don't want to cleanup if we're nested. 1044 */ 1045 if (!nested) { 1046 rules_free(return_rules); 1047 free(return_rules); 1048 } 1049 1050 return NULL; 1051 } 1052} 1053