]>
Commit | Line | Data |
---|---|---|
19feb351 GKH |
1 | /* |
2 | * namedev_parse.c | |
3 | * | |
4 | * Userspace devfs | |
5 | * | |
274812b5 | 6 | * Copyright (C) 2003,2004 Greg Kroah-Hartman <greg@kroah.com> |
19feb351 GKH |
7 | * |
8 | * | |
9 | * This program is free software; you can redistribute it and/or modify it | |
10 | * under the terms of the GNU General Public License as published by the | |
11 | * Free Software Foundation version 2 of the License. | |
12 | * | |
13 | * This program is distributed in the hope that it will be useful, but | |
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
16 | * General Public License for more details. | |
17 | * | |
18 | * You should have received a copy of the GNU General Public License along | |
19 | * with this program; if not, write to the Free Software Foundation, Inc., | |
20 | * 675 Mass Ave, Cambridge, MA 02139, USA. | |
21 | * | |
22 | */ | |
23 | ||
851cd18d GKH |
24 | #ifdef DEBUG |
25 | /* define this to enable parsing debugging also */ | |
19feb351 | 26 | /* #define DEBUG_PARSER */ |
851cd18d | 27 | #endif |
19feb351 GKH |
28 | |
29 | #include <stddef.h> | |
30 | #include <stdlib.h> | |
31 | #include <string.h> | |
32 | #include <stdio.h> | |
19feb351 GKH |
33 | #include <ctype.h> |
34 | #include <unistd.h> | |
e41245cb | 35 | #include <sys/stat.h> |
19feb351 GKH |
36 | #include <errno.h> |
37 | ||
38 | #include "udev.h" | |
c81b35c0 | 39 | #include "udev_lib.h" |
54988802 | 40 | #include "logging.h" |
19feb351 GKH |
41 | #include "namedev.h" |
42 | ||
e41016d3 | 43 | |
a8b01705 GKH |
44 | static int add_config_dev(struct config_device *new_dev) |
45 | { | |
46 | struct config_device *tmp_dev; | |
47 | ||
48 | tmp_dev = malloc(sizeof(*tmp_dev)); | |
49 | if (tmp_dev == NULL) | |
50 | return -ENOMEM; | |
51 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
52 | list_add_tail(&tmp_dev->node, &config_device_list); | |
53 | //dump_config_dev(tmp_dev); | |
54 | return 0; | |
55 | } | |
56 | ||
19feb351 GKH |
57 | void dump_config_dev(struct config_device *dev) |
58 | { | |
ac28b86d KS |
59 | dbg_parse("name='%s', symlink='%s', bus='%s', place='%s', id='%s', " |
60 | "sysfs_file[0]='%s', sysfs_value[0]='%s', " | |
3e441450 | 61 | "kernel='%s', program='%s', result='%s'" |
e41016d3 | 62 | "owner='%s', group='%s', mode=%#o", |
ac28b86d KS |
63 | dev->name, dev->symlink, dev->bus, dev->place, dev->id, |
64 | dev->sysfs_pair[0].file, dev->sysfs_pair[0].value, | |
2731fd98 | 65 | dev->kernel, dev->program, dev->result, |
e41016d3 | 66 | dev->owner, dev->group, dev->mode); |
19feb351 GKH |
67 | } |
68 | ||
69 | void dump_config_dev_list(void) | |
70 | { | |
843d1a84 | 71 | struct config_device *dev; |
19feb351 | 72 | |
843d1a84 | 73 | list_for_each_entry(dev, &config_device_list, node) |
19feb351 | 74 | dump_config_dev(dev); |
19feb351 | 75 | } |
61219c75 | 76 | |
e41016d3 KS |
77 | static int add_perm_dev(struct perm_device *new_dev) |
78 | { | |
79 | struct perm_device *dev; | |
80 | struct perm_device *tmp_dev; | |
81 | ||
82 | /* update the values if we already have the device */ | |
83 | list_for_each_entry(dev, &perm_device_list, node) { | |
84 | if (strcmp(new_dev->name, dev->name) != 0) | |
85 | continue; | |
86 | ||
87 | set_empty_perms(dev, new_dev->mode, new_dev->owner, new_dev->group); | |
88 | return 0; | |
89 | } | |
90 | ||
91 | /* not found, add new structure to the perm list */ | |
92 | tmp_dev = malloc(sizeof(*tmp_dev)); | |
93 | if (!tmp_dev) | |
94 | return -ENOMEM; | |
95 | ||
96 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
97 | list_add_tail(&tmp_dev->node, &perm_device_list); | |
98 | //dump_perm_dev(tmp_dev); | |
99 | return 0; | |
100 | } | |
101 | ||
61219c75 GKH |
102 | void dump_perm_dev(struct perm_device *dev) |
103 | { | |
104 | dbg_parse("name='%s', owner='%s', group='%s', mode=%#o", | |
105 | dev->name, dev->owner, dev->group, dev->mode); | |
106 | } | |
107 | ||
108 | void dump_perm_dev_list(void) | |
109 | { | |
843d1a84 | 110 | struct perm_device *dev; |
61219c75 | 111 | |
843d1a84 | 112 | list_for_each_entry(dev, &perm_device_list, node) |
61219c75 | 113 | dump_perm_dev(dev); |
61219c75 GKH |
114 | } |
115 | ||
bb738647 KS |
116 | /* extract possible KEY{attr} or KEY_attr */ |
117 | static char *get_key_attribute(char *str) | |
118 | { | |
119 | char *pos; | |
120 | char *attr; | |
121 | ||
bb738647 KS |
122 | attr = strchr(str, '{'); |
123 | if (attr != NULL) { | |
124 | attr++; | |
125 | pos = strchr(attr, '}'); | |
126 | if (pos == NULL) { | |
127 | dbg("missing closing brace for format"); | |
128 | return NULL; | |
129 | } | |
130 | pos[0] = '\0'; | |
131 | dbg("attribute='%s'", attr); | |
132 | return attr; | |
133 | } | |
134 | ||
50e5de03 KS |
135 | attr = strchr(str, '_'); |
136 | if (attr != NULL) { | |
137 | attr++; | |
138 | dbg("attribute='%s'", attr); | |
139 | return attr; | |
140 | } | |
141 | ||
bb738647 KS |
142 | return NULL; |
143 | } | |
144 | ||
e41245cb | 145 | static int namedev_parse_rules(char *filename) |
19feb351 | 146 | { |
3e441450 | 147 | char line[LINE_SIZE]; |
148 | char *bufline; | |
19feb351 GKH |
149 | int lineno; |
150 | char *temp; | |
151 | char *temp2; | |
152 | char *temp3; | |
bb738647 | 153 | char *attr; |
c81b35c0 KS |
154 | char *buf; |
155 | size_t bufsize; | |
156 | size_t cur; | |
157 | size_t count; | |
ac28b86d | 158 | int program_given = 0; |
3e441450 | 159 | int valid; |
19feb351 GKH |
160 | int retval = 0; |
161 | struct config_device dev; | |
162 | ||
c81b35c0 | 163 | if (file_map(filename, &buf, &bufsize) == 0) { |
e41245cb | 164 | dbg("reading '%s' as rules file", filename); |
e8baccca | 165 | } else { |
c81b35c0 KS |
166 | dbg("can't open '%s' as rules file", filename); |
167 | return -1; | |
19feb351 GKH |
168 | } |
169 | ||
170 | /* loop through the whole file */ | |
c81b35c0 | 171 | cur = 0; |
19feb351 | 172 | lineno = 0; |
3e441450 | 173 | while (cur < bufsize) { |
c81b35c0 | 174 | count = buf_get_line(buf, bufsize, cur); |
3e441450 | 175 | bufline = &buf[cur]; |
c81b35c0 | 176 | cur += count+1; |
3e441450 | 177 | lineno++; |
19feb351 | 178 | |
3e441450 | 179 | if (count >= LINE_SIZE) { |
180 | info("line too long, rule skipped %s, line %d", | |
181 | filename, lineno); | |
182 | continue; | |
183 | } | |
093bf8f4 | 184 | |
19feb351 | 185 | /* empty line? */ |
3e441450 | 186 | if (bufline[0] == '\0' || bufline[0] == '\n') |
19feb351 GKH |
187 | continue; |
188 | ||
3e441450 | 189 | /* eat the whitespace */ |
190 | while (isspace(bufline[0])) { | |
191 | bufline++; | |
192 | count--; | |
193 | } | |
194 | ||
19feb351 | 195 | /* see if this is a comment */ |
3e441450 | 196 | if (bufline[0] == COMMENT_CHARACTER) |
19feb351 GKH |
197 | continue; |
198 | ||
3e441450 | 199 | strncpy(line, bufline, count); |
200 | line[count] = '\0'; | |
201 | dbg_parse("read '%s'", line); | |
19feb351 | 202 | |
d94df232 | 203 | /* get all known keys */ |
3e441450 | 204 | memset(&dev, 0x00, sizeof(struct config_device)); |
205 | temp = line; | |
206 | valid = 0; | |
207 | ||
d94df232 | 208 | while (1) { |
274812b5 | 209 | retval = parse_get_pair(&temp, &temp2, &temp3); |
19feb351 GKH |
210 | if (retval) |
211 | break; | |
3d150dfb | 212 | |
d94df232 KS |
213 | if (strcasecmp(temp2, FIELD_BUS) == 0) { |
214 | strfieldcpy(dev.bus, temp3); | |
3e441450 | 215 | valid = 1; |
d94df232 KS |
216 | continue; |
217 | } | |
218 | ||
219 | if (strcasecmp(temp2, FIELD_ID) == 0) { | |
220 | strfieldcpy(dev.id, temp3); | |
3e441450 | 221 | valid = 1; |
d94df232 KS |
222 | continue; |
223 | } | |
224 | ||
225 | if (strcasecmp(temp2, FIELD_PLACE) == 0) { | |
226 | strfieldcpy(dev.place, temp3); | |
3e441450 | 227 | valid = 1; |
d94df232 KS |
228 | continue; |
229 | } | |
230 | ||
231 | if (strncasecmp(temp2, FIELD_SYSFS, sizeof(FIELD_SYSFS)-1) == 0) { | |
a8b01705 GKH |
232 | struct sysfs_pair *pair = &dev.sysfs_pair[0]; |
233 | int sysfs_pair_num = 0; | |
234 | ||
235 | /* find first unused pair */ | |
236 | while (pair->file[0] != '\0') { | |
237 | ++sysfs_pair_num; | |
238 | if (sysfs_pair_num >= MAX_SYSFS_PAIRS) { | |
239 | pair = NULL; | |
240 | break; | |
241 | } | |
242 | ++pair; | |
243 | } | |
244 | if (pair) { | |
bb738647 KS |
245 | attr = get_key_attribute(temp2 + sizeof(FIELD_SYSFS)-1); |
246 | if (attr == NULL) { | |
247 | dbg("error parsing " FIELD_SYSFS " attribute"); | |
248 | continue; | |
249 | } | |
250 | strfieldcpy(pair->file, attr); | |
a8b01705 | 251 | strfieldcpy(pair->value, temp3); |
3e441450 | 252 | valid = 1; |
a8b01705 | 253 | } |
d94df232 KS |
254 | continue; |
255 | } | |
256 | ||
257 | if (strcasecmp(temp2, FIELD_KERNEL) == 0) { | |
ac28b86d | 258 | strfieldcpy(dev.kernel, temp3); |
3e441450 | 259 | valid = 1; |
d94df232 KS |
260 | continue; |
261 | } | |
262 | ||
263 | if (strcasecmp(temp2, FIELD_PROGRAM) == 0) { | |
ac28b86d KS |
264 | program_given = 1; |
265 | strfieldcpy(dev.program, temp3); | |
3e441450 | 266 | valid = 1; |
ac28b86d KS |
267 | continue; |
268 | } | |
269 | ||
270 | if (strcasecmp(temp2, FIELD_RESULT) == 0) { | |
271 | strfieldcpy(dev.result, temp3); | |
3e441450 | 272 | valid = 1; |
d94df232 KS |
273 | continue; |
274 | } | |
275 | ||
50e5de03 KS |
276 | if (strncasecmp(temp2, FIELD_NAME, sizeof(FIELD_NAME)-1) == 0) { |
277 | attr = get_key_attribute(temp2 + sizeof(FIELD_NAME)-1); | |
3e441450 | 278 | if (attr != NULL && strcasecmp(attr, ATTR_PARTITIONS) == 0) { |
50e5de03 KS |
279 | dbg_parse("creation of partition nodes requested"); |
280 | dev.partitions = PARTITIONS_COUNT; | |
281 | } | |
d94df232 | 282 | strfieldcpy(dev.name, temp3); |
3e441450 | 283 | valid = 1; |
d94df232 KS |
284 | continue; |
285 | } | |
286 | ||
287 | if (strcasecmp(temp2, FIELD_SYMLINK) == 0) { | |
3d150dfb | 288 | strfieldcpy(dev.symlink, temp3); |
3e441450 | 289 | valid = 1; |
d94df232 KS |
290 | continue; |
291 | } | |
3d150dfb | 292 | |
e41016d3 KS |
293 | if (strcasecmp(temp2, FIELD_OWNER) == 0) { |
294 | strfieldcpy(dev.owner, temp3); | |
3e441450 | 295 | valid = 1; |
e41016d3 KS |
296 | continue; |
297 | } | |
298 | ||
299 | if (strcasecmp(temp2, FIELD_GROUP) == 0) { | |
300 | strfieldcpy(dev.group, temp3); | |
3e441450 | 301 | valid = 1; |
e41016d3 KS |
302 | continue; |
303 | } | |
304 | ||
305 | if (strcasecmp(temp2, FIELD_MODE) == 0) { | |
306 | dev.mode = strtol(temp3, NULL, 8); | |
3e441450 | 307 | valid = 1; |
e41016d3 KS |
308 | continue; |
309 | } | |
310 | ||
851cd18d | 311 | dbg("unknown type of field '%s'", temp2); |
851cd18d | 312 | goto error; |
d94df232 KS |
313 | } |
314 | ||
3e441450 | 315 | /* skip line if not any valid key was found */ |
316 | if (!valid) | |
317 | goto error; | |
318 | ||
e41016d3 | 319 | /* simple plausibility checks for given keys */ |
ac28b86d KS |
320 | if ((dev.sysfs_pair[0].file[0] == '\0') ^ |
321 | (dev.sysfs_pair[0].value[0] == '\0')) { | |
3e441450 | 322 | info("inconsistency in " FIELD_SYSFS " key"); |
ac28b86d KS |
323 | goto error; |
324 | } | |
325 | ||
326 | if ((dev.result[0] != '\0') && (program_given == 0)) { | |
3e441450 | 327 | info(FIELD_RESULT " is only useful when " |
328 | FIELD_PROGRAM " is called in any rule before"); | |
d94df232 | 329 | goto error; |
19feb351 GKH |
330 | } |
331 | ||
54988802 | 332 | dev.config_line = lineno; |
bd5f8e7c | 333 | strfieldcpy(dev.config_file, filename); |
19feb351 GKH |
334 | retval = add_config_dev(&dev); |
335 | if (retval) { | |
336 | dbg("add_config_dev returned with error %d", retval); | |
d94df232 | 337 | continue; |
ac28b86d | 338 | error: |
3e441450 | 339 | info("parse error %s, line %d:%d, rule skipped", |
340 | filename, lineno, temp - line); | |
19feb351 GKH |
341 | } |
342 | } | |
c81b35c0 KS |
343 | |
344 | file_unmap(buf, bufsize); | |
19feb351 | 345 | return retval; |
d94df232 | 346 | } |
19feb351 | 347 | |
e41245cb | 348 | static int namedev_parse_permissions(char *filename) |
19feb351 | 349 | { |
3e441450 | 350 | char line[LINE_SIZE]; |
351 | char *bufline; | |
19feb351 GKH |
352 | char *temp; |
353 | char *temp2; | |
c81b35c0 KS |
354 | char *buf; |
355 | size_t bufsize; | |
356 | size_t cur; | |
357 | size_t count; | |
19feb351 | 358 | int retval = 0; |
61219c75 | 359 | struct perm_device dev; |
3e441450 | 360 | int lineno; |
19feb351 | 361 | |
c81b35c0 | 362 | if (file_map(filename, &buf, &bufsize) == 0) { |
e41245cb | 363 | dbg("reading '%s' as permissions file", filename); |
e8baccca | 364 | } else { |
e41245cb | 365 | dbg("can't open '%s' as permissions file", filename); |
c81b35c0 | 366 | return -1; |
19feb351 GKH |
367 | } |
368 | ||
369 | /* loop through the whole file */ | |
c81b35c0 | 370 | cur = 0; |
3e441450 | 371 | lineno = 0; |
372 | while (cur < bufsize) { | |
c81b35c0 | 373 | count = buf_get_line(buf, bufsize, cur); |
3e441450 | 374 | bufline = &buf[cur]; |
c81b35c0 | 375 | cur += count+1; |
3e441450 | 376 | lineno++; |
19feb351 | 377 | |
3e441450 | 378 | if (count >= LINE_SIZE) { |
379 | info("line too long, rule skipped %s, line %d", | |
380 | filename, lineno); | |
381 | continue; | |
382 | } | |
19feb351 GKH |
383 | |
384 | /* empty line? */ | |
3e441450 | 385 | if (bufline[0] == '\0' || bufline[0] == '\n') |
19feb351 GKH |
386 | continue; |
387 | ||
3e441450 | 388 | /* eat the whitespace */ |
389 | while (isspace(bufline[0])) { | |
390 | bufline++; | |
391 | count--; | |
392 | } | |
393 | ||
19feb351 | 394 | /* see if this is a comment */ |
3e441450 | 395 | if (bufline[0] == COMMENT_CHARACTER) |
19feb351 GKH |
396 | continue; |
397 | ||
3e441450 | 398 | strncpy(line, bufline, count); |
399 | line[count] = '\0'; | |
400 | dbg_parse("read '%s'", line); | |
19feb351 GKH |
401 | |
402 | /* parse the line */ | |
3e441450 | 403 | memset(&dev, 0x00, sizeof(struct perm_device)); |
404 | temp = line; | |
405 | ||
19feb351 GKH |
406 | temp2 = strsep(&temp, ":"); |
407 | if (!temp2) { | |
408 | dbg("cannot parse line '%s'", line); | |
409 | continue; | |
410 | } | |
c472e3c8 | 411 | strfieldcpy(dev.name, temp2); |
19feb351 GKH |
412 | |
413 | temp2 = strsep(&temp, ":"); | |
414 | if (!temp2) { | |
415 | dbg("cannot parse line '%s'", line); | |
416 | continue; | |
417 | } | |
c472e3c8 | 418 | strfieldcpy(dev.owner, temp2); |
19feb351 GKH |
419 | |
420 | temp2 = strsep(&temp, ":"); | |
421 | if (!temp2) { | |
422 | dbg("cannot parse line '%s'", line); | |
423 | continue; | |
424 | } | |
c472e3c8 | 425 | strfieldcpy(dev.group, temp2); |
19feb351 GKH |
426 | |
427 | if (!temp) { | |
e41016d3 | 428 | dbg("cannot parse line '%s'", line); |
19feb351 GKH |
429 | continue; |
430 | } | |
431 | dev.mode = strtol(temp, NULL, 8); | |
432 | ||
433 | dbg_parse("name='%s', owner='%s', group='%s', mode=%#o", | |
e41016d3 KS |
434 | dev.name, dev.owner, dev.group, dev.mode); |
435 | ||
61219c75 | 436 | retval = add_perm_dev(&dev); |
19feb351 | 437 | if (retval) { |
3d150dfb | 438 | dbg("add_perm_dev returned with error %d", retval); |
19feb351 GKH |
439 | goto exit; |
440 | } | |
441 | } | |
442 | ||
443 | exit: | |
c81b35c0 | 444 | file_unmap(buf, bufsize); |
19feb351 | 445 | return retval; |
267f534d | 446 | } |
19feb351 | 447 | |
4a539daf | 448 | int namedev_init_rules() |
e41245cb | 449 | { |
e41245cb | 450 | struct stat stats; |
e41245cb | 451 | |
4a539daf | 452 | stat(udev_rules_filename, &stats); |
e41245cb | 453 | if ((stats.st_mode & S_IFMT) != S_IFDIR) |
4a539daf KS |
454 | return namedev_parse_rules(udev_rules_filename); |
455 | else | |
456 | return call_foreach_file(namedev_parse_rules, | |
457 | udev_rules_filename, RULEFILE_SUFFIX); | |
e41245cb KS |
458 | } |
459 | ||
460 | int namedev_init_permissions() | |
461 | { | |
4a539daf KS |
462 | struct stat stats; |
463 | ||
464 | stat(udev_permissions_filename, &stats); | |
465 | if ((stats.st_mode & S_IFMT) != S_IFDIR) | |
466 | return namedev_parse_permissions(udev_permissions_filename); | |
467 | else | |
468 | return call_foreach_file(namedev_parse_permissions, | |
469 | udev_permissions_filename, PERMFILE_SUFFIX); | |
e41245cb | 470 | } |