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