init_parser.c revision e46f9d510db9351682cf17c49115110870147335
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <fcntl.h>
21#include <stdarg.h>
22#include <string.h>
23#include <stddef.h>
24#include <ctype.h>
25
26#include "init.h"
27#include "parser.h"
28#include "init_parser.h"
29#include "log.h"
30#include "property_service.h"
31#include "util.h"
32
33#include <cutils/iosched_policy.h>
34#include <cutils/list.h>
35
36#define _REALLY_INCLUDE_SYS__SYSTEM_PROPERTIES_H_
37#include <sys/_system_properties.h>
38
39static list_declare(service_list);
40static list_declare(action_list);
41static list_declare(action_queue);
42
43struct import {
44    struct listnode list;
45    const char *filename;
46};
47
48static void *parse_service(struct parse_state *state, int nargs, char **args);
49static void parse_line_service(struct parse_state *state, int nargs, char **args);
50
51static void *parse_action(struct parse_state *state, int nargs, char **args);
52static void parse_line_action(struct parse_state *state, int nargs, char **args);
53
54#define SECTION 0x01
55#define COMMAND 0x02
56#define OPTION  0x04
57
58#include "keywords.h"
59
60#define KEYWORD(symbol, flags, nargs, func) \
61    [ K_##symbol ] = { #symbol, func, nargs + 1, flags, },
62
63struct {
64    const char *name;
65    int (*func)(int nargs, char **args);
66    unsigned char nargs;
67    unsigned char flags;
68} keyword_info[KEYWORD_COUNT] = {
69    [ K_UNKNOWN ] = { "unknown", 0, 0, 0 },
70#include "keywords.h"
71};
72#undef KEYWORD
73
74#define kw_is(kw, type) (keyword_info[kw].flags & (type))
75#define kw_name(kw) (keyword_info[kw].name)
76#define kw_func(kw) (keyword_info[kw].func)
77#define kw_nargs(kw) (keyword_info[kw].nargs)
78
79int lookup_keyword(const char *s)
80{
81    switch (*s++) {
82    case 'c':
83    if (!strcmp(s, "opy")) return K_copy;
84        if (!strcmp(s, "apability")) return K_capability;
85        if (!strcmp(s, "hdir")) return K_chdir;
86        if (!strcmp(s, "hroot")) return K_chroot;
87        if (!strcmp(s, "lass")) return K_class;
88        if (!strcmp(s, "lass_start")) return K_class_start;
89        if (!strcmp(s, "lass_stop")) return K_class_stop;
90        if (!strcmp(s, "lass_reset")) return K_class_reset;
91        if (!strcmp(s, "onsole")) return K_console;
92        if (!strcmp(s, "hown")) return K_chown;
93        if (!strcmp(s, "hmod")) return K_chmod;
94        if (!strcmp(s, "ritical")) return K_critical;
95        break;
96    case 'd':
97        if (!strcmp(s, "isabled")) return K_disabled;
98        if (!strcmp(s, "omainname")) return K_domainname;
99        break;
100    case 'e':
101        if (!strcmp(s, "xec")) return K_exec;
102        if (!strcmp(s, "xport")) return K_export;
103        break;
104    case 'g':
105        if (!strcmp(s, "roup")) return K_group;
106        break;
107    case 'h':
108        if (!strcmp(s, "ostname")) return K_hostname;
109        break;
110    case 'i':
111        if (!strcmp(s, "oprio")) return K_ioprio;
112        if (!strcmp(s, "fup")) return K_ifup;
113        if (!strcmp(s, "nsmod")) return K_insmod;
114        if (!strcmp(s, "mport")) return K_import;
115        break;
116    case 'k':
117        if (!strcmp(s, "eycodes")) return K_keycodes;
118        break;
119    case 'l':
120        if (!strcmp(s, "oglevel")) return K_loglevel;
121        if (!strcmp(s, "oad_persist_props")) return K_load_persist_props;
122        break;
123    case 'm':
124        if (!strcmp(s, "kdir")) return K_mkdir;
125        if (!strcmp(s, "ount")) return K_mount;
126        break;
127    case 'o':
128        if (!strcmp(s, "n")) return K_on;
129        if (!strcmp(s, "neshot")) return K_oneshot;
130        if (!strcmp(s, "nrestart")) return K_onrestart;
131        break;
132    case 'r':
133        if (!strcmp(s, "estart")) return K_restart;
134        if (!strcmp(s, "estorecon")) return K_restorecon;
135        if (!strcmp(s, "mdir")) return K_rmdir;
136        if (!strcmp(s, "m")) return K_rm;
137        break;
138    case 's':
139        if (!strcmp(s, "eclabel")) return K_seclabel;
140        if (!strcmp(s, "ervice")) return K_service;
141        if (!strcmp(s, "etcon")) return K_setcon;
142        if (!strcmp(s, "etenforce")) return K_setenforce;
143        if (!strcmp(s, "etenv")) return K_setenv;
144        if (!strcmp(s, "etkey")) return K_setkey;
145        if (!strcmp(s, "etprop")) return K_setprop;
146        if (!strcmp(s, "etrlimit")) return K_setrlimit;
147        if (!strcmp(s, "etsebool")) return K_setsebool;
148        if (!strcmp(s, "ocket")) return K_socket;
149        if (!strcmp(s, "tart")) return K_start;
150        if (!strcmp(s, "top")) return K_stop;
151        if (!strcmp(s, "ymlink")) return K_symlink;
152        if (!strcmp(s, "ysclktz")) return K_sysclktz;
153        break;
154    case 't':
155        if (!strcmp(s, "rigger")) return K_trigger;
156        break;
157    case 'u':
158        if (!strcmp(s, "ser")) return K_user;
159        break;
160    case 'w':
161        if (!strcmp(s, "rite")) return K_write;
162        if (!strcmp(s, "ait")) return K_wait;
163        break;
164    }
165    return K_UNKNOWN;
166}
167
168void parse_line_no_op(struct parse_state *state, int nargs, char **args)
169{
170}
171
172static int push_chars(char **dst, int *len, const char *chars, int cnt)
173{
174    if (cnt > *len)
175        return -1;
176
177    memcpy(*dst, chars, cnt);
178    *dst += cnt;
179    *len -= cnt;
180
181    return 0;
182}
183
184int expand_props(char *dst, const char *src, int dst_size)
185{
186    int cnt = 0;
187    char *dst_ptr = dst;
188    const char *src_ptr = src;
189    int src_len;
190    int idx = 0;
191    int ret = 0;
192    int left = dst_size - 1;
193
194    if (!src || !dst || dst_size == 0)
195        return -1;
196
197    src_len = strlen(src);
198
199    /* - variables can either be $x.y or ${x.y}, in case they are only part
200     *   of the string.
201     * - will accept $$ as a literal $.
202     * - no nested property expansion, i.e. ${foo.${bar}} is not supported,
203     *   bad things will happen
204     */
205    while (*src_ptr && left > 0) {
206        char *c;
207        char prop[PROP_NAME_MAX + 1];
208        const char *prop_val;
209        int prop_len = 0;
210
211        c = strchr(src_ptr, '$');
212        if (!c) {
213            while (left-- > 0 && *src_ptr)
214                *(dst_ptr++) = *(src_ptr++);
215            break;
216        }
217
218        memset(prop, 0, sizeof(prop));
219
220        ret = push_chars(&dst_ptr, &left, src_ptr, c - src_ptr);
221        if (ret < 0)
222            goto err_nospace;
223        c++;
224
225        if (*c == '$') {
226            *(dst_ptr++) = *(c++);
227            src_ptr = c;
228            left--;
229            continue;
230        } else if (*c == '\0') {
231            break;
232        }
233
234        if (*c == '{') {
235            c++;
236            while (*c && *c != '}' && prop_len < PROP_NAME_MAX)
237                prop[prop_len++] = *(c++);
238            if (*c != '}') {
239                /* failed to find closing brace, abort. */
240                if (prop_len == PROP_NAME_MAX)
241                    ERROR("prop name too long during expansion of '%s'\n",
242                          src);
243                else if (*c == '\0')
244                    ERROR("unexpected end of string in '%s', looking for }\n",
245                          src);
246                goto err;
247            }
248            prop[prop_len] = '\0';
249            c++;
250        } else if (*c) {
251            while (*c && prop_len < PROP_NAME_MAX)
252                prop[prop_len++] = *(c++);
253            if (prop_len == PROP_NAME_MAX && *c != '\0') {
254                ERROR("prop name too long in '%s'\n", src);
255                goto err;
256            }
257            prop[prop_len] = '\0';
258            ERROR("using deprecated syntax for specifying property '%s', use ${name} instead\n",
259                  prop);
260        }
261
262        if (prop_len == 0) {
263            ERROR("invalid zero-length prop name in '%s'\n", src);
264            goto err;
265        }
266
267        prop_val = property_get(prop);
268        if (!prop_val) {
269            ERROR("property '%s' doesn't exist while expanding '%s'\n",
270                  prop, src);
271            goto err;
272        }
273
274        ret = push_chars(&dst_ptr, &left, prop_val, strlen(prop_val));
275        if (ret < 0)
276            goto err_nospace;
277        src_ptr = c;
278        continue;
279    }
280
281    *dst_ptr = '\0';
282    return 0;
283
284err_nospace:
285    ERROR("destination buffer overflow while expanding '%s'\n", src);
286err:
287    return -1;
288}
289
290void parse_import(struct parse_state *state, int nargs, char **args)
291{
292    struct listnode *import_list = state->priv;
293    struct import *import;
294    char conf_file[PATH_MAX];
295    int ret;
296
297    if (nargs != 2) {
298        ERROR("single argument needed for import\n");
299        return;
300    }
301
302    ret = expand_props(conf_file, args[1], sizeof(conf_file));
303    if (ret) {
304        ERROR("error while handling import on line '%d' in '%s'\n",
305              state->line, state->filename);
306        return;
307    }
308
309    import = calloc(1, sizeof(struct import));
310    import->filename = strdup(conf_file);
311    list_add_tail(import_list, &import->list);
312    INFO("found import '%s', adding to import list", import->filename);
313}
314
315void parse_new_section(struct parse_state *state, int kw,
316                       int nargs, char **args)
317{
318    printf("[ %s %s ]\n", args[0],
319           nargs > 1 ? args[1] : "");
320    switch(kw) {
321    case K_service:
322        state->context = parse_service(state, nargs, args);
323        if (state->context) {
324            state->parse_line = parse_line_service;
325            return;
326        }
327        break;
328    case K_on:
329        state->context = parse_action(state, nargs, args);
330        if (state->context) {
331            state->parse_line = parse_line_action;
332            return;
333        }
334        break;
335    case K_import:
336        parse_import(state, nargs, args);
337        break;
338    }
339    state->parse_line = parse_line_no_op;
340}
341
342static void parse_config(const char *fn, char *s)
343{
344    struct parse_state state;
345    struct listnode import_list;
346    struct listnode *node;
347    char *args[INIT_PARSER_MAXARGS];
348    int nargs;
349
350    nargs = 0;
351    state.filename = fn;
352    state.line = 0;
353    state.ptr = s;
354    state.nexttoken = 0;
355    state.parse_line = parse_line_no_op;
356
357    list_init(&import_list);
358    state.priv = &import_list;
359
360    for (;;) {
361        switch (next_token(&state)) {
362        case T_EOF:
363            state.parse_line(&state, 0, 0);
364            goto parser_done;
365        case T_NEWLINE:
366            state.line++;
367            if (nargs) {
368                int kw = lookup_keyword(args[0]);
369                if (kw_is(kw, SECTION)) {
370                    state.parse_line(&state, 0, 0);
371                    parse_new_section(&state, kw, nargs, args);
372                } else {
373                    state.parse_line(&state, nargs, args);
374                }
375                nargs = 0;
376            }
377            break;
378        case T_TEXT:
379            if (nargs < INIT_PARSER_MAXARGS) {
380                args[nargs++] = state.text;
381            }
382            break;
383        }
384    }
385
386parser_done:
387    list_for_each(node, &import_list) {
388         struct import *import = node_to_item(node, struct import, list);
389         int ret;
390
391         INFO("importing '%s'", import->filename);
392         ret = init_parse_config_file(import->filename);
393         if (ret)
394             ERROR("could not import file '%s' from '%s'\n",
395                   import->filename, fn);
396    }
397}
398
399int init_parse_config_file(const char *fn)
400{
401    char *data;
402    data = read_file(fn, 0);
403    if (!data) return -1;
404
405    parse_config(fn, data);
406    DUMP();
407    return 0;
408}
409
410static int valid_name(const char *name)
411{
412    if (strlen(name) > 16) {
413        return 0;
414    }
415    while (*name) {
416        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
417            return 0;
418        }
419        name++;
420    }
421    return 1;
422}
423
424struct service *service_find_by_name(const char *name)
425{
426    struct listnode *node;
427    struct service *svc;
428    list_for_each(node, &service_list) {
429        svc = node_to_item(node, struct service, slist);
430        if (!strcmp(svc->name, name)) {
431            return svc;
432        }
433    }
434    return 0;
435}
436
437struct service *service_find_by_pid(pid_t pid)
438{
439    struct listnode *node;
440    struct service *svc;
441    list_for_each(node, &service_list) {
442        svc = node_to_item(node, struct service, slist);
443        if (svc->pid == pid) {
444            return svc;
445        }
446    }
447    return 0;
448}
449
450struct service *service_find_by_keychord(int keychord_id)
451{
452    struct listnode *node;
453    struct service *svc;
454    list_for_each(node, &service_list) {
455        svc = node_to_item(node, struct service, slist);
456        if (svc->keychord_id == keychord_id) {
457            return svc;
458        }
459    }
460    return 0;
461}
462
463void service_for_each(void (*func)(struct service *svc))
464{
465    struct listnode *node;
466    struct service *svc;
467    list_for_each(node, &service_list) {
468        svc = node_to_item(node, struct service, slist);
469        func(svc);
470    }
471}
472
473void service_for_each_class(const char *classname,
474                            void (*func)(struct service *svc))
475{
476    struct listnode *node;
477    struct service *svc;
478    list_for_each(node, &service_list) {
479        svc = node_to_item(node, struct service, slist);
480        if (!strcmp(svc->classname, classname)) {
481            func(svc);
482        }
483    }
484}
485
486void service_for_each_flags(unsigned matchflags,
487                            void (*func)(struct service *svc))
488{
489    struct listnode *node;
490    struct service *svc;
491    list_for_each(node, &service_list) {
492        svc = node_to_item(node, struct service, slist);
493        if (svc->flags & matchflags) {
494            func(svc);
495        }
496    }
497}
498
499void action_for_each_trigger(const char *trigger,
500                             void (*func)(struct action *act))
501{
502    struct listnode *node;
503    struct action *act;
504    list_for_each(node, &action_list) {
505        act = node_to_item(node, struct action, alist);
506        if (!strcmp(act->name, trigger)) {
507            func(act);
508        }
509    }
510}
511
512void queue_property_triggers(const char *name, const char *value)
513{
514    struct listnode *node;
515    struct action *act;
516    list_for_each(node, &action_list) {
517        act = node_to_item(node, struct action, alist);
518        if (!strncmp(act->name, "property:", strlen("property:"))) {
519            const char *test = act->name + strlen("property:");
520            int name_length = strlen(name);
521
522            if (!strncmp(name, test, name_length) &&
523                    test[name_length] == '=' &&
524                    (!strcmp(test + name_length + 1, value) ||
525                     !strcmp(test + name_length + 1, "*"))) {
526                action_add_queue_tail(act);
527            }
528        }
529    }
530}
531
532void queue_all_property_triggers()
533{
534    struct listnode *node;
535    struct action *act;
536    list_for_each(node, &action_list) {
537        act = node_to_item(node, struct action, alist);
538        if (!strncmp(act->name, "property:", strlen("property:"))) {
539            /* parse property name and value
540               syntax is property:<name>=<value> */
541            const char* name = act->name + strlen("property:");
542            const char* equals = strchr(name, '=');
543            if (equals) {
544                char prop_name[PROP_NAME_MAX + 1];
545                const char* value;
546                int length = equals - name;
547                if (length > PROP_NAME_MAX) {
548                    ERROR("property name too long in trigger %s", act->name);
549                } else {
550                    memcpy(prop_name, name, length);
551                    prop_name[length] = 0;
552
553                    /* does the property exist, and match the trigger value? */
554                    value = property_get(prop_name);
555                    if (value && (!strcmp(equals + 1, value) ||
556                                  !strcmp(equals + 1, "*"))) {
557                        action_add_queue_tail(act);
558                    }
559                }
560            }
561        }
562    }
563}
564
565void queue_builtin_action(int (*func)(int nargs, char **args), char *name)
566{
567    struct action *act;
568    struct command *cmd;
569
570    act = calloc(1, sizeof(*act));
571    act->name = name;
572    list_init(&act->commands);
573
574    cmd = calloc(1, sizeof(*cmd));
575    cmd->func = func;
576    cmd->args[0] = name;
577    list_add_tail(&act->commands, &cmd->clist);
578
579    list_add_tail(&action_list, &act->alist);
580    action_add_queue_tail(act);
581}
582
583void action_add_queue_tail(struct action *act)
584{
585    list_add_tail(&action_queue, &act->qlist);
586}
587
588struct action *action_remove_queue_head(void)
589{
590    if (list_empty(&action_queue)) {
591        return 0;
592    } else {
593        struct listnode *node = list_head(&action_queue);
594        struct action *act = node_to_item(node, struct action, qlist);
595        list_remove(node);
596        return act;
597    }
598}
599
600int action_queue_empty()
601{
602    return list_empty(&action_queue);
603}
604
605static void *parse_service(struct parse_state *state, int nargs, char **args)
606{
607    struct service *svc;
608    if (nargs < 3) {
609        parse_error(state, "services must have a name and a program\n");
610        return 0;
611    }
612    if (!valid_name(args[1])) {
613        parse_error(state, "invalid service name '%s'\n", args[1]);
614        return 0;
615    }
616
617    svc = service_find_by_name(args[1]);
618    if (svc) {
619        parse_error(state, "ignored duplicate definition of service '%s'\n", args[1]);
620        return 0;
621    }
622
623    nargs -= 2;
624    svc = calloc(1, sizeof(*svc) + sizeof(char*) * nargs);
625    if (!svc) {
626        parse_error(state, "out of memory\n");
627        return 0;
628    }
629    svc->name = args[1];
630    svc->classname = "default";
631    memcpy(svc->args, args + 2, sizeof(char*) * nargs);
632    svc->args[nargs] = 0;
633    svc->nargs = nargs;
634    svc->onrestart.name = "onrestart";
635    list_init(&svc->onrestart.commands);
636    list_add_tail(&service_list, &svc->slist);
637    return svc;
638}
639
640static void parse_line_service(struct parse_state *state, int nargs, char **args)
641{
642    struct service *svc = state->context;
643    struct command *cmd;
644    int i, kw, kw_nargs;
645
646    if (nargs == 0) {
647        return;
648    }
649
650    svc->ioprio_class = IoSchedClass_NONE;
651
652    kw = lookup_keyword(args[0]);
653    switch (kw) {
654    case K_capability:
655        break;
656    case K_class:
657        if (nargs != 2) {
658            parse_error(state, "class option requires a classname\n");
659        } else {
660            svc->classname = args[1];
661        }
662        break;
663    case K_console:
664        svc->flags |= SVC_CONSOLE;
665        break;
666    case K_disabled:
667        svc->flags |= SVC_DISABLED;
668        svc->flags |= SVC_RC_DISABLED;
669        break;
670    case K_ioprio:
671        if (nargs != 3) {
672            parse_error(state, "ioprio optin usage: ioprio <rt|be|idle> <ioprio 0-7>\n");
673        } else {
674            svc->ioprio_pri = strtoul(args[2], 0, 8);
675
676            if (svc->ioprio_pri < 0 || svc->ioprio_pri > 7) {
677                parse_error(state, "priority value must be range 0 - 7\n");
678                break;
679            }
680
681            if (!strcmp(args[1], "rt")) {
682                svc->ioprio_class = IoSchedClass_RT;
683            } else if (!strcmp(args[1], "be")) {
684                svc->ioprio_class = IoSchedClass_BE;
685            } else if (!strcmp(args[1], "idle")) {
686                svc->ioprio_class = IoSchedClass_IDLE;
687            } else {
688                parse_error(state, "ioprio option usage: ioprio <rt|be|idle> <0-7>\n");
689            }
690        }
691        break;
692    case K_group:
693        if (nargs < 2) {
694            parse_error(state, "group option requires a group id\n");
695        } else if (nargs > NR_SVC_SUPP_GIDS + 2) {
696            parse_error(state, "group option accepts at most %d supp. groups\n",
697                        NR_SVC_SUPP_GIDS);
698        } else {
699            int n;
700            svc->gid = decode_uid(args[1]);
701            for (n = 2; n < nargs; n++) {
702                svc->supp_gids[n-2] = decode_uid(args[n]);
703            }
704            svc->nr_supp_gids = n - 2;
705        }
706        break;
707    case K_keycodes:
708        if (nargs < 2) {
709            parse_error(state, "keycodes option requires atleast one keycode\n");
710        } else {
711            svc->keycodes = malloc((nargs - 1) * sizeof(svc->keycodes[0]));
712            if (!svc->keycodes) {
713                parse_error(state, "could not allocate keycodes\n");
714            } else {
715                svc->nkeycodes = nargs - 1;
716                for (i = 1; i < nargs; i++) {
717                    svc->keycodes[i - 1] = atoi(args[i]);
718                }
719            }
720        }
721        break;
722    case K_oneshot:
723        svc->flags |= SVC_ONESHOT;
724        break;
725    case K_onrestart:
726        nargs--;
727        args++;
728        kw = lookup_keyword(args[0]);
729        if (!kw_is(kw, COMMAND)) {
730            parse_error(state, "invalid command '%s'\n", args[0]);
731            break;
732        }
733        kw_nargs = kw_nargs(kw);
734        if (nargs < kw_nargs) {
735            parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
736                kw_nargs > 2 ? "arguments" : "argument");
737            break;
738        }
739
740        cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
741        cmd->func = kw_func(kw);
742        cmd->nargs = nargs;
743        memcpy(cmd->args, args, sizeof(char*) * nargs);
744        list_add_tail(&svc->onrestart.commands, &cmd->clist);
745        break;
746    case K_critical:
747        svc->flags |= SVC_CRITICAL;
748        break;
749    case K_setenv: { /* name value */
750        struct svcenvinfo *ei;
751        if (nargs < 2) {
752            parse_error(state, "setenv option requires name and value arguments\n");
753            break;
754        }
755        ei = calloc(1, sizeof(*ei));
756        if (!ei) {
757            parse_error(state, "out of memory\n");
758            break;
759        }
760        ei->name = args[1];
761        ei->value = args[2];
762        ei->next = svc->envvars;
763        svc->envvars = ei;
764        break;
765    }
766    case K_socket: {/* name type perm [ uid gid ] */
767        struct socketinfo *si;
768        if (nargs < 4) {
769            parse_error(state, "socket option requires name, type, perm arguments\n");
770            break;
771        }
772        if (strcmp(args[2],"dgram") && strcmp(args[2],"stream")
773                && strcmp(args[2],"seqpacket")) {
774            parse_error(state, "socket type must be 'dgram', 'stream' or 'seqpacket'\n");
775            break;
776        }
777        si = calloc(1, sizeof(*si));
778        if (!si) {
779            parse_error(state, "out of memory\n");
780            break;
781        }
782        si->name = args[1];
783        si->type = args[2];
784        si->perm = strtoul(args[3], 0, 8);
785        if (nargs > 4)
786            si->uid = decode_uid(args[4]);
787        if (nargs > 5)
788            si->gid = decode_uid(args[5]);
789        si->next = svc->sockets;
790        svc->sockets = si;
791        break;
792    }
793    case K_user:
794        if (nargs != 2) {
795            parse_error(state, "user option requires a user id\n");
796        } else {
797            svc->uid = decode_uid(args[1]);
798        }
799        break;
800    case K_seclabel:
801#ifdef HAVE_SELINUX
802        if (nargs != 2) {
803            parse_error(state, "seclabel option requires a label string\n");
804        } else {
805            svc->seclabel = args[1];
806        }
807#endif
808        break;
809
810    default:
811        parse_error(state, "invalid option '%s'\n", args[0]);
812    }
813}
814
815static void *parse_action(struct parse_state *state, int nargs, char **args)
816{
817    struct action *act;
818    if (nargs < 2) {
819        parse_error(state, "actions must have a trigger\n");
820        return 0;
821    }
822    if (nargs > 2) {
823        parse_error(state, "actions may not have extra parameters\n");
824        return 0;
825    }
826    act = calloc(1, sizeof(*act));
827    act->name = args[1];
828    list_init(&act->commands);
829    list_add_tail(&action_list, &act->alist);
830        /* XXX add to hash */
831    return act;
832}
833
834static void parse_line_action(struct parse_state* state, int nargs, char **args)
835{
836    struct command *cmd;
837    struct action *act = state->context;
838    int (*func)(int nargs, char **args);
839    int kw, n;
840
841    if (nargs == 0) {
842        return;
843    }
844
845    kw = lookup_keyword(args[0]);
846    if (!kw_is(kw, COMMAND)) {
847        parse_error(state, "invalid command '%s'\n", args[0]);
848        return;
849    }
850
851    n = kw_nargs(kw);
852    if (nargs < n) {
853        parse_error(state, "%s requires %d %s\n", args[0], n - 1,
854            n > 2 ? "arguments" : "argument");
855        return;
856    }
857    cmd = malloc(sizeof(*cmd) + sizeof(char*) * nargs);
858    cmd->func = kw_func(kw);
859    cmd->nargs = nargs;
860    memcpy(cmd->args, args, sizeof(char*) * nargs);
861    list_add_tail(&act->commands, &cmd->clist);
862}
863