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