chiark / gitweb /
[PATCH] udev use new libsysfs header file location
[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 /**
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
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  */
60 static int sysfs_get_fs_mnt_path(const unsigned char *fs_type, 
61                                 unsigned char *mnt_path, size_t len)
62 {
63 #ifdef __KLIBC__
64         strcpy(mnt_path, "/sys");
65         return 0;
66 #else
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) {
79                 dprintf("Error getting mount information\n");
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 {
88                                 dprintf("Error - mount path too long\n");
89                                 ret = -1;
90                         }
91                 }
92         }
93         endmntent(mnt);
94         if (dirlen == 0 && ret == 0) {
95                 dprintf("Filesystem %s not found!\n", fs_type);
96                 errno = EINVAL;
97                 ret = -1;
98         }
99         if ((sysfs_remove_trailing_slash(mnt_path)) != 0)
100                 ret = -1;
101         
102         return ret;
103 #endif
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  */
112 int sysfs_get_mnt_path(unsigned char *mnt_path, size_t len)
113 {
114         char *sysfs_path = NULL;
115         int ret = 0;
116
117         if (mnt_path == NULL) {
118                 errno = EINVAL;
119                 return -1;
120         }
121         sysfs_path = getenv(SYSFS_PATH_ENV);
122         if (sysfs_path != NULL) {
123                 strncpy(mnt_path, sysfs_path, len);
124                 if ((sysfs_remove_trailing_slash(mnt_path)) != 0)
125                         return 1;
126         } else
127                 ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len);
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  */
138 int sysfs_get_name_from_path(const unsigned char *path, unsigned char *name, 
139                                                                 size_t len)
140 {
141         unsigned char tmp[SYSFS_PATH_MAX];
142         unsigned char *n = NULL;
143                                                                                 
144         if (path == NULL || name == NULL) {
145                 errno = EINVAL;
146                 return -1;
147         }
148         memset(tmp, 0, SYSFS_PATH_MAX);
149         strcpy(tmp, path);
150         n = strrchr(tmp, '/');
151         if (n == NULL) {
152                 errno = EINVAL;
153                 return -1;
154         }
155         if (*(n+1) == '\0') {
156                 *n = '\0';
157                 n = strrchr(tmp, '/');
158                 if (n == NULL) {
159                         errno = EINVAL;
160                         return -1;
161                 }
162         }
163         n++;
164         strncpy(name, n, len);
165         return 0;
166 }
167         
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  */
174 int sysfs_get_link(const unsigned char *path, unsigned char *target, size_t len)
175 {
176         unsigned char devdir[SYSFS_PATH_MAX];
177         unsigned char linkpath[SYSFS_PATH_MAX];
178         unsigned char *d = NULL, *s = NULL;
179         int slashes = 0, count = 0;
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);
188         strncpy(devdir, path, SYSFS_PATH_MAX);
189
190         if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) {
191                 return -1;
192         }
193         d = linkpath;
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                         }                       
252         }
253         return 0;
254 }
255
256 /**
257  * sysfs_del_name: free function for sysfs_open_subsystem_list
258  * @name: memory area to be freed
259  */ 
260 static void sysfs_del_name(void *name)
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;
286         unsigned char *c = NULL;
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         }
297
298         strcat(sysfs_path, "/");
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
306         if ((sysfs_read_dir_subdirs(dir)) != 0) {
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);
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          */ 
334         if (strcmp(name, SYSFS_CLASS_NAME) == 0) {
335                 c = strstr(sysfs_path, SYSFS_CLASS_NAME);
336                 if (c == NULL)
337                         goto out;
338                 strcpy(c, SYSFS_BLOCK_NAME);
339                 if ((sysfs_path_is_dir(sysfs_path)) == 0) {
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:
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
370         strcat(sysfs_path, "/");
371         strcat(sysfs_path, SYSFS_BUS_NAME);
372         strcat(sysfs_path, "/");
373         strcat(sysfs_path, name);
374         strcat(sysfs_path, "/");
375         strcat(sysfs_path, SYSFS_DEVICES_NAME);
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
382         if ((sysfs_read_dir_links(dir)) != 0) {
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
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;
427                 
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;
450                 
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;
473                 
474         return 1;
475 }