]>
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" | |
9af5bb2f | 39 | #include "udev_utils.h" |
54988802 | 40 | #include "logging.h" |
19feb351 GKH |
41 | #include "namedev.h" |
42 | ||
8b36cc0f | 43 | LIST_HEAD(config_device_list); |
e41016d3 | 44 | |
a8b01705 GKH |
45 | static int add_config_dev(struct config_device *new_dev) |
46 | { | |
47 | struct config_device *tmp_dev; | |
48 | ||
49 | tmp_dev = malloc(sizeof(*tmp_dev)); | |
50 | if (tmp_dev == NULL) | |
51 | return -ENOMEM; | |
52 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
53 | list_add_tail(&tmp_dev->node, &config_device_list); | |
54 | //dump_config_dev(tmp_dev); | |
55 | return 0; | |
56 | } | |
57 | ||
19feb351 GKH |
58 | void dump_config_dev(struct config_device *dev) |
59 | { | |
ac28b86d KS |
60 | dbg_parse("name='%s', symlink='%s', bus='%s', place='%s', id='%s', " |
61 | "sysfs_file[0]='%s', sysfs_value[0]='%s', " | |
3e441450 | 62 | "kernel='%s', program='%s', result='%s'" |
e41016d3 | 63 | "owner='%s', group='%s', mode=%#o", |
ac28b86d KS |
64 | dev->name, dev->symlink, dev->bus, dev->place, dev->id, |
65 | dev->sysfs_pair[0].file, dev->sysfs_pair[0].value, | |
2731fd98 | 66 | dev->kernel, dev->program, dev->result, |
e41016d3 | 67 | dev->owner, dev->group, dev->mode); |
19feb351 GKH |
68 | } |
69 | ||
70 | void dump_config_dev_list(void) | |
71 | { | |
843d1a84 | 72 | struct config_device *dev; |
19feb351 | 73 | |
843d1a84 | 74 | list_for_each_entry(dev, &config_device_list, node) |
19feb351 | 75 | dump_config_dev(dev); |
19feb351 | 76 | } |
61219c75 | 77 | |
d4a32aa2 | 78 | /* extract possible KEY{attr} */ |
bb738647 KS |
79 | static char *get_key_attribute(char *str) |
80 | { | |
81 | char *pos; | |
82 | char *attr; | |
83 | ||
bb738647 KS |
84 | attr = strchr(str, '{'); |
85 | if (attr != NULL) { | |
86 | attr++; | |
87 | pos = strchr(attr, '}'); | |
88 | if (pos == NULL) { | |
89 | dbg("missing closing brace for format"); | |
90 | return NULL; | |
91 | } | |
92 | pos[0] = '\0'; | |
93 | dbg("attribute='%s'", attr); | |
94 | return attr; | |
95 | } | |
96 | ||
97 | return NULL; | |
98 | } | |
99 | ||
8b36cc0f | 100 | static int namedev_parse(const char *filename, void *data) |
19feb351 | 101 | { |
3e441450 | 102 | char line[LINE_SIZE]; |
103 | char *bufline; | |
19feb351 GKH |
104 | int lineno; |
105 | char *temp; | |
106 | char *temp2; | |
107 | char *temp3; | |
bb738647 | 108 | char *attr; |
c81b35c0 KS |
109 | char *buf; |
110 | size_t bufsize; | |
111 | size_t cur; | |
112 | size_t count; | |
ac28b86d | 113 | int program_given = 0; |
3e441450 | 114 | int valid; |
19feb351 GKH |
115 | int retval = 0; |
116 | struct config_device dev; | |
117 | ||
c81b35c0 | 118 | if (file_map(filename, &buf, &bufsize) == 0) { |
e41245cb | 119 | dbg("reading '%s' as rules file", filename); |
e8baccca | 120 | } else { |
c81b35c0 KS |
121 | dbg("can't open '%s' as rules file", filename); |
122 | return -1; | |
19feb351 GKH |
123 | } |
124 | ||
125 | /* loop through the whole file */ | |
c81b35c0 | 126 | cur = 0; |
19feb351 | 127 | lineno = 0; |
3e441450 | 128 | while (cur < bufsize) { |
1134a81b | 129 | unsigned int i, j; |
9f8dfa19 | 130 | |
c81b35c0 | 131 | count = buf_get_line(buf, bufsize, cur); |
3e441450 | 132 | bufline = &buf[cur]; |
c81b35c0 | 133 | cur += count+1; |
3e441450 | 134 | lineno++; |
19feb351 | 135 | |
3e441450 | 136 | if (count >= LINE_SIZE) { |
9f8dfa19 | 137 | info("line too long, rule skipped %s, line %d", filename, lineno); |
3e441450 | 138 | continue; |
139 | } | |
093bf8f4 | 140 | |
3e441450 | 141 | /* eat the whitespace */ |
3db7fa27 | 142 | while ((count > 0) && isspace(bufline[0])) { |
3e441450 | 143 | bufline++; |
144 | count--; | |
145 | } | |
3db7fa27 KS |
146 | if (count == 0) |
147 | continue; | |
3e441450 | 148 | |
19feb351 | 149 | /* see if this is a comment */ |
3e441450 | 150 | if (bufline[0] == COMMENT_CHARACTER) |
19feb351 GKH |
151 | continue; |
152 | ||
9f8dfa19 KS |
153 | /* skip backslash and newline from multi line rules */ |
154 | for (i = j = 0; i < count; i++) { | |
155 | if (bufline[i] == '\\' || bufline[i] == '\n') | |
156 | continue; | |
157 | ||
158 | line[j++] = bufline[i]; | |
159 | } | |
160 | line[j] = '\0'; | |
3e441450 | 161 | dbg_parse("read '%s'", line); |
19feb351 | 162 | |
d94df232 | 163 | /* get all known keys */ |
3e441450 | 164 | memset(&dev, 0x00, sizeof(struct config_device)); |
165 | temp = line; | |
166 | valid = 0; | |
167 | ||
d94df232 | 168 | while (1) { |
274812b5 | 169 | retval = parse_get_pair(&temp, &temp2, &temp3); |
19feb351 GKH |
170 | if (retval) |
171 | break; | |
3d150dfb | 172 | |
d94df232 KS |
173 | if (strcasecmp(temp2, FIELD_BUS) == 0) { |
174 | strfieldcpy(dev.bus, temp3); | |
3e441450 | 175 | valid = 1; |
d94df232 KS |
176 | continue; |
177 | } | |
178 | ||
179 | if (strcasecmp(temp2, FIELD_ID) == 0) { | |
180 | strfieldcpy(dev.id, temp3); | |
3e441450 | 181 | valid = 1; |
d94df232 KS |
182 | continue; |
183 | } | |
184 | ||
185 | if (strcasecmp(temp2, FIELD_PLACE) == 0) { | |
186 | strfieldcpy(dev.place, temp3); | |
3e441450 | 187 | valid = 1; |
d94df232 KS |
188 | continue; |
189 | } | |
190 | ||
191 | if (strncasecmp(temp2, FIELD_SYSFS, sizeof(FIELD_SYSFS)-1) == 0) { | |
a8b01705 GKH |
192 | struct sysfs_pair *pair = &dev.sysfs_pair[0]; |
193 | int sysfs_pair_num = 0; | |
194 | ||
195 | /* find first unused pair */ | |
196 | while (pair->file[0] != '\0') { | |
197 | ++sysfs_pair_num; | |
198 | if (sysfs_pair_num >= MAX_SYSFS_PAIRS) { | |
199 | pair = NULL; | |
200 | break; | |
201 | } | |
202 | ++pair; | |
203 | } | |
204 | if (pair) { | |
bb738647 KS |
205 | attr = get_key_attribute(temp2 + sizeof(FIELD_SYSFS)-1); |
206 | if (attr == NULL) { | |
207 | dbg("error parsing " FIELD_SYSFS " attribute"); | |
208 | continue; | |
209 | } | |
210 | strfieldcpy(pair->file, attr); | |
a8b01705 | 211 | strfieldcpy(pair->value, temp3); |
3e441450 | 212 | valid = 1; |
a8b01705 | 213 | } |
d94df232 KS |
214 | continue; |
215 | } | |
216 | ||
217 | if (strcasecmp(temp2, FIELD_KERNEL) == 0) { | |
ac28b86d | 218 | strfieldcpy(dev.kernel, temp3); |
3e441450 | 219 | valid = 1; |
d94df232 KS |
220 | continue; |
221 | } | |
222 | ||
6818c51d KS |
223 | if (strcasecmp(temp2, FIELD_SUBSYSTEM) == 0) { |
224 | strfieldcpy(dev.subsystem, temp3); | |
225 | valid = 1; | |
226 | continue; | |
227 | } | |
228 | ||
2092fbcd KS |
229 | if (strcasecmp(temp2, FIELD_DRIVER) == 0) { |
230 | strfieldcpy(dev.driver, temp3); | |
231 | valid = 1; | |
232 | continue; | |
233 | } | |
234 | ||
d94df232 | 235 | if (strcasecmp(temp2, FIELD_PROGRAM) == 0) { |
ac28b86d KS |
236 | program_given = 1; |
237 | strfieldcpy(dev.program, temp3); | |
3e441450 | 238 | valid = 1; |
ac28b86d KS |
239 | continue; |
240 | } | |
241 | ||
242 | if (strcasecmp(temp2, FIELD_RESULT) == 0) { | |
243 | strfieldcpy(dev.result, temp3); | |
3e441450 | 244 | valid = 1; |
d94df232 KS |
245 | continue; |
246 | } | |
247 | ||
50e5de03 KS |
248 | if (strncasecmp(temp2, FIELD_NAME, sizeof(FIELD_NAME)-1) == 0) { |
249 | attr = get_key_attribute(temp2 + sizeof(FIELD_NAME)-1); | |
7efa217d KS |
250 | if (attr != NULL) { |
251 | if (strstr(attr, ATTR_PARTITIONS) != NULL) { | |
50e5de03 KS |
252 | dbg_parse("creation of partition nodes requested"); |
253 | dev.partitions = PARTITIONS_COUNT; | |
254 | } | |
7efa217d KS |
255 | if (strstr(attr, ATTR_IGNORE_REMOVE) != NULL) { |
256 | dbg_parse("remove event should be ignored"); | |
257 | dev.ignore_remove = 1; | |
258 | } | |
259 | } | |
d94df232 | 260 | strfieldcpy(dev.name, temp3); |
3e441450 | 261 | valid = 1; |
d94df232 KS |
262 | continue; |
263 | } | |
264 | ||
265 | if (strcasecmp(temp2, FIELD_SYMLINK) == 0) { | |
3d150dfb | 266 | strfieldcpy(dev.symlink, temp3); |
3e441450 | 267 | valid = 1; |
d94df232 KS |
268 | continue; |
269 | } | |
3d150dfb | 270 | |
e41016d3 KS |
271 | if (strcasecmp(temp2, FIELD_OWNER) == 0) { |
272 | strfieldcpy(dev.owner, temp3); | |
3e441450 | 273 | valid = 1; |
e41016d3 KS |
274 | continue; |
275 | } | |
276 | ||
277 | if (strcasecmp(temp2, FIELD_GROUP) == 0) { | |
278 | strfieldcpy(dev.group, temp3); | |
3e441450 | 279 | valid = 1; |
e41016d3 KS |
280 | continue; |
281 | } | |
282 | ||
283 | if (strcasecmp(temp2, FIELD_MODE) == 0) { | |
284 | dev.mode = strtol(temp3, NULL, 8); | |
3e441450 | 285 | valid = 1; |
e41016d3 KS |
286 | continue; |
287 | } | |
288 | ||
851cd18d | 289 | dbg("unknown type of field '%s'", temp2); |
851cd18d | 290 | goto error; |
d94df232 KS |
291 | } |
292 | ||
3e441450 | 293 | /* skip line if not any valid key was found */ |
294 | if (!valid) | |
295 | goto error; | |
296 | ||
e41016d3 | 297 | /* simple plausibility checks for given keys */ |
ac28b86d KS |
298 | if ((dev.sysfs_pair[0].file[0] == '\0') ^ |
299 | (dev.sysfs_pair[0].value[0] == '\0')) { | |
3e441450 | 300 | info("inconsistency in " FIELD_SYSFS " key"); |
ac28b86d KS |
301 | goto error; |
302 | } | |
303 | ||
304 | if ((dev.result[0] != '\0') && (program_given == 0)) { | |
3e441450 | 305 | info(FIELD_RESULT " is only useful when " |
306 | FIELD_PROGRAM " is called in any rule before"); | |
d94df232 | 307 | goto error; |
19feb351 GKH |
308 | } |
309 | ||
54988802 | 310 | dev.config_line = lineno; |
bd5f8e7c | 311 | strfieldcpy(dev.config_file, filename); |
19feb351 GKH |
312 | retval = add_config_dev(&dev); |
313 | if (retval) { | |
314 | dbg("add_config_dev returned with error %d", retval); | |
d94df232 | 315 | continue; |
ac28b86d | 316 | error: |
3e441450 | 317 | info("parse error %s, line %d:%d, rule skipped", |
318 | filename, lineno, temp - line); | |
19feb351 GKH |
319 | } |
320 | } | |
c81b35c0 KS |
321 | |
322 | file_unmap(buf, bufsize); | |
19feb351 | 323 | return retval; |
d94df232 | 324 | } |
19feb351 | 325 | |
8b36cc0f | 326 | int namedev_init(void) |
19feb351 | 327 | { |
8b36cc0f KS |
328 | struct stat stats; |
329 | int retval; | |
19feb351 | 330 | |
8b36cc0f | 331 | if (stat(udev_rules_filename, &stats) != 0) |
c81b35c0 | 332 | return -1; |
19feb351 | 333 | |
e41245cb | 334 | if ((stats.st_mode & S_IFMT) != S_IFDIR) |
8b36cc0f | 335 | retval = namedev_parse(udev_rules_filename, NULL); |
4a539daf | 336 | else |
8b36cc0f | 337 | retval = call_foreach_file(namedev_parse, udev_rules_filename, RULEFILE_SUFFIX, NULL); |
e41245cb | 338 | |
8b36cc0f | 339 | return retval; |
e41245cb | 340 | } |