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 <ctype.h>
18#include <errno.h>
19#include <stdio.h>
20#include <unistd.h>
21#include <stdarg.h>
22#include <stdlib.h>
23#include <string.h>
24
25#include "ueventd.h"
26#include "ueventd_parser.h"
27#include "parser.h"
28#include "log.h"
29#include "util.h"
30
31static list_declare(subsystem_list);
32
33static void parse_line_device(struct parse_state *state, int nargs, char **args);
34
35#define SECTION 0x01
36#define OPTION  0x02
37
38#include "ueventd_keywords.h"
39
40#define KEYWORD(symbol, flags, nargs) \
41    [ K_##symbol ] = { #symbol, nargs + 1, flags, },
42
43static struct {
44    const char *name;
45    unsigned char nargs;
46    unsigned char flags;
47} keyword_info[KEYWORD_COUNT] = {
48    [ K_UNKNOWN ] = { "unknown", 0, 0 },
49#include "ueventd_keywords.h"
50};
51#undef KEYWORD
52
53#define kw_is(kw, type) (keyword_info[kw].flags & (type))
54#define kw_nargs(kw) (keyword_info[kw].nargs)
55
56static int lookup_keyword(const char *s)
57{
58    switch (*s++) {
59    case 'd':
60        if (!strcmp(s, "evname")) return K_devname;
61        if (!strcmp(s, "irname")) return K_dirname;
62        break;
63    case 's':
64        if (!strcmp(s, "ubsystem")) return K_subsystem;
65        break;
66    }
67    return K_UNKNOWN;
68}
69
70static void parse_line_no_op(struct parse_state *state __attribute__((unused)),
71        int nargs __attribute__((unused)), char **args  __attribute__((unused)))
72{
73}
74
75static int valid_name(const char *name)
76{
77    while (*name) {
78        if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
79            return 0;
80        }
81        name++;
82    }
83    return 1;
84}
85
86struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
87{
88    struct listnode *node;
89    struct ueventd_subsystem *s;
90
91    list_for_each(node, &subsystem_list) {
92        s = node_to_item(node, struct ueventd_subsystem, slist);
93        if (!strcmp(s->name, name)) {
94            return s;
95        }
96    }
97    return 0;
98}
99
100static void *parse_subsystem(struct parse_state *state,
101        int nargs __attribute__((unused)), char **args)
102{
103    struct ueventd_subsystem *s;
104
105    if (!valid_name(args[1])) {
106        parse_error(state, "invalid subsystem name '%s'\n", args[1]);
107        return 0;
108    }
109
110    s = ueventd_subsystem_find_by_name(args[1]);
111    if (s) {
112        parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
113                args[1]);
114        return 0;
115    }
116
117    s = calloc(1, sizeof(*s));
118    if (!s) {
119        parse_error(state, "out of memory\n");
120        return 0;
121    }
122    s->name = args[1];
123    s->dirname = "/dev";
124    list_add_tail(&subsystem_list, &s->slist);
125    return s;
126}
127
128static void parse_line_subsystem(struct parse_state *state, int nargs,
129        char **args)
130{
131    struct ueventd_subsystem *s = state->context;
132    int kw;
133
134    if (nargs == 0) {
135        return;
136    }
137
138    kw = lookup_keyword(args[0]);
139    switch (kw) {
140    case K_devname:
141        if (!strcmp(args[1], "uevent_devname"))
142            s->devname_src = DEVNAME_UEVENT_DEVNAME;
143        else if (!strcmp(args[1], "uevent_devpath"))
144            s->devname_src = DEVNAME_UEVENT_DEVPATH;
145        else
146            parse_error(state, "invalid devname '%s'\n", args[1]);
147        break;
148
149    case K_dirname:
150        if (args[1][0] == '/')
151            s->dirname = args[1];
152        else
153            parse_error(state, "dirname '%s' does not start with '/'\n",
154                    args[1]);
155        break;
156
157    default:
158        parse_error(state, "invalid option '%s'\n", args[0]);
159    }
160}
161
162static void parse_new_section(struct parse_state *state, int kw,
163                       int nargs, char **args)
164{
165    printf("[ %s %s ]\n", args[0],
166           nargs > 1 ? args[1] : "");
167
168    switch(kw) {
169    case K_subsystem:
170        state->context = parse_subsystem(state, nargs, args);
171        if (state->context) {
172            state->parse_line = parse_line_subsystem;
173            return;
174        }
175        break;
176    }
177    state->parse_line = parse_line_no_op;
178}
179
180static void parse_line(struct parse_state *state, char **args, int nargs)
181{
182    int kw = lookup_keyword(args[0]);
183    int kw_nargs = kw_nargs(kw);
184
185    if (nargs < kw_nargs) {
186        parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
187            kw_nargs > 2 ? "arguments" : "argument");
188        return;
189    }
190
191    if (kw_is(kw, SECTION)) {
192        parse_new_section(state, kw, nargs, args);
193    } else if (kw_is(kw, OPTION)) {
194        state->parse_line(state, nargs, args);
195    } else {
196        parse_line_device(state, nargs, args);
197    }
198}
199
200static void parse_config(const char *fn, char *s)
201{
202    struct parse_state state;
203    char *args[UEVENTD_PARSER_MAXARGS];
204    int nargs;
205    nargs = 0;
206    state.filename = fn;
207    state.line = 1;
208    state.ptr = s;
209    state.nexttoken = 0;
210    state.parse_line = parse_line_no_op;
211    for (;;) {
212        int token = next_token(&state);
213        switch (token) {
214        case T_EOF:
215            parse_line(&state, args, nargs);
216            return;
217        case T_NEWLINE:
218            if (nargs) {
219                parse_line(&state, args, nargs);
220                nargs = 0;
221            }
222            state.line++;
223            break;
224        case T_TEXT:
225            if (nargs < UEVENTD_PARSER_MAXARGS) {
226                args[nargs++] = state.text;
227            }
228            break;
229        }
230    }
231}
232
233int ueventd_parse_config_file(const char *fn)
234{
235    char *data;
236    data = read_file(fn, 0);
237    if (!data) return -1;
238
239    parse_config(fn, data);
240    DUMP();
241    return 0;
242}
243
244static void parse_line_device(struct parse_state *state __attribute__((unused)),
245        int nargs, char **args)
246{
247    set_device_permission(nargs, args);
248}
249