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