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