]> git.ipfire.org Git - thirdparty/systemd.git/blob - libsysfs/sysfs_utils.c
[PATCH] rework the logging code so that each program logs with the proper name in...
[thirdparty/systemd.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 "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 }