chiark / gitweb /
[PATCH] add scsi_id "extra" program from Patrick Mansfield <patmans@us.ibm.com>
[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_label(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
570 {
571         struct sysfs_attribute *tmpattr = NULL;
572         struct config_device *dev;
573         struct list_head *tmp;
574         char *temp = NULL;
575
576         list_for_each(tmp, &config_device_list) {
577                 dev = list_entry(tmp, struct config_device, node);
578                 if (dev->type != LABEL)
579                         continue;
580
581                 dbg_parse("LABEL: match file '%s' with value '%s'",
582                                 dev->sysfs_file, dev->sysfs_value);
583                 /* try to find the attribute in the class device directory */
584                 tmpattr = sysfs_get_classdev_attr(class_dev, dev->sysfs_file);
585                 if (tmpattr)
586                         goto label_found;
587
588                 /* look in the class device directory if present */
589                 if (sysfs_device) {
590                         tmpattr = sysfs_get_device_attr(sysfs_device, dev->sysfs_file);
591                         if (tmpattr)
592                                 goto label_found;
593                 }
594
595                 continue;
596
597 label_found:
598                 tmpattr->value[strlen(tmpattr->value)-1] = 0x00;
599                 dbg_parse("file '%s' found with value '%s' compare with '%s'", dev->sysfs_file, tmpattr->value, dev->sysfs_value);
600                 if (strcmp(dev->sysfs_value, tmpattr->value) != 0)
601                         continue;
602
603                 strcpy(udev->name, dev->name);
604                 if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
605                         temp = &class_dev->path[strlen(class_dev->path)-1];
606                         strcat(udev->name, temp);
607                 }
608                 if (dev->mode != 0) {
609                         udev->mode = dev->mode;
610                         strcpy(udev->owner, dev->owner);
611                         strcpy(udev->group, dev->group);
612                 }
613                 dbg_parse("file '%s' with value '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
614                         dev->sysfs_file, dev->sysfs_value, udev->name, 
615                         dev->owner, dev->group, dev->mode);
616
617                 return 0;
618         }
619         return -ENODEV;
620 }
621
622 static int do_number(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
623 {
624         struct config_device *dev;
625         struct list_head *tmp;
626         char path[SYSFS_PATH_MAX];
627         int found;
628         char *temp = NULL;
629
630         /* we have to have a sysfs device for NUMBER to work */
631         if (!sysfs_device)
632                 return -ENODEV;
633
634         list_for_each(tmp, &config_device_list) {
635                 dev = list_entry(tmp, struct config_device, node);
636                 if (dev->type != NUMBER)
637                         continue;
638
639                 found = 0;
640                 strcpy(path, sysfs_device->path);
641                 temp = strrchr(path, '/');
642                 dbg_parse("NUMBER path = '%s'", path);
643                 dbg_parse("NUMBER temp = '%s' id = '%s'", temp, dev->id);
644                 if (strstr(temp, dev->id) != NULL) {
645                         found = 1;
646                 } else {
647                         *temp = 0x00;
648                         temp = strrchr(path, '/');
649                         dbg_parse("NUMBER temp = '%s' id = '%s'", temp, dev->id);
650                         if (strstr(temp, dev->id) != NULL)
651                                 found = 1;
652                 }
653                 if (!found)
654                         continue;
655                 strcpy(udev->name, dev->name);
656                 if (dev->mode != 0) {
657                         udev->mode = dev->mode;
658                         strcpy(udev->owner, dev->owner);
659                         strcpy(udev->group, dev->group);
660                 }
661                 dbg_parse("device id '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
662                         dev->id, udev->name, 
663                         dev->owner, dev->group, dev->mode);
664                 return 0;
665         }
666         return -ENODEV;
667 }
668
669
670 static int do_topology(struct sysfs_class_device *class_dev, struct udevice *udev, struct sysfs_device *sysfs_device)
671 {
672         struct config_device *dev;
673         struct list_head *tmp;
674         char path[SYSFS_PATH_MAX];
675         int found;
676         char *temp = NULL;
677
678         /* we have to have a sysfs device for TOPOLOGY to work */
679         if (!sysfs_device)
680                 return -ENODEV;
681
682         list_for_each(tmp, &config_device_list) {
683                 dev = list_entry(tmp, struct config_device, node);
684                 if (dev->type != TOPOLOGY)
685                         continue;
686
687                 found = 0;
688                 strcpy(path, sysfs_device->path);
689                 temp = strrchr(path, '/');
690                 dbg_parse("TOPOLOGY path = '%s'", path);
691                 dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp, dev->place);
692                 if (strstr(temp, dev->place) != NULL) {
693                         found = 1;
694                 } else {
695                         *temp = 0x00;
696                         temp = strrchr(path, '/');
697                         dbg_parse("TOPOLOGY temp = '%s' place = '%s'", temp, dev->place);
698                         if (strstr(temp, dev->place) != NULL)
699                                 found = 1;
700                 }
701                 if (!found)
702                         continue;
703
704                 strcpy(udev->name, dev->name);
705                 if (dev->mode != 0) {
706                         udev->mode = dev->mode;
707                         strcpy(udev->owner, dev->owner);
708                         strcpy(udev->group, dev->group);
709                 }
710                 dbg_parse("device at '%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
711                         dev->place, udev->name, 
712                         dev->owner, dev->group, dev->mode);
713                 
714                 return 0;
715         }
716         return -ENODEV;
717 }
718
719 static int do_replace(struct sysfs_class_device *class_dev, struct udevice *udev)
720 {
721         struct config_device *dev;
722         struct list_head *tmp;
723
724         list_for_each(tmp, &config_device_list) {
725                 dev = list_entry(tmp, struct config_device, node);
726                 if (dev->type != REPLACE)
727                         continue;
728
729                 dbg_parse("REPLACE: replace name '%s' with '%s'",
730                           dev->kernel_name, dev->name);
731                 if (strcmp(dev->kernel_name, class_dev->name) != 0)
732                         continue;
733
734                 strcpy(udev->name, dev->name);
735                 if (dev->mode != 0) {
736                         udev->mode = dev->mode;
737                         strcpy(udev->owner, dev->owner);
738                         strcpy(udev->group, dev->group);
739                 }
740                 dbg_parse("'%s' becomes '%s' - owner = %s, group = %s, mode = %#o",
741                         dev->kernel_name, udev->name, 
742                         dev->owner, dev->group, dev->mode);
743                 
744                 return 0;
745         }
746         return -ENODEV;
747 }
748
749 static int get_attr(struct sysfs_class_device *class_dev, struct udevice *udev)
750 {
751         struct sysfs_device *sysfs_device = NULL;
752         struct sysfs_class_device *class_dev_parent = NULL;
753         int retval = 0;
754         char *temp = NULL;
755
756         udev->mode = 0;
757
758         /* find the sysfs_device for this class device */
759         /* Wouldn't it really be nice if libsysfs could do this for us? */
760         if (class_dev->sysdevice) {
761                 sysfs_device = class_dev->sysdevice;
762         } else {
763                 /* bah, let's go backwards up a level to see if the device is there,
764                  * as block partitions don't point to the physical device.  Need to fix that
765                  * up in the kernel...
766                  */
767                 if (strstr(class_dev->path, "block")) {
768                         dbg_parse("looking at block device...");
769                         if (isdigit(class_dev->path[strlen(class_dev->path)-1])) {
770                                 char path[SYSFS_PATH_MAX];
771
772                                 dbg_parse("really is a partition...");
773                                 strcpy(path, class_dev->path);
774                                 temp = strrchr(path, '/');
775                                 *temp = 0x00;
776                                 dbg_parse("looking for a class device at '%s'", path);
777                                 class_dev_parent = sysfs_open_class_device(path);
778                                 if (class_dev_parent == NULL) {
779                                         dbg("sysfs_open_class_device at '%s' failed", path);
780                                 } else {
781                                         dbg_parse("class_dev_parent->name = %s", class_dev_parent->name);
782                                         if (class_dev_parent->sysdevice)
783                                                 sysfs_device = class_dev_parent->sysdevice;
784                                 }
785                         }
786                 }
787         }
788                 
789         if (sysfs_device) {
790                 dbg_parse("sysfs_device->path = '%s'", sysfs_device->path);
791                 dbg_parse("sysfs_device->bus_id = '%s'", sysfs_device->bus_id);
792         } else {
793                 dbg_parse("class_dev->name = '%s'", class_dev->name);
794         }
795
796         /* rules are looked at in priority order */
797         retval = do_callout(class_dev, udev);
798         if (retval == 0)
799                 goto done;
800
801         retval = do_label(class_dev, udev, sysfs_device);
802         if (retval == 0)
803                 goto done;
804
805         retval = do_number(class_dev, udev, sysfs_device);
806         if (retval == 0)
807                 goto done;
808
809         retval = do_topology(class_dev, udev, sysfs_device);
810         if (retval == 0)
811                 goto done;
812
813         retval = do_replace(class_dev, udev);
814         if (retval == 0)
815                 goto done;
816
817         strcpy(udev->name, class_dev->name);
818
819 done:
820         /* mode was never set above */
821         if (!udev->mode) {
822                 udev->mode = get_default_mode(class_dev);
823                 udev->owner[0] = 0x00;
824                 udev->group[0] = 0x00;
825         }
826
827         if (class_dev_parent)
828                 sysfs_close_class_device(class_dev_parent);
829
830         return 0;
831 }
832
833 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *dev)
834 {
835         int retval;
836
837         retval = get_attr(class_dev, dev);
838         if (retval)
839                 dbg("get_attr failed");
840
841         return retval;
842 }
843
844 int namedev_init(void)
845 {
846         int retval;
847         
848         retval = namedev_init_config();
849         if (retval)
850                 return retval;
851
852         retval = namedev_init_permissions();
853         if (retval)
854                 return retval;
855
856         dump_dev_list();
857         return retval;
858 }
859
860