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 
31 static list_declare(subsystem_list);
32 
33 static 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 
43 static 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 
lookup_keyword(const char * s)56 static 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 
parse_line_no_op(struct parse_state *,int,char **)70 static void parse_line_no_op(struct parse_state*, int, char**) {
71 }
72 
valid_name(const char * name)73 static int valid_name(const char *name)
74 {
75     while (*name) {
76         if (!isalnum(*name) && (*name != '_') && (*name != '-')) {
77             return 0;
78         }
79         name++;
80     }
81     return 1;
82 }
83 
ueventd_subsystem_find_by_name(const char * name)84 struct ueventd_subsystem *ueventd_subsystem_find_by_name(const char *name)
85 {
86     struct listnode *node;
87     struct ueventd_subsystem *s;
88 
89     list_for_each(node, &subsystem_list) {
90         s = node_to_item(node, struct ueventd_subsystem, slist);
91         if (!strcmp(s->name, name)) {
92             return s;
93         }
94     }
95     return 0;
96 }
97 
parse_subsystem(parse_state * state,int,char ** args)98 static void *parse_subsystem(parse_state* state, int /*nargs*/, char** args) {
99     if (!valid_name(args[1])) {
100         parse_error(state, "invalid subsystem name '%s'\n", args[1]);
101         return 0;
102     }
103 
104     ueventd_subsystem* s = ueventd_subsystem_find_by_name(args[1]);
105     if (s) {
106         parse_error(state, "ignored duplicate definition of subsystem '%s'\n",
107                 args[1]);
108         return 0;
109     }
110 
111     s = (ueventd_subsystem*) calloc(1, sizeof(*s));
112     if (!s) {
113         parse_error(state, "out of memory\n");
114         return 0;
115     }
116     s->name = args[1];
117     s->dirname = "/dev";
118     list_add_tail(&subsystem_list, &s->slist);
119     return s;
120 }
121 
parse_line_subsystem(struct parse_state * state,int nargs,char ** args)122 static void parse_line_subsystem(struct parse_state *state, int nargs,
123         char **args)
124 {
125     struct ueventd_subsystem *s = (ueventd_subsystem*) state->context;
126     int kw;
127 
128     if (nargs == 0) {
129         return;
130     }
131 
132     kw = lookup_keyword(args[0]);
133     switch (kw) {
134     case K_devname:
135         if (!strcmp(args[1], "uevent_devname"))
136             s->devname_src = DEVNAME_UEVENT_DEVNAME;
137         else if (!strcmp(args[1], "uevent_devpath"))
138             s->devname_src = DEVNAME_UEVENT_DEVPATH;
139         else
140             parse_error(state, "invalid devname '%s'\n", args[1]);
141         break;
142 
143     case K_dirname:
144         if (args[1][0] == '/')
145             s->dirname = args[1];
146         else
147             parse_error(state, "dirname '%s' does not start with '/'\n",
148                     args[1]);
149         break;
150 
151     default:
152         parse_error(state, "invalid option '%s'\n", args[0]);
153     }
154 }
155 
parse_new_section(struct parse_state * state,int kw,int nargs,char ** args)156 static void parse_new_section(struct parse_state *state, int kw,
157                        int nargs, char **args)
158 {
159     printf("[ %s %s ]\n", args[0],
160            nargs > 1 ? args[1] : "");
161 
162     switch(kw) {
163     case K_subsystem:
164         state->context = parse_subsystem(state, nargs, args);
165         if (state->context) {
166             state->parse_line = parse_line_subsystem;
167             return;
168         }
169         break;
170     }
171     state->parse_line = parse_line_no_op;
172 }
173 
parse_line(struct parse_state * state,char ** args,int nargs)174 static void parse_line(struct parse_state *state, char **args, int nargs)
175 {
176     int kw = lookup_keyword(args[0]);
177     int kw_nargs = kw_nargs(kw);
178 
179     if (nargs < kw_nargs) {
180         parse_error(state, "%s requires %d %s\n", args[0], kw_nargs - 1,
181             kw_nargs > 2 ? "arguments" : "argument");
182         return;
183     }
184 
185     if (kw_is(kw, SECTION)) {
186         parse_new_section(state, kw, nargs, args);
187     } else if (kw_is(kw, OPTION)) {
188         state->parse_line(state, nargs, args);
189     } else {
190         parse_line_device(state, nargs, args);
191     }
192 }
193 
parse_config(const char * fn,const std::string & data)194 static void parse_config(const char *fn, const std::string& data)
195 {
196     char *args[UEVENTD_PARSER_MAXARGS];
197 
198     int nargs = 0;
199     parse_state state;
200     state.filename = fn;
201     state.line = 1;
202     state.ptr = strdup(data.c_str());  // TODO: fix this code!
203     state.nexttoken = 0;
204     state.parse_line = parse_line_no_op;
205     for (;;) {
206         int token = next_token(&state);
207         switch (token) {
208         case T_EOF:
209             parse_line(&state, args, nargs);
210             return;
211         case T_NEWLINE:
212             if (nargs) {
213                 parse_line(&state, args, nargs);
214                 nargs = 0;
215             }
216             state.line++;
217             break;
218         case T_TEXT:
219             if (nargs < UEVENTD_PARSER_MAXARGS) {
220                 args[nargs++] = state.text;
221             }
222             break;
223         }
224     }
225 }
226 
ueventd_parse_config_file(const char * fn)227 int ueventd_parse_config_file(const char *fn)
228 {
229     std::string data;
230     if (!read_file(fn, &data)) {
231         return -1;
232     }
233 
234     data.push_back('\n'); // TODO: fix parse_config.
235     parse_config(fn, data);
236     return 0;
237 }
238 
parse_line_device(parse_state *,int nargs,char ** args)239 static void parse_line_device(parse_state*, int nargs, char** args) {
240     set_device_permission(nargs, args);
241 }
242