]> git.ipfire.org Git - thirdparty/systemd.git/blob - udev_rules_parse.c
added translated (jp) version of writing udev rules file.
[thirdparty/systemd.git] / udev_rules_parse.c
1 /*
2 * udev_rules_parse.c
3 *
4 * Userspace devfs
5 *
6 * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com>
7 * Copyright (C) 2003-2005 Kay Sievers <kay.sievers@vrfy.org>
8 *
9 *
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by the
12 * Free Software Foundation version 2 of the License.
13 *
14 * This program is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 675 Mass Ave, Cambridge, MA 02139, USA.
22 *
23 */
24
25 #include <stddef.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <ctype.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #include <errno.h>
33
34 #include "udev_libc_wrapper.h"
35 #include "udev.h"
36 #include "udev_utils.h"
37 #include "logging.h"
38 #include "udev_rules.h"
39
40 LIST_HEAD(udev_rule_list);
41
42 static int add_config_dev(struct udev_rule *rule)
43 {
44 struct udev_rule *tmp_rule;
45
46 tmp_rule = malloc(sizeof(*tmp_rule));
47 if (tmp_rule == NULL)
48 return -ENOMEM;
49 memcpy(tmp_rule, rule, sizeof(struct udev_rule));
50 list_add_tail(&tmp_rule->node, &udev_rule_list);
51
52 dbg("name='%s', symlink='%s', bus='%s', id='%s', "
53 "sysfs_file[0]='%s', sysfs_value[0]='%s', "
54 "kernel='%s', program='%s', result='%s', "
55 "owner='%s', group='%s', mode=%#o, "
56 "all_partions=%u, ignore_remove=%u, ignore_device=%u, last_rule=%u",
57 rule->name, rule->symlink, rule->bus, rule->id,
58 rule->sysfs_pair[0].name, rule->sysfs_pair[0].value,
59 rule->kernel, rule->program, rule->result, rule->owner, rule->group, rule->mode,
60 rule->partitions, rule->ignore_remove, rule->ignore_device, rule->last_rule);
61
62 return 0;
63 }
64
65 static int get_key(char **line, char **key, enum key_operation *operation, char **value)
66 {
67 char *linepos;
68 char *temp;
69
70 linepos = *line;
71 if (!linepos)
72 return -1;
73
74 /* skip whitespace */
75 while (isspace(linepos[0]) || linepos[0] == ',')
76 linepos++;
77
78 /* get the key */
79 *key = linepos;
80 while (1) {
81 linepos++;
82 if (linepos[0] == '\0')
83 return -1;
84 if (isspace(linepos[0]))
85 break;
86 if (linepos[0] == '=')
87 break;
88 if (linepos[0] == '+')
89 break;
90 if (linepos[0] == '!')
91 break;
92 }
93
94 /* remember end of key */
95 temp = linepos;
96
97 /* skip whitespace after key */
98 while (isspace(linepos[0]))
99 linepos++;
100
101 /* get operation type */
102 if (linepos[0] == '=' && linepos[1] == '=') {
103 *operation = KEY_OP_MATCH;
104 linepos += 2;
105 dbg("operator=match");
106 } else if (linepos[0] == '!' && linepos[1] == '=') {
107 *operation = KEY_OP_NOMATCH;
108 linepos += 2;
109 dbg("operator=nomatch");
110 } else if (linepos[0] == '+' && linepos[1] == '=') {
111 *operation = KEY_OP_ADD;
112 linepos += 2;
113 dbg("operator=add");
114 } else if (linepos[0] == '=') {
115 *operation = KEY_OP_ASSIGN;
116 linepos++;
117 dbg("operator=assign");
118 } else
119 return -1;
120
121 /* terminate key */
122 temp[0] = '\0';
123 dbg("key='%s'", *key);
124
125 /* skip whitespace after operator */
126 while (isspace(linepos[0]))
127 linepos++;
128
129 /* get the value*/
130 if (linepos[0] == '"')
131 linepos++;
132 else
133 return -1;
134 *value = linepos;
135
136 temp = strchr(linepos, '"');
137 if (!temp)
138 return -1;
139 temp[0] = '\0';
140 temp++;
141 dbg("value='%s'", *value);
142
143 /* move line to next key */
144 *line = temp;
145
146 return 0;
147 }
148
149 /* extract possible KEY{attr} */
150 static char *get_key_attribute(char *str)
151 {
152 char *pos;
153 char *attr;
154
155 attr = strchr(str, '{');
156 if (attr != NULL) {
157 attr++;
158 pos = strchr(attr, '}');
159 if (pos == NULL) {
160 err("missing closing brace for format");
161 return NULL;
162 }
163 pos[0] = '\0';
164 dbg("attribute='%s'", attr);
165 return attr;
166 }
167
168 return NULL;
169 }
170
171 static int rules_parse(const char *filename)
172 {
173 char line[LINE_SIZE];
174 char *bufline;
175 int lineno;
176 char *linepos;
177 char *attr;
178 char *buf;
179 size_t bufsize;
180 size_t cur;
181 size_t count;
182 int program_given = 0;
183 int valid;
184 int retval = 0;
185 struct udev_rule rule;
186
187 if (file_map(filename, &buf, &bufsize) != 0) {
188 err("can't open '%s' as rules file", filename);
189 return -1;
190 }
191 dbg("reading '%s' as rules file", filename);
192
193 /* loop through the whole file */
194 cur = 0;
195 lineno = 0;
196 while (cur < bufsize) {
197 unsigned int i, j;
198
199 count = buf_get_line(buf, bufsize, cur);
200 bufline = &buf[cur];
201 cur += count+1;
202 lineno++;
203
204 if (count >= sizeof(line)) {
205 info("line too long, rule skipped %s, line %d", filename, lineno);
206 continue;
207 }
208
209 /* eat the whitespace */
210 while ((count > 0) && isspace(bufline[0])) {
211 bufline++;
212 count--;
213 }
214 if (count == 0)
215 continue;
216
217 /* see if this is a comment */
218 if (bufline[0] == COMMENT_CHARACTER)
219 continue;
220
221 /* skip backslash and newline from multi line rules */
222 for (i = j = 0; i < count; i++) {
223 if (bufline[i] == '\\' && bufline[i+1] == '\n')
224 continue;
225
226 line[j++] = bufline[i];
227 }
228 line[j] = '\0';
229 dbg("read '%s'", line);
230
231 /* get all known keys */
232 memset(&rule, 0x00, sizeof(struct udev_rule));
233 linepos = line;
234 valid = 0;
235
236 while (1) {
237 char *key;
238 char *value;
239 enum key_operation operation = KEY_OP_UNSET;
240
241 retval = get_key(&linepos, &key, &operation, &value);
242 if (retval)
243 break;
244
245 if (strcasecmp(key, KEY_KERNEL) == 0) {
246 strlcpy(rule.kernel, value, sizeof(rule.kernel));
247 rule.kernel_operation = operation;
248 valid = 1;
249 continue;
250 }
251
252 if (strcasecmp(key, KEY_SUBSYSTEM) == 0) {
253 strlcpy(rule.subsystem, value, sizeof(rule.subsystem));
254 rule.subsystem_operation = operation;
255 valid = 1;
256 continue;
257 }
258
259 if (strcasecmp(key, KEY_ACTION) == 0) {
260 strlcpy(rule.action, value, sizeof(rule.action));
261 rule.action_operation = operation;
262 valid = 1;
263 continue;
264 }
265
266 if (strcasecmp(key, KEY_BUS) == 0) {
267 strlcpy(rule.bus, value, sizeof(rule.bus));
268 rule.bus_operation = operation;
269 valid = 1;
270 continue;
271 }
272
273 if (strcasecmp(key, KEY_ID) == 0) {
274 strlcpy(rule.id, value, sizeof(rule.id));
275 rule.id_operation = operation;
276 valid = 1;
277 continue;
278 }
279
280 if (strncasecmp(key, KEY_SYSFS, sizeof(KEY_SYSFS)-1) == 0) {
281 struct key_pair *pair;
282
283 if (rule.sysfs_pair_count >= KEY_SYSFS_PAIRS_MAX) {
284 err("skip rule, to many " KEY_SYSFS " keys in a single rule");
285 goto error;
286 }
287 pair = &rule.sysfs_pair[rule.sysfs_pair_count];
288 attr = get_key_attribute(key + sizeof(KEY_SYSFS)-1);
289 if (attr == NULL) {
290 err("error parsing " KEY_SYSFS " attribute");
291 goto error;
292 }
293 strlcpy(pair->name, attr, sizeof(pair->name));
294 strlcpy(pair->value, value, sizeof(pair->value));
295 pair->operation = operation;
296 rule.sysfs_pair_count++;
297 valid = 1;
298 continue;
299 }
300
301 if (strncasecmp(key, KEY_ENV, sizeof(KEY_ENV)-1) == 0) {
302 struct key_pair *pair;
303
304 if (rule.env_pair_count >= KEY_ENV_PAIRS_MAX) {
305 err("skip rule, to many " KEY_ENV " keys in a single rule");
306 goto error;
307 }
308 pair = &rule.env_pair[rule.env_pair_count];
309 attr = get_key_attribute(key + sizeof(KEY_ENV)-1);
310 if (attr == NULL) {
311 err("error parsing " KEY_ENV " attribute");
312 continue;
313 }
314 strlcpy(pair->name, attr, sizeof(pair->name));
315 strlcpy(pair->value, value, sizeof(pair->value));
316 pair->operation = operation;
317 rule.env_pair_count++;
318 valid = 1;
319 continue;
320 }
321
322 if (strcasecmp(key, KEY_DRIVER) == 0) {
323 strlcpy(rule.driver, value, sizeof(rule.driver));
324 rule.driver_operation = operation;
325 valid = 1;
326 continue;
327 }
328
329 if (strcasecmp(key, KEY_RESULT) == 0) {
330 strlcpy(rule.result, value, sizeof(rule.result));
331 rule.result_operation = operation;
332 valid = 1;
333 continue;
334 }
335
336 if (strcasecmp(key, KEY_PROGRAM) == 0) {
337 strlcpy(rule.program, value, sizeof(rule.program));
338 rule.program_operation = operation;
339 program_given = 1;
340 valid = 1;
341 continue;
342 }
343
344 if (strncasecmp(key, KEY_NAME, sizeof(KEY_NAME)-1) == 0) {
345 attr = get_key_attribute(key + sizeof(KEY_NAME)-1);
346 /* FIXME: remove old style options and make OPTIONS= mandatory */
347 if (attr != NULL) {
348 if (strstr(attr, OPTION_PARTITIONS) != NULL) {
349 dbg("creation of partition nodes requested");
350 rule.partitions = DEFAULT_PARTITIONS_COUNT;
351 }
352 if (strstr(attr, OPTION_IGNORE_REMOVE) != NULL) {
353 dbg("remove event should be ignored");
354 rule.ignore_remove = 1;
355 }
356 }
357 if (value[0] != '\0')
358 strlcpy(rule.name, value, sizeof(rule.name));
359 else
360 rule.ignore_device = 1;
361 valid = 1;
362 continue;
363 }
364
365 if (strcasecmp(key, KEY_SYMLINK) == 0) {
366 strlcpy(rule.symlink, value, sizeof(rule.symlink));
367 valid = 1;
368 continue;
369 }
370
371 if (strcasecmp(key, KEY_OWNER) == 0) {
372 strlcpy(rule.owner, value, sizeof(rule.owner));
373 valid = 1;
374 continue;
375 }
376
377 if (strcasecmp(key, KEY_GROUP) == 0) {
378 strlcpy(rule.group, value, sizeof(rule.group));
379 valid = 1;
380 continue;
381 }
382
383 if (strcasecmp(key, KEY_MODE) == 0) {
384 rule.mode = strtol(value, NULL, 8);
385 valid = 1;
386 continue;
387 }
388
389 if (strcasecmp(key, KEY_RUN) == 0) {
390 strlcpy(rule.run, value, sizeof(rule.run));
391 valid = 1;
392 continue;
393 }
394
395 if (strcasecmp(key, KEY_OPTIONS) == 0) {
396 if (strstr(value, OPTION_LAST_RULE) != NULL) {
397 dbg("last rule to be applied");
398 rule.last_rule = 1;
399 }
400 if (strstr(value, OPTION_IGNORE_DEVICE) != NULL) {
401 dbg("device should be ignored");
402 rule.ignore_device = 1;
403 }
404 if (strstr(value, OPTION_IGNORE_REMOVE) != NULL) {
405 dbg("remove event should be ignored");
406 rule.ignore_remove = 1;
407 }
408 if (strstr(value, OPTION_PARTITIONS) != NULL) {
409 dbg("creation of partition nodes requested");
410 rule.partitions = DEFAULT_PARTITIONS_COUNT;
411 }
412 valid = 1;
413 continue;
414 }
415
416 err("unknown key '%s'", key);
417 goto error;
418 }
419
420 /* skip line if not any valid key was found */
421 if (!valid)
422 goto error;
423
424 if ((rule.result[0] != '\0') && (program_given == 0)) {
425 info(KEY_RESULT " is only useful when " KEY_PROGRAM " is called in any rule before");
426 goto error;
427 }
428
429 rule.config_line = lineno;
430 strlcpy(rule.config_file, filename, sizeof(rule.config_file));
431 retval = add_config_dev(&rule);
432 if (retval) {
433 dbg("add_config_dev returned with error %d", retval);
434 continue;
435 error:
436 err("parse error %s, line %d:%d, rule skipped",
437 filename, lineno, (int) (linepos - line));
438 }
439 }
440
441 file_unmap(buf, bufsize);
442 return retval;
443 }
444
445 int udev_rules_init(void)
446 {
447 struct stat stats;
448 int retval;
449
450 if (stat(udev_rules_filename, &stats) != 0)
451 return -1;
452
453 if ((stats.st_mode & S_IFMT) != S_IFDIR)
454 retval = rules_parse(udev_rules_filename);
455 else {
456 struct name_entry *name_loop, *name_tmp;
457 LIST_HEAD(name_list);
458
459 retval = add_matching_files(&name_list, udev_rules_filename, RULEFILE_SUFFIX);
460
461 list_for_each_entry_safe(name_loop, name_tmp, &name_list, node) {
462 rules_parse(name_loop->name);
463 list_del(&name_loop->node);
464 }
465 }
466
467 return retval;
468 }
469
470 void udev_rules_close(void)
471 {
472 struct udev_rule *rule;
473 struct udev_rule *temp_rule;
474
475 list_for_each_entry_safe(rule, temp_rule, &udev_rule_list, node) {
476 list_del(&rule->node);
477 free(rule);
478 }
479 }
480