chiark / gitweb /
[PATCH] libsysfs changes for sysfsutils 0.3.0
[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_get_mnt_path: Gets the sysfs mount point.
81  * @mnt_path: place to put "sysfs" mount point
82  * @len: size of mnt_path
83  * returns 0 with success and -1 with error.
84  */
85 int sysfs_get_mnt_path(unsigned char *mnt_path, size_t len)
86 {
87         char *sysfs_path = NULL;
88         int ret = 0;
89
90         if (mnt_path == NULL) {
91                 errno = EINVAL;
92                 return -1;
93         }
94         sysfs_path = getenv(SYSFS_PATH_ENV);
95         if (sysfs_path != NULL) 
96                 strncpy(mnt_path, sysfs_path, len);
97         else
98                 ret = sysfs_get_fs_mnt_path(SYSFS_FSTYPE_NAME, mnt_path, len);
99
100         return ret;
101 }
102
103 /**
104  * sysfs_get_name_from_path: returns last name from a "/" delimited path
105  * @path: path to get name from
106  * @name: where to put name
107  * @len: size of name
108  */
109 int sysfs_get_name_from_path(const unsigned char *path, unsigned char *name, 
110                                                                 size_t len)
111 {
112         unsigned char tmp[SYSFS_PATH_MAX];
113         unsigned char *n = NULL;
114                                                                                 
115         if (path == NULL || name == NULL) {
116                 errno = EINVAL;
117                 return -1;
118         }
119         memset(tmp, 0, SYSFS_PATH_MAX);
120         strcpy(tmp, path);
121         n = &tmp[strlen(tmp)-1];
122         if (strncmp(n, "/", 1) == 0)
123                 *n = '\0';      
124         n = strrchr(tmp, '/');
125         if (n == NULL) {
126                 errno = EINVAL;
127                 return -1;
128         }
129         n++;
130         strncpy(name, n, len);
131
132         return 0;
133 }
134
135 /**
136  * sysfs_get_link: returns link source
137  * @path: symbolic link's path
138  * @target: where to put name
139  * @len: size of name
140  */
141 int sysfs_get_link(const unsigned char *path, unsigned char *target, size_t len)
142 {
143         unsigned char devdir[SYSFS_PATH_MAX];
144         unsigned char linkpath[SYSFS_PATH_MAX];
145         unsigned char *d = NULL, *s = NULL;
146         int slashes = 0, count = 0;
147
148         if (path == NULL || target == NULL) {
149                 errno = EINVAL;
150                 return -1;
151         }
152
153         memset(devdir, 0, SYSFS_PATH_MAX);
154         memset(linkpath, 0, SYSFS_PATH_MAX);
155         strncpy(devdir, path, SYSFS_PATH_MAX);
156
157         if ((readlink(path, linkpath, SYSFS_PATH_MAX)) < 0) {
158                 return -1;
159         }
160                                                                                 
161         d = linkpath;
162
163         /* getting rid of leading "../.." */    
164         while (*d == '/' || *d == '.') {
165                 if (*d == '/')
166                         slashes++;
167                 d++;
168         }
169
170         d--;
171
172         s = &devdir[strlen(devdir)-1];
173         while (s != NULL && count != (slashes+1)) {
174                 s--;
175                 if (*s == '/')
176                         count++;
177         }
178         
179         strncpy(s, d, (SYSFS_PATH_MAX-strlen(devdir)));
180         strncpy(target, devdir, len);
181
182         return 0;
183 }
184
185
186 /**
187  * sysfs_del_name: free function for sysfs_open_subsystem_list
188  * @name: memory area to be freed
189  */ 
190 static void sysfs_del_name(void *name)
191 {
192         free(name);
193 }
194
195
196 /**
197  * sysfs_close_list: generic list free routine
198  * @list: dlist to free
199  * Returns nothing
200  */
201 void sysfs_close_list(struct dlist *list)
202 {
203         if (list != NULL)
204                 dlist_destroy(list);
205 }
206
207 /**
208  * sysfs_open_subsystem_list: gets a list of all supported "name" subsystem
209  *      details from the system
210  * @name: name of the subsystem, eg., "bus", "class", "devices"
211  * Returns a dlist of supported names or NULL if subsystem not supported
212  */ 
213 struct dlist *sysfs_open_subsystem_list(unsigned char *name)
214 {
215         unsigned char sysfs_path[SYSFS_PATH_MAX], *subsys_name = NULL;
216         unsigned char *c = NULL;
217         struct sysfs_directory *dir = NULL, *cur = NULL;
218         struct dlist *list = NULL;
219         struct stat astats;
220         
221         if (name == NULL)
222                 return NULL;
223
224         if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
225                 dprintf("Error getting sysfs mount point\n");
226                 return NULL;
227         }
228
229         strcat(sysfs_path, name);
230         dir = sysfs_open_directory(sysfs_path);
231         if (dir == NULL) {
232                 dprintf("Error opening sysfs_directory at %s\n", sysfs_path);
233                 return NULL;
234         }
235
236         if (sysfs_read_directory(dir) != 0) {
237                 dprintf("Error reading sysfs_directory at %s\n", sysfs_path);
238                 sysfs_close_directory(dir);
239                 return NULL;
240         }
241
242         if (dir->subdirs != NULL) {
243                 list = dlist_new_with_delete(SYSFS_NAME_LEN,
244                                 sysfs_del_name);
245                 if (list == NULL) {
246                         dprintf("Error creating list\n");
247                         sysfs_close_directory(dir);
248                         return NULL;
249                 }
250
251                 dlist_for_each_data(dir->subdirs, cur,
252                                 struct sysfs_directory) {
253                         subsys_name = (char *)calloc(1, SYSFS_NAME_LEN);
254                         strcpy(subsys_name, cur->name);
255                         dlist_unshift(list, subsys_name);
256                 }
257         }
258         sysfs_close_directory(dir);
259         /*
260          * We are now considering "block" as a "class". Hence, if the subsys
261          * name requested here is "class", verify if "block" is supported on
262          * this system and return the same.
263          */ 
264         if (strcmp(name, SYSFS_CLASS_DIR) == 0) {
265                 c = strstr(sysfs_path, SYSFS_CLASS_NAME);
266                 if (c == NULL)
267                         goto out;
268                 strcpy(c, SYSFS_BLOCK_NAME);
269                 if ((lstat(sysfs_path, &astats)) != 0) {
270                         dprintf("stat() failed\n");
271                         goto out;
272                 }
273                 if (S_ISDIR(astats.st_mode)) {
274                         subsys_name = (char *)calloc(1, SYSFS_NAME_LEN);
275                         strcpy(subsys_name, SYSFS_BLOCK_NAME);
276                         dlist_unshift(list, subsys_name);
277                 }
278         }
279 out:
280         return list;
281 }
282
283
284 /**
285  * sysfs_open_bus_devices_list: gets a list of all devices on "name" bus
286  * @name: name of the subsystem, eg., "pci", "scsi", "usb"
287  * Returns a dlist of supported names or NULL if subsystem not supported
288  */ 
289 struct dlist *sysfs_open_bus_devices_list(unsigned char *name)
290 {
291         unsigned char sysfs_path[SYSFS_PATH_MAX], *device_name = NULL;
292         struct sysfs_directory *dir = NULL;
293         struct sysfs_link *cur = NULL;
294         struct dlist *list = NULL;
295         
296         if (name == NULL)
297                 return NULL;
298
299         if (sysfs_get_mnt_path(sysfs_path, SYSFS_PATH_MAX) != 0) {
300                 dprintf("Error getting sysfs mount point\n");
301                 return NULL;
302         }
303
304         strcat(sysfs_path, SYSFS_BUS_DIR);
305         strcat(sysfs_path, "/");
306         strcat(sysfs_path, name);
307         strcat(sysfs_path, SYSFS_DEVICES_DIR);
308         dir = sysfs_open_directory(sysfs_path);
309         if (dir == NULL) {
310                 dprintf("Error opening sysfs_directory at %s\n", sysfs_path);
311                 return NULL;
312         }
313
314         if (sysfs_read_directory(dir) != 0) {
315                 dprintf("Error reading sysfs_directory at %s\n", sysfs_path);
316                 sysfs_close_directory(dir);
317                 return NULL;
318         }
319
320         if (dir->links != NULL) {
321                 list = dlist_new_with_delete(SYSFS_NAME_LEN,
322                                 sysfs_del_name);
323                 if (list == NULL) {
324                         dprintf("Error creating list\n");
325                         sysfs_close_directory(dir);
326                         return NULL;
327                 }
328
329                 dlist_for_each_data(dir->links, cur,
330                                 struct sysfs_link) {
331                         device_name = (char *)calloc(1, SYSFS_NAME_LEN);
332                         strcpy(device_name, cur->name);
333                         dlist_unshift(list, device_name);
334                 }
335         }
336         sysfs_close_directory(dir);
337         return list;
338 }
339