Merge branch 'Teaman-ND' into Teaman-RT
[tomato.git] / release / src / router / hotplug2 / rules.c
blobbb00841f8110b984f0cab70be6bafdb7e7eb31a3
1 /*****************************************************************************\
2 * _ _ _ _ ___ *
3 * | || | ___ | |_ _ __ | | _ _ __ _ |_ ) *
4 * | __ |/ _ \| _|| '_ \| || || |/ _` | / / *
5 * |_||_|\___/ \__|| .__/|_| \_,_|\__, |/___| *
6 * |_| |___/ *
7 \*****************************************************************************/
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>
24 #include "mem_utils.h"
25 #include "filemap_utils.h"
26 #include "hotplug2.h"
27 #include "rules.h"
30 /**
31 * Function supplementing 'mkdir -p'.
33 * @1 Path to be mkdir'd
35 * Returns: void
37 static void mkdir_p(char *path) {
38 char *ptr;
39 struct stat statbuf;
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;
50 for (ptr = path; ptr != NULL; ptr = strchr(ptr, '/')) {
51 if (ptr == path) {
52 ptr++;
53 continue;
56 errno = 0;
57 *ptr='\0';
58 mkdir(path, 0755);
59 *ptr='/';
60 if (errno != 0 && errno != EEXIST)
61 break;
63 ptr++;
65 mkdir(path, 0755);
66 free(path);
69 /**
70 * Function supplementing 'rmdir -p'.
72 * @1 Path to be rmdir'd
74 * Returns: void
76 static void rmdir_p(char *path) {
77 char *ptr;
79 path = strdup(path);
80 ptr = path;
81 while (ptr != NULL) {
82 ptr = strrchr(path, '/');
83 if (ptr == NULL)
84 break;
86 *ptr = '\0';
88 if (rmdir(path))
89 break;
91 free(path);
94 /**
95 * Replaces all needles by a given value.
97 * @1 Haystack (which gets free'd in the function)
98 * @2 Needle
99 * @3 Needle replacement
101 * Returns: Newly allocated haysteck after replacement.
103 static 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;
110 if (replacement == NULL || *replacement=='\0')
111 return hay;
113 if (needle == NULL || *needle=='\0')
114 return hay;
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;
126 } else {
127 j=0;
131 if (occurences <= 0)
132 return hay;
134 haystack_len = (size_t)(ptr - hay);
135 replacement_len = strlen(replacement);
136 needle_len = strlen(needle);
138 buf = xmalloc(haystack_len + (replacement_len - needle_len) * occurences + 1);
139 start = hay;
140 ptr = hay;
142 bptr = buf;
143 while (occurences-- > 0) {
144 while (*ptr != '\0')
145 ++ptr;
147 if (ptr-start > 0) {
148 memcpy(bptr, start, ptr - start);
149 bptr +=ptr - start;
152 memcpy(bptr, replacement, replacement_len);
153 bptr+=replacement_len;
154 ptr += needle_len;
155 start = ptr;
158 while (*ptr != '\0')
159 ++ptr;
161 if (ptr-start > 0) {
162 memcpy(bptr, start, ptr - start);
163 bptr +=ptr - start;
165 *bptr='\0';
167 free(hay);
169 return buf;
173 * Trivial utility, figuring out whether a character is escaped or not.
175 * @1 Haystack
176 * @2 Pointer to the character in question
178 * Returns: 1 if escaped, 0 otherwise
180 static inline int isescaped(char *hay, char *ptr) {
181 if (ptr <= hay)
182 return 0;
184 if (*(ptr-1) != '\\')
185 return 0;
187 return 1;
191 * Performs replacement of all keys by their value based on the hotplug
192 * event structure. Keys are identified as strings %KEY%.
194 * @1 Haystack
195 * @2 Hotplug event structure
197 * Returns: Newly allocated haystack (old is freed)
199 static char *replace_key_by_value(char *hay, struct hotplug2_event_t *event) {
200 char *sptr = hay, *ptr = hay;
201 char *buf, *replacement;
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);
210 buf[ptr - sptr] = '\0';
211 replacement = get_hotplug2_value_by_key(event, &buf[1]);
212 buf[ptr - sptr] = '%';
214 if (replacement != NULL) {
215 hay = replace_str(hay, buf, replacement);
216 sptr = hay;
217 } else {
218 sptr++;
221 free(buf);
222 } else {
223 sptr++;
227 hay = replace_str(hay, "%\\", "%");
229 return hay;
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.
237 * @1 Hotplug event structure
238 * @2 Path (may contain keys)
239 * @3 Mode of the file
241 * Returns: 0 if success, non-zero otherwise
243 static 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;
247 major = get_hotplug2_value_by_key(event, "MAJOR");
248 if (major == NULL)
249 goto return_value;
251 minor = get_hotplug2_value_by_key(event, "MINOR");
252 if (minor == NULL)
253 goto return_value;
255 devpath = get_hotplug2_value_by_key(event, "DEVPATH");
256 if (devpath == NULL)
257 goto return_value;
259 subsystem = get_hotplug2_value_by_key(event, "SUBSYSTEM");
260 if (!strcmp(subsystem, "block"))
261 devmode |= S_IFBLK;
262 else
263 devmode |= S_IFCHR;
265 path = replace_key_by_value(path, event);
266 mkdir_p(path);
267 rv = mknod(path, devmode, makedev(atoi(major), atoi(minor)));
270 * Fixes an issue caused by devmode being modified by umask.
272 chmod(path, devmode);
274 free(path);
276 return_value:
277 return rv;
281 * Execute an application without invoking a shell.
283 * @1 Hotplug event structure
284 * @2 Path to the application, with expandable keys
285 * @3 Argv for the application, with expandable keys
287 * Returns: Exit status of the application.
289 static int exec_noshell(struct hotplug2_event_t *event, char *application, char **argv) {
290 pid_t p;
291 int i, status;
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);
302 execvp(application, argv);
303 exit(127);
304 break;
305 default:
306 if (waitpid(p, &status, 0) == -1)
307 return -1;
309 return WEXITSTATUS(status);
310 break;
315 * Execute an application while invoking a shell.
317 * @1 Hotplug event structure
318 * @2 The application and all its arguments, with expandable keys
320 * Returns: Exit status of the application.
322 static int exec_shell(struct hotplug2_event_t *event, char *application) {
323 int rv;
325 application = replace_key_by_value(strdup(application), event);
326 rv = WEXITSTATUS(system(application));
327 free(application);
328 return rv;
332 * Create a symlink, with necessary parent directories.
334 * @1 Hotplug event structure
335 * @2 Link target, with expandable keys
336 * @3 Link name, with expandable keys
338 * Returns: return value of symlink()
340 static int make_symlink(struct hotplug2_event_t *event, char *target, char *linkname) {
341 int rv;
343 target = replace_key_by_value(strdup(target), event);
344 linkname = replace_key_by_value(strdup(linkname), event);
346 mkdir_p(linkname);
347 rv = symlink(target, linkname);
349 free(target);
350 free(linkname);
352 return rv;
356 * Chmod a given file.
358 * @1 Hotplug event structure
359 * @2 File name, with expandable keys
360 * @3 Chmod value, with expandable keys
362 * Returns: return value of chmod()
364 static int chmod_file(struct hotplug2_event_t *event, char *file, char *value) {
365 int rv;
367 file = replace_key_by_value(strdup(file), event);
368 value = replace_key_by_value(strdup(value), event);
370 rv = chmod(file, strtoul(value, 0, 8));
372 free(file);
373 free(value);
375 return rv;
380 * Change owner or group of a given file.
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
387 * Returns: return value of chown()
389 static 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;
394 file = replace_key_by_value(strdup(file), event);
395 param = replace_key_by_value(strdup(param), event);
397 rv = -1;
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;
410 free(file);
411 free(param);
413 return rv;
417 * Prints all uevent keys.
419 * @1 Hotplug event structure
421 * Returns: void
423 static void print_debug(struct hotplug2_event_t *event) {
424 int i;
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);
431 * Evaluates a condition according to a given hotplug event structure.
433 * @1 Hotplug event structure
434 * @2 Condition to be evaluated
436 * Returns: 1 if match, 0 if no match, EVAL_NOT_AVAILABLE if unable to
437 * perform evaluation
439 int rule_condition_eval(struct hotplug2_event_t *event, struct condition_t *condition) {
440 int rv;
441 char *event_value = NULL;
442 regex_t preg;
444 event_value = get_hotplug2_value_by_key(event, condition->key);
446 switch (condition->type) {
447 case COND_MATCH_CMP:
448 case COND_NMATCH_CMP:
449 if (event_value == NULL)
450 return EVAL_NOT_AVAILABLE;
452 rv = strcmp(condition->value, event_value) ? EVAL_NOT_MATCH : EVAL_MATCH;
453 if (condition->type == COND_NMATCH_CMP)
454 rv = !rv;
456 return rv;
458 case COND_MATCH_RE:
459 case COND_NMATCH_RE:
460 if (event_value == NULL)
461 return EVAL_NOT_AVAILABLE;
463 regcomp(&preg, condition->value, REG_EXTENDED | REG_NOSUB);
465 rv = regexec(&preg, event_value, 0, NULL, 0) ? EVAL_NOT_MATCH : EVAL_MATCH;
466 if (condition->type == COND_NMATCH_RE)
467 rv = !rv;
469 regfree(&preg);
471 return rv;
473 case COND_MATCH_IS:
474 if (!strcasecmp(condition->value, "set"))
475 return event_value != NULL;
477 if (!strcasecmp(condition->value, "unset"))
478 return event_value == NULL;
481 return EVAL_NOT_AVAILABLE;
485 * Creates a "key=value" string from the given key and value
487 * @1 Key
488 * @2 Value
490 * Returns: Newly allocated string in "key=value" form
493 static char* alloc_env(const char *key, const char *value) {
494 size_t keylen, vallen;
495 char *combined;
497 keylen = strlen(key);
498 vallen = strlen(value) + 1;
500 combined = xmalloc(keylen + vallen + 1);
501 memcpy(combined, key, keylen);
502 combined[keylen] = '=';
503 memcpy(&combined[keylen + 1], value, vallen);
505 return combined;
509 * Executes a rule. Contains evaluation of all conditions prior
510 * to execution.
512 * @1 Hotplug event structure
513 * @2 The rule to be executed
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
518 int rule_execute(struct hotplug2_event_t *event, struct rule_t *rule) {
519 int i, last_rv, res;
520 char **env;
522 for (i = 0; i < rule->conditions_c; i++) {
523 if (rule_condition_eval(event, &(rule->conditions[i])) != EVAL_MATCH)
524 return 0;
527 res = 0;
528 last_rv = 0;
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]);
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;
583 if (res != 0)
584 break;
587 for (i = 0; i < event->env_vars_c; i++) {
588 unsetenv(event->env_vars[i].key);
589 free(env[i]);
591 free(env);
593 return res;
597 * Sets the flags of the given rule.
599 * @1 Rule structure
601 * Returns: void
603 void rule_flags(struct rule_t *rule) {
604 int i;
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;
614 return;
618 * Checks whether the given character should initiate
619 * further parsing.
621 * @1 Character to examine
623 * Returns: 1 if it should, 0 otherwise
625 static inline int isinitiator(int c) {
626 switch (c) {
627 case ',':
628 case ';':
629 case '{':
630 case '}':
631 return 1;
634 return 0;
638 * Appends a character to a buffer. Enlarges if necessary.
640 * @1 Pointer to the buffer
641 * @2 Pointer to buffer size
642 * @3 Pointer to last buffer character
643 * @4 Appended character
645 * Returns: void
647 static 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);
653 (*buf)[*slen] = c;
654 (*buf)[*slen+1] = '\0';
655 *slen += 1;
659 * Parses a string into a syntactically acceptable value.
661 * @1 Input string
662 * @2 Pointer to the new position
664 * Returns: Newly allocated string.
666 static char *rules_get_value(char *input, char **nptr) {
667 int quotes = QUOTES_NONE;
668 char *ptr = input;
670 int blen, slen;
671 char *buf;
673 blen = slen = 0;
674 buf = NULL;
676 if (isinitiator(*ptr)) {
677 add_buffer(&buf, &blen, &slen, *ptr);
678 ptr++;
679 goto return_value;
682 while (isspace(*ptr) && *ptr != '\0')
683 ptr++;
685 if (*ptr == '\0')
686 return NULL;
688 switch (*ptr) {
689 case '"':
690 quotes = QUOTES_DOUBLE;
691 ptr++;
692 break;
693 case '\'':
694 quotes = QUOTES_SINGLE;
695 ptr++;
696 break;
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;
718 ptr++;
720 } else {
721 while (!isspace(*ptr) && *ptr != '\0') {
722 if (isinitiator(*ptr))
723 break;
725 if (*ptr == '\\')
726 ptr++;
728 add_buffer(&buf, &blen, &slen, *ptr);
729 ptr++;
733 return_value:
734 while (isspace(*ptr) && *ptr != '\0')
735 ptr++;
737 if (nptr != NULL)
738 *nptr = ptr;
740 return buf;
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!
749 * @1 The ruleset to be freed
751 * Returns: void
753 void rules_free(struct rules_t *rules) {
754 int i, j, k;
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);
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);
768 free(rules->rules[i].actions);
769 free(rules->rules[i].conditions);
771 free(rules->rules);
775 * Includes a rule file.
777 * @1 Filename
778 * @2 The ruleset structure
780 * Returns: 0 if success, -1 otherwise
782 int rules_include(const char *filename, struct rules_t **return_rules) {
783 struct filemap_t filemap;
784 struct rules_t *rules;
786 if (map_file(filename, &filemap)) {
787 ERROR("rules parse","Unable to open/mmap rules file.");
788 return -1;
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;
796 *return_rules = rules;
798 unmap_file(&filemap);
800 return 0;
804 * Parses an entire file of rules.
806 * @1 The whole file in memory or mmap'd
808 * Returns: A newly allocated ruleset.
810 struct 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;
818 * TODO: cleanup
820 * BIIIG cleanup... Use callbacks for actions and for internal actions.
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}
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}
858 * A little trick for inclusion.
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;
869 status = STATUS_KEY;
871 last_rule.actions = NULL;
872 last_rule.actions_c = 0;
873 last_rule.conditions = NULL;
874 last_rule.conditions_c = 0;
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;
885 if (buf[0] == '#') {
886 /* Skip to next line */
887 while (*input != '\0' && *input != '\n')
888 input++;
890 free(buf);
891 continue;
892 } else if (buf[0] == '$') {
893 buf++;
896 * Warning, hack ahead...
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);
905 free(buf);
906 continue;
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);
915 status = STATUS_CONDTYPE;
916 break;
917 case STATUS_CONDTYPE:
918 last_rule.conditions[last_rule.conditions_c-1].type = -1;
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;
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;
932 status = STATUS_VALUE;
933 break;
934 case STATUS_VALUE:
935 last_rule.conditions[last_rule.conditions_c-1].value = strdup(buf);
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;
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);
955 last_rule.actions = NULL;
956 last_rule.actions_c = 0;
957 last_rule.conditions = NULL;
958 last_rule.conditions_c = 0;
959 break;
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;
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;
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;
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;
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;
990 last_rule.actions[last_rule.actions_c-1].parameter[j] = replace_str(last_rule.actions[last_rule.actions_c-1].parameter[j], "\\}", "}");
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;
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;
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;
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], "\\;", ";");
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);
1017 free(last_rule.actions[last_rule.actions_c-1].parameter[j]);
1018 last_rule.actions[last_rule.actions_c-1].parameter[j] = NULL;
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);
1025 last_rule.actions = NULL;
1026 last_rule.actions_c = 0;
1027 last_rule.conditions = NULL;
1028 last_rule.conditions_c = 0;
1030 break;
1033 free(buf);
1034 } while (*input != '\0' && !terminate);
1036 if (!terminate) {
1037 /* A little bit hacky cleanup */
1038 if (!nested)
1039 return_rules->rules_c--;
1040 return return_rules;
1041 } else {
1043 * We don't want to cleanup if we're nested.
1045 if (!nested) {
1046 rules_free(return_rules);
1047 free(return_rules);
1050 return NULL;