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