]>
Commit | Line | Data |
---|---|---|
2232cac8 GKH |
1 | /* |
2 | * namedev.c | |
3 | * | |
4 | * Userspace devfs | |
5 | * | |
6 | * Copyright (C) 2003 Greg Kroah-Hartman <greg@kroah.com> | |
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 | ||
5ef7b799 | 24 | /* define this to enable parsing debugging */ |
a4318095 | 25 | /* #define DEBUG_PARSER */ |
149f2106 | 26 | |
2232cac8 GKH |
27 | #include <stddef.h> |
28 | #include <stdlib.h> | |
29 | #include <string.h> | |
30 | #include <stdio.h> | |
31 | #include <fcntl.h> | |
32 | #include <ctype.h> | |
33 | #include <unistd.h> | |
34 | #include <errno.h> | |
c27e6911 | 35 | #include <sys/wait.h> |
2232cac8 GKH |
36 | |
37 | #include "list.h" | |
38 | #include "udev.h" | |
39 | #include "udev_version.h" | |
40 | #include "namedev.h" | |
185a35a4 | 41 | #include "libsysfs/libsysfs.h" |
2232cac8 GKH |
42 | |
43 | #define TYPE_LABEL "LABEL" | |
44 | #define TYPE_NUMBER "NUMBER" | |
45 | #define TYPE_TOPOLOGY "TOPOLOGY" | |
46 | #define TYPE_REPLACE "REPLACE" | |
c27e6911 | 47 | #define TYPE_CALLOUT "CALLOUT" |
2232cac8 | 48 | |
2232cac8 GKH |
49 | static LIST_HEAD(config_device_list); |
50 | ||
469c7cff GKH |
51 | static void dump_dev(struct config_device *dev) |
52 | { | |
53 | switch (dev->type) { | |
54 | case KERNEL_NAME: | |
5ef7b799 | 55 | dbg_parse("KERNEL name ='%s'" |
185a35a4 | 56 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
57 | dev->name, |
58 | dev->owner, dev->group, dev->mode); | |
469c7cff GKH |
59 | break; |
60 | case LABEL: | |
5ef7b799 | 61 | dbg_parse("LABEL name = '%s', bus = '%s', sysfs_file = '%s', sysfs_value = '%s'" |
185a35a4 | 62 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
63 | dev->name, dev->bus, dev->sysfs_file, dev->sysfs_value, |
64 | dev->owner, dev->group, dev->mode); | |
469c7cff GKH |
65 | break; |
66 | case NUMBER: | |
5ef7b799 | 67 | dbg_parse("NUMBER name = '%s', bus = '%s', id = '%s'" |
185a35a4 | 68 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
69 | dev->name, dev->bus, dev->id, |
70 | dev->owner, dev->group, dev->mode); | |
469c7cff GKH |
71 | break; |
72 | case TOPOLOGY: | |
5ef7b799 | 73 | dbg_parse("TOPOLOGY name = '%s', bus = '%s', place = '%s'" |
185a35a4 | 74 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
75 | dev->name, dev->bus, dev->place, |
76 | dev->owner, dev->group, dev->mode); | |
469c7cff GKH |
77 | break; |
78 | case REPLACE: | |
5ef7b799 | 79 | dbg_parse("REPLACE name = %s, kernel_name = %s" |
185a35a4 | 80 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
81 | dev->name, dev->kernel_name, |
82 | dev->owner, dev->group, dev->mode); | |
469c7cff | 83 | break; |
c27e6911 | 84 | case CALLOUT: |
5ef7b799 | 85 | dbg_parse("CALLOUT name = '%s', program ='%s', bus = '%s', id = '%s'" |
c27e6911 | 86 | " owner = '%s', group = '%s', mode = '%#o'", |
9d496c74 GKH |
87 | dev->name, dev->exec_program, dev->bus, dev->id, |
88 | dev->owner, dev->group, dev->mode); | |
c27e6911 | 89 | break; |
469c7cff | 90 | default: |
5ef7b799 | 91 | dbg_parse("Unknown type of device!"); |
469c7cff GKH |
92 | } |
93 | } | |
94 | ||
2232cac8 GKH |
95 | #define copy_var(a, b, var) \ |
96 | if (b->var) \ | |
469c7cff | 97 | a->var = b->var; |
2232cac8 GKH |
98 | |
99 | #define copy_string(a, b, var) \ | |
100 | if (strlen(b->var)) \ | |
469c7cff | 101 | strcpy(a->var, b->var); |
2232cac8 GKH |
102 | |
103 | static int add_dev(struct config_device *new_dev) | |
104 | { | |
105 | struct list_head *tmp; | |
106 | struct config_device *tmp_dev; | |
107 | ||
108 | /* loop through the whole list of devices to see if we already have | |
109 | * this one... */ | |
110 | list_for_each(tmp, &config_device_list) { | |
111 | struct config_device *dev = list_entry(tmp, struct config_device, node); | |
9d496c74 | 112 | if (strcmp(dev->name, new_dev->name) == 0) { |
2232cac8 | 113 | /* the same, copy the new info into this structure */ |
469c7cff | 114 | copy_var(dev, new_dev, type); |
9d496c74 | 115 | copy_var(dev, new_dev, mode); |
469c7cff GKH |
116 | copy_string(dev, new_dev, bus); |
117 | copy_string(dev, new_dev, sysfs_file); | |
118 | copy_string(dev, new_dev, sysfs_value); | |
119 | copy_string(dev, new_dev, id); | |
120 | copy_string(dev, new_dev, place); | |
121 | copy_string(dev, new_dev, kernel_name); | |
9d496c74 GKH |
122 | copy_string(dev, new_dev, owner); |
123 | copy_string(dev, new_dev, group); | |
2232cac8 GKH |
124 | return 0; |
125 | } | |
126 | } | |
127 | ||
128 | /* not found, lets create a new structure, and add it to the list */ | |
129 | tmp_dev = malloc(sizeof(*tmp_dev)); | |
130 | if (!tmp_dev) | |
131 | return -ENOMEM; | |
132 | memcpy(tmp_dev, new_dev, sizeof(*tmp_dev)); | |
133 | list_add(&tmp_dev->node, &config_device_list); | |
469c7cff | 134 | //dump_dev(tmp_dev); |
2232cac8 GKH |
135 | return 0; |
136 | } | |
137 | ||
469c7cff GKH |
138 | static void dump_dev_list(void) |
139 | { | |
140 | struct list_head *tmp; | |
141 | ||
142 | list_for_each(tmp, &config_device_list) { | |
143 | struct config_device *dev = list_entry(tmp, struct config_device, node); | |
144 | dump_dev(dev); | |
145 | } | |
146 | } | |
2232cac8 | 147 | |
469c7cff GKH |
148 | static int get_pair(char **orig_string, char **left, char **right) |
149 | { | |
150 | char *temp; | |
151 | char *string = *orig_string; | |
152 | ||
70033702 AB |
153 | if (!string) |
154 | return -ENODEV; | |
155 | ||
469c7cff GKH |
156 | /* eat any whitespace */ |
157 | while (isspace(*string)) | |
158 | ++string; | |
159 | ||
160 | /* split based on '=' */ | |
161 | temp = strsep(&string, "="); | |
162 | *left = temp; | |
70033702 AB |
163 | if (!string) |
164 | return -ENODEV; | |
469c7cff GKH |
165 | |
166 | /* take the right side and strip off the '"' */ | |
167 | while (isspace(*string)) | |
168 | ++string; | |
169 | if (*string == '"') | |
170 | ++string; | |
70033702 AB |
171 | else |
172 | return -ENODEV; | |
173 | ||
469c7cff | 174 | temp = strsep(&string, "\""); |
70033702 AB |
175 | if (!string || *temp == '\0') |
176 | return -ENODEV; | |
469c7cff GKH |
177 | *right = temp; |
178 | *orig_string = string; | |
179 | ||
180 | return 0; | |
181 | } | |
2232cac8 | 182 | |
70033702 AB |
183 | static int get_value(const char *left, char **orig_string, char **ret_string) |
184 | { | |
185 | int retval; | |
186 | char *left_string; | |
187 | ||
188 | retval = get_pair(orig_string, &left_string, ret_string); | |
189 | if (retval) | |
190 | return retval; | |
191 | if (strcasecmp(left_string, left) != 0) | |
192 | return -ENODEV; | |
193 | return 0; | |
194 | } | |
195 | ||
2232cac8 GKH |
196 | static int namedev_init_config(void) |
197 | { | |
2232cac8 | 198 | char line[255]; |
70033702 | 199 | int lineno; |
2232cac8 GKH |
200 | char *temp; |
201 | char *temp2; | |
202 | char *temp3; | |
203 | FILE *fd; | |
204 | int retval = 0; | |
205 | struct config_device dev; | |
206 | ||
116176b0 | 207 | dbg("opening %s to read as config", udev_config_filename); |
c056c514 | 208 | fd = fopen(udev_config_filename, "r"); |
2232cac8 | 209 | if (fd == NULL) { |
c056c514 | 210 | dbg("Can't open %s", udev_config_filename); |
2232cac8 GKH |
211 | return -ENODEV; |
212 | } | |
213 | ||
214 | /* loop through the whole file */ | |
70033702 | 215 | lineno = 0; |
2232cac8 GKH |
216 | while (1) { |
217 | /* get a line */ | |
218 | temp = fgets(line, sizeof(line), fd); | |
219 | if (temp == NULL) | |
70033702 AB |
220 | goto exit; |
221 | lineno++; | |
2232cac8 | 222 | |
5ef7b799 | 223 | dbg_parse("read %s", temp); |
2232cac8 GKH |
224 | |
225 | /* eat the whitespace at the beginning of the line */ | |
226 | while (isspace(*temp)) | |
227 | ++temp; | |
228 | ||
229 | /* no more line? */ | |
230 | if (*temp == 0x00) | |
231 | continue; | |
232 | ||
233 | /* see if this is a comment */ | |
234 | if (*temp == COMMENT_CHARACTER) | |
235 | continue; | |
236 | ||
469c7cff | 237 | memset(&dev, 0x00, sizeof(struct config_device)); |
2232cac8 GKH |
238 | |
239 | /* parse the line */ | |
240 | temp2 = strsep(&temp, ","); | |
241 | if (strcasecmp(temp2, TYPE_LABEL) == 0) { | |
242 | /* label type */ | |
243 | dev.type = LABEL; | |
244 | ||
245 | /* BUS="bus" */ | |
246 | retval = get_value("BUS", &temp, &temp3); | |
247 | if (retval) | |
70033702 AB |
248 | break; |
249 | strfieldcpy(dev.bus, temp3); | |
469c7cff GKH |
250 | |
251 | /* file="value" */ | |
252 | temp2 = strsep(&temp, ","); | |
253 | retval = get_pair(&temp, &temp2, &temp3); | |
254 | if (retval) | |
70033702 AB |
255 | break; |
256 | strfieldcpy(dev.sysfs_file, temp2); | |
257 | strfieldcpy(dev.sysfs_value, temp3); | |
469c7cff GKH |
258 | |
259 | /* NAME="new_name" */ | |
260 | temp2 = strsep(&temp, ","); | |
261 | retval = get_value("NAME", &temp, &temp3); | |
262 | if (retval) | |
70033702 AB |
263 | break; |
264 | strfieldcpy(dev.name, temp3); | |
469c7cff | 265 | |
5ef7b799 GKH |
266 | dbg_parse("LABEL name = '%s', bus = '%s', " |
267 | "sysfs_file = '%s', sysfs_value = '%s'", | |
9d496c74 | 268 | dev.name, dev.bus, dev.sysfs_file, |
5ef7b799 | 269 | dev.sysfs_value); |
2232cac8 GKH |
270 | } |
271 | ||
272 | if (strcasecmp(temp2, TYPE_NUMBER) == 0) { | |
273 | /* number type */ | |
274 | dev.type = NUMBER; | |
275 | ||
276 | /* BUS="bus" */ | |
277 | retval = get_value("BUS", &temp, &temp3); | |
278 | if (retval) | |
70033702 AB |
279 | break; |
280 | strfieldcpy(dev.bus, temp3); | |
469c7cff GKH |
281 | |
282 | /* ID="id" */ | |
283 | temp2 = strsep(&temp, ","); | |
284 | retval = get_value("id", &temp, &temp3); | |
285 | if (retval) | |
70033702 AB |
286 | break; |
287 | strfieldcpy(dev.id, temp3); | |
469c7cff GKH |
288 | |
289 | /* NAME="new_name" */ | |
290 | temp2 = strsep(&temp, ","); | |
291 | retval = get_value("NAME", &temp, &temp3); | |
292 | if (retval) | |
70033702 AB |
293 | break; |
294 | strfieldcpy(dev.name, temp3); | |
469c7cff | 295 | |
5ef7b799 | 296 | dbg_parse("NUMBER name = '%s', bus = '%s', id = '%s'", |
9d496c74 | 297 | dev.name, dev.bus, dev.id); |
2232cac8 GKH |
298 | } |
299 | ||
300 | if (strcasecmp(temp2, TYPE_TOPOLOGY) == 0) { | |
301 | /* number type */ | |
302 | dev.type = TOPOLOGY; | |
303 | ||
304 | /* BUS="bus" */ | |
305 | retval = get_value("BUS", &temp, &temp3); | |
306 | if (retval) | |
70033702 AB |
307 | break; |
308 | strfieldcpy(dev.bus, temp3); | |
469c7cff GKH |
309 | |
310 | /* PLACE="place" */ | |
311 | temp2 = strsep(&temp, ","); | |
312 | retval = get_value("place", &temp, &temp3); | |
313 | if (retval) | |
70033702 AB |
314 | break; |
315 | strfieldcpy(dev.place, temp3); | |
469c7cff GKH |
316 | |
317 | /* NAME="new_name" */ | |
318 | temp2 = strsep(&temp, ","); | |
319 | retval = get_value("NAME", &temp, &temp3); | |
320 | if (retval) | |
70033702 AB |
321 | break; |
322 | strfieldcpy(dev.name, temp3); | |
469c7cff | 323 | |
5ef7b799 | 324 | dbg_parse("TOPOLOGY name = '%s', bus = '%s', place = '%s'", |
9d496c74 | 325 | dev.name, dev.bus, dev.place); |
2232cac8 GKH |
326 | } |
327 | ||
328 | if (strcasecmp(temp2, TYPE_REPLACE) == 0) { | |
329 | /* number type */ | |
330 | dev.type = REPLACE; | |
331 | ||
332 | /* KERNEL="kernel_name" */ | |
333 | retval = get_value("KERNEL", &temp, &temp3); | |
334 | if (retval) | |
70033702 AB |
335 | break; |
336 | strfieldcpy(dev.kernel_name, temp3); | |
2232cac8 GKH |
337 | |
338 | /* NAME="new_name" */ | |
339 | temp2 = strsep(&temp, ","); | |
340 | retval = get_value("NAME", &temp, &temp3); | |
341 | if (retval) | |
70033702 AB |
342 | break; |
343 | strfieldcpy(dev.name, temp3); | |
5ef7b799 | 344 | dbg_parse("REPLACE name = %s, kernel_name = %s", |
9d496c74 | 345 | dev.name, dev.kernel_name); |
2232cac8 | 346 | } |
c27e6911 PM |
347 | if (strcasecmp(temp2, TYPE_CALLOUT) == 0) { |
348 | /* number type */ | |
349 | dev.type = CALLOUT; | |
350 | ||
351 | /* PROGRAM="executable" */ | |
352 | retval = get_value("PROGRAM", &temp, &temp3); | |
353 | if (retval) | |
70033702 AB |
354 | break; |
355 | strfieldcpy(dev.exec_program, temp3); | |
c27e6911 PM |
356 | |
357 | /* BUS="bus" */ | |
358 | temp2 = strsep(&temp, ","); | |
359 | retval = get_value("BUS", &temp, &temp3); | |
360 | if (retval) | |
70033702 AB |
361 | break; |
362 | strfieldcpy(dev.bus, temp3); | |
c27e6911 PM |
363 | |
364 | /* ID="id" */ | |
365 | temp2 = strsep(&temp, ","); | |
366 | retval = get_value("ID", &temp, &temp3); | |
367 | if (retval) | |
70033702 AB |
368 | break; |
369 | strfieldcpy(dev.id, temp3); | |
c27e6911 PM |
370 | |
371 | /* NAME="new_name" */ | |
372 | temp2 = strsep(&temp, ","); | |
373 | retval = get_value("NAME", &temp, &temp3); | |
374 | if (retval) | |
70033702 AB |
375 | break; |
376 | strfieldcpy(dev.name, temp3); | |
5ef7b799 | 377 | dbg_parse("CALLOUT name = %s, program = %s", |
9d496c74 | 378 | dev.name, dev.exec_program); |
c27e6911 | 379 | } |
2232cac8 GKH |
380 | |
381 | retval = add_dev(&dev); | |
382 | if (retval) { | |
383 | dbg("add_dev returned with error %d", retval); | |
384 | goto exit; | |
385 | } | |
386 | } | |
70033702 AB |
387 | dbg_parse("%s:%d:%Zd: error parsing ``%s''", udev_config_filename, |
388 | lineno, temp - line, temp); | |
2232cac8 GKH |
389 | exit: |
390 | fclose(fd); | |
391 | return retval; | |
392 | } | |
393 | ||
394 | ||
395 | static int namedev_init_permissions(void) | |
396 | { | |
2232cac8 GKH |
397 | char line[255]; |
398 | char *temp; | |
399 | char *temp2; | |
400 | FILE *fd; | |
401 | int retval = 0; | |
402 | struct config_device dev; | |
403 | ||
c056c514 GKH |
404 | dbg("opening %s to read as permissions config", udev_config_permission_filename); |
405 | fd = fopen(udev_config_permission_filename, "r"); | |
2232cac8 | 406 | if (fd == NULL) { |
c056c514 | 407 | dbg("Can't open %s", udev_config_permission_filename); |
2232cac8 GKH |
408 | return -ENODEV; |
409 | } | |
410 | ||
411 | /* loop through the whole file */ | |
412 | while (1) { | |
413 | /* get a line */ | |
414 | temp = fgets(line, sizeof(line), fd); | |
415 | if (temp == NULL) | |
416 | break; | |
417 | ||
5ef7b799 | 418 | dbg_parse("read %s", temp); |
2232cac8 GKH |
419 | |
420 | /* eat the whitespace at the beginning of the line */ | |
421 | while (isspace(*temp)) | |
422 | ++temp; | |
423 | ||
424 | /* no more line? */ | |
425 | if (*temp == 0x00) | |
426 | continue; | |
427 | ||
428 | /* see if this is a comment */ | |
429 | if (*temp == COMMENT_CHARACTER) | |
430 | continue; | |
431 | ||
432 | memset(&dev, 0x00, sizeof(dev)); | |
433 | ||
434 | /* parse the line */ | |
435 | temp2 = strsep(&temp, ":"); | |
9d496c74 | 436 | strncpy(dev.name, temp2, sizeof(dev.name)); |
2232cac8 GKH |
437 | |
438 | temp2 = strsep(&temp, ":"); | |
9d496c74 | 439 | strncpy(dev.owner, temp2, sizeof(dev.owner)); |
2232cac8 GKH |
440 | |
441 | temp2 = strsep(&temp, ":"); | |
9d496c74 | 442 | strncpy(dev.group, temp2, sizeof(dev.owner)); |
2232cac8 | 443 | |
9d496c74 | 444 | dev.mode = strtol(temp, NULL, 8); |
2232cac8 | 445 | |
5ef7b799 | 446 | dbg_parse("name = %s, owner = %s, group = %s, mode = %#o", |
9d496c74 GKH |
447 | dev.name, dev.owner, dev.group, |
448 | dev.mode); | |
2232cac8 GKH |
449 | retval = add_dev(&dev); |
450 | if (retval) { | |
451 | dbg("add_dev returned with error %d", retval); | |
452 | goto exit; | |
453 | } | |
454 | } | |
455 | ||
456 | exit: | |
457 | fclose(fd); | |
458 | return retval; | |
459 | } | |
460 | ||
c2405f50 | 461 | static mode_t get_default_mode(struct sysfs_class_device *class_dev) |
185a35a4 | 462 | { |
19dc5d4c GKH |
463 | /* just default everyone to rw for the world! */ |
464 | return 0666; | |
185a35a4 GKH |
465 | } |
466 | ||
19dc5d4c | 467 | |
c27e6911 PM |
468 | static int exec_callout(struct config_device *dev, char *value, int len) |
469 | { | |
470 | int retval; | |
471 | int res; | |
472 | int status; | |
473 | int fds[2]; | |
474 | pid_t pid; | |
475 | int value_set = 0; | |
476 | char buffer[256]; | |
477 | ||
478 | dbg("callout to %s\n", dev->exec_program); | |
479 | retval = pipe(fds); | |
480 | if (retval != 0) { | |
481 | dbg("pipe failed"); | |
482 | return -1; | |
483 | } | |
484 | pid = fork(); | |
485 | if (pid == -1) { | |
486 | dbg("fork failed"); | |
487 | return -1; | |
488 | } | |
489 | ||
490 | if (pid == 0) { | |
491 | /* | |
492 | * child | |
493 | */ | |
494 | close(STDOUT_FILENO); | |
495 | dup(fds[1]); /* dup write side of pipe to STDOUT */ | |
496 | retval = execve(dev->exec_program, main_argv, main_envp); | |
497 | if (retval != 0) { | |
498 | dbg("child execve failed"); | |
499 | exit(1); | |
500 | } | |
501 | return -1; /* avoid compiler warning */ | |
502 | } else { | |
503 | /* | |
504 | * Parent reads from fds[0]. | |
505 | */ | |
506 | close(fds[1]); | |
507 | retval = 0; | |
508 | while (1) { | |
509 | res = read(fds[0], buffer, sizeof(buffer) - 1); | |
510 | if (res <= 0) | |
511 | break; | |
512 | buffer[res] = '\0'; | |
513 | if (res > len) { | |
514 | dbg("callout len %d too short\n", len); | |
515 | retval = -1; | |
516 | } | |
517 | if (value_set) { | |
518 | dbg("callout value already set"); | |
519 | retval = -1; | |
520 | } else { | |
521 | value_set = 1; | |
522 | strncpy(value, buffer, len); | |
523 | } | |
524 | } | |
525 | close(fds[0]); | |
526 | res = wait(&status); | |
527 | if (res < 0) { | |
528 | dbg("wait failed result %d", res); | |
529 | retval = -1; | |
530 | } | |
531 | ||
1e959a4b | 532 | #ifndef __KLIBC__ |
c27e6911 PM |
533 | if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) { |
534 | dbg("callout program status 0x%x", status); | |
535 | retval = -1; | |
536 | } | |
1e959a4b | 537 | #endif |
c27e6911 PM |
538 | } |
539 | return retval; | |
540 | } | |
541 | ||
120d45d0 GKH |
542 | static int do_callout(struct sysfs_class_device *class_dev, struct udevice *udev) |
543 | { | |
544 | struct config_device *dev; | |
545 | struct list_head *tmp; | |
546 | char value[ID_SIZE]; | |
547 | ||
548 | list_for_each(tmp, &config_device_list) { | |
549 | dev = list_entry(tmp, struct config_device, node); | |
550 | if (dev->type != CALLOUT) | |
551 | continue; | |
552 | ||
553 | if (exec_callout(dev, value, sizeof(value))) | |
554 | continue; | |
555 | if (strncmp(value, dev->id, sizeof(value)) != 0) | |
556 | continue; | |
70033702 | 557 | strfieldcpy(udev->name, dev->name); |
120d45d0 GKH |
558 | if (dev->mode != 0) { |
559 | udev->mode = dev->mode; | |
70033702 AB |
560 | strfieldcpy(udev->owner, dev->owner); |
561 | strfieldcpy(udev->group, dev->group); | |
120d45d0 GKH |
562 | } |
563 | dbg_parse("device callout '%s' becomes '%s' - owner = %s, group = %s, mode = %#o", | |
564 | dev->id, udev->name, | |
565 | dev->owner, dev->group, dev->mode); | |
566 | return 0; | |
567 | } | |
568 | return -ENODEV; | |
569 | } | |
570 | ||
ca1cc0fe GKH |
571 | static int do_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
572 | { | |
573 | struct sysfs_attribute *tmpattr = NULL; | |
574 | struct config_device *dev; | |
575 | struct list_head *tmp; | |
ca1cc0fe GKH |
576 | |
577 | list_for_each(tmp, &config_device_list) { | |
578 | dev = list_entry(tmp, struct config_device, node); | |
579 | if (dev->type != LABEL) | |
580 | continue; | |
581 | ||
582 | dbg_parse("LABEL: match file '%s' with value '%s'", | |
583 | dev->sysfs_file, dev->sysfs_value); | |
584 | /* try to find the attribute in the class device directory */ | |
585 | tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file); | |
586 | if (tmpattr) | |
587 | goto label_found; | |
588 | ||
589 | /* look in the class device directory if present */ | |
590 | if (sysfs_device) { | |
591 | tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file); | |
592 | if (tmpattr) | |
593 | goto label_found; | |
594 | } | |
595 | ||
596 | continue; | |
597 | ||
598 | label_found: | |
599 | tmpattr->value[strlen(tmpattr->value)-1] = 0x00; | |
600 | dbg_parse("file '%s' found with value '%s' compare with '%s'", dev->sysfs_file, tmpattr->value, dev->sysfs_value); | |
601 | if (strcmp(dev->sysfs_value, tmpattr->value) != 0) | |
602 | continue; | |
603 | ||
70033702 | 604 | strfieldcpy(udev->name, dev->name); |
ca1cc0fe GKH |
605 | if (dev->mode != 0) { |
606 | udev->mode = dev->mode; | |
70033702 AB |
607 | strfieldcpy(udev->owner, dev->owner); |
608 | strfieldcpy(udev->group, dev->group); | |
ca1cc0fe GKH |
609 | } |
610 | dbg_parse("file '%s' with value '%s' becomes '%s' - owner = %s, group = %s, mode = %#o", | |
611 | dev->sysfs_file, dev->sysfs_value, udev->name, | |
612 | dev->owner, dev->group, dev->mode); | |
613 | ||
614 | return 0; | |
615 | } | |
616 | return -ENODEV; | |
617 | } | |
618 | ||
619 | static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) | |
620 | { | |
621 | struct config_device *dev; | |
622 | struct list_head *tmp; | |
623 | char path[SYSFS_PATH_MAX]; | |
624 | int found; | |
625 | char *temp = NULL; | |
626 | ||
627 | /* we have to have a sysfs device for NUMBER to work */ | |
628 | if (!sysfs_device) | |
629 | return -ENODEV; | |
630 | ||
631 | list_for_each(tmp, &config_device_list) { | |
632 | dev = list_entry(tmp, struct config_device, node); | |
633 | if (dev->type != NUMBER) | |
634 | continue; | |
635 | ||
636 | found = 0; | |
70033702 | 637 | strfieldcpy(path, sysfs_device->path); |
ca1cc0fe GKH |
638 | temp = strrchr(path, '/'); |
639 | dbg_parse("NUMBER path = '%s'", path); | |
640 | dbg_parse("NUMBER temp = '%s' id = '%s'", temp, dev->id); | |
641 | if (strstr(temp, dev->id) != NULL) { | |
642 | found = 1; | |
643 | } else { | |
644 | *temp = 0x00; | |
645 | temp = strrchr(path, '/'); | |
646 | dbg_parse("NUMBER temp = '%s' id = '%s'", temp, dev->id); | |
647 | if (strstr(temp, dev->id) != NULL) | |
648 | found = 1; | |
649 | } | |
650 | if (!found) | |
651 | continue; | |
70033702 | 652 | strfieldcpy(udev->name, dev->name); |
ca1cc0fe GKH |
653 | if (dev->mode != 0) { |
654 | udev->mode = dev->mode; | |
70033702 AB |
655 | strfieldcpy(udev->owner, dev->owner); |
656 | strfieldcpy(udev->group, dev->group); | |
ca1cc0fe GKH |
657 | } |
658 | dbg_parse("device id '%s' becomes '%s' - owner = %s, group = %s, mode = %#o", | |
659 | dev->id, udev->name, | |
660 | dev->owner, dev->group, dev->mode); | |
661 | return 0; | |
662 | } | |
663 | return -ENODEV; | |
664 | } | |
665 | ||
666 | ||
8c51bbfe GKH |
667 | static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device) |
668 | { | |
669 | struct config_device *dev; | |
670 | struct list_head *tmp; | |
671 | char path[SYSFS_PATH_MAX]; | |
672 | int found; | |
673 | char *temp = NULL; | |
674 | ||
675 | /* we have to have a sysfs device for TOPOLOGY to work */ | |
676 | if (!sysfs_device) | |
677 | return -ENODEV; | |
678 | ||
679 | list_for_each(tmp, &config_device_list) { | |
680 | dev = list_entry(tmp, struct config_device, node); | |
681 | if (dev->type != TOPOLOGY) | |
682 | continue; | |
683 | ||
20ff86bd | 684 | found = 0; |
70033702 | 685 | strfieldcpy(path, sysfs_device->path); |
8c51bbfe GKH |
686 | temp = strrchr(path, '/'); |
687 | dbg_parse("TOPOLOGY path = '%s'", path); | |
688 | dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp, dev->place); | |
689 | if (strstr(temp, dev->place) != NULL) { | |
690 | found = 1; | |
691 | } else { | |
692 | *temp = 0x00; | |
693 | temp = strrchr(path, '/'); | |
694 | dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp, dev->place); | |
695 | if (strstr(temp, dev->place) != NULL) | |
696 | found = 1; | |
697 | } | |
698 | if (!found) | |
699 | continue; | |
700 | ||
70033702 | 701 | strfieldcpy(udev->name, dev->name); |
8c51bbfe GKH |
702 | if (dev->mode != 0) { |
703 | udev->mode = dev->mode; | |
70033702 AB |
704 | strfieldcpy(udev->owner, dev->owner); |
705 | strfieldcpy(udev->group, dev->group); | |
8c51bbfe GKH |
706 | } |
707 | dbg_parse("device at '%s' becomes '%s' - owner = %s, group = %s, mode = %#o", | |
708 | dev->place, udev->name, | |
709 | dev->owner, dev->group, dev->mode); | |
710 | ||
711 | return 0; | |
712 | } | |
713 | return -ENODEV; | |
714 | } | |
715 | ||
120d45d0 GKH |
716 | static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev) |
717 | { | |
718 | struct config_device *dev; | |
719 | struct list_head *tmp; | |
720 | ||
721 | list_for_each(tmp, &config_device_list) { | |
722 | dev = list_entry(tmp, struct config_device, node); | |
723 | if (dev->type != REPLACE) | |
724 | continue; | |
725 | ||
726 | dbg_parse("REPLACE: replace name '%s' with '%s'", | |
727 | dev->kernel_name, dev->name); | |
728 | if (strcmp(dev->kernel_name, class_dev->name) != 0) | |
729 | continue; | |
730 | ||
70033702 | 731 | strfieldcpy(udev->name, dev->name); |
120d45d0 GKH |
732 | if (dev->mode != 0) { |
733 | udev->mode = dev->mode; | |
70033702 AB |
734 | strfieldcpy(udev->owner, dev->owner); |
735 | strfieldcpy(udev->group, dev->group); | |
120d45d0 GKH |
736 | } |
737 | dbg_parse("'%s' becomes '%s' - owner = %s, group = %s, mode = %#o", | |
738 | dev->kernel_name, udev->name, | |
739 | dev->owner, dev->group, dev->mode); | |
740 | ||
741 | return 0; | |
742 | } | |
743 | return -ENODEV; | |
744 | } | |
745 | ||
9d496c74 | 746 | static int get_attr(struct sysfs_class_device *class_dev, struct udevice *udev) |
185a35a4 | 747 | { |
7bd22a78 GKH |
748 | struct sysfs_device *sysfs_device = NULL; |
749 | struct sysfs_class_device *class_dev_parent = NULL; | |
185a35a4 | 750 | int retval = 0; |
7bd22a78 | 751 | char *temp = NULL; |
185a35a4 | 752 | |
9d496c74 | 753 | udev->mode = 0; |
7bd22a78 GKH |
754 | |
755 | /* find the sysfs_device for this class device */ | |
8a0c11d3 | 756 | /* Wouldn't it really be nice if libsysfs could do this for us? */ |
03e64c8f | 757 | if (class_dev->sysdevice) { |
7bd22a78 GKH |
758 | sysfs_device = class_dev->sysdevice; |
759 | } else { | |
760 | /* bah, let's go backwards up a level to see if the device is there, | |
761 | * as block partitions don't point to the physical device. Need to fix that | |
762 | * up in the kernel... | |
763 | */ | |
764 | if (strstr(class_dev->path, "block")) { | |
765 | dbg_parse("looking at block device..."); | |
766 | if (isdigit(class_dev->path[strlen(class_dev->path)-1])) { | |
767 | char path[SYSFS_PATH_MAX]; | |
768 | ||
769 | dbg_parse("really is a partition..."); | |
70033702 | 770 | strfieldcpy(path, class_dev->path); |
7bd22a78 GKH |
771 | temp = strrchr(path, '/'); |
772 | *temp = 0x00; | |
773 | dbg_parse("looking for a class device at '%s'", path); | |
774 | class_dev_parent = sysfs_open_class_device(path); | |
775 | if (class_dev_parent == NULL) { | |
776 | dbg("sysfs_open_class_device at '%s' failed", path); | |
777 | } else { | |
778 | dbg_parse("class_dev_parent->name = %s", class_dev_parent->name); | |
779 | if (class_dev_parent->sysdevice) | |
780 | sysfs_device = class_dev_parent->sysdevice; | |
781 | } | |
782 | } | |
783 | } | |
784 | } | |
785 | ||
786 | if (sysfs_device) { | |
787 | dbg_parse("sysfs_device->path = '%s'", sysfs_device->path); | |
788 | dbg_parse("sysfs_device->bus_id = '%s'", sysfs_device->bus_id); | |
03e64c8f | 789 | } else { |
5ef7b799 | 790 | dbg_parse("class_dev->name = '%s'", class_dev->name); |
03e64c8f | 791 | } |
120d45d0 GKH |
792 | |
793 | /* rules are looked at in priority order */ | |
120d45d0 GKH |
794 | retval = do_callout(class_dev, udev); |
795 | if (retval == 0) | |
796 | goto done; | |
797 | ||
ca1cc0fe GKH |
798 | retval = do_label(class_dev, udev, sysfs_device); |
799 | if (retval == 0) | |
800 | goto done; | |
801 | ||
802 | retval = do_number(class_dev, udev, sysfs_device); | |
803 | if (retval == 0) | |
804 | goto done; | |
805 | ||
8c51bbfe GKH |
806 | retval = do_topology(class_dev, udev, sysfs_device); |
807 | if (retval == 0) | |
808 | goto done; | |
809 | ||
120d45d0 GKH |
810 | retval = do_replace(class_dev, udev); |
811 | if (retval == 0) | |
812 | goto done; | |
813 | ||
70033702 | 814 | strfieldcpy(udev->name, class_dev->name); |
c2405f50 | 815 | |
615e05f8 | 816 | done: |
98b88dbf KS |
817 | /* substitute placeholder in NAME */ |
818 | while (1) { | |
7408a7fb GKH |
819 | char *pos = strchr(udev->name, '%'); |
820 | char *dig; | |
821 | char name[NAME_SIZE]; | |
98b88dbf | 822 | if (pos) { |
70033702 | 823 | strfieldcpy(name, pos+2); |
98b88dbf KS |
824 | *pos = 0x00; |
825 | switch (pos[1]) { | |
ae2859df AB |
826 | case 'b': |
827 | if (!sysfs_device) | |
828 | break; | |
829 | strcat(udev->name, sysfs_device->bus_id); | |
830 | dbg("bus_id appended: %s", | |
831 | sysfs_device->bus_id); | |
832 | break; | |
98b88dbf KS |
833 | case 'n': |
834 | dig = class_dev->name + strlen(class_dev->name); | |
835 | while (isdigit(*(dig-1))) | |
836 | dig--; | |
837 | strcat(udev->name, dig); | |
5e6e29fd | 838 | dbg("kernel number appended: %s", dig); |
98b88dbf KS |
839 | break; |
840 | case 'm': | |
841 | sprintf(pos, "%u", udev->minor); | |
5e6e29fd | 842 | dbg("minor number appended: %u", udev->minor); |
98b88dbf KS |
843 | break; |
844 | case 'M': | |
845 | sprintf(pos, "%u", udev->major); | |
5e6e29fd | 846 | dbg("major number appended: %u", udev->major); |
98b88dbf KS |
847 | break; |
848 | default: | |
5e6e29fd | 849 | dbg("unknown substitution type: %%%c", pos[1]); |
98b88dbf KS |
850 | break; |
851 | } | |
852 | strcat(udev->name, name); | |
853 | } else | |
854 | break; | |
855 | } | |
856 | ||
c2405f50 | 857 | /* mode was never set above */ |
9d496c74 GKH |
858 | if (!udev->mode) { |
859 | udev->mode = get_default_mode(class_dev); | |
860 | udev->owner[0] = 0x00; | |
861 | udev->group[0] = 0x00; | |
615e05f8 | 862 | } |
7bd22a78 GKH |
863 | |
864 | if (class_dev_parent) | |
865 | sysfs_close_class_device(class_dev_parent); | |
866 | ||
120d45d0 | 867 | return 0; |
185a35a4 GKH |
868 | } |
869 | ||
9d496c74 | 870 | int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *dev) |
185a35a4 | 871 | { |
185a35a4 GKH |
872 | int retval; |
873 | ||
9d496c74 | 874 | retval = get_attr(class_dev, dev); |
19dc5d4c GKH |
875 | if (retval) |
876 | dbg("get_attr failed"); | |
877 | ||
185a35a4 GKH |
878 | return retval; |
879 | } | |
2232cac8 GKH |
880 | |
881 | int namedev_init(void) | |
882 | { | |
883 | int retval; | |
185a35a4 | 884 | |
2232cac8 GKH |
885 | retval = namedev_init_config(); |
886 | if (retval) | |
887 | return retval; | |
888 | ||
889 | retval = namedev_init_permissions(); | |
890 | if (retval) | |
891 | return retval; | |
892 | ||
469c7cff | 893 | dump_dev_list(); |
2232cac8 GKH |
894 | return retval; |
895 | } | |
896 | ||
185a35a4 | 897 |