chiark / gitweb /
[PATCH] add remove actions to the test scripts
[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_pair(char **orig_string, char **left, char **right)
149 {
150         char *temp;
151         char *string = *orig_string;
152
153         if (!string)
154                 return -ENODEV;
155
156         /* eat any whitespace */
157         while (isspace(*string))
158                 ++string;
159
160         /* split based on '=' */
161         temp = strsep(&string, "=");
162         *left = temp;
163         if (!string)
164                 return -ENODEV;
165
166         /* take the right side and strip off the '"' */
167         while (isspace(*string))
168                 ++string;
169         if (*string == '"')
170                 ++string;
171         else
172                 return -ENODEV;
173
174         temp = strsep(&string, "\"");
175         if (!string || *temp == '\0')
176                 return -ENODEV;
177         *right = temp;
178         *orig_string = string;
179         
180         return 0;
181 }
182
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
196 static int namedev_init_config(void)
197 {
198         char line[255];
199         int lineno;
200         char *temp;
201         char *temp2;
202         char *temp3;
203         FILE *fd;
204         int retval = 0;
205         struct config_device dev;
206
207         dbg("opening %s to read as config", udev_config_filename);
208         fd = fopen(udev_config_filename, "r");
209         if (fd == NULL) {
210                 dbg("Can't open %s", udev_config_filename);
211                 return -ENODEV;
212         }
213
214         /* loop through the whole file */
215         lineno = 0;
216         while (1) {
217                 /* get a line */
218                 temp = fgets(line, sizeof(line), fd);
219                 if (temp == NULL)
220                         goto exit;
221                 lineno++;
222
223                 dbg_parse("read %s", temp);
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
237                 memset(&dev, 0x00, sizeof(struct config_device));
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)
248                                 break;
249                         strfieldcpy(dev.bus, temp3);
250
251                         /* file="value" */
252                         temp2 = strsep(&temp, ",");
253                         retval = get_pair(&temp, &temp2, &temp3);
254                         if (retval)
255                                 break;
256                         strfieldcpy(dev.sysfs_file, temp2);
257                         strfieldcpy(dev.sysfs_value, temp3);
258
259                         /* NAME="new_name" */
260                         temp2 = strsep(&temp, ",");
261                         retval = get_value("NAME", &temp, &temp3);
262                         if (retval)
263                                 break;
264                         strfieldcpy(dev.name, temp3);
265
266                         dbg_parse("LABEL name = '%s', bus = '%s', "
267                                 "sysfs_file = '%s', sysfs_value = '%s'", 
268                                 dev.name, dev.bus, dev.sysfs_file, 
269                                 dev.sysfs_value);
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)
279                                 break;
280                         strfieldcpy(dev.bus, temp3);
281
282                         /* ID="id" */
283                         temp2 = strsep(&temp, ",");
284                         retval = get_value("id", &temp, &temp3);
285                         if (retval)
286                                 break;
287                         strfieldcpy(dev.id, temp3);
288
289                         /* NAME="new_name" */
290                         temp2 = strsep(&temp, ",");
291                         retval = get_value("NAME", &temp, &temp3);
292                         if (retval)
293                                 break;
294                         strfieldcpy(dev.name, temp3);
295
296                         dbg_parse("NUMBER name = '%s', bus = '%s', id = '%s'",
297                                         dev.name, dev.bus, dev.id);
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)
307                                 break;
308                         strfieldcpy(dev.bus, temp3);
309
310                         /* PLACE="place" */
311                         temp2 = strsep(&temp, ",");
312                         retval = get_value("place", &temp, &temp3);
313                         if (retval)
314                                 break;
315                         strfieldcpy(dev.place, temp3);
316
317                         /* NAME="new_name" */
318                         temp2 = strsep(&temp, ",");
319                         retval = get_value("NAME", &temp, &temp3);
320                         if (retval)
321                                 break;
322                         strfieldcpy(dev.name, temp3);
323
324                         dbg_parse("TOPOLOGY name = '%s', bus = '%s', place = '%s'",
325                                         dev.name, dev.bus, dev.place);
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)
335                                 break;
336                         strfieldcpy(dev.kernel_name, temp3);
337
338                         /* NAME="new_name" */
339                         temp2 = strsep(&temp, ",");
340                         retval = get_value("NAME", &temp, &temp3);
341                         if (retval)
342                                 break;
343                         strfieldcpy(dev.name, temp3);
344                         dbg_parse("REPLACE name = %s, kernel_name = %s",
345                                         dev.name, dev.kernel_name);
346                 }
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)
354                                 break;
355                         strfieldcpy(dev.exec_program, temp3);
356
357                         /* BUS="bus" */
358                         temp2 = strsep(&temp, ",");
359                         retval = get_value("BUS", &temp, &temp3);
360                         if (retval)
361                                 break;
362                         strfieldcpy(dev.bus, temp3);
363
364                         /* ID="id" */
365                         temp2 = strsep(&temp, ",");
366                         retval = get_value("ID", &temp, &temp3);
367                         if (retval)
368                                 break;
369                         strfieldcpy(dev.id, temp3);
370
371                         /* NAME="new_name" */
372                         temp2 = strsep(&temp, ",");
373                         retval = get_value("NAME", &temp, &temp3);
374                         if (retval)
375                                 break;
376                         strfieldcpy(dev.name, temp3);
377                         dbg_parse("CALLOUT name = %s, program = %s",
378                                         dev.name, dev.exec_program);
379                 }
380
381                 retval = add_dev(&dev);
382                 if (retval) {
383                         dbg("add_dev returned with error %d", retval);
384                         goto exit;
385                 }
386         }
387         dbg_parse("%s:%d:%Zd: error parsing ``%s''", udev_config_filename,
388                   lineno, temp - line, temp);
389 exit:
390         fclose(fd);
391         return retval;
392 }       
393
394
395 static int namedev_init_permissions(void)
396 {
397         char line[255];
398         char *temp;
399         char *temp2;
400         FILE *fd;
401         int retval = 0;
402         struct config_device dev;
403
404         dbg("opening %s to read as permissions config", udev_config_permission_filename);
405         fd = fopen(udev_config_permission_filename, "r");
406         if (fd == NULL) {
407                 dbg("Can't open %s", udev_config_permission_filename);
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
418                 dbg_parse("read %s", temp);
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, ":");
436                 strncpy(dev.name, temp2, sizeof(dev.name));
437
438                 temp2 = strsep(&temp, ":");
439                 strncpy(dev.owner, temp2, sizeof(dev.owner));
440
441                 temp2 = strsep(&temp, ":");
442                 strncpy(dev.group, temp2, sizeof(dev.owner));
443
444                 dev.mode = strtol(temp, NULL, 8);
445
446                 dbg_parse("name = %s, owner = %s, group = %s, mode = %#o",
447                                 dev.name, dev.owner, dev.group,
448                                 dev.mode);
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
461 static mode_t get_default_mode(struct sysfs_class_device *class_dev)
462 {
463         /* just default everyone to rw for the world! */
464         return 0666;
465 }
466
467
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
532 #ifndef __KLIBC__
533                 if (!WIFEXITED(status) || (WEXITSTATUS(status) != 0)) {
534                         dbg("callout program status 0x%x", status);
535                         retval = -1;
536                 }
537 #endif
538         }
539         return retval;
540 }
541
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;
557                 strfieldcpy(udev->name, dev->name);
558                 if (dev->mode != 0) {
559                         udev->mode = dev->mode;
560                         strfieldcpy(udev->owner, dev->owner);
561                         strfieldcpy(udev->group, dev->group);
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
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;
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
604                 strfieldcpy(udev->name, dev->name);
605                 if (dev->mode != 0) {
606                         udev->mode = dev->mode;
607                         strfieldcpy(udev->owner, dev->owner);
608                         strfieldcpy(udev->group, dev->group);
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;
637                 strfieldcpy(path, sysfs_device->path);
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;
652                 strfieldcpy(udev->name, dev->name);
653                 if (dev->mode != 0) {
654                         udev->mode = dev->mode;
655                         strfieldcpy(udev->owner, dev->owner);
656                         strfieldcpy(udev->group, dev->group);
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
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
684                 found = 0;
685                 strfieldcpy(path, sysfs_device->path);
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
701                 strfieldcpy(udev->name, dev->name);
702                 if (dev->mode != 0) {
703                         udev->mode = dev->mode;
704                         strfieldcpy(udev->owner, dev->owner);
705                         strfieldcpy(udev->group, dev->group);
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
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
731                 strfieldcpy(udev->name, dev->name);
732                 if (dev->mode != 0) {
733                         udev->mode = dev->mode;
734                         strfieldcpy(udev->owner, dev->owner);
735                         strfieldcpy(udev->group, dev->group);
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
746 static int get_attr(struct sysfs_class_device *class_dev, struct udevice *udev)
747 {
748         struct sysfs_device *sysfs_device = NULL;
749         struct sysfs_class_device *class_dev_parent = NULL;
750         int retval = 0;
751         char *temp = NULL;
752
753         udev->mode = 0;
754
755         /* find the sysfs_device for this class device */
756         /* Wouldn't it really be nice if libsysfs could do this for us? */
757         if (class_dev->sysdevice) {
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...");
770                                 strfieldcpy(path, class_dev->path);
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);
789         } else {
790                 dbg_parse("class_dev->name = '%s'", class_dev->name);
791         }
792
793         /* rules are looked at in priority order */
794         retval = do_callout(class_dev, udev);
795         if (retval == 0)
796                 goto done;
797
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
806         retval = do_topology(class_dev, udev, sysfs_device);
807         if (retval == 0)
808                 goto done;
809
810         retval = do_replace(class_dev, udev);
811         if (retval == 0)
812                 goto done;
813
814         strfieldcpy(udev->name, class_dev->name);
815
816 done:
817         /* substitute placeholder in NAME  */
818         while (1) {
819                 char *pos = strchr(udev->name, '%');
820                 char *dig;
821                 char name[NAME_SIZE];
822                 if (pos) {
823                         strfieldcpy(name, pos+2);
824                         *pos = 0x00;
825                         switch (pos[1]) {
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;
833                         case 'n':
834                                 dig = class_dev->name + strlen(class_dev->name);
835                                 while (isdigit(*(dig-1)))
836                                         dig--;
837                                 strcat(udev->name, dig);
838                                 dbg("kernel number appended: %s", dig);
839                                 break;
840                         case 'm':
841                                 sprintf(pos, "%u", udev->minor);
842                                 dbg("minor number appended: %u", udev->minor);
843                                 break;
844                         case 'M':
845                                 sprintf(pos, "%u", udev->major);
846                                 dbg("major number appended: %u", udev->major);
847                                 break;
848                         default:
849                                 dbg("unknown substitution type: %%%c", pos[1]);
850                                 break;
851                         }
852                         strcat(udev->name, name);
853                 } else
854                         break;
855         }
856
857         /* mode was never set above */
858         if (!udev->mode) {
859                 udev->mode = get_default_mode(class_dev);
860                 udev->owner[0] = 0x00;
861                 udev->group[0] = 0x00;
862         }
863
864         if (class_dev_parent)
865                 sysfs_close_class_device(class_dev_parent);
866
867         return 0;
868 }
869
870 int namedev_name_device(struct sysfs_class_device *class_dev, struct udevice *dev)
871 {
872         int retval;
873
874         retval = get_attr(class_dev, dev);
875         if (retval)
876                 dbg("get_attr failed");
877
878         return retval;
879 }
880
881 int namedev_init(void)
882 {
883         int retval;
884         
885         retval = namedev_init_config();
886         if (retval)
887                 return retval;
888
889         retval = namedev_init_permissions();
890         if (retval)
891                 return retval;
892
893         dump_dev_list();
894         return retval;
895 }
896
897