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