]>
Commit | Line | Data |
---|---|---|
070f5422 | 1 | /* |
bcbe2d8e | 2 | * sysfs_dir.c |
070f5422 GKH |
3 | * |
4 | * Directory utility functions for libsysfs | |
5 | * | |
fe3fe3b2 | 6 | * Copyright (C) IBM Corp. 2003 |
070f5422 GKH |
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 | ||
fe3fe3b2 DS |
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 | ||
070f5422 GKH |
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 | */ | |
fe3fe3b2 | 129 | struct sysfs_attribute *sysfs_open_attribute(const unsigned char *path) |
070f5422 GKH |
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) { | |
fe3fe3b2 DS |
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); | |
070f5422 GKH |
148 | return NULL; |
149 | } | |
bcbe2d8e | 150 | strncpy(sysattr->path, path, SYSFS_PATH_MAX); |
070f5422 | 151 | if ((stat(sysattr->path, &fileinfo)) != 0) { |
fe3fe3b2 | 152 | dprintf("Stat failed: No such attribute?\n"); |
070f5422 | 153 | sysattr->method = 0; |
fe3fe3b2 DS |
154 | free(sysattr); |
155 | sysattr = NULL; | |
070f5422 GKH |
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 | ||
fe3fe3b2 DS |
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) { | |
5d4754f1 DS |
190 | /* |
191 | * read attribute again to see if we can get an updated value | |
192 | */ | |
193 | if ((sysfs_read_attribute(sysattr)) != 0) { | |
194 | dprintf("Error reading attribute\n"); | |
195 | return -1; | |
196 | } | |
fe3fe3b2 | 197 | if ((strncmp(sysattr->value, new_value, sysattr->len)) == 0) { |
616a7078 | 198 | dprintf("Attr %s already has the requested value %s\n", |
fe3fe3b2 DS |
199 | sysattr->name, new_value); |
200 | return 0; | |
201 | } | |
202 | } | |
203 | /* | |
204 | * open O_WRONLY since some attributes have no "read" but only | |
205 | * "write" permission | |
206 | */ | |
207 | if ((fd = open(sysattr->path, O_WRONLY)) < 0) { | |
208 | dprintf("Error reading attribute %s\n", sysattr->path); | |
209 | return -1; | |
210 | } | |
211 | ||
212 | length = write(fd, new_value, len); | |
213 | if (length < 0) { | |
214 | dprintf("Error writing to the attribute %s - invalid value?\n", | |
215 | sysattr->name); | |
216 | close(fd); | |
217 | return -1; | |
218 | } else if (length != len) { | |
219 | dprintf("Could not write %d bytes to attribute %s\n", | |
220 | len, sysattr->name); | |
221 | /* | |
222 | * since we could not write user supplied number of bytes, | |
223 | * restore the old value if one available | |
224 | */ | |
225 | if (sysattr->method & SYSFS_METHOD_SHOW) { | |
226 | length = write(fd, sysattr->value, sysattr->len); | |
227 | close(fd); | |
228 | return -1; | |
229 | } | |
230 | } | |
231 | ||
232 | /* | |
233 | * Validate length that has been copied. Alloc appropriate area | |
234 | * in sysfs_attribute. Verify first if the attribute supports reading | |
235 | * (show method). If it does not, do not bother | |
236 | */ | |
237 | if (sysattr->method & SYSFS_METHOD_SHOW) { | |
238 | if (length != sysattr->len) { | |
239 | sysattr->value = (char *)realloc(sysattr->value, | |
240 | length); | |
241 | sysattr->len = length; | |
242 | strncpy(sysattr->value, new_value, length); | |
243 | } else { | |
244 | /*"length" of the new value is same as old one */ | |
245 | strncpy(sysattr->value, new_value, length); | |
246 | } | |
247 | } | |
248 | ||
249 | close(fd); | |
250 | return 0; | |
251 | } | |
252 | ||
253 | ||
070f5422 GKH |
254 | /** |
255 | * sysfs_read_attribute: reads value from attribute | |
256 | * @sysattr: attribute to read | |
257 | * returns 0 with success and -1 with error. | |
258 | */ | |
259 | int sysfs_read_attribute(struct sysfs_attribute *sysattr) | |
260 | { | |
fe3fe3b2 DS |
261 | unsigned char *fbuf = NULL; |
262 | unsigned char *vbuf = NULL; | |
bcbe2d8e | 263 | ssize_t length = 0; |
5d4754f1 | 264 | long pgsize = 0; |
070f5422 GKH |
265 | int fd; |
266 | ||
267 | if (sysattr == NULL) { | |
268 | errno = EINVAL; | |
269 | return -1; | |
270 | } | |
271 | if (!(sysattr->method & SYSFS_METHOD_SHOW)) { | |
fe3fe3b2 | 272 | dprintf("Show method not supported for attribute %s\n", |
070f5422 GKH |
273 | sysattr->path); |
274 | return -1; | |
275 | } | |
1e959a4b | 276 | #ifdef __KLIBC__ |
616a7078 | 277 | pgsize = 0x1000; |
1e959a4b | 278 | #else |
5d4754f1 | 279 | pgsize = sysconf(_SC_PAGESIZE); |
1e959a4b | 280 | #endif |
fe3fe3b2 | 281 | fbuf = (unsigned char *)calloc(1, pgsize+1); |
070f5422 | 282 | if (fbuf == NULL) { |
fe3fe3b2 | 283 | dprintf("calloc failed\n"); |
070f5422 GKH |
284 | return -1; |
285 | } | |
286 | if ((fd = open(sysattr->path, O_RDONLY)) < 0) { | |
fe3fe3b2 | 287 | dprintf("Error reading attribute %s\n", sysattr->path); |
070f5422 GKH |
288 | free(fbuf); |
289 | return -1; | |
290 | } | |
291 | length = read(fd, fbuf, pgsize); | |
292 | if (length < 0) { | |
fe3fe3b2 | 293 | dprintf("Error reading from attribute %s\n", sysattr->path); |
070f5422 GKH |
294 | close(fd); |
295 | free(fbuf); | |
296 | return -1; | |
297 | } | |
5d4754f1 DS |
298 | if (sysattr->len > 0) { |
299 | if ((sysattr->len == length) && | |
300 | (!(strncmp(sysattr->value, fbuf, length)))) { | |
301 | close(fd); | |
302 | return 0; | |
303 | } | |
304 | free(sysattr->value); | |
305 | } | |
070f5422 GKH |
306 | sysattr->len = length; |
307 | close(fd); | |
fe3fe3b2 | 308 | vbuf = (unsigned char *)realloc(fbuf, length+1); |
070f5422 | 309 | if (vbuf == NULL) { |
fe3fe3b2 | 310 | dprintf("realloc failed\n"); |
070f5422 GKH |
311 | free(fbuf); |
312 | return -1; | |
313 | } | |
314 | sysattr->value = vbuf; | |
315 | ||
316 | return 0; | |
317 | } | |
318 | ||
319 | /** | |
320 | * sysfs_read_attribute_value: given path to attribute, return its value. | |
321 | * values can be up to a pagesize, if buffer is smaller the value will | |
322 | * be truncated. | |
323 | * @attrpath: sysfs path to attribute | |
324 | * @value: buffer to put value | |
325 | * @vsize: size of value buffer | |
326 | * returns 0 with success and -1 with error. | |
327 | */ | |
fe3fe3b2 DS |
328 | int sysfs_read_attribute_value(const unsigned char *attrpath, |
329 | unsigned char *value, size_t vsize) | |
070f5422 GKH |
330 | { |
331 | struct sysfs_attribute *attr = NULL; | |
332 | size_t length = 0; | |
333 | ||
334 | if (attrpath == NULL || value == NULL) { | |
335 | errno = EINVAL; | |
336 | return -1; | |
337 | } | |
338 | ||
339 | attr = sysfs_open_attribute(attrpath); | |
340 | if (attr == NULL) { | |
fe3fe3b2 | 341 | dprintf("Invalid attribute path %s\n", attrpath); |
070f5422 GKH |
342 | errno = EINVAL; |
343 | return -1; | |
344 | } | |
345 | if((sysfs_read_attribute(attr)) != 0 || attr->value == NULL) { | |
fe3fe3b2 | 346 | dprintf("Error reading from attribute %s\n", attrpath); |
070f5422 GKH |
347 | sysfs_close_attribute(attr); |
348 | return -1; | |
349 | } | |
350 | length = strlen(attr->value); | |
351 | if (length > vsize) | |
fe3fe3b2 | 352 | dprintf("Value length %d is larger than supplied buffer %d\n", |
070f5422 GKH |
353 | length, vsize); |
354 | strncpy(value, attr->value, vsize); | |
355 | sysfs_close_attribute(attr); | |
356 | ||
357 | return 0; | |
358 | } | |
359 | ||
360 | /** | |
361 | * sysfs_get_value_from_attrbutes: given a linked list of attributes and an | |
362 | * attribute name, return its value | |
363 | * @attr: attribute to search | |
364 | * @name: name to look for | |
fe3fe3b2 | 365 | * returns unsigned char * value - could be NULL |
070f5422 | 366 | */ |
fe3fe3b2 DS |
367 | unsigned char *sysfs_get_value_from_attributes(struct dlist *attr, |
368 | const unsigned char *name) | |
070f5422 GKH |
369 | { |
370 | struct sysfs_attribute *cur = NULL; | |
070f5422 GKH |
371 | |
372 | if (attr == NULL || name == NULL) { | |
373 | errno = EINVAL; | |
374 | return NULL; | |
fe3fe3b2 DS |
375 | } |
376 | dlist_for_each_data(attr, cur, struct sysfs_attribute) { | |
377 | if (strcmp(cur->name, name) == 0) | |
070f5422 | 378 | return cur->value; |
070f5422 GKH |
379 | } |
380 | return NULL; | |
381 | } | |
382 | ||
383 | /** | |
fe3fe3b2 DS |
384 | * sysfs_close_link: closes and cleans up link. |
385 | * @ln: link to close. | |
070f5422 | 386 | */ |
fe3fe3b2 | 387 | void sysfs_close_link(struct sysfs_link *ln) |
070f5422 | 388 | { |
fe3fe3b2 DS |
389 | if (ln != NULL) |
390 | free(ln); | |
070f5422 GKH |
391 | } |
392 | ||
393 | /** | |
394 | * sysfs_close_directory: closes directory, cleans up attributes and links | |
395 | * @sysdir: sysfs_directory to close | |
396 | */ | |
397 | void sysfs_close_directory(struct sysfs_directory *sysdir) | |
398 | { | |
070f5422 | 399 | if (sysdir != NULL) { |
fe3fe3b2 DS |
400 | if (sysdir->subdirs != NULL) |
401 | dlist_destroy(sysdir->subdirs); | |
402 | if (sysdir->links != NULL) | |
403 | dlist_destroy(sysdir->links); | |
404 | if (sysdir->attributes != NULL) | |
405 | dlist_destroy(sysdir->attributes); | |
070f5422 | 406 | free(sysdir); |
5d4754f1 | 407 | sysdir = NULL; |
070f5422 GKH |
408 | } |
409 | } | |
410 | ||
411 | /** | |
412 | * alloc_directory: allocates and initializes directory structure | |
413 | * returns struct sysfs_directory with success or NULL with error. | |
414 | */ | |
415 | static struct sysfs_directory *alloc_directory(void) | |
416 | { | |
417 | return (struct sysfs_directory *) | |
418 | calloc(1, sizeof(struct sysfs_directory)); | |
419 | } | |
420 | ||
421 | /** | |
fe3fe3b2 DS |
422 | * alloc_link: allocates and initializes link structure |
423 | * returns struct sysfs_link with success or NULL with error. | |
424 | */ | |
425 | static struct sysfs_link *alloc_link(void) | |
426 | { | |
427 | return (struct sysfs_link *)calloc(1, sizeof(struct sysfs_link)); | |
428 | } | |
429 | ||
430 | /** | |
431 | * sysfs_read_all_subdirs: calls sysfs_read_directory for all subdirs | |
432 | * @sysdir: directory whose subdirs need reading. | |
433 | * returns 0 with success and -1 with error. | |
070f5422 | 434 | */ |
fe3fe3b2 | 435 | int sysfs_read_all_subdirs(struct sysfs_directory *sysdir) |
070f5422 | 436 | { |
fe3fe3b2 DS |
437 | struct sysfs_directory *cursub = NULL; |
438 | ||
439 | if (sysdir == NULL) { | |
440 | errno = EINVAL; | |
441 | return -1; | |
442 | } | |
5d4754f1 | 443 | if (sysdir->subdirs == NULL) |
bcbe2d8e | 444 | if ((sysfs_read_dir_subdirs(sysdir)) != 0) |
5d4754f1 | 445 | return 0; |
bcbe2d8e DS |
446 | if (sysdir->subdirs != NULL) { |
447 | dlist_for_each_data(sysdir->subdirs, cursub, | |
448 | struct sysfs_directory) { | |
449 | if ((sysfs_read_dir_subdirs(cursub)) != 0) | |
450 | dprintf ("Error reading subdirectory %s\n", | |
451 | cursub->name); | |
452 | } | |
fe3fe3b2 DS |
453 | } |
454 | return 0; | |
070f5422 GKH |
455 | } |
456 | ||
457 | /** | |
458 | * sysfs_open_directory: opens a sysfs directory, creates dir struct, and | |
459 | * returns. | |
460 | * @path: path of directory to open. | |
461 | * returns: struct sysfs_directory * with success and NULL on error. | |
462 | */ | |
fe3fe3b2 | 463 | struct sysfs_directory *sysfs_open_directory(const unsigned char *path) |
070f5422 GKH |
464 | { |
465 | struct sysfs_directory *sdir = NULL; | |
466 | ||
467 | if (path == NULL) { | |
468 | errno = EINVAL; | |
469 | return NULL; | |
470 | } | |
616a7078 AM |
471 | |
472 | if (sysfs_path_is_dir(path) != 0) { | |
473 | dprintf("Invalid path directory %s\n", path); | |
474 | errno = EINVAL; | |
475 | return NULL; | |
476 | } | |
477 | ||
070f5422 GKH |
478 | sdir = alloc_directory(); |
479 | if (sdir == NULL) { | |
fe3fe3b2 DS |
480 | dprintf("Error allocating directory %s\n", path); |
481 | return NULL; | |
482 | } | |
483 | if (sysfs_get_name_from_path(path, sdir->name, SYSFS_NAME_LEN) != 0) { | |
484 | dprintf("Error getting directory name from path: %s\n", path); | |
485 | sysfs_close_directory(sdir); | |
070f5422 GKH |
486 | return NULL; |
487 | } | |
bcbe2d8e | 488 | strncpy(sdir->path, path, SYSFS_PATH_MAX); |
070f5422 GKH |
489 | |
490 | return sdir; | |
491 | } | |
492 | ||
493 | /** | |
fe3fe3b2 | 494 | * sysfs_open_link: opens a sysfs link, creates struct, and returns |
070f5422 | 495 | * @path: path of link to open. |
fe3fe3b2 | 496 | * returns: struct sysfs_link * with success and NULL on error. |
070f5422 | 497 | */ |
fe3fe3b2 | 498 | struct sysfs_link *sysfs_open_link(const unsigned char *linkpath) |
070f5422 | 499 | { |
fe3fe3b2 | 500 | struct sysfs_link *ln = NULL; |
070f5422 | 501 | |
fe3fe3b2 | 502 | if (linkpath == NULL || strlen(linkpath) > SYSFS_PATH_MAX) { |
070f5422 GKH |
503 | errno = EINVAL; |
504 | return NULL; | |
505 | } | |
506 | ||
fe3fe3b2 DS |
507 | ln = alloc_link(); |
508 | if (ln == NULL) { | |
509 | dprintf("Error allocating link %s\n", linkpath); | |
070f5422 GKH |
510 | return NULL; |
511 | } | |
fe3fe3b2 DS |
512 | strcpy(ln->path, linkpath); |
513 | if ((sysfs_get_name_from_path(linkpath, ln->name, SYSFS_NAME_LEN)) != 0 | |
514 | || (sysfs_get_link(linkpath, ln->target, SYSFS_PATH_MAX)) != 0) { | |
515 | errno = EINVAL; | |
516 | dprintf("Invalid link path %s\n", linkpath); | |
070f5422 GKH |
517 | return NULL; |
518 | } | |
070f5422 | 519 | |
fe3fe3b2 | 520 | return ln; |
070f5422 GKH |
521 | } |
522 | ||
5d4754f1 DS |
523 | /** |
524 | * add_attribute: open and add attribute at path to given directory | |
525 | * @sysdir: directory to add attribute to | |
526 | * @path: path to attribute | |
527 | * returns 0 with success and -1 with error. | |
528 | */ | |
529 | static int add_attribute(struct sysfs_directory *sysdir, | |
530 | const unsigned char *path) | |
531 | { | |
532 | struct sysfs_attribute *attr = NULL; | |
533 | ||
534 | attr = sysfs_open_attribute(path); | |
535 | if (attr == NULL) { | |
536 | dprintf("Error opening attribute %s\n", path); | |
537 | return -1; | |
538 | } | |
539 | if (attr->method & SYSFS_METHOD_SHOW) { | |
540 | if ((sysfs_read_attribute(attr)) != 0) { | |
541 | dprintf("Error reading attribute %s\n", path); | |
542 | sysfs_close_attribute(attr); | |
543 | return 0; | |
544 | } | |
545 | } | |
546 | ||
547 | if (sysdir->attributes == NULL) { | |
548 | sysdir->attributes = dlist_new_with_delete | |
549 | (sizeof(struct sysfs_attribute), sysfs_del_attribute); | |
550 | } | |
551 | dlist_unshift(sysdir->attributes, attr); | |
552 | ||
553 | return 0; | |
554 | } | |
555 | ||
556 | /** | |
557 | * add_subdirectory: open and add subdirectory at path to given directory | |
558 | * @sysdir: directory to add subdir to | |
559 | * @path: path to subdirectory | |
560 | * returns 0 with success and -1 with error. | |
561 | */ | |
562 | static int add_subdirectory(struct sysfs_directory *sysdir, | |
563 | const unsigned char *path) | |
564 | { | |
565 | struct sysfs_directory *subdir = NULL; | |
566 | ||
567 | subdir = sysfs_open_directory(path); | |
568 | if (subdir == NULL) { | |
569 | dprintf("Error opening directory %s\n", path); | |
570 | return -1; | |
571 | } | |
572 | if (sysdir->subdirs == NULL) | |
573 | sysdir->subdirs = dlist_new_with_delete | |
574 | (sizeof(struct sysfs_directory), sysfs_del_directory); | |
575 | dlist_unshift(sysdir->subdirs, subdir); | |
576 | return 0; | |
577 | } | |
578 | ||
579 | /** | |
580 | * add_link: open and add link at path to given directory | |
581 | * @sysdir: directory to add link to | |
582 | * @path: path to link | |
583 | * returns 0 with success and -1 with error. | |
584 | */ | |
585 | static int add_link(struct sysfs_directory *sysdir, const unsigned char *path) | |
586 | { | |
587 | struct sysfs_link *ln = NULL; | |
588 | ||
589 | ln = sysfs_open_link(path); | |
590 | if (ln == NULL) { | |
591 | dprintf("Error opening link %s\n", path); | |
592 | return -1; | |
593 | } | |
594 | if (sysdir->links == NULL) | |
595 | sysdir->links = dlist_new_with_delete | |
596 | (sizeof(struct sysfs_link), sysfs_del_link); | |
597 | dlist_unshift(sysdir->links, ln); | |
598 | return 0; | |
599 | } | |
600 | ||
601 | /** | |
602 | * sysfs_read_dir_attributes: grabs attributes for the given directory | |
070f5422 GKH |
603 | * @sysdir: sysfs directory to open |
604 | * returns 0 with success and -1 with error. | |
605 | */ | |
5d4754f1 | 606 | int sysfs_read_dir_attributes(struct sysfs_directory *sysdir) |
070f5422 GKH |
607 | { |
608 | DIR *dir = NULL; | |
609 | struct dirent *dirent = NULL; | |
fe3fe3b2 | 610 | unsigned char file_path[SYSFS_PATH_MAX]; |
070f5422 GKH |
611 | int retval = 0; |
612 | ||
613 | if (sysdir == NULL) { | |
614 | errno = EINVAL; | |
615 | return -1; | |
616 | } | |
617 | dir = opendir(sysdir->path); | |
618 | if (dir == NULL) { | |
fe3fe3b2 | 619 | dprintf("Error opening directory %s\n", sysdir->path); |
070f5422 GKH |
620 | return -1; |
621 | } | |
622 | while(((dirent = readdir(dir)) != NULL) && retval == 0) { | |
623 | if (0 == strcmp(dirent->d_name, ".")) | |
624 | continue; | |
625 | if (0 == strcmp(dirent->d_name, "..")) | |
626 | continue; | |
627 | memset(file_path, 0, SYSFS_PATH_MAX); | |
bcbe2d8e DS |
628 | strncpy(file_path, sysdir->path, SYSFS_PATH_MAX); |
629 | strcat(file_path, "/"); | |
630 | strcat(file_path, dirent->d_name); | |
616a7078 | 631 | if ((sysfs_path_is_file(file_path)) == 0) |
5d4754f1 DS |
632 | retval = add_attribute(sysdir, file_path); |
633 | } | |
634 | closedir(dir); | |
635 | return(retval); | |
636 | } | |
637 | ||
638 | /** | |
639 | * sysfs_read_dir_links: grabs links in a specific directory | |
640 | * @sysdir: sysfs directory to read links | |
641 | * returns 0 with success and -1 with error. | |
642 | */ | |
643 | int sysfs_read_dir_links(struct sysfs_directory *sysdir) | |
644 | { | |
645 | DIR *dir = NULL; | |
646 | struct dirent *dirent = NULL; | |
5d4754f1 DS |
647 | unsigned char file_path[SYSFS_PATH_MAX]; |
648 | int retval = 0; | |
649 | ||
650 | if (sysdir == NULL) { | |
651 | errno = EINVAL; | |
652 | return -1; | |
653 | } | |
654 | dir = opendir(sysdir->path); | |
655 | if (dir == NULL) { | |
656 | dprintf("Error opening directory %s\n", sysdir->path); | |
657 | return -1; | |
658 | } | |
659 | while(((dirent = readdir(dir)) != NULL) && retval == 0) { | |
660 | if (0 == strcmp(dirent->d_name, ".")) | |
661 | continue; | |
662 | if (0 == strcmp(dirent->d_name, "..")) | |
663 | continue; | |
664 | memset(file_path, 0, SYSFS_PATH_MAX); | |
bcbe2d8e DS |
665 | strncpy(file_path, sysdir->path, SYSFS_PATH_MAX); |
666 | strcat(file_path, "/"); | |
667 | strcat(file_path, dirent->d_name); | |
616a7078 | 668 | if ((sysfs_path_is_link(file_path)) == 0) { |
5d4754f1 DS |
669 | retval = add_link(sysdir, file_path); |
670 | if (retval != 0) | |
070f5422 | 671 | break; |
070f5422 GKH |
672 | } |
673 | } | |
674 | closedir(dir); | |
675 | return(retval); | |
676 | } | |
677 | ||
678 | /** | |
5d4754f1 DS |
679 | * sysfs_read_dir_subdirs: grabs subdirs in a specific directory |
680 | * @sysdir: sysfs directory to read links | |
681 | * returns 0 with success and -1 with error. | |
682 | */ | |
683 | int sysfs_read_dir_subdirs(struct sysfs_directory *sysdir) | |
684 | { | |
685 | DIR *dir = NULL; | |
686 | struct dirent *dirent = NULL; | |
5d4754f1 DS |
687 | unsigned char file_path[SYSFS_PATH_MAX]; |
688 | int retval = 0; | |
689 | ||
690 | if (sysdir == NULL) { | |
691 | errno = EINVAL; | |
692 | return -1; | |
693 | } | |
694 | dir = opendir(sysdir->path); | |
695 | if (dir == NULL) { | |
696 | dprintf("Error opening directory %s\n", sysdir->path); | |
697 | return -1; | |
698 | } | |
699 | while(((dirent = readdir(dir)) != NULL) && retval == 0) { | |
700 | if (0 == strcmp(dirent->d_name, ".")) | |
701 | continue; | |
702 | if (0 == strcmp(dirent->d_name, "..")) | |
703 | continue; | |
704 | memset(file_path, 0, SYSFS_PATH_MAX); | |
bcbe2d8e DS |
705 | strncpy(file_path, sysdir->path, SYSFS_PATH_MAX); |
706 | strcat(file_path, "/"); | |
707 | strcat(file_path, dirent->d_name); | |
616a7078 | 708 | if ((sysfs_path_is_dir(file_path)) == 0) |
5d4754f1 DS |
709 | retval = add_subdirectory(sysdir, file_path); |
710 | } | |
711 | closedir(dir); | |
712 | return(retval); | |
713 | } | |
714 | ||
715 | /** | |
716 | * sysfs_read_directory: grabs attributes, links, and subdirectories | |
717 | * @sysdir: sysfs directory to open | |
718 | * returns 0 with success and -1 with error. | |
719 | */ | |
720 | int sysfs_read_directory(struct sysfs_directory *sysdir) | |
721 | { | |
722 | DIR *dir = NULL; | |
723 | struct dirent *dirent = NULL; | |
724 | struct stat astats; | |
725 | unsigned char file_path[SYSFS_PATH_MAX]; | |
726 | int retval = 0; | |
727 | ||
728 | if (sysdir == NULL) { | |
729 | errno = EINVAL; | |
730 | return -1; | |
731 | } | |
732 | dir = opendir(sysdir->path); | |
733 | if (dir == NULL) { | |
734 | dprintf("Error opening directory %s\n", sysdir->path); | |
735 | return -1; | |
736 | } | |
737 | while(((dirent = readdir(dir)) != NULL) && retval == 0) { | |
738 | if (0 == strcmp(dirent->d_name, ".")) | |
739 | continue; | |
740 | if (0 == strcmp(dirent->d_name, "..")) | |
741 | continue; | |
742 | memset(file_path, 0, SYSFS_PATH_MAX); | |
bcbe2d8e DS |
743 | strncpy(file_path, sysdir->path, SYSFS_PATH_MAX); |
744 | strcat(file_path, "/"); | |
745 | strcat(file_path, dirent->d_name); | |
5d4754f1 DS |
746 | if ((lstat(file_path, &astats)) != 0) { |
747 | dprintf("stat failed\n"); | |
748 | continue; | |
749 | } | |
750 | if (S_ISDIR(astats.st_mode)) | |
751 | retval = add_subdirectory(sysdir, file_path); | |
752 | ||
753 | else if (S_ISLNK(astats.st_mode)) | |
754 | retval = add_link(sysdir, file_path); | |
755 | ||
756 | else if (S_ISREG(astats.st_mode)) | |
757 | retval = add_attribute(sysdir, file_path); | |
758 | } | |
759 | closedir(dir); | |
760 | return(retval); | |
761 | } | |
762 | ||
616a7078 AM |
763 | /** |
764 | * sysfs_refresh_dir_attributes: Refresh attributes list | |
765 | * @sysdir: directory whose list of attributes to refresh | |
766 | * Returns 0 on success, 1 on failure | |
767 | */ | |
768 | int sysfs_refresh_dir_attributes(struct sysfs_directory *sysdir) | |
769 | { | |
770 | if (sysdir == NULL) { | |
771 | errno = EINVAL; | |
772 | return 1; | |
773 | } | |
774 | if ((sysfs_path_is_dir(sysdir->path)) != 0) { | |
775 | dprintf("Invalid path to directory %s\n", sysdir->path); | |
776 | errno = EINVAL; | |
777 | return 1; | |
778 | } | |
779 | if (sysdir->attributes != NULL) { | |
780 | dlist_destroy(sysdir->attributes); | |
781 | sysdir->attributes = NULL; | |
782 | } | |
783 | if ((sysfs_read_dir_attributes(sysdir)) != 0) { | |
784 | dprintf("Error refreshing attributes for directory %s\n", | |
785 | sysdir->path); | |
786 | return 1; | |
787 | } | |
788 | return 0; | |
789 | } | |
790 | ||
791 | /** | |
792 | * sysfs_refresh_dir_links: Refresh links list | |
793 | * @sysdir: directory whose list of links to refresh | |
794 | * Returns 0 on success, 1 on failure | |
795 | */ | |
796 | int sysfs_refresh_dir_links(struct sysfs_directory *sysdir) | |
797 | { | |
798 | if (sysdir == NULL) { | |
799 | errno = EINVAL; | |
800 | return 1; | |
801 | } | |
802 | if ((sysfs_path_is_dir(sysdir->path)) != 0) { | |
803 | dprintf("Invalid path to directory %s\n", sysdir->path); | |
804 | errno = EINVAL; | |
805 | return 1; | |
806 | } | |
807 | if (sysdir->links != NULL) { | |
808 | dlist_destroy(sysdir->links); | |
809 | sysdir->links = NULL; | |
810 | } | |
811 | if ((sysfs_read_dir_links(sysdir)) != 0) { | |
812 | dprintf("Error refreshing links for directory %s\n", | |
813 | sysdir->path); | |
814 | return 1; | |
815 | } | |
816 | return 0; | |
817 | } | |
818 | ||
819 | /** | |
820 | * sysfs_refresh_dir_subdirs: Refresh subdirs list | |
821 | * @sysdir: directory whose list of subdirs to refresh | |
822 | * Returns 0 on success, 1 on failure | |
823 | */ | |
824 | int sysfs_refresh_dir_subdirs(struct sysfs_directory *sysdir) | |
825 | { | |
826 | if (sysdir == NULL) { | |
827 | errno = EINVAL; | |
828 | return 1; | |
829 | } | |
830 | if ((sysfs_path_is_dir(sysdir->path)) != 0) { | |
831 | dprintf("Invalid path to directory %s\n", sysdir->path); | |
832 | errno = EINVAL; | |
833 | return 1; | |
834 | } | |
835 | if (sysdir->subdirs != NULL) { | |
836 | dlist_destroy(sysdir->subdirs); | |
837 | sysdir->subdirs = NULL; | |
838 | } | |
839 | if ((sysfs_read_dir_subdirs(sysdir)) != 0) { | |
840 | dprintf("Error refreshing subdirs for directory %s\n", | |
841 | sysdir->path); | |
842 | return 1; | |
843 | } | |
844 | return 0; | |
845 | } | |
846 | ||
5d4754f1 DS |
847 | /** |
848 | * sysfs_get_directory_attribute: retrieves attribute attrname from current | |
849 | * directory only | |
fe3fe3b2 DS |
850 | * @dir: directory to retrieve attribute from |
851 | * @attrname: name of attribute to look for | |
852 | * returns sysfs_attribute if found and NULL if not found | |
070f5422 | 853 | */ |
fe3fe3b2 DS |
854 | struct sysfs_attribute *sysfs_get_directory_attribute |
855 | (struct sysfs_directory *dir, unsigned char *attrname) | |
070f5422 | 856 | { |
fe3fe3b2 | 857 | struct sysfs_attribute *attr = NULL; |
5d4754f1 | 858 | unsigned char new_path[SYSFS_PATH_MAX]; |
fe3fe3b2 DS |
859 | |
860 | if (dir == NULL || attrname == NULL) { | |
861 | errno = EINVAL; | |
862 | return NULL; | |
863 | } | |
5d4754f1 DS |
864 | |
865 | if (dir->attributes == NULL) | |
866 | if ((sysfs_read_dir_attributes(dir) != 0) | |
867 | || (dir->attributes == NULL)) | |
868 | return NULL; | |
869 | ||
870 | attr = (struct sysfs_attribute *)dlist_find_custom | |
871 | (dir->attributes, attrname, dir_attribute_name_equal); | |
616a7078 AM |
872 | if (attr != NULL) { |
873 | if ((sysfs_read_attribute(attr)) != 0) { | |
874 | dprintf("Error reading attribute %s\n", attr->name); | |
875 | return NULL; | |
876 | } | |
877 | } else { | |
5d4754f1 DS |
878 | memset(new_path, 0, SYSFS_PATH_MAX); |
879 | strcpy(new_path, dir->path); | |
880 | strcat(new_path, "/"); | |
881 | strcat(new_path, attrname); | |
882 | if ((sysfs_path_is_file(new_path)) == 0) { | |
616a7078 | 883 | if ((add_attribute(dir, new_path)) == 0) { |
bcbe2d8e | 884 | attr = (struct sysfs_attribute *) |
616a7078 | 885 | dlist_find_custom(dir->attributes, |
bcbe2d8e | 886 | attrname, dir_attribute_name_equal); |
5d4754f1 | 887 | } |
fe3fe3b2 DS |
888 | } |
889 | } | |
616a7078 | 890 | |
bcbe2d8e | 891 | return attr; |
fe3fe3b2 | 892 | } |
070f5422 | 893 | |
fe3fe3b2 DS |
894 | /** |
895 | * sysfs_get_directory_link: retrieves link from one directory list | |
896 | * @dir: directory to retrieve link from | |
897 | * @linkname: name of link to look for | |
898 | * returns reference to sysfs_link if found and NULL if not found | |
899 | */ | |
900 | struct sysfs_link *sysfs_get_directory_link | |
901 | (struct sysfs_directory *dir, unsigned char *linkname) | |
902 | { | |
903 | if (dir == NULL || linkname == NULL) { | |
070f5422 | 904 | errno = EINVAL; |
fe3fe3b2 | 905 | return NULL; |
070f5422 | 906 | } |
616a7078 | 907 | if (dir->links == NULL) { |
5d4754f1 DS |
908 | if ((sysfs_read_dir_links(dir) != 0) || (dir->links == NULL)) |
909 | return NULL; | |
616a7078 AM |
910 | } else { |
911 | if ((sysfs_refresh_dir_links(dir)) != 0) | |
912 | return NULL; | |
913 | } | |
5d4754f1 | 914 | |
fe3fe3b2 DS |
915 | return (struct sysfs_link *)dlist_find_custom(dir->links, |
916 | linkname, dir_link_name_equal); | |
917 | } | |
918 | ||
919 | /** | |
920 | * sysfs_get_subdirectory: retrieves subdirectory by name. | |
921 | * @dir: directory to search for subdirectory. | |
922 | * @subname: subdirectory name to get. | |
923 | * returns reference to subdirectory or NULL if not found | |
924 | */ | |
925 | struct sysfs_directory *sysfs_get_subdirectory(struct sysfs_directory *dir, | |
926 | unsigned char *subname) | |
927 | { | |
928 | struct sysfs_directory *sub = NULL, *cursub = NULL; | |
929 | ||
5d4754f1 | 930 | if (dir == NULL || subname == NULL) { |
fe3fe3b2 DS |
931 | errno = EINVAL; |
932 | return NULL; | |
933 | } | |
5d4754f1 DS |
934 | |
935 | if (dir->subdirs == NULL) | |
936 | if (sysfs_read_dir_subdirs(dir) != 0) | |
937 | return NULL; | |
938 | ||
fe3fe3b2 DS |
939 | sub = (struct sysfs_directory *)dlist_find_custom(dir->subdirs, |
940 | subname, dir_subdir_name_equal); | |
941 | if (sub != NULL) | |
942 | return sub; | |
943 | ||
944 | if (dir->subdirs != NULL) { | |
945 | dlist_for_each_data(dir->subdirs, cursub, | |
946 | struct sysfs_directory) { | |
5d4754f1 DS |
947 | if (cursub->subdirs == NULL) { |
948 | if (sysfs_read_dir_subdirs(cursub) != 0) | |
949 | continue; | |
950 | if (cursub->subdirs == NULL) | |
951 | continue; | |
952 | } | |
fe3fe3b2 DS |
953 | sub = sysfs_get_subdirectory(cursub, subname); |
954 | if (sub != NULL) | |
955 | return sub; | |
070f5422 | 956 | } |
070f5422 | 957 | } |
fe3fe3b2 DS |
958 | return NULL; |
959 | } | |
960 | ||
961 | /** | |
962 | * sysfs_get_subdirectory_link: looks through all subdirs for specific link. | |
963 | * @dir: directory and subdirectories to search for link. | |
964 | * @linkname: link name to get. | |
965 | * returns reference to link or NULL if not found | |
966 | */ | |
967 | struct sysfs_link *sysfs_get_subdirectory_link(struct sysfs_directory *dir, | |
968 | unsigned char *linkname) | |
969 | { | |
970 | struct sysfs_directory *cursub = NULL; | |
971 | struct sysfs_link *ln = NULL; | |
972 | ||
5d4754f1 | 973 | if (dir == NULL || linkname == NULL) { |
fe3fe3b2 DS |
974 | errno = EINVAL; |
975 | return NULL; | |
976 | } | |
977 | ||
978 | ln = sysfs_get_directory_link(dir, linkname); | |
979 | if (ln != NULL) | |
980 | return ln; | |
981 | ||
5d4754f1 DS |
982 | if (dir->subdirs == NULL) |
983 | if (sysfs_read_dir_subdirs(dir) != 0) | |
984 | return NULL; | |
fe3fe3b2 DS |
985 | |
986 | if (dir->subdirs != NULL) { | |
987 | dlist_for_each_data(dir->subdirs, cursub, | |
988 | struct sysfs_directory) { | |
fe3fe3b2 DS |
989 | ln = sysfs_get_subdirectory_link(cursub, linkname); |
990 | if (ln != NULL) | |
991 | return ln; | |
992 | } | |
993 | } | |
994 | return NULL; | |
070f5422 | 995 | } |
616a7078 AM |
996 | |
997 | /** | |
998 | * sysfs_get_dir_attributes: returns dlist of directory attributes | |
999 | * @dir: directory to retrieve attributes from | |
1000 | * returns dlist of attributes or NULL | |
1001 | */ | |
1002 | struct dlist *sysfs_get_dir_attributes(struct sysfs_directory *dir) | |
1003 | { | |
1004 | if (dir == NULL) { | |
1005 | errno = EINVAL; | |
1006 | return NULL; | |
1007 | } | |
1008 | ||
1009 | if (dir->attributes == NULL) { | |
1010 | if (sysfs_read_dir_attributes(dir) != 0) | |
1011 | return NULL; | |
1012 | } | |
1013 | ||
1014 | return (dir->attributes); | |
1015 | } | |
1016 | ||
1017 | /** | |
1018 | * sysfs_get_dir_links: returns dlist of directory links | |
1019 | * @dir: directory to return links for | |
1020 | * returns dlist of links or NULL | |
1021 | */ | |
1022 | struct dlist *sysfs_get_dir_links(struct sysfs_directory *dir) | |
1023 | { | |
1024 | if (dir == NULL) { | |
1025 | errno = EINVAL; | |
1026 | return NULL; | |
1027 | } | |
1028 | ||
1029 | if (dir->links == NULL) { | |
1030 | if (sysfs_read_dir_links(dir) != 0) | |
1031 | return NULL; | |
1032 | } | |
1033 | ||
1034 | return (dir->links); | |
1035 | } | |
1036 | ||
1037 | /** | |
1038 | * sysfs_get_dir_subdirs: returns dlist of directory subdirectories | |
1039 | * @dir: directory to return subdirs for | |
1040 | * returns dlist of subdirs or NULL | |
1041 | */ | |
1042 | struct dlist *sysfs_get_dir_subdirs(struct sysfs_directory *dir) | |
1043 | { | |
1044 | if (dir == NULL) { | |
1045 | errno = EINVAL; | |
1046 | return NULL; | |
1047 | } | |
1048 | ||
1049 | if (dir->subdirs == NULL) { | |
1050 | if (sysfs_read_dir_subdirs(dir) != 0) | |
1051 | return NULL; | |
1052 | } | |
1053 | ||
1054 | return (dir->subdirs); | |
1055 | } |