]>
Commit | Line | Data |
---|---|---|
070f5422 | 1 | /* |
bcbe2d8e | 2 | * sysfs_utils.c |
070f5422 GKH |
3 | * |
4 | * System 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" | |
1e959a4b GKH |
25 | #ifndef __KLIBC__ |
26 | #include <mntent.h> | |
27 | #endif | |
070f5422 | 28 | |
616a7078 AM |
29 | /** |
30 | * sysfs_remove_trailing_slash: Removes any trailing '/' in the given path | |
31 | * @path: Path to look for the trailing '/' | |
32 | * Returns 0 on success 1 on error | |
33 | */ | |
34 | int sysfs_remove_trailing_slash(unsigned char *path) | |
35 | { | |
36 | unsigned char *c = NULL; | |
37 | ||
38 | if (path == NULL) { | |
39 | errno = EINVAL; | |
40 | return 1; | |
41 | } | |
42 | c = strrchr(path, '/'); | |
43 | if (c == NULL) { | |
44 | dprintf("Invalid path %s\n", path); | |
45 | errno = EINVAL; | |
46 | return 1; | |
47 | } | |
48 | if (*(c+1) == '\0') | |
49 | *c = '\0'; | |
50 | return 0; | |
51 | } | |
52 | ||
070f5422 GKH |
53 | /** |
54 | * sysfs_get_mnt_path: Gets the mount point for specified filesystem. | |
55 | * @fs_type: filesystem type to retrieve mount point | |
56 | * @mnt_path: place to put the retrieved mount path | |
57 | * @len: size of mnt_path | |
58 | * returns 0 with success and -1 with error. | |
59 | */ | |
fe3fe3b2 DS |
60 | static int sysfs_get_fs_mnt_path(const unsigned char *fs_type, |
61 | unsigned char *mnt_path, size_t len) | |
070f5422 | 62 | { |
1e959a4b GKH |
63 | #ifdef __KLIBC__ |
64 | strcpy(mnt_path, "/sys"); | |
65 | return 0; | |
66 | #else | |
070f5422 GKH |
67 | FILE *mnt; |
68 | struct mntent *mntent; | |
69 | int ret = 0; | |
70 | size_t dirlen = 0; | |
71 | ||
72 | /* check arg */ | |
73 | if (fs_type == NULL || mnt_path == NULL) { | |
74 | errno = EINVAL; | |
75 | return -1; | |
76 | } | |
77 | ||
78 | if ((mnt = setmntent(SYSFS_PROC_MNTS, "r")) == NULL) { | |
fe3fe3b2 | 79 | dprintf("Error getting mount information\n"); |
070f5422 GKH |
80 | return -1; |
81 | } | |
82 | while (ret == 0 && dirlen == 0 && (mntent = getmntent(mnt)) != NULL) { | |
83 | if (strcmp(mntent->mnt_type, fs_type) == 0) { | |
84 | dirlen = strlen(mntent->mnt_dir); | |
85 | if (dirlen <= (len - 1)) { | |
86 | strcpy(mnt_path, mntent->mnt_dir); | |
87 | } else { | |
fe3fe3b2 | 88 | dprintf("Error - mount path too long\n"); |
070f5422 GKH |
89 | ret = -1; |
90 | } | |
91 | } | |
92 | } | |
93 | endmntent(mnt); | |
94 | if (dirlen == 0 && ret == 0) { | |
fe3fe3b2 | 95 | dprintf("Filesystem %s not found!\n", fs_type); |
070f5422 GKH |
96 | errno = EINVAL; |
97 | ret = -1; | |
98 | } | |
616a7078 AM |
99 | if ((sysfs_remove_trailing_slash(mnt_path)) != 0) |
100 | ret = -1; | |
101 | ||
070f5422 | 102 | return ret; |
1e959a4b | 103 | #endif |
070f5422 GKH |
104 | } |
105 | ||
106 | /* | |
107 | * sysfs_get_mnt_path: Gets the sysfs mount point. | |
108 | * @mnt_path: place to put "sysfs" mount point | |
109 | * @len: size of mnt_path | |
110 | * returns 0 with success and -1 with error. | |
111 | */ | |
fe3fe3b2 | 112 | int sysfs_get_mnt_path(unsigned char *mnt_path, size_t len) |
070f5422 | 113 | { |
8d817251 DS |
114 | char *sysfs_path = NULL; |
115 | int ret = 0; | |
070f5422 | 116 | |
8d817251 | 117 | if (mnt_path == NULL) { |
070f5422 | 118 | errno = EINVAL; |
8d817251 DS |
119 | return -1; |
120 | } | |
121 | sysfs_path = getenv(SYSFS_PATH_ENV); | |
616a7078 | 122 | if (sysfs_path != NULL) { |
8d817251 | 123 | strncpy(mnt_path, sysfs_path, len); |
616a7078 AM |
124 | if ((sysfs_remove_trailing_slash(mnt_path)) != 0) |
125 | return 1; | |
126 | } else | |
8d817251 | 127 | ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len); |
070f5422 GKH |
128 | |
129 | return ret; | |
130 | } | |
131 | ||
132 | /** | |
133 | * sysfs_get_name_from_path: returns last name from a "/" delimited path | |
134 | * @path: path to get name from | |
135 | * @name: where to put name | |
136 | * @len: size of name | |
137 | */ | |
fe3fe3b2 DS |
138 | int sysfs_get_name_from_path(const unsigned char *path, unsigned char *name, |
139 | size_t len) | |
070f5422 | 140 | { |
ff44a6b0 | 141 | unsigned char tmp[SYSFS_PATH_MAX]; |
fe3fe3b2 | 142 | unsigned char *n = NULL; |
070f5422 GKH |
143 | |
144 | if (path == NULL || name == NULL) { | |
145 | errno = EINVAL; | |
146 | return -1; | |
147 | } | |
ff44a6b0 DS |
148 | memset(tmp, 0, SYSFS_PATH_MAX); |
149 | strcpy(tmp, path); | |
ff44a6b0 | 150 | n = strrchr(tmp, '/'); |
070f5422 GKH |
151 | if (n == NULL) { |
152 | errno = EINVAL; | |
153 | return -1; | |
154 | } | |
bcbe2d8e DS |
155 | if (*(n+1) == '\0') { |
156 | *n = '\0'; | |
157 | n = strrchr(tmp, '/'); | |
158 | if (n == NULL) { | |
159 | errno = EINVAL; | |
160 | return -1; | |
161 | } | |
162 | } | |
070f5422 GKH |
163 | n++; |
164 | strncpy(name, n, len); | |
070f5422 GKH |
165 | return 0; |
166 | } | |
616a7078 | 167 | |
070f5422 GKH |
168 | /** |
169 | * sysfs_get_link: returns link source | |
170 | * @path: symbolic link's path | |
171 | * @target: where to put name | |
172 | * @len: size of name | |
173 | */ | |
fe3fe3b2 | 174 | int sysfs_get_link(const unsigned char *path, unsigned char *target, size_t len) |
070f5422 | 175 | { |
fe3fe3b2 DS |
176 | unsigned char devdir[SYSFS_PATH_MAX]; |
177 | unsigned char linkpath[SYSFS_PATH_MAX]; | |
8d817251 DS |
178 | unsigned char *d = NULL, *s = NULL; |
179 | int slashes = 0, count = 0; | |
070f5422 GKH |
180 | |
181 | if (path == NULL || target == NULL) { | |
182 | errno = EINVAL; | |
183 | return -1; | |
184 | } | |
185 | ||
186 | memset(devdir, 0, SYSFS_PATH_MAX); | |
187 | memset(linkpath, 0, SYSFS_PATH_MAX); | |
8d817251 | 188 | strncpy(devdir, path, SYSFS_PATH_MAX); |
070f5422 | 189 | |
070f5422 GKH |
190 | if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) { |
191 | return -1; | |
192 | } | |
070f5422 | 193 | d = linkpath; |
616a7078 AM |
194 | /* |
195 | * Three cases here: | |
196 | * 1. relative path => format ../.. | |
197 | * 2. absolute path => format /abcd/efgh | |
198 | * 3. relative path _from_ this dir => format abcd/efgh | |
199 | */ | |
200 | switch (*d) { | |
201 | case '.': | |
202 | /* | |
203 | * handle the case where link is of type ./abcd/xxx | |
204 | */ | |
205 | strncpy(target, devdir, len); | |
206 | if (*(d+1) == '/') | |
207 | d += 2; | |
208 | else if (*(d+1) == '.') | |
209 | goto parse_path; | |
210 | s = strrchr(target, '/'); | |
211 | if (s != NULL) { | |
212 | *(s+1) = '\0'; | |
213 | strcat(target, d); | |
214 | } else { | |
215 | strcpy(target, d); | |
216 | } | |
217 | break; | |
218 | /* | |
219 | * relative path | |
220 | * getting rid of leading "../.." | |
221 | */ | |
222 | parse_path: | |
223 | while (*d == '/' || *d == '.') { | |
224 | if (*d == '/') | |
225 | slashes++; | |
226 | d++; | |
227 | } | |
228 | d--; | |
229 | s = &devdir[strlen(devdir)-1]; | |
230 | while (s != NULL && count != (slashes+1)) { | |
231 | s--; | |
232 | if (*s == '/') | |
233 | count++; | |
234 | } | |
235 | strncpy(s, d, (SYSFS_PATH_MAX-strlen(devdir))); | |
236 | strncpy(target, devdir, len); | |
237 | break; | |
238 | case '/': | |
239 | /* absolute path - copy as is */ | |
240 | strncpy(target, linkpath, len); | |
241 | break; | |
242 | default: | |
243 | /* relative path from this directory */ | |
244 | strncpy(target, devdir, len); | |
245 | s = strrchr(target, '/'); | |
246 | if (s != NULL) { | |
247 | *(s+1) = '\0'; | |
248 | strcat(target, linkpath); | |
249 | } else { | |
250 | strcpy(target, linkpath); | |
251 | } | |
8d817251 | 252 | } |
070f5422 GKH |
253 | return 0; |
254 | } | |
fe3fe3b2 | 255 | |
fe3fe3b2 DS |
256 | /** |
257 | * sysfs_del_name: free function for sysfs_open_subsystem_list | |
258 | * @name: memory area to be freed | |
259 | */ | |
ff44a6b0 | 260 | static void sysfs_del_name(void *name) |
fe3fe3b2 DS |
261 | { |
262 | free(name); | |
263 | } | |
264 | ||
265 | ||
266 | /** | |
267 | * sysfs_close_list: generic list free routine | |
268 | * @list: dlist to free | |
269 | * Returns nothing | |
270 | */ | |
271 | void sysfs_close_list(struct dlist *list) | |
272 | { | |
273 | if (list != NULL) | |
274 | dlist_destroy(list); | |
275 | } | |
276 | ||
277 | /** | |
278 | * sysfs_open_subsystem_list: gets a list of all supported "name" subsystem | |
279 | * details from the system | |
280 | * @name: name of the subsystem, eg., "bus", "class", "devices" | |
281 | * Returns a dlist of supported names or NULL if subsystem not supported | |
282 | */ | |
283 | struct dlist *sysfs_open_subsystem_list(unsigned char *name) | |
284 | { | |
285 | unsigned char sysfs_path[SYSFS_PATH_MAX], *subsys_name = NULL; | |
ff44a6b0 | 286 | unsigned char *c = NULL; |
fe3fe3b2 DS |
287 | struct sysfs_directory *dir = NULL, *cur = NULL; |
288 | struct dlist *list = NULL; | |
289 | ||
290 | if (name == NULL) | |
291 | return NULL; | |
292 | ||
293 | if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) { | |
294 | dprintf("Error getting sysfs mount point\n"); | |
295 | return NULL; | |
296 | } | |
616a7078 AM |
297 | |
298 | strcat(sysfs_path, "/"); | |
fe3fe3b2 DS |
299 | strcat(sysfs_path, name); |
300 | dir = sysfs_open_directory(sysfs_path); | |
301 | if (dir == NULL) { | |
302 | dprintf("Error opening sysfs_directory at %s\n", sysfs_path); | |
303 | return NULL; | |
304 | } | |
305 | ||
bcbe2d8e | 306 | if ((sysfs_read_dir_subdirs(dir)) != 0) { |
fe3fe3b2 DS |
307 | dprintf("Error reading sysfs_directory at %s\n", sysfs_path); |
308 | sysfs_close_directory(dir); | |
309 | return NULL; | |
310 | } | |
311 | ||
312 | if (dir->subdirs != NULL) { | |
313 | list = dlist_new_with_delete(SYSFS_NAME_LEN, | |
314 | sysfs_del_name); | |
315 | if (list == NULL) { | |
316 | dprintf("Error creating list\n"); | |
317 | sysfs_close_directory(dir); | |
318 | return NULL; | |
319 | } | |
320 | ||
321 | dlist_for_each_data(dir->subdirs, cur, | |
322 | struct sysfs_directory) { | |
323 | subsys_name = (char *)calloc(1, SYSFS_NAME_LEN); | |
324 | strcpy(subsys_name, cur->name); | |
325 | dlist_unshift(list, subsys_name); | |
326 | } | |
327 | } | |
328 | sysfs_close_directory(dir); | |
ff44a6b0 DS |
329 | /* |
330 | * We are now considering "block" as a "class". Hence, if the subsys | |
331 | * name requested here is "class", verify if "block" is supported on | |
332 | * this system and return the same. | |
333 | */ | |
5d4754f1 | 334 | if (strcmp(name, SYSFS_CLASS_NAME) == 0) { |
ff44a6b0 DS |
335 | c = strstr(sysfs_path, SYSFS_CLASS_NAME); |
336 | if (c == NULL) | |
337 | goto out; | |
338 | strcpy(c, SYSFS_BLOCK_NAME); | |
bcbe2d8e | 339 | if ((sysfs_path_is_dir(sysfs_path)) == 0) { |
ff44a6b0 DS |
340 | subsys_name = (char *)calloc(1, SYSFS_NAME_LEN); |
341 | strcpy(subsys_name, SYSFS_BLOCK_NAME); | |
342 | dlist_unshift(list, subsys_name); | |
343 | } | |
344 | } | |
345 | out: | |
fe3fe3b2 DS |
346 | return list; |
347 | } | |
348 | ||
349 | ||
350 | /** | |
351 | * sysfs_open_bus_devices_list: gets a list of all devices on "name" bus | |
352 | * @name: name of the subsystem, eg., "pci", "scsi", "usb" | |
353 | * Returns a dlist of supported names or NULL if subsystem not supported | |
354 | */ | |
355 | struct dlist *sysfs_open_bus_devices_list(unsigned char *name) | |
356 | { | |
357 | unsigned char sysfs_path[SYSFS_PATH_MAX], *device_name = NULL; | |
358 | struct sysfs_directory *dir = NULL; | |
359 | struct sysfs_link *cur = NULL; | |
360 | struct dlist *list = NULL; | |
361 | ||
362 | if (name == NULL) | |
363 | return NULL; | |
364 | ||
365 | if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) { | |
366 | dprintf("Error getting sysfs mount point\n"); | |
367 | return NULL; | |
368 | } | |
369 | ||
616a7078 | 370 | strcat(sysfs_path, "/"); |
edcd3364 | 371 | strcat(sysfs_path, SYSFS_BUS_NAME); |
fe3fe3b2 DS |
372 | strcat(sysfs_path, "/"); |
373 | strcat(sysfs_path, name); | |
5d4754f1 DS |
374 | strcat(sysfs_path, "/"); |
375 | strcat(sysfs_path, SYSFS_DEVICES_NAME); | |
fe3fe3b2 DS |
376 | dir = sysfs_open_directory(sysfs_path); |
377 | if (dir == NULL) { | |
378 | dprintf("Error opening sysfs_directory at %s\n", sysfs_path); | |
379 | return NULL; | |
380 | } | |
381 | ||
bcbe2d8e | 382 | if ((sysfs_read_dir_links(dir)) != 0) { |
fe3fe3b2 DS |
383 | dprintf("Error reading sysfs_directory at %s\n", sysfs_path); |
384 | sysfs_close_directory(dir); | |
385 | return NULL; | |
386 | } | |
387 | ||
388 | if (dir->links != NULL) { | |
389 | list = dlist_new_with_delete(SYSFS_NAME_LEN, | |
390 | sysfs_del_name); | |
391 | if (list == NULL) { | |
392 | dprintf("Error creating list\n"); | |
393 | sysfs_close_directory(dir); | |
394 | return NULL; | |
395 | } | |
396 | ||
397 | dlist_for_each_data(dir->links, cur, | |
398 | struct sysfs_link) { | |
399 | device_name = (char *)calloc(1, SYSFS_NAME_LEN); | |
400 | strcpy(device_name, cur->name); | |
401 | dlist_unshift(list, device_name); | |
402 | } | |
403 | } | |
404 | sysfs_close_directory(dir); | |
405 | return list; | |
406 | } | |
407 | ||
5d4754f1 DS |
408 | /** |
409 | * sysfs_path_is_dir: Check if the path supplied points to a directory | |
410 | * @path: path to validate | |
411 | * Returns 0 if path points to dir, 1 otherwise | |
412 | */ | |
413 | int sysfs_path_is_dir(const unsigned char *path) | |
414 | { | |
415 | struct stat astats; | |
416 | ||
417 | if (path == NULL) { | |
418 | errno = EINVAL; | |
419 | return 1; | |
420 | } | |
421 | if ((lstat(path, &astats)) != 0) { | |
422 | dprintf("stat() failed\n"); | |
423 | return 1; | |
424 | } | |
425 | if (S_ISDIR(astats.st_mode)) | |
426 | return 0; | |
616a7078 | 427 | |
5d4754f1 DS |
428 | return 1; |
429 | } | |
430 | ||
431 | /** | |
432 | * sysfs_path_is_link: Check if the path supplied points to a link | |
433 | * @path: path to validate | |
434 | * Returns 0 if path points to link, 1 otherwise | |
435 | */ | |
436 | int sysfs_path_is_link(const unsigned char *path) | |
437 | { | |
438 | struct stat astats; | |
439 | ||
440 | if (path == NULL) { | |
441 | errno = EINVAL; | |
442 | return 1; | |
443 | } | |
444 | if ((lstat(path, &astats)) != 0) { | |
445 | dprintf("stat() failed\n"); | |
446 | return 1; | |
447 | } | |
448 | if (S_ISLNK(astats.st_mode)) | |
449 | return 0; | |
616a7078 | 450 | |
5d4754f1 DS |
451 | return 1; |
452 | } | |
453 | ||
454 | /** | |
455 | * sysfs_path_is_file: Check if the path supplied points to a file | |
456 | * @path: path to validate | |
457 | * Returns 0 if path points to file, 1 otherwise | |
458 | */ | |
459 | int sysfs_path_is_file(const unsigned char *path) | |
460 | { | |
461 | struct stat astats; | |
462 | ||
463 | if (path == NULL) { | |
464 | errno = EINVAL; | |
465 | return 1; | |
466 | } | |
467 | if ((lstat(path, &astats)) != 0) { | |
468 | dprintf("stat() failed\n"); | |
469 | return 1; | |
470 | } | |
471 | if (S_ISREG(astats.st_mode)) | |
472 | return 0; | |
616a7078 | 473 | |
5d4754f1 DS |
474 | return 1; |
475 | } |