chiark / gitweb /
[PATCH] new version of libsysfs patch
[elogind.git] / libsysfs / sysfs_dir.c
1 /*
2  * syfs_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, sizeof(sysattr->path));
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                 if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) {
191                         dprintf("Attribute %s already has the requested value %s\n",
192                                         sysattr->name, new_value);
193                         return 0;       
194                 }
195         }
196         /* 
197          * open O_WRONLY since some attributes have no "read" but only
198          * "write" permission 
199          */ 
200         if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
201                 dprintf("Error reading attribute %s\n", sysattr->path);
202                 return -1;
203         }
204
205         length = write(fd, new_value, len);
206         if (length < 0) {
207                 dprintf("Error writing to the attribute %s - invalid value?\n",
208                         sysattr->name);
209                 close(fd);
210                 return -1;
211         } else if (length != len) {
212                 dprintf("Could not write %d bytes to attribute %s\n", 
213                                         len, sysattr->name);
214                 /* 
215                  * since we could not write user supplied number of bytes,
216                  * restore the old value if one available
217                  */
218                 if (sysattr->method & SYSFS_METHOD_SHOW) {
219                         length = write(fd, sysattr->value, sysattr->len);
220                         close(fd);
221                         return -1;
222                 }
223         }
224         
225         /*
226          * Validate length that has been copied. Alloc appropriate area
227          * in sysfs_attribute. Verify first if the attribute supports reading
228          * (show method). If it does not, do not bother
229          */ 
230         if (sysattr->method & SYSFS_METHOD_SHOW) {
231                 if (length != sysattr->len) {
232                         sysattr->value = (char *)realloc(sysattr->value, 
233                                                                 length);
234                         sysattr->len = length;
235                         strncpy(sysattr->value, new_value, length);
236                 } else {
237                         /*"length" of the new value is same as old one */ 
238                         strncpy(sysattr->value, new_value, length);
239                 }
240         }
241                         
242         close(fd);      
243         return 0;
244 }
245
246
247 /**
248  * sysfs_read_attribute: reads value from attribute
249  * @sysattr: attribute to read
250  * returns 0 with success and -1 with error.
251  */
252 int sysfs_read_attribute(struct sysfs_attribute *sysattr)
253 {
254         unsigned char *fbuf = NULL;
255         unsigned char *vbuf = NULL;
256         size_t length = 0;
257         int pgsize = 0;
258         int fd;
259
260         if (sysattr == NULL) {
261                 errno = EINVAL;
262                 return -1;
263         }
264         if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
265                 dprintf("Show method not supported for attribute %s\n",
266                         sysattr->path);
267                 return -1;
268         }
269         pgsize = getpagesize();
270         fbuf = (unsigned char *)calloc(1, pgsize+1);
271         if (fbuf == NULL) {
272                 dprintf("calloc failed\n");
273                 return -1;
274         }
275         if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
276                 dprintf("Error reading attribute %s\n", sysattr->path);
277                 free(fbuf);
278                 return -1;
279         }
280         length = read(fd, fbuf, pgsize);
281         if (length < 0) {
282                 dprintf("Error reading from attribute %s\n", sysattr->path);
283                 close(fd);
284                 free(fbuf);
285                 return -1;
286         }
287         sysattr->len = length;
288         close(fd);
289         vbuf = (unsigned char *)realloc(fbuf, length+1);
290         if (vbuf == NULL) {
291                 dprintf("realloc failed\n");
292                 free(fbuf);
293                 return -1;
294         }
295         sysattr->value = vbuf;
296
297         return 0;
298 }
299
300 /**
301  * sysfs_read_attribute_value: given path to attribute, return its value.
302  *      values can be up to a pagesize, if buffer is smaller the value will 
303  *      be truncated. 
304  * @attrpath: sysfs path to attribute
305  * @value: buffer to put value
306  * @vsize: size of value buffer
307  * returns 0 with success and -1 with error.
308  */
309 int sysfs_read_attribute_value(const unsigned char *attrpath, 
310                                         unsigned char *value, size_t vsize)
311 {
312         struct sysfs_attribute *attr = NULL;
313         size_t length = 0;
314
315         if (attrpath == NULL || value == NULL) {
316                 errno = EINVAL;
317                 return -1;
318         }
319
320         attr = sysfs_open_attribute(attrpath);
321         if (attr == NULL) {
322                 dprintf("Invalid attribute path %s\n", attrpath);
323                 errno = EINVAL;
324                 return -1;
325         }
326         if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) {
327                 dprintf("Error reading from attribute %s\n", attrpath);
328                 sysfs_close_attribute(attr);
329                 return -1;
330         }
331         length = strlen(attr->value);
332         if (length > vsize) 
333                 dprintf("Value length %d is larger than supplied buffer %d\n",
334                         length, vsize);
335         strncpy(value, attr->value, vsize);
336         sysfs_close_attribute(attr);
337
338         return 0;
339 }
340
341 /**
342  * sysfs_get_value_from_attrbutes: given a linked list of attributes and an 
343  *      attribute name, return its value
344  * @attr: attribute to search
345  * @name: name to look for
346  * returns unsigned char * value - could be NULL
347  */
348 unsigned char *sysfs_get_value_from_attributes(struct dlist *attr, 
349                                         const unsigned char *name)
350 {       
351         struct sysfs_attribute *cur = NULL;
352         
353         if (attr == NULL || name == NULL) {
354                 errno = EINVAL;
355                 return NULL;
356         }
357         dlist_for_each_data(attr, cur, struct sysfs_attribute) {
358                 if (strcmp(cur->name, name) == 0)
359                         return cur->value;
360         }
361         return NULL;
362 }
363
364 /**
365  * sysfs_close_link: closes and cleans up link.
366  * @ln: link to close.
367  */
368 void sysfs_close_link(struct sysfs_link *ln)
369 {
370         if (ln != NULL) 
371                 free(ln);
372 }
373
374 /**
375  * sysfs_close_directory: closes directory, cleans up attributes and links
376  * @sysdir: sysfs_directory to close
377  */
378 void sysfs_close_directory(struct sysfs_directory *sysdir)
379 {
380         if (sysdir != NULL) {
381                 if (sysdir->subdirs != NULL) 
382                         dlist_destroy(sysdir->subdirs);
383                 if (sysdir->links != NULL)
384                         dlist_destroy(sysdir->links);
385                 if (sysdir->attributes != NULL) 
386                         dlist_destroy(sysdir->attributes);
387                 free(sysdir);
388         }
389 }
390
391 /**
392  * alloc_directory: allocates and initializes directory structure
393  * returns struct sysfs_directory with success or NULL with error.
394  */
395 static struct sysfs_directory *alloc_directory(void)
396 {
397         return (struct sysfs_directory *)
398                         calloc(1, sizeof(struct sysfs_directory));
399 }
400
401 /**
402  * alloc_link: allocates and initializes link structure
403  * returns struct sysfs_link with success or NULL with error.
404  */
405 static struct sysfs_link *alloc_link(void)
406 {
407         return (struct sysfs_link *)calloc(1, sizeof(struct sysfs_link));
408 }
409
410 /**
411  * sysfs_read_all_subdirs: calls sysfs_read_directory for all subdirs
412  * @sysdir: directory whose subdirs need reading.
413  * returns 0 with success and -1 with error.
414  */
415 int sysfs_read_all_subdirs(struct sysfs_directory *sysdir)
416 {
417         struct sysfs_directory *cursub = NULL;
418
419         if (sysdir == NULL) {
420                 errno = EINVAL;
421                 return -1;
422         }
423         if (sysdir->subdirs == NULL)
424                 return 0;
425         dlist_for_each_data(sysdir->subdirs, cursub, struct sysfs_directory) {
426                 if (sysfs_read_directory(cursub) != 0) 
427                         dprintf ("Error reading subdirectory %s\n",
428                                 cursub->name);
429         }
430         return 0;
431 }
432
433 /**
434  * sysfs_open_directory: opens a sysfs directory, creates dir struct, and
435  *              returns.
436  * @path: path of directory to open.
437  * returns: struct sysfs_directory * with success and NULL on error.
438  */
439 struct sysfs_directory *sysfs_open_directory(const unsigned char *path)
440 {
441         struct sysfs_directory *sdir = NULL;
442
443         if (path == NULL) {
444                 errno = EINVAL;
445                 return NULL;
446         }
447         sdir = alloc_directory();
448         if (sdir == NULL) {
449                 dprintf("Error allocating directory %s\n", path);
450                 return NULL;
451         }
452         if (sysfs_get_name_from_path(path, sdir->name, SYSFS_NAME_LEN) != 0) {
453                 dprintf("Error getting directory name from path: %s\n", path);
454                 sysfs_close_directory(sdir);
455                 return NULL;
456         }
457         strncpy(sdir->path, path, sizeof(sdir->path));
458
459         return sdir;
460 }
461
462 /**
463  * sysfs_open_link: opens a sysfs link, creates struct, and returns
464  * @path: path of link to open.
465  * returns: struct sysfs_link * with success and NULL on error.
466  */
467 struct sysfs_link *sysfs_open_link(const unsigned char *linkpath)
468 {
469         struct sysfs_link *ln = NULL;
470
471         if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) {
472                 errno = EINVAL;
473                 return NULL;
474         }
475
476         ln = alloc_link();
477         if (ln == NULL) {
478                 dprintf("Error allocating link %s\n", linkpath);
479                 return NULL;
480         }
481         strcpy(ln->path, linkpath);
482         if ((sysfs_get_name_from_path(linkpath, ln->name, SYSFS_NAME_LEN)) != 0
483             || (sysfs_get_link(linkpath, ln->target, SYSFS_PATH_MAX)) != 0) {
484                 errno = EINVAL;
485                 dprintf("Invalid link path %s\n", linkpath);
486                 return NULL;
487         }
488
489         return ln;
490 }
491
492 /**
493  * sysfs_read_directory: grabs attributes, links, and subdirectories
494  * @sysdir: sysfs directory to open
495  * returns 0 with success and -1 with error.
496  */
497 int sysfs_read_directory(struct sysfs_directory *sysdir)
498 {
499         DIR *dir = NULL;
500         struct dirent *dirent = NULL;
501         struct stat astats;
502         struct sysfs_attribute *attr = NULL;
503         struct sysfs_directory *subdir = NULL;
504         struct sysfs_link *ln = NULL;
505         unsigned char file_path[SYSFS_PATH_MAX];
506         int retval = 0;
507
508         if (sysdir == NULL) {
509                 errno = EINVAL;
510                 return -1;
511         }
512         dir = opendir(sysdir->path);
513         if (dir == NULL) {
514                 dprintf("Error opening directory %s\n", sysdir->path);
515                 return -1;
516         }
517         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
518                 if (0 == strcmp(dirent->d_name, "."))
519                          continue;
520                 if (0 == strcmp(dirent->d_name, ".."))
521                         continue;
522                 memset(file_path, 0, SYSFS_PATH_MAX);
523                 strncpy(file_path, sysdir->path, sizeof(file_path));
524                 strncat(file_path, "/", sizeof(file_path));
525                 strncat(file_path, dirent->d_name, sizeof(file_path));
526                 if ((lstat(file_path, &astats)) != 0) {
527                         dprintf("stat failed\n");
528                         continue;
529                 }
530                 if (S_ISREG(astats.st_mode)) {  
531                         attr = sysfs_open_attribute(file_path);
532                         if (attr == NULL) {
533                                 dprintf("Error opening attribute %s\n",
534                                         file_path);
535                                 retval = -1;
536                                 break;
537                         }
538                         if (attr->method & SYSFS_METHOD_SHOW) {
539                                 if ((sysfs_read_attribute(attr)) != 0) {
540                                         dprintf("Error reading attribute %s\n",
541                                                 file_path);
542                                         sysfs_close_attribute(attr);
543                                         continue;
544                                 }
545                         }
546                                                 
547                         if (sysdir->attributes == NULL) {
548                                 sysdir->attributes = dlist_new_with_delete
549                                         (sizeof(struct sysfs_attribute),
550                                                         sysfs_del_attribute);
551                         }
552                         dlist_unshift(sysdir->attributes, attr);
553                 } else if (S_ISDIR(astats.st_mode)) {
554                         subdir = sysfs_open_directory(file_path);
555                         if (subdir == NULL) {
556                                 dprintf("Error opening directory %s\n",
557                                         file_path);
558                                 retval = -1;
559                                 break;
560                         }
561                         if (sysdir->subdirs == NULL)
562                                 sysdir->subdirs = dlist_new_with_delete
563                                         (sizeof(struct sysfs_directory),
564                                                         sysfs_del_directory);
565                         dlist_unshift(sysdir->subdirs, subdir);
566                 } else if (S_ISLNK(astats.st_mode)) {
567                         ln = sysfs_open_link(file_path);
568                         if (ln == NULL) {
569                                 dprintf("Error opening link %s\n", file_path);
570                                 retval = -1;
571                                 break;
572                         }
573                         if (sysdir->links == NULL)
574                                 sysdir->links = dlist_new_with_delete
575                                                 (sizeof(struct sysfs_link),
576                                                                 sysfs_del_link);
577                         dlist_unshift(sysdir->links, ln);
578                 }
579         }
580         closedir(dir);
581         return(retval);
582 }
583
584 /**
585  * sysfs_get_directory_attribute: retrieves attribute attrname
586  * @dir: directory to retrieve attribute from
587  * @attrname: name of attribute to look for
588  * returns sysfs_attribute if found and NULL if not found
589  */
590 struct sysfs_attribute *sysfs_get_directory_attribute
591                         (struct sysfs_directory *dir, unsigned char *attrname)
592 {
593         struct sysfs_directory *sdir = NULL;
594         struct sysfs_attribute *attr = NULL;
595         
596         if (dir == NULL || attrname == NULL) {
597                 errno = EINVAL;
598                 return NULL;
599         }
600         
601         attr = (struct sysfs_attribute *)dlist_find_custom(dir->attributes,
602                 attrname, dir_attribute_name_equal);
603         if (attr != NULL)
604                 return attr;
605         
606         if (dir->subdirs != NULL) {
607                 dlist_for_each_data(dir->subdirs, sdir, 
608                                         struct sysfs_directory) {
609                         if (sdir->attributes == NULL)
610                                 continue;
611                         attr = sysfs_get_directory_attribute(sdir, attrname);
612                         if (attr != NULL)
613                                 return attr;
614                 }
615         }
616         return NULL;
617 }
618
619 /**
620  * sysfs_get_directory_link: retrieves link from one directory list
621  * @dir: directory to retrieve link from
622  * @linkname: name of link to look for
623  * returns reference to sysfs_link if found and NULL if not found
624  */
625 struct sysfs_link *sysfs_get_directory_link
626                         (struct sysfs_directory *dir, unsigned char *linkname)
627 {
628         if (dir == NULL || linkname == NULL) {
629                 errno = EINVAL;
630                 return NULL;
631         }
632         return (struct sysfs_link *)dlist_find_custom(dir->links,
633                 linkname, dir_link_name_equal);
634 }
635
636 /**
637  * sysfs_get_subdirectory: retrieves subdirectory by name.
638  * @dir: directory to search for subdirectory.
639  * @subname: subdirectory name to get.
640  * returns reference to subdirectory or NULL if not found
641  */
642 struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir,
643                                                 unsigned char *subname)
644 {
645         struct sysfs_directory *sub = NULL, *cursub = NULL;
646
647         if (dir == NULL || dir->subdirs == NULL || subname == NULL) {
648                 errno = EINVAL;
649                 return NULL;
650         }
651         sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs,
652                 subname, dir_subdir_name_equal);
653         if (sub != NULL) 
654                 return sub;
655
656         if (dir->subdirs != NULL) {
657                 dlist_for_each_data(dir->subdirs, cursub, 
658                                         struct sysfs_directory) {
659                         if (cursub->subdirs == NULL)
660                                 continue;
661                         sub = sysfs_get_subdirectory(cursub, subname);
662                         if (sub != NULL)
663                                 return sub;
664                 }
665         }
666         return NULL;
667 }
668
669 /**
670  * sysfs_get_subdirectory_link: looks through all subdirs for specific link.
671  * @dir: directory and subdirectories to search for link.
672  * @linkname: link name to get.
673  * returns reference to link or NULL if not found
674  */
675 struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir,
676                                                 unsigned char *linkname)
677 {
678         struct sysfs_directory *cursub = NULL;
679         struct sysfs_link *ln = NULL;
680
681         if (dir == NULL || dir->links == NULL || linkname == NULL) {
682                 errno = EINVAL;
683                 return NULL;
684         }
685
686         ln = sysfs_get_directory_link(dir, linkname);
687         if (ln != NULL)
688                 return ln;
689
690         if (dir->subdirs == NULL)
691                 return NULL;
692
693         if (dir->subdirs != NULL) {
694                 dlist_for_each_data(dir->subdirs, cursub, 
695                                                 struct sysfs_directory) {
696                         if (cursub->subdirs == NULL)
697                                 continue;
698                         ln = sysfs_get_subdirectory_link(cursub, linkname);
699                         if (ln != NULL)
700                                 return ln;
701                 }
702         }
703         return NULL;
704 }