chiark / gitweb /
[PATCH] got REPLACE to work properly.
[elogind.git] / libsysfs / sysfs_dir.c
1 /*
2  * syfs_dir.c
3  *
4  * Directory utility functions for libsysfs
5  *
6  * Copyright (C) 2003 International Business Machines, Inc.
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_close_attribute: closes and cleans up attribute
28  * @sysattr: attribute to close.
29  */
30 void sysfs_close_attribute(struct sysfs_attribute *sysattr)
31 {
32         if (sysattr != NULL) {
33                 if (sysattr->value != NULL)
34                         free(sysattr->value);
35                 free(sysattr);
36         }
37 }
38
39 /**
40  * alloc_attribute: allocates and initializes attribute structure
41  * returns struct sysfs_attribute with success and NULL with error.
42  */
43 static struct sysfs_attribute *alloc_attribute(void)
44 {
45         return (struct sysfs_attribute *)
46                         calloc(1, sizeof(struct sysfs_attribute));
47 }
48
49 /**
50  * sysfs_open_attribute: creates sysfs_attribute structure
51  * @path: path to attribute.
52  * returns sysfs_attribute struct with success and NULL with error.
53  */
54 struct sysfs_attribute *sysfs_open_attribute(const char *path)
55 {
56         struct sysfs_attribute *sysattr = NULL;
57         struct stat fileinfo;
58         
59         if (path == NULL) {
60                 errno = EINVAL;
61                 return NULL;
62         }
63         sysattr = alloc_attribute();
64         if (sysattr == NULL) {
65                 dprintf(stderr, "Error allocating attribute at %s\n", path);
66                 return NULL;
67         }
68         strncpy(sysattr->path, path, sizeof(sysattr->path));
69         if ((stat(sysattr->path, &fileinfo)) != 0) {
70                 perror("stat");
71                 sysattr->method = 0;
72         } else {
73                 if (fileinfo.st_mode & S_IRUSR)
74                         sysattr->method |= SYSFS_METHOD_SHOW;
75                 if (fileinfo.st_mode & S_IWUSR)
76                         sysattr->method |= SYSFS_METHOD_STORE;
77         }
78
79         return sysattr;
80 }
81
82 /**
83  * sysfs_read_attribute: reads value from attribute
84  * @sysattr: attribute to read
85  * returns 0 with success and -1 with error.
86  */
87 int sysfs_read_attribute(struct sysfs_attribute *sysattr)
88 {
89         char *fbuf = NULL;
90         char *vbuf = NULL;
91         size_t length = 0;
92         int pgsize = 0;
93         int fd;
94
95         if (sysattr == NULL) {
96                 errno = EINVAL;
97                 return -1;
98         }
99         if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
100                 dprintf (stderr, "Show method not supported for attribute %s\n",
101                         sysattr->path);
102                 return -1;
103         }
104         pgsize = getpagesize();
105         fbuf = (char *)calloc(1, pgsize+1);
106         if (fbuf == NULL) {
107                 perror("calloc");
108                 return -1;
109         }
110         if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
111                 dprintf (stderr, "Error reading attribute %s\n", sysattr->path);
112                 free(fbuf);
113                 return -1;
114         }
115         length = read(fd, fbuf, pgsize);
116         if (length < 0) {
117                 dprintf (stderr, "Error reading from attribute %s\n",
118                         sysattr->path);
119                 close(fd);
120                 free(fbuf);
121                 return -1;
122         }
123         sysattr->len = length;
124         close(fd);
125         vbuf = (char *)realloc(fbuf, length+1);
126         if (vbuf == NULL) {
127                 perror("realloc");
128                 free(fbuf);
129                 return -1;
130         }
131         sysattr->value = vbuf;
132
133         return 0;
134 }
135
136 /**
137  * sysfs_read_attribute_value: given path to attribute, return its value.
138  *      values can be up to a pagesize, if buffer is smaller the value will 
139  *      be truncated. 
140  * @attrpath: sysfs path to attribute
141  * @value: buffer to put value
142  * @vsize: size of value buffer
143  * returns 0 with success and -1 with error.
144  */
145 int sysfs_read_attribute_value(const char *attrpath, char *value, size_t vsize)
146 {
147         struct sysfs_attribute *attr = NULL;
148         size_t length = 0;
149
150         if (attrpath == NULL || value == NULL) {
151                 errno = EINVAL;
152                 return -1;
153         }
154
155         attr = sysfs_open_attribute(attrpath);
156         if (attr == NULL) {
157                 dprintf(stderr, "Invalid attribute path %s\n", attrpath);
158                 errno = EINVAL;
159                 return -1;
160         }
161         if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) {
162                 dprintf(stderr, "Error reading from attribute %s\n", attrpath);
163                 sysfs_close_attribute(attr);
164                 return -1;
165         }
166         length = strlen(attr->value);
167         if (length > vsize) 
168                 dprintf(stderr, 
169                         "Value length %d is larger than supplied buffer %d\n",
170                         length, vsize);
171         strncpy(value, attr->value, vsize);
172         sysfs_close_attribute(attr);
173
174         return 0;
175 }
176
177 /**
178  * sysfs_get_value_from_attrbutes: given a linked list of attributes and an 
179  *      attribute name, return its value
180  * @attr: attribute to search
181  * @name: name to look for
182  * returns char * value - could be NULL
183  */
184 char *sysfs_get_value_from_attributes(struct sysfs_attribute *attr, 
185                                         const char *name)
186 {       
187         struct sysfs_attribute *cur = NULL;
188         char tmpname[SYSFS_NAME_LEN];
189         
190         if (attr == NULL || name == NULL) {
191                 errno = EINVAL;
192                 return NULL;
193         }       
194         cur = attr;
195         while (cur != NULL) {
196                 memset(tmpname, 0, SYSFS_NAME_LEN);     
197                 if ((sysfs_get_name_from_path(cur->path, tmpname,
198                     SYSFS_NAME_LEN)) != 0) {
199                         cur = cur->next;
200                         continue;
201                 }
202                 if (strcmp(tmpname, name) == 0)
203                         return cur->value;
204                 cur = cur->next;
205         }
206         return NULL;
207 }
208
209 /**
210  * add_subdir_to_dir: adds subdirectory to directory's subdirs
211  * @sysdir: directory to add subdir to
212  * @subdir: subdirectory to add.
213  */
214 static void add_subdir_to_dir(struct sysfs_directory *sysdir, 
215                      struct sysfs_directory *subdir)
216 {
217         if (sysdir != NULL && subdir != NULL) {
218                 subdir->next = sysdir->subdirs;
219                 sysdir->subdirs = subdir;
220         }
221 }
222
223 /**
224  * add_attr_to_dir: adds attribute to directory's attributes
225  * @sysdir: directory to add attribute to
226  * @sysattr: attribute to add.
227  */
228 static void add_attr_to_dir(struct sysfs_directory *sysdir, 
229                                         struct sysfs_attribute *sysattr)
230 {
231         if (sysdir != NULL && sysattr != NULL) {
232                 sysattr->next = sysdir->attributes;
233                 sysdir->attributes = sysattr;
234         }
235 }
236
237 /**
238  * sysfs_close_dlink: closes and cleans up directory link.
239  * @dlink: directory link to close.
240  */
241 void sysfs_close_dlink(struct sysfs_dlink *dlink)
242 {
243         if (dlink != NULL) {
244                 dlink->next = NULL;
245                 if (dlink->target != NULL)
246                         sysfs_close_directory(dlink->target);
247                 free(dlink);
248         }
249 }
250
251 /**
252  * add_dlink_to_dir: adds directory link to directory's links list.
253  * @sysdir: directory to add it to.
254  * @dlink: link to add.
255  */
256 static void add_dlink_to_dir(struct sysfs_directory *sysdir, 
257                                         struct sysfs_dlink *dlink)
258 {
259         if (sysdir != NULL && dlink != NULL) {
260                 dlink->next = sysdir->links;
261                 sysdir->links = dlink;
262         }
263 }
264
265 /**
266  * sysfs_close_directory: closes directory, cleans up attributes and links
267  * @sysdir: sysfs_directory to close
268  */
269 void sysfs_close_directory(struct sysfs_directory *sysdir)
270 {
271         struct sysfs_directory *sdir = NULL, *dnext = NULL;
272         struct sysfs_dlink *dlink = NULL, *nextl = NULL;
273         struct sysfs_attribute *attr = NULL, *anext = NULL;
274
275         if (sysdir != NULL) {
276                 if (sysdir->subdirs != NULL) {
277                         for (sdir = sysdir->subdirs; sdir != NULL;
278                              sdir = dnext) {
279                                 dnext = sdir->next;
280                                 sysfs_close_directory(sdir);
281                         }
282                 }
283                 if (sysdir->links != NULL) {
284                         for (dlink = sysdir->links; dlink != NULL;
285                             dlink = nextl) {
286                                 nextl = dlink->next;
287                                 sysfs_close_dlink(dlink);
288                         }
289                 }
290                 if (sysdir->attributes != NULL) {
291                         for (attr = sysdir->attributes; attr != NULL;
292                              attr = anext) {
293                                 anext = attr->next;
294                                 /* sysfs_close_attribute(attr); */
295                                 if (attr->value != NULL)
296                                         free(attr->value);
297                                 free(attr);
298                         }
299                 }
300                 free(sysdir);
301         }
302 }
303
304 /**
305  * alloc_directory: allocates and initializes directory structure
306  * returns struct sysfs_directory with success or NULL with error.
307  */
308 static struct sysfs_directory *alloc_directory(void)
309 {
310         return (struct sysfs_directory *)
311                         calloc(1, sizeof(struct sysfs_directory));
312 }
313
314 /**
315  * alloc_dlink: allocates and initializes directory link structure
316  * returns struct sysfs_dlink with success or NULL with error.
317  */
318 static struct sysfs_dlink *alloc_dlink(void)
319 {
320         return (struct sysfs_dlink *)calloc(1, sizeof(struct sysfs_dlink));
321 }
322
323 /**
324  * sysfs_open_directory: opens a sysfs directory, creates dir struct, and
325  *              returns.
326  * @path: path of directory to open.
327  * returns: struct sysfs_directory * with success and NULL on error.
328  */
329 struct sysfs_directory *sysfs_open_directory(const char *path)
330 {
331         struct sysfs_directory *sdir = NULL;
332
333         if (path == NULL) {
334                 errno = EINVAL;
335                 return NULL;
336         }
337         sdir = alloc_directory();
338         if (sdir == NULL) {
339                 dprintf(stderr, "Error allocating directory %s\n", path);
340                 return NULL;
341         }
342         strncpy(sdir->path, path, sizeof(sdir->path));
343
344         return sdir;
345 }
346
347 /**
348  * sysfs_open_dlink: opens a sysfs directory link, creates struct, and returns
349  * @path: path of link to open.
350  * returns: struct sysfs_dlink * with success and NULL on error.
351  */
352 struct sysfs_dlink *sysfs_open_dlink(const char *linkpath)
353 {
354         struct sysfs_dlink *dlink = NULL;
355         struct sysfs_directory *tdir = NULL;
356         char name[SYSFS_NAME_LEN];
357         char target[SYSFS_PATH_MAX];
358
359         if (linkpath == NULL) {
360                 errno = EINVAL;
361                 return NULL;
362         }
363
364         memset(name, 0, SYSFS_NAME_LEN);
365         memset(target, 0, SYSFS_PATH_MAX);
366         if ((sysfs_get_name_from_path(linkpath, name, SYSFS_NAME_LEN)) != 0
367             || (sysfs_get_link(linkpath, target, SYSFS_PATH_MAX)) != 0) {
368                 errno = EINVAL;
369                 dprintf(stderr, "Invalid link path %s\n", linkpath);
370                 return NULL;
371         }
372         dlink = alloc_dlink();
373         if (dlink == NULL) {
374                 dprintf(stderr, 
375                         "Error allocating directory link %s\n", linkpath);
376                 return NULL;
377         }
378         strcpy(dlink->name, name);
379         tdir = sysfs_open_directory(target);
380         if (tdir == NULL) {
381                 dprintf(stderr, "Invalid directory link target %s\n", target);
382                 sysfs_close_dlink(dlink);
383                 return NULL;
384         }       
385         dlink->target = tdir;
386
387         return dlink;
388 }
389
390 /**
391  * sysfs_read_directory: grabs attributes, links, and subdirectories
392  * @sysdir: sysfs directory to open
393  * returns 0 with success and -1 with error.
394  */
395 int sysfs_read_directory(struct sysfs_directory *sysdir)
396 {
397         DIR *dir = NULL;
398         struct dirent *dirent = NULL;
399         struct stat astats;
400         struct sysfs_attribute *attr = NULL;
401         struct sysfs_directory *subdir = NULL;
402         struct sysfs_dlink *dlink = NULL;
403         char file_path[SYSFS_PATH_MAX];
404         int retval = 0;
405
406         if (sysdir == NULL) {
407                 errno = EINVAL;
408                 return -1;
409         }
410         dir = opendir(sysdir->path);
411         if (dir == NULL) {
412                 perror("opendir");
413                 return -1;
414         }
415         while(((dirent = readdir(dir)) != NULL) && retval == 0) {
416                 if (0 == strcmp(dirent->d_name, "."))
417                          continue;
418                 if (0 == strcmp(dirent->d_name, ".."))
419                         continue;
420                 memset(file_path, 0, SYSFS_PATH_MAX);
421                 strncpy(file_path, sysdir->path, sizeof(file_path));
422                 strncat(file_path, "/", sizeof(file_path));
423                 strncat(file_path, dirent->d_name, sizeof(file_path));
424                 if ((lstat(file_path, &astats)) != 0) {
425                         perror("stat");
426                         continue;
427                 }
428                 if (S_ISREG(astats.st_mode)) {  
429                         attr = sysfs_open_attribute(file_path);
430                         if (attr == NULL) {
431                                 dprintf (stderr, "Error opening attribute %s\n",
432                                         file_path);
433                                 retval = -1;
434                                 break;
435                         }
436                         if (attr->method & SYSFS_METHOD_SHOW) {
437                                 if ((sysfs_read_attribute(attr)) != 0) {
438                                         dprintf (stderr, 
439                                                 "Error reading attribute %s\n",
440                                                 file_path);
441                                         sysfs_close_attribute(attr);
442                                         continue;
443                                 }
444                         }
445                         add_attr_to_dir(sysdir, attr);
446                 } else if (S_ISDIR(astats.st_mode)) {
447                         subdir = sysfs_open_directory(file_path);
448                         if (subdir == NULL) {
449                                 dprintf (stderr, "Error opening directory %s\n",
450                                         file_path);
451                                 retval = -1;
452                                 break;
453                         }
454                         add_subdir_to_dir(sysdir, subdir);
455                 } else if (S_ISLNK(astats.st_mode)) {
456                         dlink = sysfs_open_dlink(file_path);
457                         if (dlink == NULL) {
458                                 dprintf(stderr, "Error opening link %s\n",
459                                         file_path);
460                                 retval = -1;
461                                 break;
462                         }
463                         add_dlink_to_dir(sysdir, dlink);
464                 }
465         }
466         closedir(dir);
467         return(retval);
468 }
469
470 /**
471  * sysfs_read_dlinks: reads a directory link's target directory. Can
472  *      supply a linked list of links.
473  * @dlink: directory link to read.
474  * returns 0 with success or -1 with error.
475  */
476 int sysfs_read_dlinks(struct sysfs_dlink *dlink)
477 {
478         struct sysfs_dlink *cur = NULL;
479
480         if (dlink == NULL || dlink->target == NULL) {
481                 errno = EINVAL;
482                 return -1;
483         }
484         cur = dlink;
485         while (cur != NULL) {
486                 if ((sysfs_read_directory(cur->target)) != 0) {
487                         dprintf(stderr, 
488                                 "Error reading directory link target %s\n",
489                                 dlink->name);
490                         return -1;
491                 }
492                 cur = cur->next;
493         }
494         
495         return 0;
496 }