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
sort_char(void * new,void * old)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 */
sysfs_del_name(void * name)36 static void sysfs_del_name(void *name)
37 {
38 free(name);
39 }
40
41 /**
42 * sysfs_del_attribute: routine for dlist integration
43 */
sysfs_del_attribute(void * attr)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 */
attr_name_equal(void * a,void * b)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 */
sysfs_close_attribute(struct sysfs_attribute * sysattr)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 */
alloc_attribute(void)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 */
sysfs_open_attribute(const char * path)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 */
sysfs_read_attribute(struct sysfs_attribute * sysattr)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 */
sysfs_write_attribute(struct sysfs_attribute * sysattr,const char * new_value,size_t len)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 (len == sysattr->len)) {
227 dprintf("Attr %s already has the requested value %s\n",
228 sysattr->name, new_value);
229 return 0;
230 }
231 }
232 /*
233 * open O_WRONLY since some attributes have no "read" but only
234 * "write" permission
235 */
236 if ((fd = open(sysattr->path, O_WRONLY)) < 0) {
237 dprintf("Error reading attribute %s\n", sysattr->path);
238 return -1;
239 }
240
241 length = write(fd, new_value, len);
242 if (length < 0) {
243 dprintf("Error writing to the attribute %s - invalid value?\n",
244 sysattr->name);
245 close(fd);
246 return -1;
247 } else if ((unsigned int)length != len) {
248 dprintf("Could not write %zd bytes to attribute %s\n",
249 len, sysattr->name);
250 /*
251 * since we could not write user supplied number of bytes,
252 * restore the old value if one available
253 */
254 if (sysattr->method & SYSFS_METHOD_SHOW) {
255 length = write(fd, sysattr->value, sysattr->len);
256 close(fd);
257 return -1;
258 }
259 }
260
261 /*
262 * Validate length that has been copied. Alloc appropriate area
263 * in sysfs_attribute. Verify first if the attribute supports reading
264 * (show method). If it does not, do not bother
265 */
266 if (sysattr->method & SYSFS_METHOD_SHOW) {
267 if (length != sysattr->len) {
268 sysattr->value = (char *)realloc
269 (sysattr->value, length);
270 sysattr->len = length;
271 safestrcpymax(sysattr->value, new_value, length);
272 } else {
273 /*"length" of the new value is same as old one */
274 safestrcpymax(sysattr->value, new_value, length);
275 }
276 }
277
278 close(fd);
279 return 0;
280 }
281
282 /**
283 * add_attribute_to_list: open and add attribute at path to given dlist
284 * @list: dlist attribute is to be added
285 * @path: path to attribute
286 * returns pointer to attr added with success and NULL with error.
287 */
add_attribute_to_list(struct dlist * alist,const char * path)288 static struct sysfs_attribute *add_attribute_to_list(struct dlist *alist,
289 const char *path)
290 {
291 struct sysfs_attribute *attr;
292
293 attr = sysfs_open_attribute(path);
294 if (!attr) {
295 dprintf("Error opening attribute %s\n", path);
296 return NULL;
297 }
298 if (attr->method & SYSFS_METHOD_SHOW) {
299 if (sysfs_read_attribute(attr)) {
300 dprintf("Error reading attribute %s\n", path);
301 sysfs_close_attribute(attr);
302 return NULL;
303 }
304 }
305
306 if (!alist) {
307 alist = dlist_new_with_delete
308 (sizeof(struct sysfs_attribute), sysfs_del_attribute);
309 }
310 dlist_unshift_sorted(alist, attr, sort_list);
311 return attr;
312 }
313
314 /**
315 * add_attribute: open and add attribute at path to given directory
316 * @dev: device whose attribute is to be added
317 * @path: path to attribute
318 * returns pointer to attr added with success and NULL with error.
319 */
add_attribute(void * dev,const char * path)320 static struct sysfs_attribute *add_attribute(void *dev, const char *path)
321 {
322 struct sysfs_attribute *attr;
323
324 attr = sysfs_open_attribute(path);
325 if (!attr) {
326 dprintf("Error opening attribute %s\n", path);
327 return NULL;
328 }
329 if (attr->method & SYSFS_METHOD_SHOW) {
330 if (sysfs_read_attribute(attr)) {
331 dprintf("Error reading attribute %s\n", path);
332 sysfs_close_attribute(attr);
333 return NULL;
334 }
335 }
336
337 if (!((struct sysfs_device *)dev)->attrlist) {
338 ((struct sysfs_device *)dev)->attrlist = dlist_new_with_delete
339 (sizeof(struct sysfs_attribute), sysfs_del_attribute);
340 }
341 dlist_unshift_sorted(((struct sysfs_device *)dev)->attrlist,
342 attr, sort_list);
343
344 return attr;
345 }
346
347 /*
348 * get_attribute - given a sysfs_* struct and a name, return the
349 * sysfs_attribute corresponding to "name"
350 * returns sysfs_attribute on success and NULL on error
351 */
get_attribute(void * dev,const char * name)352 struct sysfs_attribute *get_attribute(void *dev, const char *name)
353 {
354 struct sysfs_attribute *cur = NULL;
355 char path[SYSFS_PATH_MAX];
356
357 if (!dev || !name) {
358 errno = EINVAL;
359 return NULL;
360 }
361
362 if (((struct sysfs_device *)dev)->attrlist) {
363 /* check if attr is already in the list */
364 cur = (struct sysfs_attribute *)dlist_find_custom
365 ((((struct sysfs_device *)dev)->attrlist),
366 (void *)name, attr_name_equal);
367 if (cur)
368 return cur;
369 }
370 safestrcpymax(path, ((struct sysfs_device *)dev)->path,
371 SYSFS_PATH_MAX);
372 safestrcatmax(path, "/", SYSFS_PATH_MAX);
373 safestrcatmax(path, name, SYSFS_PATH_MAX);
374 if (!sysfs_path_is_file(path))
375 cur = add_attribute((void *)dev, path);
376 return cur;
377 }
378
379 /**
380 * read_dir_links: grabs links in a specific directory
381 * @sysdir: sysfs directory to read
382 * returns list of link names with success and NULL with error.
383 */
read_dir_links(const char * path)384 struct dlist *read_dir_links(const char *path)
385 {
386 DIR *dir = NULL;
387 struct dirent *dirent = NULL;
388 char file_path[SYSFS_PATH_MAX], *linkname;
389 struct dlist *linklist = NULL;
390
391 if (!path) {
392 errno = EINVAL;
393 return NULL;
394 }
395 dir = opendir(path);
396 if (!dir) {
397 dprintf("Error opening directory %s\n", path);
398 return NULL;
399 }
400 while ((dirent = readdir(dir)) != NULL) {
401 if (0 == strcmp(dirent->d_name, "."))
402 continue;
403 if (0 == strcmp(dirent->d_name, ".."))
404 continue;
405 memset(file_path, 0, SYSFS_PATH_MAX);
406 safestrcpy(file_path, path);
407 safestrcat(file_path, "/");
408 safestrcat(file_path, dirent->d_name);
409 if (!sysfs_path_is_link(file_path)) {
410 if (!linklist) {
411 linklist = dlist_new_with_delete
412 (SYSFS_NAME_LEN, sysfs_del_name);
413 if (!linklist) {
414 dprintf("Error creating list\n");
415 return NULL;
416 }
417 }
418 linkname = (char *)calloc(1, SYSFS_NAME_LEN);
419 safestrcpymax(linkname, dirent->d_name, SYSFS_NAME_LEN);
420 dlist_unshift_sorted(linklist, linkname, sort_char);
421 }
422 }
423 closedir(dir);
424 return linklist;
425 }
426
427 void sysfs_close_dev_tree(void *dev);
428
add_subdirectory(struct sysfs_device * dev,char * path)429 int add_subdirectory(struct sysfs_device *dev, char *path)
430 {
431 struct sysfs_device *newdev;
432
433 if (!path)
434 return -1;
435
436 newdev = sysfs_open_device_path(path);
437 if (newdev == NULL)
438 return -1;
439
440 if (dev->children == NULL)
441 dev->children = dlist_new_with_delete(
442 sizeof(struct sysfs_device), sysfs_close_dev_tree);
443
444 dlist_unshift_sorted(dev->children, newdev, sort_list);
445 return 0;
446 }
447
448 /**
449 * read_dir_subdirs: grabs subdirs in a specific directory
450 * @sysdir: sysfs directory to read
451 * returns list of directory names with success and NULL with error.
452 */
sysfs_read_dir_subdirs(const char * path)453 struct sysfs_device *sysfs_read_dir_subdirs(const char *path)
454 {
455 DIR *dir = NULL;
456 struct dirent *dirent = NULL;
457 char file_path[SYSFS_PATH_MAX];
458 struct sysfs_device *dev = NULL;
459
460 if (!path) {
461 errno = EINVAL;
462 return NULL;
463 }
464
465 dev = sysfs_open_device_path(path);
466
467 dir = opendir(path);
468 if (!dir) {
469 dprintf("Error opening directory %s\n", path);
470 return NULL;
471 }
472 while ((dirent = readdir(dir)) != NULL) {
473 if (0 == strcmp(dirent->d_name, "."))
474 continue;
475 if (0 == strcmp(dirent->d_name, ".."))
476 continue;
477 memset(file_path, 0, SYSFS_PATH_MAX);
478 safestrcpy(file_path, path);
479 safestrcat(file_path, "/");
480 safestrcat(file_path, dirent->d_name);
481 if (!sysfs_path_is_dir(file_path))
482 add_subdirectory(dev, file_path);
483 }
484 closedir(dir);
485 return dev;
486 }
487
488 /**
489 * read_dir_subdirs: grabs subdirs in a specific directory
490 * @sysdir: sysfs directory to read
491 * returns list of directory names with success and NULL with error.
492 */
read_dir_subdirs(const char * path)493 struct dlist *read_dir_subdirs(const char *path)
494 {
495 DIR *dir = NULL;
496 struct dirent *dirent = NULL;
497 char file_path[SYSFS_PATH_MAX], *dir_name;
498 struct dlist *dirlist = NULL;
499
500 if (!path) {
501 errno = EINVAL;
502 return NULL;
503 }
504 dir = opendir(path);
505 if (!dir) {
506 dprintf("Error opening directory %s\n", path);
507 return NULL;
508 }
509 while ((dirent = readdir(dir)) != NULL) {
510 if (0 == strcmp(dirent->d_name, "."))
511 continue;
512 if (0 == strcmp(dirent->d_name, ".."))
513 continue;
514 memset(file_path, 0, SYSFS_PATH_MAX);
515 safestrcpy(file_path, path);
516 safestrcat(file_path, "/");
517 safestrcat(file_path, dirent->d_name);
518 if (!sysfs_path_is_dir(file_path)) {
519 if (!dirlist) {
520 dirlist = dlist_new_with_delete
521 (SYSFS_NAME_LEN, sysfs_del_name);
522 if (!dirlist) {
523 dprintf("Error creating list\n");
524 return NULL;
525 }
526 }
527 dir_name = (char *)calloc(1, SYSFS_NAME_LEN);
528 safestrcpymax(dir_name, dirent->d_name, SYSFS_NAME_LEN);
529 dlist_unshift_sorted(dirlist, dir_name, sort_char);
530 }
531 }
532 closedir(dir);
533 return dirlist;
534 }
535
536 /**
537 * get_attributes_list: build a list of attributes for the given path
538 * @path: grab attributes at the given path
539 * returns dlist of attributes on success and NULL on failure
540 */
get_attributes_list(struct dlist * alist,const char * path)541 struct dlist *get_attributes_list(struct dlist *alist, const char *path)
542 {
543 DIR *dir = NULL;
544 struct dirent *dirent = NULL;
545 char file_path[SYSFS_PATH_MAX];
546
547 if (!path) {
548 errno = EINVAL;
549 return NULL;
550 }
551
552 dir = opendir(path);
553 if (!dir) {
554 dprintf("Error opening directory %s\n", path);
555 return NULL;
556 }
557 while ((dirent = readdir(dir)) != NULL) {
558 if (0 == strcmp(dirent->d_name, "."))
559 continue;
560 if (0 == strcmp(dirent->d_name, ".."))
561 continue;
562 memset(file_path, 0, SYSFS_PATH_MAX);
563 safestrcpy(file_path, path);
564 safestrcat(file_path, "/");
565 safestrcat(file_path, dirent->d_name);
566 if (!sysfs_path_is_file(file_path)) {
567 if (!alist) {
568 alist = dlist_new_with_delete
569 (sizeof(struct sysfs_attribute),
570 sysfs_del_attribute);
571 if (!alist) {
572 dprintf("Error creating list\n");
573 return NULL;
574 }
575 }
576 add_attribute_to_list(alist, file_path);
577 }
578 }
579 closedir(dir);
580 return alist;
581 }
582
583 /**
584 * get_dev_attributes_list: build a list of attributes for the given device
585 * @dev: devices whose attributes list is required
586 * returns dlist of attributes on success and NULL on failure
587 */
get_dev_attributes_list(void * dev)588 struct dlist *get_dev_attributes_list(void *dev)
589 {
590 DIR *dir = NULL;
591 struct dirent *dirent = NULL;
592 struct sysfs_attribute *attr = NULL;
593 char file_path[SYSFS_PATH_MAX], path[SYSFS_PATH_MAX];
594
595 if (!dev) {
596 errno = EINVAL;
597 return NULL;
598 }
599 memset(path, 0, SYSFS_PATH_MAX);
600 safestrcpy(path, ((struct sysfs_device *)dev)->path);
601 dir = opendir(path);
602 if (!dir) {
603 dprintf("Error opening directory %s\n", path);
604 return NULL;
605 }
606 while ((dirent = readdir(dir)) != NULL) {
607 if (0 == strcmp(dirent->d_name, "."))
608 continue;
609 if (0 == strcmp(dirent->d_name, ".."))
610 continue;
611 memset(file_path, 0, SYSFS_PATH_MAX);
612 safestrcpy(file_path, path);
613 safestrcat(file_path, "/");
614 safestrcat(file_path, dirent->d_name);
615 if (!sysfs_path_is_file(file_path)) {
616 if (((struct sysfs_device *)dev)->attrlist) {
617 /* check if attr is already in the list */
618 attr = (struct sysfs_attribute *)
619 dlist_find_custom
620 ((((struct sysfs_device *)dev)->attrlist),
621 (void *)dirent->d_name, attr_name_equal);
622 if (attr)
623 continue;
624 else
625 add_attribute(dev, file_path);
626 } else
627 attr = add_attribute(dev, file_path);
628 }
629 }
630 closedir(dir);
631 return ((struct sysfs_device *)dev)->attrlist;
632 }
633