chiark / gitweb /
[PATCH] udev - reverse user query options
[elogind.git] / libsysfs / sysfs_dir.c
1 /*
2  * sysfs_dir.c
3  *
4  * Directory utility functions for libsysfs
5  *
6  * Copyright (C) IBM Corp. 2003
7  *
8  *  This library is free software; you can redistribute it and/or
9  *  modify it under the terms of the GNU Lesser General Public
10  *  License as published by the Free Software Foundation; either
11  *  version 2.1 of the License, or (at your option) any later version.
12  *
13  *  This library is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  *  Lesser General Public License for more details.
17  *
18  *  You should have received a copy of the GNU Lesser General Public
19  *  License along with this library; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  *
22  */
23 #include "libsysfs.h"
24 #include "sysfs.h"
25
26 /**
27  * sysfs_del_attribute: routine for dlist integration
28  */
29 static void sysfs_del_attribute(void *attr)
30 {
31         sysfs_close_attribute((struct sysfs_attribute *)attr);
32 }
33
34 /**
35  * sysfs_del_link: routine for dlist integration
36  */
37 static void sysfs_del_link(void *ln)
38 {
39         sysfs_close_link((struct sysfs_link *)ln);
40 }
41
42 /**
43  * sysfs_del_dir: routine for dlist integration
44  */
45 static void sysfs_del_directory(void *dir)
46 {
47         sysfs_close_directory((struct sysfs_directory *)dir);
48 }
49
50 /**
51  * dir_attribute_name_equal: compares dir attributes by name
52  * @a: attribute name for comparison
53  * @b: sysfs_attribute to be compared.
54  * returns 1 if a==b->name or 0 if not equal
55  */
56 static int dir_attribute_name_equal(void *a, void *b)
57 {
58         if (a == NULL || b == NULL)
59                 return 0;
60
61         if (strcmp(((unsigned char *)a), ((struct sysfs_attribute *)b)->name) 
62             == 0)
63                 return 1;
64         return 0;
65 }
66
67 /**
68  * dir_link_name_equal: compares dir links by name
69  * @a: link name for comparison
70  * @b: sysfs_link to be compared.
71  * returns 1 if a==b->name or 0 if not equal
72  */
73 static int dir_link_name_equal(void *a, void *b)
74 {
75         if (a == NULL || b == NULL)
76                 return 0;
77
78         if (strcmp(((unsigned char *)a), ((struct sysfs_link *)b)->name) 
79             == 0)
80                 return 1;
81         return 0;
82 }
83
84 /**
85  * dir_subdir_name_equal: compares subdirs by name
86  * @a: name of subdirectory to compare
87  * @b: sysfs_directory subdirectory to be compared
88  * returns 1 if a==b->name or 0 if not equal
89  */
90 static int dir_subdir_name_equal(void *a, void *b)
91 {
92         if (a == NULL || b == NULL)
93                 return 0;
94
95         if (strcmp(((unsigned char *)a), ((struct sysfs_directory *)b)->name)
96             == 0)
97                 return 1;
98         return 0;
99 }
100
101 /**
102  * sysfs_close_attribute: closes and cleans up attribute
103  * @sysattr: attribute to close.
104  */
105 void sysfs_close_attribute(struct sysfs_attribute *sysattr)
106 {
107         if (sysattr != NULL) {
108                 if (sysattr->value != NULL)
109                         free(sysattr->value);
110                 free(sysattr);
111         }
112 }
113
114 /**
115  * alloc_attribute: allocates and initializes attribute structure
116  * returns struct sysfs_attribute with success and NULL with error.
117  */
118 static struct sysfs_attribute *alloc_attribute(void)
119 {
120         return (struct sysfs_attribute *)
121                         calloc(1, sizeof(struct sysfs_attribute));
122 }
123
124 /**
125  * sysfs_open_attribute: creates sysfs_attribute structure
126  * @path: path to attribute.
127  * returns sysfs_attribute struct with success and NULL with error.
128  */
129 struct sysfs_attribute *sysfs_open_attribute(const unsigned char *path)
130 {
131         struct sysfs_attribute *sysattr = NULL;
132         struct stat fileinfo;
133         
134         if (path == NULL) {
135                 errno = EINVAL;
136                 return NULL;
137         }
138         sysattr = alloc_attribute();
139         if (sysattr == NULL) {
140                 dprintf("Error allocating attribute at %s\n", path);
141                 return NULL;
142         }
143         if (sysfs_get_name_from_path(path, sysattr->name, SYSFS_NAME_LEN) 
144             != 0) {
145                 dprintf("Error retrieving attribute name from path: %s\n", 
146                         path);
147                 sysfs_close_attribute(sysattr);
148                 return NULL;
149         }
150         strncpy(sysattr->path, path, SYSFS_PATH_MAX);
151         if ((stat(sysattr->path, &fileinfo)) != 0) {
152                 dprintf("Stat failed: No such attribute?\n");
153                 sysattr->method = 0;
154                 free(sysattr);
155                 sysattr = NULL;
156         } else {
157                 if (fileinfo.st_mode & S_IRUSR)
158                         sysattr->method |= SYSFS_METHOD_SHOW;
159                 if (fileinfo.st_mode & S_IWUSR)
160                         sysattr->method |= SYSFS_METHOD_STORE;
161         }
162
163         return sysattr;
164 }
165
166 /**
167  * sysfs_write_attribute: write value to the attribute
168  * @sysattr: attribute to write
169  * @new_value: value to write
170  * @len: length of "new_value"
171  * returns 0 with success and -1 with error.
172  */
173 int sysfs_write_attribute(struct sysfs_attribute *sysattr,
174                 const unsigned char *new_value, size_t len)
175 {
176         int fd;
177         int length;
178         
179         if (sysattr == NULL || new_value == NULL || len == 0) {
180                 errno = EINVAL;
181                 return -1;
182         }
183         
184         if (!(sysattr->method & SYSFS_METHOD_STORE)) {
185                 dprintf ("Store method not supported for attribute %s\n",
186                         sysattr->path);
187                 return -1;
188         }
189         if (sysattr->method & SYSFS_METHOD_SHOW) {
190                 /*
191                  * read attribute again to see if we can get an updated value 
192                  */
193                 if ((sysfs_read_attribute(sysattr)) != 0) {
194                         dprintf("Error reading attribute\n");
195                         return -1;
196                 }
197                 if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) {
198                         dprintf("Attr %s already has the requested value %s\n",
199                                         sysattr->name, new_value);
200                         return 0;       
201                 }
202         }
203         /* 
204          * open O_WRONLY since some attributes have no "read" but only
205          * "write" permission 
206          */ 
207         if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
208                 dprintf("Error reading attribute %s\n", sysattr->path);
209                 return -1;
210         }
211
212         length = write(fd, new_value, len);
213         if (length < 0) {
214                 dprintf("Error writing to the attribute %s - invalid value?\n",
215                         sysattr->name);
216                 close(fd);
217                 return -1;
218         } else if (length != len) {
219                 dprintf("Could not write %d bytes to attribute %s\n", 
220                                         len, sysattr->name);
221                 /* 
222                  * since we could not write user supplied number of bytes,
223                  * restore the old value if one available
224                  */
225                 if (sysattr->method & SYSFS_METHOD_SHOW) {
226                         length = write(fd, sysattr->value, sysattr->len);
227                         close(fd);
228                         return -1;
229                 }
230         }
231         
232         /*
233          * Validate length that has been copied. Alloc appropriate area
234          * in sysfs_attribute. Verify first if the attribute supports reading
235          * (show method). If it does not, do not bother
236          */ 
237         if (sysattr->method & SYSFS_METHOD_SHOW) {
238                 if (length != sysattr->len) {
239                         sysattr->value = (char *)realloc(sysattr->value, 
240                                                                 length);
241                         sysattr->len = length;
242                         strncpy(sysattr->value, new_value, length);
243                 } else {
244                         /*"length" of the new value is same as old one */ 
245                         strncpy(sysattr->value, new_value, length);
246                 }
247         }
248                         
249         close(fd);      
250         return 0;
251 }
252
253
254 /**
255  * sysfs_read_attribute: reads value from attribute
256  * @sysattr: attribute to read
257  * returns 0 with success and -1 with error.
258  */
259 int sysfs_read_attribute(struct sysfs_attribute *sysattr)
260 {
261         unsigned char *fbuf = NULL;
262         unsigned char *vbuf = NULL;
263         ssize_t length = 0;
264         long pgsize = 0;
265         int fd;
266
267         if (sysattr == NULL) {
268                 errno = EINVAL;
269                 return -1;
270         }
271         if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
272                 dprintf("Show method not supported for attribute %s\n",
273                         sysattr->path);
274                 return -1;
275         }
276 #ifdef __KLIBC__
277         pgsize = 0x1000;
278 #else
279         pgsize = sysconf(_SC_PAGESIZE);
280 #endif
281         fbuf = (unsigned char *)calloc(1, pgsize+1);
282         if (fbuf == NULL) {
283                 dprintf("calloc failed\n");
284                 return -1;
285         }
286         if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
287                 dprintf("Error reading attribute %s\n", sysattr->path);
288                 free(fbuf);
289                 return -1;
290         }
291         length = read(fd, fbuf, pgsize);
292         if (length < 0) {
293                 dprintf("Error reading from attribute %s\n", sysattr->path);
294                 close(fd);
295                 free(fbuf);
296                 return -1;
297         }
298         if (sysattr->len > 0) {
299                 if ((sysattr->len == length) && 
300                                 (!(strncmp(sysattr->value, fbuf, length)))) {
301                         close(fd);
302                         return 0;
303                 }
304                 free(sysattr->value);
305         }
306         sysattr->len = length;
307         close(fd);
308         vbuf = (unsigned char *)realloc(fbuf, length+1);
309         if (vbuf == NULL) {
310                 dprintf("realloc failed\n");
311                 free(fbuf);
312                 return -1;
313         }
314         sysattr->value = vbuf;
315
316         return 0;
317 }
318
319 /**
320  * sysfs_read_attribute_value: given path to attribute, return its value.
321  *      values can be up to a pagesize, if buffer is smaller the value will 
322  *      be truncated. 
323  * @attrpath: sysfs path to attribute
324  * @value: buffer to put value
325  * @vsize: size of value buffer
326  * returns 0 with success and -1 with error.
327  */
328 int sysfs_read_attribute_value(const unsigned char *attrpath, 
329                                         unsigned char *value, size_t vsize)
330 {
331         struct sysfs_attribute *attr = NULL;
332         size_t length = 0;
333
334         if (attrpath == NULL || value == NULL) {
335                 errno = EINVAL;
336                 return -1;
337         }
338
339         attr = sysfs_open_attribute(attrpath);
340         if (attr == NULL) {
341                 dprintf("Invalid attribute path %s\n", attrpath);
342                 errno = EINVAL;
343                 return -1;
344         }
345         if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) {
346                 dprintf("Error reading from attribute %s\n", attrpath);
347                 sysfs_close_attribute(attr);
348                 return -1;
349         }
350         length = strlen(attr->value);
351         if (length > vsize) 
352                 dprintf("Value length %d is larger than supplied buffer %d\n",
353                         length, vsize);
354         strncpy(value, attr->value, vsize);
355         sysfs_close_attribute(attr);
356
357         return 0;
358 }
359
360 /**
361  * sysfs_get_value_from_attrbutes: given a linked list of attributes and an 
362  *      attribute name, return its value
363  * @attr: attribute to search
364  * @name: name to look for
365  * returns unsigned char * value - could be NULL
366  */
367 unsigned char *sysfs_get_value_from_attributes(struct dlist *attr, 
368                                         const unsigned char *name)
369 {       
370         struct sysfs_attribute *cur = NULL;
371         
372         if (attr == NULL || name == NULL) {
373                 errno = EINVAL;
374                 return NULL;
375         }
376         dlist_for_each_data(attr, cur, struct sysfs_attribute) {
377                 if (strcmp(cur->name, name) == 0)
378                         return cur->value;
379         }
380         return NULL;
381 }
382
383 /**
384  * sysfs_close_link: closes and cleans up link.
385  * @ln: link to close.
386  */
387 void sysfs_close_link(struct sysfs_link *ln)
388 {
389         if (ln != NULL) 
390                 free(ln);
391 }
392
393 /**
394  * sysfs_close_directory: closes directory, cleans up attributes and links
395  * @sysdir: sysfs_directory to close
396  */
397 void sysfs_close_directory(struct sysfs_directory *sysdir)
398 {
399         if (sysdir != NULL) {
400                 if (sysdir->subdirs != NULL) 
401                         dlist_destroy(sysdir->subdirs);
402                 if (sysdir->links != NULL)
403                         dlist_destroy(sysdir->links);
404                 if (sysdir->attributes != NULL) 
405                         dlist_destroy(sysdir->attributes);
406                 free(sysdir);
407                 sysdir = NULL;
408         }
409 }
410
411 /**
412  * alloc_directory: allocates and initializes directory structure
413  * returns struct sysfs_directory with success or NULL with error.
414  */
415 static struct sysfs_directory *alloc_directory(void)
416 {
417         return (struct sysfs_directory *)
418                         calloc(1, sizeof(struct sysfs_directory));
419 }
420
421 /**
422  * alloc_link: allocates and initializes link structure
423  * returns struct sysfs_link with success or NULL with error.
424  */
425 static struct sysfs_link *alloc_link(void)
426 {
427         return (struct sysfs_link *)calloc(1, sizeof(struct sysfs_link));
428 }
429
430 /**
431  * sysfs_read_all_subdirs: calls sysfs_read_directory for all subdirs
432  * @sysdir: directory whose subdirs need reading.
433  * returns 0 with success and -1 with error.
434  */
435 int sysfs_read_all_subdirs(struct sysfs_directory *sysdir)
436 {
437         struct sysfs_directory *cursub = NULL;
438
439         if (sysdir == NULL) {
440                 errno = EINVAL;
441                 return -1;
442         }
443         if (sysdir->subdirs == NULL) 
444                 if ((sysfs_read_dir_subdirs(sysdir)) != 0) 
445                         return 0;
446         if (sysdir->subdirs != NULL) {
447                 dlist_for_each_data(sysdir->subdirs, cursub, 
448                                                 struct sysfs_directory) {
449                         if ((sysfs_read_dir_subdirs(cursub)) != 0) 
450                                 dprintf ("Error reading subdirectory %s\n",
451                                                 cursub->name);
452                 }
453         }
454         return 0;
455 }
456
457 /**
458  * sysfs_open_directory: opens a sysfs directory, creates dir struct, and
459  *              returns.
460  * @path: path of directory to open.
461  * returns: struct sysfs_directory * with success and NULL on error.
462  */
463 struct sysfs_directory *sysfs_open_directory(const unsigned char *path)
464 {
465         struct sysfs_directory *sdir = NULL;
466
467         if (path == NULL) {
468                 errno = EINVAL;
469                 return NULL;
470         }
471
472         if (sysfs_path_is_dir(path) != 0) {
473                 dprintf("Invalid path directory %s\n", path);
474                 errno = EINVAL;
475                 return NULL;
476         }
477
478         sdir = alloc_directory();
479         if (sdir == NULL) {
480                 dprintf("Error allocating directory %s\n", path);
481                 return NULL;
482         }
483         if (sysfs_get_name_from_path(path, sdir->name, SYSFS_NAME_LEN) != 0) {
484                 dprintf("Error getting directory name from path: %s\n", path);
485                 sysfs_close_directory(sdir);
486                 return NULL;
487         }
488         strncpy(sdir->path, path, SYSFS_PATH_MAX);
489
490         return sdir;
491 }
492
493 /**
494  * sysfs_open_link: opens a sysfs link, creates struct, and returns
495  * @path: path of link to open.
496  * returns: struct sysfs_link * with success and NULL on error.
497  */
498 struct sysfs_link *sysfs_open_link(const unsigned char *linkpath)
499 {
500         struct sysfs_link *ln = NULL;
501
502         if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) {
503                 errno = EINVAL;
504                 return NULL;
505         }
506
507         ln = alloc_link();
508         if (ln == NULL) {
509                 dprintf("Error allocating link %s\n", linkpath);
510                 return NULL;
511         }
512         strcpy(ln->path, linkpath);
513         if ((sysfs_get_name_from_path(linkpath, ln->name, SYSFS_NAME_LEN)) != 0
514             || (sysfs_get_link(linkpath, ln->target, SYSFS_PATH_MAX)) != 0) {
515                 errno = EINVAL;
516                 dprintf("Invalid link path %s\n", linkpath);
517                 return NULL;
518         }
519
520         return ln;
521 }
522
523 /**
524  * add_attribute: open and add attribute at path to given directory
525  * @sysdir: directory to add attribute to
526  * @path: path to attribute
527  * returns 0 with success and -1 with error.
528  */
529 static int add_attribute(struct sysfs_directory *sysdir, 
530                                         const unsigned char *path)
531 {
532         struct sysfs_attribute *attr = NULL;
533
534         attr = sysfs_open_attribute(path);
535         if (attr == NULL) {
536                 dprintf("Error opening attribute %s\n", path);
537                 return -1;
538         }
539         if (attr->method & SYSFS_METHOD_SHOW) {
540                 if ((sysfs_read_attribute(attr)) != 0) {
541                         dprintf("Error reading attribute %s\n", path);
542                         sysfs_close_attribute(attr);
543                         return 0;
544                 }
545         }
546                                                 
547         if (sysdir->attributes == NULL) {
548                 sysdir->attributes = dlist_new_with_delete
549                         (sizeof(struct sysfs_attribute), sysfs_del_attribute);
550         }
551         dlist_unshift(sysdir->attributes, attr);
552
553         return 0;
554 }
555
556 /**
557  * add_subdirectory: open and add subdirectory at path to given directory
558  * @sysdir: directory to add subdir to
559  * @path: path to subdirectory
560  * returns 0 with success and -1 with error.
561  */
562 static int add_subdirectory(struct sysfs_directory *sysdir, 
563                                         const unsigned char *path)
564 {
565         struct sysfs_directory *subdir = NULL;
566
567         subdir = sysfs_open_directory(path);
568         if (subdir == NULL) {
569                 dprintf("Error opening directory %s\n", path);
570                 return -1;
571         }
572         if (sysdir->subdirs == NULL)
573                 sysdir->subdirs = dlist_new_with_delete
574                         (sizeof(struct sysfs_directory), sysfs_del_directory);
575         dlist_unshift(sysdir->subdirs, subdir);
576         return 0;
577 }
578
579 /**
580  * add_link: open and add link at path to given directory
581  * @sysdir: directory to add link to
582  * @path: path to link
583  * returns 0 with success and -1 with error.
584  */
585 static int add_link(struct sysfs_directory *sysdir, const unsigned char *path)
586 {
587         struct sysfs_link *ln = NULL;
588
589         ln = sysfs_open_link(path);
590         if (ln == NULL) {
591                 dprintf("Error opening link %s\n", path);
592                 return -1;
593         }
594         if (sysdir->links == NULL)
595                 sysdir->links = dlist_new_with_delete
596                                 (sizeof(struct sysfs_link), sysfs_del_link);
597         dlist_unshift(sysdir->links, ln);
598         return 0;
599 }
600
601 /**
602  * sysfs_read_dir_attributes: grabs attributes for the given directory
603  * @sysdir: sysfs directory to open
604  * returns 0 with success and -1 with error.
605  */
606 int sysfs_read_dir_attributes(struct sysfs_directory *sysdir)
607 {
608         DIR *dir = NULL;
609         struct dirent *dirent = NULL;
610         unsigned char file_path[SYSFS_PATH_MAX];
611         int retval = 0;
612
613         if (sysdir == NULL) {
614                 errno = EINVAL;
615                 return -1;
616         }
617         dir = opendir(sysdir->path);
618         if (dir == NULL) {
619                 dprintf("Error opening directory %s\n", sysdir->path);
620                 return -1;
621         }
622         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
623                 if (0 == strcmp(dirent->d_name, "."))
624                          continue;
625                 if (0 == strcmp(dirent->d_name, ".."))
626                         continue;
627                 memset(file_path, 0, SYSFS_PATH_MAX);
628                 strncpy(file_path, sysdir->path, SYSFS_PATH_MAX);
629                 strcat(file_path, "/");
630                 strcat(file_path, dirent->d_name);
631                 if ((sysfs_path_is_file(file_path)) == 0)
632                         retval = add_attribute(sysdir, file_path);
633         }
634         closedir(dir);
635         return(retval);
636 }
637
638 /**
639  * sysfs_read_dir_links: grabs links in a specific directory
640  * @sysdir: sysfs directory to read links
641  * returns 0 with success and -1 with error.
642  */
643 int sysfs_read_dir_links(struct sysfs_directory *sysdir)
644 {
645         DIR *dir = NULL;
646         struct dirent *dirent = NULL;
647         unsigned char file_path[SYSFS_PATH_MAX];
648         int retval = 0;
649
650         if (sysdir == NULL) {
651                 errno = EINVAL;
652                 return -1;
653         }
654         dir = opendir(sysdir->path);
655         if (dir == NULL) {
656                 dprintf("Error opening directory %s\n", sysdir->path);
657                 return -1;
658         }
659         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
660                 if (0 == strcmp(dirent->d_name, "."))
661                          continue;
662                 if (0 == strcmp(dirent->d_name, ".."))
663                         continue;
664                 memset(file_path, 0, SYSFS_PATH_MAX);
665                 strncpy(file_path, sysdir->path, SYSFS_PATH_MAX);
666                 strcat(file_path, "/");
667                 strcat(file_path, dirent->d_name);
668                 if ((sysfs_path_is_link(file_path)) == 0) {
669                         retval = add_link(sysdir, file_path);
670                         if (retval != 0)
671                                 break;
672                 }
673         }
674         closedir(dir);
675         return(retval);
676 }
677
678 /**
679  * sysfs_read_dir_subdirs: grabs subdirs in a specific directory
680  * @sysdir: sysfs directory to read links
681  * returns 0 with success and -1 with error.
682  */
683 int sysfs_read_dir_subdirs(struct sysfs_directory *sysdir)
684 {
685         DIR *dir = NULL;
686         struct dirent *dirent = NULL;
687         unsigned char file_path[SYSFS_PATH_MAX];
688         int retval = 0;
689
690         if (sysdir == NULL) {
691                 errno = EINVAL;
692                 return -1;
693         }
694         dir = opendir(sysdir->path);
695         if (dir == NULL) {
696                 dprintf("Error opening directory %s\n", sysdir->path);
697                 return -1;
698         }
699         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
700                 if (0 == strcmp(dirent->d_name, "."))
701                          continue;
702                 if (0 == strcmp(dirent->d_name, ".."))
703                         continue;
704                 memset(file_path, 0, SYSFS_PATH_MAX);
705                 strncpy(file_path, sysdir->path, SYSFS_PATH_MAX);
706                 strcat(file_path, "/");
707                 strcat(file_path, dirent->d_name);
708                 if ((sysfs_path_is_dir(file_path)) == 0)
709                         retval = add_subdirectory(sysdir, file_path);
710         }
711         closedir(dir);
712         return(retval);
713 }
714
715 /**
716  * sysfs_read_directory: grabs attributes, links, and subdirectories
717  * @sysdir: sysfs directory to open
718  * returns 0 with success and -1 with error.
719  */
720 int sysfs_read_directory(struct sysfs_directory *sysdir)
721 {
722         DIR *dir = NULL;
723         struct dirent *dirent = NULL;
724         struct stat astats;
725         unsigned char file_path[SYSFS_PATH_MAX];
726         int retval = 0;
727
728         if (sysdir == NULL) {
729                 errno = EINVAL;
730                 return -1;
731         }
732         dir = opendir(sysdir->path);
733         if (dir == NULL) {
734                 dprintf("Error opening directory %s\n", sysdir->path);
735                 return -1;
736         }
737         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
738                 if (0 == strcmp(dirent->d_name, "."))
739                          continue;
740                 if (0 == strcmp(dirent->d_name, ".."))
741                         continue;
742                 memset(file_path, 0, SYSFS_PATH_MAX);
743                 strncpy(file_path, sysdir->path, SYSFS_PATH_MAX);
744                 strcat(file_path, "/");
745                 strcat(file_path, dirent->d_name);
746                 if ((lstat(file_path, &astats)) != 0) {
747                         dprintf("stat failed\n");
748                         continue;
749                 }
750                 if (S_ISDIR(astats.st_mode)) 
751                         retval = add_subdirectory(sysdir, file_path);
752
753                 else if (S_ISLNK(astats.st_mode))
754                         retval = add_link(sysdir, file_path);
755
756                 else if (S_ISREG(astats.st_mode))
757                         retval = add_attribute(sysdir, file_path);
758         }
759         closedir(dir);
760         return(retval);
761 }
762
763 /**
764  * sysfs_refresh_dir_attributes: Refresh attributes list
765  * @sysdir: directory whose list of attributes to refresh
766  * Returns 0 on success, 1 on failure
767  */
768 int sysfs_refresh_dir_attributes(struct sysfs_directory *sysdir)
769 {
770         if (sysdir == NULL) {
771                 errno = EINVAL;
772                 return 1;
773         }
774         if ((sysfs_path_is_dir(sysdir->path)) != 0) {
775                 dprintf("Invalid path to directory %s\n", sysdir->path);
776                 errno = EINVAL;
777                 return 1;
778         }
779         if (sysdir->attributes != NULL) {
780                 dlist_destroy(sysdir->attributes);
781                 sysdir->attributes = NULL;
782         }
783         if ((sysfs_read_dir_attributes(sysdir)) != 0) {
784                 dprintf("Error refreshing attributes for directory %s\n", 
785                                                         sysdir->path);
786                 return 1;
787         }
788         return 0;
789 }
790
791 /**
792  * sysfs_refresh_dir_links: Refresh links list
793  * @sysdir: directory whose list of links to refresh
794  * Returns 0 on success, 1 on failure
795  */
796 int sysfs_refresh_dir_links(struct sysfs_directory *sysdir)
797 {
798         if (sysdir == NULL) {
799                 errno = EINVAL;
800                 return 1;
801         }
802         if ((sysfs_path_is_dir(sysdir->path)) != 0) {
803                 dprintf("Invalid path to directory %s\n", sysdir->path);
804                 errno = EINVAL;
805                 return 1;
806         }
807         if (sysdir->links != NULL) {
808                 dlist_destroy(sysdir->links);
809                 sysdir->links = NULL;
810         }
811         if ((sysfs_read_dir_links(sysdir)) != 0) {
812                 dprintf("Error refreshing links for directory %s\n", 
813                                                         sysdir->path);
814                 return 1;
815         }
816         return 0;
817 }
818
819 /**
820  * sysfs_refresh_dir_subdirs: Refresh subdirs list
821  * @sysdir: directory whose list of subdirs to refresh
822  * Returns 0 on success, 1 on failure
823  */
824 int sysfs_refresh_dir_subdirs(struct sysfs_directory *sysdir)
825 {
826         if (sysdir == NULL) {
827                 errno = EINVAL;
828                 return 1;
829         }
830         if ((sysfs_path_is_dir(sysdir->path)) != 0) {
831                 dprintf("Invalid path to directory %s\n", sysdir->path);
832                 errno = EINVAL;
833                 return 1;
834         }
835         if (sysdir->subdirs != NULL) {
836                 dlist_destroy(sysdir->subdirs);
837                 sysdir->subdirs = NULL;
838         }
839         if ((sysfs_read_dir_subdirs(sysdir)) != 0) {
840                 dprintf("Error refreshing subdirs for directory %s\n", 
841                                                         sysdir->path);
842                 return 1;
843         }
844         return 0;
845 }
846
847 /**
848  * sysfs_get_directory_attribute: retrieves attribute attrname from current
849  *      directory only
850  * @dir: directory to retrieve attribute from
851  * @attrname: name of attribute to look for
852  * returns sysfs_attribute if found and NULL if not found
853  */
854 struct sysfs_attribute *sysfs_get_directory_attribute
855                         (struct sysfs_directory *dir, unsigned char *attrname)
856 {
857         struct sysfs_attribute *attr = NULL;
858         unsigned char new_path[SYSFS_PATH_MAX];
859         
860         if (dir == NULL || attrname == NULL) {
861                 errno = EINVAL;
862                 return NULL;
863         }
864
865         if (dir->attributes == NULL) 
866                 if ((sysfs_read_dir_attributes(dir) != 0) 
867                     || (dir->attributes == NULL))
868                         return NULL;
869
870         attr = (struct sysfs_attribute *)dlist_find_custom
871                         (dir->attributes, attrname, dir_attribute_name_equal);
872         if (attr != NULL) {
873                 if ((sysfs_read_attribute(attr)) != 0) {
874                         dprintf("Error reading attribute %s\n", attr->name);
875                         return NULL;
876                 }
877         } else {
878                 memset(new_path, 0, SYSFS_PATH_MAX);
879                 strcpy(new_path, dir->path);
880                 strcat(new_path, "/");
881                 strcat(new_path, attrname);
882                 if ((sysfs_path_is_file(new_path)) == 0) {
883                         if ((add_attribute(dir, new_path)) == 0) {
884                                 attr = (struct sysfs_attribute *)
885                                         dlist_find_custom(dir->attributes,
886                                         attrname, dir_attribute_name_equal);
887                         }
888                 }
889         }
890                 
891         return attr;
892 }
893
894 /**
895  * sysfs_get_directory_link: retrieves link from one directory list
896  * @dir: directory to retrieve link from
897  * @linkname: name of link to look for
898  * returns reference to sysfs_link if found and NULL if not found
899  */
900 struct sysfs_link *sysfs_get_directory_link
901                         (struct sysfs_directory *dir, unsigned char *linkname)
902 {
903         if (dir == NULL || linkname == NULL) {
904                 errno = EINVAL;
905                 return NULL;
906         }
907         if (dir->links == NULL) {
908                 if ((sysfs_read_dir_links(dir) != 0) || (dir->links == NULL))
909                         return NULL;
910         } else {
911                 if ((sysfs_refresh_dir_links(dir)) != 0) 
912                         return NULL;
913         }
914
915         return (struct sysfs_link *)dlist_find_custom(dir->links,
916                 linkname, dir_link_name_equal);
917 }
918
919 /**
920  * sysfs_get_subdirectory: retrieves subdirectory by name.
921  * @dir: directory to search for subdirectory.
922  * @subname: subdirectory name to get.
923  * returns reference to subdirectory or NULL if not found
924  */
925 struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir,
926                                                 unsigned char *subname)
927 {
928         struct sysfs_directory *sub = NULL, *cursub = NULL;
929
930         if (dir == NULL || subname == NULL) {
931                 errno = EINVAL;
932                 return NULL;
933         }
934
935         if (dir->subdirs == NULL)
936                 if (sysfs_read_dir_subdirs(dir) != 0)
937                         return NULL;
938
939         sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs,
940                 subname, dir_subdir_name_equal);
941         if (sub != NULL) 
942                 return sub;
943
944         if (dir->subdirs != NULL) {
945                 dlist_for_each_data(dir->subdirs, cursub, 
946                                         struct sysfs_directory) {
947                         if (cursub->subdirs == NULL) {
948                                 if (sysfs_read_dir_subdirs(cursub) != 0)
949                                         continue;
950                                 if (cursub->subdirs == NULL)
951                                         continue;
952                         }
953                         sub = sysfs_get_subdirectory(cursub, subname);
954                         if (sub != NULL)
955                                 return sub;
956                 }
957         }
958         return NULL;
959 }
960
961 /**
962  * sysfs_get_subdirectory_link: looks through all subdirs for specific link.
963  * @dir: directory and subdirectories to search for link.
964  * @linkname: link name to get.
965  * returns reference to link or NULL if not found
966  */
967 struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir,
968                                                 unsigned char *linkname)
969 {
970         struct sysfs_directory *cursub = NULL;
971         struct sysfs_link *ln = NULL;
972
973         if (dir == NULL || linkname == NULL) {
974                 errno = EINVAL;
975                 return NULL;
976         }
977
978         ln = sysfs_get_directory_link(dir, linkname);
979         if (ln != NULL)
980                 return ln;
981
982         if (dir->subdirs == NULL) 
983                 if (sysfs_read_dir_subdirs(dir) != 0)
984                         return NULL;
985
986         if (dir->subdirs != NULL) {
987                 dlist_for_each_data(dir->subdirs, cursub, 
988                                                 struct sysfs_directory) {
989                         ln = sysfs_get_subdirectory_link(cursub, linkname);
990                         if (ln != NULL)
991                                 return ln;
992                 }
993         }
994         return NULL;
995 }
996
997 /**
998  * sysfs_get_dir_attributes: returns dlist of directory attributes
999  * @dir: directory to retrieve attributes from
1000  * returns dlist of attributes or NULL
1001  */
1002 struct dlist *sysfs_get_dir_attributes(struct sysfs_directory *dir)
1003 {
1004         if (dir == NULL) {
1005                 errno = EINVAL;
1006                 return NULL;
1007         }
1008
1009         if (dir->attributes == NULL) {
1010                 if (sysfs_read_dir_attributes(dir) != 0)
1011                         return NULL;
1012         }
1013
1014         return (dir->attributes);
1015 }
1016
1017 /**
1018  * sysfs_get_dir_links: returns dlist of directory links
1019  * @dir: directory to return links for
1020  * returns dlist of links or NULL
1021  */
1022 struct dlist *sysfs_get_dir_links(struct sysfs_directory *dir)
1023 {
1024         if (dir == NULL) {
1025                 errno = EINVAL;
1026                 return NULL;
1027         }
1028
1029         if (dir->links == NULL) {
1030                 if (sysfs_read_dir_links(dir) != 0)
1031                         return NULL;
1032         }
1033
1034         return (dir->links);
1035 }
1036
1037 /**
1038  * sysfs_get_dir_subdirs: returns dlist of directory subdirectories
1039  * @dir: directory to return subdirs for
1040  * returns dlist of subdirs or NULL
1041  */
1042 struct dlist *sysfs_get_dir_subdirs(struct sysfs_directory *dir)
1043 {
1044         if (dir == NULL) {
1045                 errno = EINVAL;
1046                 return NULL;
1047         }
1048
1049         if (dir->subdirs == NULL) {
1050                 if (sysfs_read_dir_subdirs(dir) != 0)
1051                         return NULL;
1052         }
1053
1054         return (dir->subdirs);
1055 }