chiark / gitweb /
[PATCH] libsysfs: version 2.0
[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-2005
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 static int sort_char(void *new, void *old)
27 {
28         return ((strncmp((char *)new, (char *)old, 
29                         strlen((char *)new))) < 0 ? 1 : 0);
30 }
31
32 /**
33  * sysfs_del_name: free function for sysfs_open_subsystem_list
34  * @name: memory area to be freed
35  */
36 static void sysfs_del_name(void *name)
37 {
38         free(name);
39 }
40
41 /**
42  * sysfs_del_attribute: routine for dlist integration
43  */
44 static void sysfs_del_attribute(void *attr)
45 {
46         sysfs_close_attribute((struct sysfs_attribute *)attr);
47 }
48
49 /**
50  * attr_name_equal: compares attributes by name
51  * @a: attribute name for comparison
52  * @b: sysfs_attribute to be compared.
53  * returns 1 if a==b->name or 0 if not equal
54  */
55 static int attr_name_equal(void *a, void *b)
56 {
57         if (!a || !b)
58                 return 0;
59
60         if (strcmp(((char *)a), ((struct sysfs_attribute *)b)->name) == 0)
61                 return 1;
62
63         return 0;
64 }
65
66 /**
67  * sysfs_close_attribute: closes and cleans up attribute
68  * @sysattr: attribute to close.
69  */
70 void sysfs_close_attribute(struct sysfs_attribute *sysattr)
71 {
72         if (sysattr) {
73                 if (sysattr->value)
74                         free(sysattr->value);
75                 free(sysattr);
76         }
77 }
78
79 /**
80  * alloc_attribute: allocates and initializes attribute structure
81  * returns struct sysfs_attribute with success and NULL with error.
82  */
83 static struct sysfs_attribute *alloc_attribute(void)
84 {
85         return (struct sysfs_attribute *)
86                         calloc(1, sizeof(struct sysfs_attribute));
87 }
88
89 /**
90  * sysfs_open_attribute: creates sysfs_attribute structure
91  * @path: path to attribute.
92  * returns sysfs_attribute struct with success and NULL with error.
93  */
94 struct sysfs_attribute *sysfs_open_attribute(const char *path)
95 {
96         struct sysfs_attribute *sysattr = NULL;
97         struct stat fileinfo;
98
99         if (!path) {
100                 errno = EINVAL;
101                 return NULL;
102         }
103         sysattr = alloc_attribute();
104         if (!sysattr) {
105                 dprintf("Error allocating attribute at %s\n", path);
106                 return NULL;
107         }
108         if (sysfs_get_name_from_path(path, sysattr->name, 
109                                 SYSFS_NAME_LEN) != 0) {
110                 dprintf("Error retrieving attrib name from path: %s\n", path);
111                 sysfs_close_attribute(sysattr);
112                 return NULL;
113         }
114         safestrcpy(sysattr->path, path);
115         if ((stat(sysattr->path, &fileinfo)) != 0) {
116                 dprintf("Stat failed: No such attribute?\n");
117                 sysattr->method = 0;
118                 free(sysattr);
119                 sysattr = NULL;
120         } else {
121                 if (fileinfo.st_mode & S_IRUSR)
122                         sysattr->method |= SYSFS_METHOD_SHOW;
123                 if (fileinfo.st_mode & S_IWUSR)
124                         sysattr->method |= SYSFS_METHOD_STORE;
125         }
126
127         return sysattr;
128 }
129
130 /**
131  * sysfs_read_attribute: reads value from attribute
132  * @sysattr: attribute to read
133  * returns 0 with success and -1 with error.
134  */
135 int sysfs_read_attribute(struct sysfs_attribute *sysattr)
136 {
137         char *fbuf = NULL;
138         char *vbuf = NULL;
139         ssize_t length = 0;
140         long pgsize = 0;
141         int fd;
142
143         if (!sysattr) {
144                 errno = EINVAL;
145                 return -1;
146         }
147         if (!(sysattr->method & SYSFS_METHOD_SHOW)) {
148                 dprintf("Show method not supported for attribute %s\n",
149                         sysattr->path);
150                 errno = EACCES;
151                 return -1;
152         }
153         pgsize = getpagesize();
154         fbuf = (char *)calloc(1, pgsize+1);
155         if (!fbuf) {
156                 dprintf("calloc failed\n");
157                 return -1;
158         }
159         if ((fd = open(sysattr->path, O_RDONLY)) < 0) {
160                 dprintf("Error reading attribute %s\n", sysattr->path);
161                 free(fbuf);
162                 return -1;
163         }
164         length = read(fd, fbuf, pgsize);
165         if (length < 0) {
166                 dprintf("Error reading from attribute %s\n", sysattr->path);
167                 close(fd);
168                 free(fbuf);
169                 return -1;
170         }
171         if (sysattr->len > 0) {
172                 if ((sysattr->len == length) && 
173                                 (!(strncmp(sysattr->value, fbuf, length)))) {
174                         close(fd);
175                         free(fbuf);
176                         return 0;
177                 }
178                 free(sysattr->value);
179         }
180         sysattr->len = length;
181         close(fd);
182         vbuf = (char *)realloc(fbuf, length+1);
183         if (!vbuf) {
184                 dprintf("realloc failed\n");
185                 free(fbuf);
186                 return -1;
187         }
188         sysattr->value = vbuf;
189
190         return 0;
191 }
192
193 /**
194  * sysfs_write_attribute: write value to the attribute
195  * @sysattr: attribute to write
196  * @new_value: value to write
197  * @len: length of "new_value"
198  * returns 0 with success and -1 with error.
199  */
200 int sysfs_write_attribute(struct sysfs_attribute *sysattr,
201                 const char *new_value, size_t len)
202 {
203         int fd;
204         int length;
205
206         if (!sysattr || !new_value || len == 0) {
207                 errno = EINVAL;
208                 return -1;
209         }
210
211         if (!(sysattr->method & SYSFS_METHOD_STORE)) {
212                 dprintf ("Store method not supported for attribute %s\n",
213                         sysattr->path);
214                 errno = EACCES;
215                 return -1;
216         }
217         if (sysattr->method & SYSFS_METHOD_SHOW) {
218                 /*
219                  * read attribute again to see if we can get an updated value 
220                  */
221                 if ((sysfs_read_attribute(sysattr))) {
222                         dprintf("Error reading attribute\n");
223                         return -1;
224                 }
225                 if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) {
226                         dprintf("Attr %s already has the requested value %s\n",
227                                         sysattr->name, new_value);
228                         return 0;       
229                 }
230         }
231         /*
232          * open O_WRONLY since some attributes have no "read" but only
233          * "write" permission 
234          */
235         if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
236                 dprintf("Error reading attribute %s\n", sysattr->path);
237                 return -1;
238         }
239
240         length = write(fd, new_value, len);
241         if (length < 0) {
242                 dprintf("Error writing to the attribute %s - invalid value?\n",
243                         sysattr->name);
244                 close(fd);
245                 return -1;
246         } else if ((unsigned int)length != len) {
247                 dprintf("Could not write %zd bytes to attribute %s\n", 
248                                         len, sysattr->name);
249                 /* 
250                  * since we could not write user supplied number of bytes,
251                  * restore the old value if one available
252                  */
253                 if (sysattr->method & SYSFS_METHOD_SHOW) {
254                         length = write(fd, sysattr->value, sysattr->len);
255                         close(fd);
256                         return -1;
257                 }
258         }
259
260         /*
261          * Validate length that has been copied. Alloc appropriate area
262          * in sysfs_attribute. Verify first if the attribute supports reading
263          * (show method). If it does not, do not bother
264          */ 
265         if (sysattr->method & SYSFS_METHOD_SHOW) {
266                 if (length != sysattr->len) {
267                         sysattr->value = (char *)realloc
268                                 (sysattr->value, length);
269                         sysattr->len = length;
270                         safestrcpymax(sysattr->value, new_value, length);
271                 } else {
272                         /*"length" of the new value is same as old one */ 
273                         safestrcpymax(sysattr->value, new_value, length);
274                 }
275         }
276                         
277         close(fd);      
278         return 0;
279 }
280
281 /**
282  * add_attribute: open and add attribute at path to given directory
283  * @dev: device whose attribute is to be added
284  * @path: path to attribute
285  * returns pointer to attr added with success and NULL with error.
286  */
287 static struct sysfs_attribute *add_attribute(void *dev, const char *path)
288 {
289         struct sysfs_attribute *attr;
290
291         attr = sysfs_open_attribute(path);
292         if (!attr) {
293                 dprintf("Error opening attribute %s\n", path);
294                 return NULL;
295         }
296         if (attr->method & SYSFS_METHOD_SHOW) {
297                 if (sysfs_read_attribute(attr)) {
298                         dprintf("Error reading attribute %s\n", path);
299                         sysfs_close_attribute(attr);
300                         return NULL;
301                 }
302         }
303
304         if (!((struct sysfs_device *)dev)->attrlist) {
305                 ((struct sysfs_device *)dev)->attrlist = dlist_new_with_delete
306                         (sizeof(struct sysfs_attribute), sysfs_del_attribute);
307         }
308         dlist_unshift_sorted(((struct sysfs_device *)dev)->attrlist, 
309                         attr, sort_list);
310
311         return attr;
312 }
313
314 /*
315  * get_attribute - given a sysfs_* struct and a name, return the 
316  * sysfs_attribute corresponding to "name"
317  * returns sysfs_attribute on success and NULL on error
318  */
319 struct sysfs_attribute *get_attribute(void *dev, const char *name)
320 {
321         struct sysfs_attribute *cur = NULL;
322         char path[SYSFS_PATH_MAX];
323
324         if (!dev || !name) {
325                 errno = EINVAL;
326                 return NULL;
327         }
328
329         if (((struct sysfs_device *)dev)->attrlist) {
330                 /* check if attr is already in the list */
331                 cur = (struct sysfs_attribute *)dlist_find_custom
332                         ((((struct sysfs_device *)dev)->attrlist), 
333                                 (void *)name, attr_name_equal);
334                 if (cur)
335                         return cur;
336         }
337         safestrcpymax(path, ((struct sysfs_device *)dev)->path, 
338                         SYSFS_PATH_MAX);
339         safestrcatmax(path, "/", SYSFS_PATH_MAX);
340         safestrcatmax(path, name, SYSFS_PATH_MAX);
341         if (!sysfs_path_is_file(path))
342                 cur = add_attribute((void *)dev, path);
343         return cur;
344 }
345
346 /**
347  * read_dir_links: grabs links in a specific directory
348  * @sysdir: sysfs directory to read
349  * returns list of link names with success and NULL with error.
350  */
351 struct dlist *read_dir_links(const char *path)
352 {
353         DIR *dir = NULL;
354         struct dirent *dirent = NULL;
355         char file_path[SYSFS_PATH_MAX], *linkname;
356         struct dlist *linklist = NULL;
357
358         if (!path) {
359                 errno = EINVAL;
360                 return NULL;
361         }
362         dir = opendir(path);
363         if (!dir) {
364                 dprintf("Error opening directory %s\n", path);
365                 return NULL;
366         }
367         while ((dirent = readdir(dir)) != NULL) {
368                 if (0 == strcmp(dirent->d_name, "."))
369                          continue;
370                 if (0 == strcmp(dirent->d_name, ".."))
371                         continue;
372                 memset(file_path, 0, SYSFS_PATH_MAX);
373                 safestrcpy(file_path, path);
374                 safestrcat(file_path, "/");
375                 safestrcat(file_path, dirent->d_name);
376                 if (!sysfs_path_is_link(file_path)) {
377                         if (!linklist) {
378                                 linklist = dlist_new_with_delete
379                                         (SYSFS_NAME_LEN, sysfs_del_name);
380                                 if (!linklist) {
381                                         dprintf("Error creating list\n");
382                                         return NULL;
383                                 }
384                         }
385                         linkname = (char *)calloc(1, SYSFS_NAME_LEN);
386                         safestrcpymax(linkname, dirent->d_name, SYSFS_NAME_LEN);
387                         dlist_unshift_sorted(linklist, linkname, sort_char);
388                 }
389         }
390         closedir(dir);
391         return linklist;
392 }
393
394 /**
395  * read_dir_subdirs: grabs subdirs in a specific directory
396  * @sysdir: sysfs directory to read
397  * returns list of directory names with success and NULL with error.
398  */
399 struct dlist *read_dir_subdirs(const char *path)
400 {
401         DIR *dir = NULL;
402         struct dirent *dirent = NULL;
403         char file_path[SYSFS_PATH_MAX], *dir_name;
404         struct dlist *dirlist = NULL;
405
406         if (!path) {
407                 errno = EINVAL;
408                 return NULL;
409         }
410         dir = opendir(path);
411         if (!dir) {
412                 dprintf("Error opening directory %s\n", path);
413                 return NULL;
414         }
415         while ((dirent = readdir(dir)) != NULL) {
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                 safestrcpy(file_path, path);
422                 safestrcat(file_path, "/");
423                 safestrcat(file_path, dirent->d_name);
424                 if (!sysfs_path_is_dir(file_path)) {
425                         if (!dirlist) {
426                                 dirlist = dlist_new_with_delete
427                                         (SYSFS_NAME_LEN, sysfs_del_name);
428                                 if (!dirlist) {
429                                         dprintf("Error creating list\n");
430                                         return NULL;
431                                 }
432                         }
433                         dir_name = (char *)calloc(1, SYSFS_NAME_LEN);
434                         safestrcpymax(dir_name, dirent->d_name, SYSFS_NAME_LEN);
435                         dlist_unshift_sorted(dirlist, dir_name, sort_char);
436                 }
437         }
438         closedir(dir);
439         return dirlist;
440 }
441
442 /**
443  * get_attributes_list: build a list of attributes for the given device
444  * @dev: devices whose attributes list is required
445  * returns dlist of attributes on success and NULL on failure
446  */
447 struct dlist *get_attributes_list(void *dev)
448 {
449         DIR *dir = NULL;
450         struct dirent *dirent = NULL;
451         struct sysfs_attribute *attr = NULL;
452         char file_path[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX];
453
454         if (!dev) {
455                 errno = EINVAL;
456                 return NULL;
457         }
458         memset(path, 0, SYSFS_PATH_MAX);
459         safestrcpy(path, ((struct sysfs_device *)dev)->path);
460         dir = opendir(path);
461         if (!dir) {
462                 dprintf("Error opening directory %s\n", path);
463                 return NULL;
464         }
465         while ((dirent = readdir(dir)) != NULL) {
466                 if (0 == strcmp(dirent->d_name, "."))
467                          continue;
468                 if (0 == strcmp(dirent->d_name, ".."))
469                         continue;
470                 memset(file_path, 0, SYSFS_PATH_MAX);
471                 safestrcpy(file_path, path);
472                 safestrcat(file_path, "/");
473                 safestrcat(file_path, dirent->d_name);
474                 if (!sysfs_path_is_file(file_path)) {
475                         if (((struct sysfs_device *)dev)->attrlist) {
476                                 /* check if attr is already in the list */
477                                 attr = (struct sysfs_attribute *)
478                                 dlist_find_custom
479                                 ((((struct sysfs_device *)dev)->attrlist), 
480                                 (void *)dirent->d_name, attr_name_equal);
481                                 if (attr) 
482                                         continue;
483                                 else 
484                                         add_attribute(dev, file_path);
485                         } else 
486                                 attr = add_attribute(dev, file_path);
487                 }
488         }
489         closedir(dir);
490         return ((struct sysfs_device *)dev)->attrlist;
491 }