chiark / gitweb /
[PATCH] update libsysfs to CVS version and fix segfaulting attribute reading
[elogind.git] / libsysfs / sysfs_device.c
1 /*
2  * sysfs_device.c
3  *
4  * Generic device 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
26 /**
27  * get_dev_driver: fills in the dev->driver_name field, but searches by
28  *      opening subsystem. Only to be used if no driver link exists in
29  *      device directory.
30  *
31  * Returns 0 on SUCCESS and 1 on error
32  */
33 static int get_dev_driver(struct sysfs_device *dev)
34 {
35         struct dlist *drvlist = NULL;
36         char path[SYSFS_PATH_MAX], devpath[SYSFS_PATH_MAX];
37         char *drv = NULL, *c = NULL;
38         
39         if (dev == NULL) {
40                 errno = EINVAL;
41                 return 1;
42         }
43         if (dev->bus[0] == '\0')
44                 return 1;
45         memset(path, 0, SYSFS_PATH_MAX);
46         memset(devpath, 0, SYSFS_PATH_MAX);
47         safestrcpy(path, SYSFS_BUS_NAME);
48         safestrcat(path, "/");
49         safestrcat(path, dev->bus);
50         safestrcat(path, "/");
51         safestrcat(path, SYSFS_DRIVERS_NAME);
52
53         safestrcpy(devpath, dev->path);
54         c = strstr(devpath, SYSFS_DEVICES_NAME);
55         if (c == NULL)
56                 return 1;
57         *c = '\0';
58         safestrcatmax(c, path, (sizeof(devpath) - strlen(devpath)));
59
60         drvlist = sysfs_open_subsystem_list(path);
61         if (drvlist != NULL) {
62                 dlist_for_each_data(drvlist, drv, char) {
63                         safestrcpy(path, devpath);
64                         safestrcat(path, "/");
65                         safestrcat(path, drv);
66                         safestrcat(path, "/");
67                         safestrcat(path, dev->bus_id);
68                         if (sysfs_path_is_link(path) == 0) {
69                                 safestrcpy(dev->driver_name, drv);
70                                 sysfs_close_list(drvlist);
71                                 return 0;
72                         }
73                 }
74                 sysfs_close_list(drvlist);
75         }
76         return 1;
77 }
78
79 /*
80  * get_device_driver_name: gets device's driver name, searches for driver
81  *      link first before going the brute force route.
82  * @dev: device to retrieve driver
83  * returns 0 with success and 1 with error
84  */
85 static int get_device_driver_name(struct sysfs_device *dev)
86 {
87         char devpath[SYSFS_PATH_MAX], drvpath[SYSFS_PATH_MAX];
88
89         if (dev == NULL) {
90                 errno = EINVAL;
91                 return 1;
92         }
93         memset(devpath, 0, SYSFS_PATH_MAX);
94         memset(drvpath, 0, SYSFS_PATH_MAX);
95         safestrcpy(devpath, dev->path);
96         safestrcat(devpath, "/driver");
97
98         if ((sysfs_get_link(devpath, drvpath, SYSFS_PATH_MAX)) != 0) 
99                 return(get_dev_driver(dev));
100
101         return (sysfs_get_name_from_path(drvpath, dev->driver_name, 
102                 SYSFS_NAME_LEN));
103 }
104         
105 /**
106  * sysfs_get_device_bus: retrieves the bus name the device is on, checks path 
107  *      to bus' link to make sure it has correct device.
108  * @dev: device to get busname.
109  * returns 0 with success and -1 with error.
110  */
111 int sysfs_get_device_bus(struct sysfs_device *dev)
112 {
113         char subsys[SYSFS_NAME_LEN], path[SYSFS_PATH_MAX];
114         char target[SYSFS_PATH_MAX], *bus = NULL, *c = NULL;
115         struct dlist *buslist = NULL;
116
117         if (dev == NULL) {
118                 errno = EINVAL;
119                 return -1;
120         }
121
122         memset(subsys, 0, SYSFS_NAME_LEN);
123         safestrcpy(subsys, SYSFS_BUS_NAME);  /* subsys = bus */
124         buslist = sysfs_open_subsystem_list(subsys);
125         if (buslist != NULL) {
126                 dlist_for_each_data(buslist, bus, char) {
127                         memset(path, 0, SYSFS_PATH_MAX);
128                         safestrcpy(path, dev->path);
129                         c = strstr(path, "/devices");
130                         if (c == NULL) {
131                                 dprintf("Invalid path to device %s\n", path);
132                                 sysfs_close_list(buslist);
133                                 return -1;
134                         }
135                         *c = '\0';
136                         safestrcat(path, "/");
137                         safestrcat(path, SYSFS_BUS_NAME);
138                         safestrcat(path, "/");
139                         safestrcat(path, bus);
140                         safestrcat(path, "/");
141                         safestrcat(path, SYSFS_DEVICES_NAME);
142                         safestrcat(path, "/");
143                         safestrcat(path, dev->bus_id);
144                         if ((sysfs_path_is_link(path)) == 0) {
145                                 memset(target, 0, SYSFS_PATH_MAX);
146                                 if ((sysfs_get_link(path, target, 
147                                                 SYSFS_PATH_MAX)) != 0) {
148                                         dprintf("Error getting link target\n");
149                                         sysfs_close_list(buslist);
150                                         return -1;
151                                 }
152                                 if (!(strncmp(target, dev->path, 
153                                                         SYSFS_PATH_MAX))) {
154                                         safestrcpy(dev->bus, bus);
155                                         sysfs_close_list(buslist);
156                                         return 0;
157                                 }
158                         }
159                 }
160                 sysfs_close_list(buslist);
161         }
162         return -1;
163 }
164
165 /**
166  * sysfs_close_device_tree: closes every device in the supplied tree, 
167  *      closing children only.
168  * @devroot: device root of tree.
169  */
170 void sysfs_close_device_tree(struct sysfs_device *devroot)
171 {
172         if (devroot != NULL) {
173                 if (devroot->children != NULL) {
174                         struct sysfs_device *child = NULL;
175
176                         dlist_for_each_data(devroot->children, child,
177                                         struct sysfs_device) {
178                                 sysfs_close_device_tree(child);
179                         }
180                 }
181                 sysfs_close_device(devroot);
182         }
183 }
184
185 /**
186  * sysfs_close_dev_tree: routine for dlist integration
187  */
188 static void sysfs_close_dev_tree(void *dev)
189 {
190         sysfs_close_device_tree((struct sysfs_device *)dev);
191 }
192
193 /**
194  * sysfs_close_device: closes and cleans up a device
195  * @dev = device to clean up
196  */
197 void sysfs_close_device(struct sysfs_device *dev)
198 {
199         if (dev != NULL) {
200                 if (dev->parent != NULL)
201                         sysfs_close_device(dev->parent);
202                 if (dev->directory != NULL)
203                         sysfs_close_directory(dev->directory);
204                 if (dev->children != NULL && dev->children->count == 0)
205                         dlist_destroy(dev->children);
206                 free(dev);
207         }
208 }
209
210 /**
211  * alloc_device: allocates and initializes device structure
212  * returns struct sysfs_device
213  */
214 static struct sysfs_device *alloc_device(void)
215 {
216         return (struct sysfs_device *)calloc(1, sizeof(struct sysfs_device));
217 }
218
219 /**
220  * open_device_dir: opens up sysfs_directory for specific root dev
221  * @name: name of root
222  * returns struct sysfs_directory with success and NULL with error
223  */
224 static struct sysfs_directory *open_device_dir(const char *path)
225 {
226         struct sysfs_directory *rdir = NULL;
227
228         if (path == NULL) {
229                 errno = EINVAL;
230                 return NULL;
231         }
232
233         rdir = sysfs_open_directory(path);
234         if (rdir == NULL) {
235                 errno = EINVAL;
236                 dprintf ("Device %s not supported on this system\n", path);
237                 return NULL;
238         }
239         if ((sysfs_read_dir_subdirs(rdir)) != 0) {
240                 dprintf ("Error reading device at dir %s\n", path);
241                 sysfs_close_directory(rdir);
242                 return NULL;
243         }
244         
245         return rdir;
246 }
247
248 /**
249  * sysfs_open_device_path: opens and populates device structure
250  * @path: path to device, this is the /sys/devices/ path
251  * returns sysfs_device structure with success or NULL with error
252  */
253 struct sysfs_device *sysfs_open_device_path(const char *path)
254 {
255         struct sysfs_device *dev = NULL;
256
257         if (path == NULL) {
258                 errno = EINVAL;
259                 return NULL;
260         }
261         if ((sysfs_path_is_dir(path)) != 0) {
262                 dprintf("Incorrect path to device: %s\n", path);
263                 return NULL;
264         }
265         dev = alloc_device();   
266         if (dev == NULL) {
267                 dprintf("Error allocating device at %s\n", path);
268                 return NULL;
269         }
270         if ((sysfs_get_name_from_path(path, dev->bus_id, 
271                                         SYSFS_NAME_LEN)) != 0) {
272                 errno = EINVAL;
273                 dprintf("Error getting device bus_id\n");
274                 sysfs_close_device(dev);
275                 return NULL;
276         }
277         safestrcpy(dev->path, path);
278         if ((sysfs_remove_trailing_slash(dev->path)) != 0) {
279                 dprintf("Invalid path to device %s\n", dev->path);
280                 sysfs_close_device(dev);
281                 return NULL;
282         }
283         /* 
284          * The "name" attribute no longer exists... return the device's
285          * sysfs representation instead, in the "dev->name" field, which
286          * implies that the dev->name and dev->bus_id contain same data.
287          */
288         safestrcpy(dev->name, dev->bus_id);
289         
290         if (sysfs_get_device_bus(dev) != 0)
291                 dprintf("Could not get device bus\n");
292         
293         if (get_device_driver_name(dev) != 0) {
294                 dprintf("Could not get device %s's driver\n", dev->bus_id);
295                 safestrcpy(dev->driver_name, SYSFS_UNKNOWN);
296         }
297
298         return dev;
299 }
300
301 /**
302  * sysfs_open_device_tree: opens root device and all of its children,
303  *      creating a tree of devices. Only opens children.
304  * @path: sysfs path to devices
305  * returns struct sysfs_device and its children with success or NULL with
306  *      error.
307  */
308 struct sysfs_device *sysfs_open_device_tree(const char *path)
309 {
310         struct sysfs_device *rootdev = NULL, *new = NULL;
311         struct sysfs_directory *cur = NULL;
312
313         if (path == NULL) {
314                 errno = EINVAL;
315                 return NULL;
316         }
317         rootdev = sysfs_open_device_path(path);
318         if (rootdev == NULL) {
319                 dprintf("Error opening root device at %s\n", path);
320                 return NULL;
321         }
322         if (rootdev->directory == NULL) {
323                 rootdev->directory = open_device_dir(rootdev->path);
324                 if (rootdev->directory == NULL) 
325                         return NULL;
326         }
327         if (rootdev->directory->subdirs != NULL) {
328                 dlist_for_each_data(rootdev->directory->subdirs, cur,
329                                 struct sysfs_directory) {
330                         new = sysfs_open_device_tree(cur->path);
331                         if (new == NULL) {
332                                 dprintf("Error opening device tree at %s\n",
333                                         cur->path);
334                                 sysfs_close_device_tree(rootdev);
335                                 return NULL;
336                         }
337                         if (rootdev->children == NULL)
338                                 rootdev->children = dlist_new_with_delete
339                                         (sizeof(struct sysfs_device),
340                                         sysfs_close_dev_tree);
341                         dlist_unshift_sorted(rootdev->children, 
342                                                         new, sort_list);
343                 }
344         }
345
346         return rootdev;
347 }
348
349 /**
350  * sysfs_close_root_device: closes root and all devices
351  * @root: root device to close
352  */
353 void sysfs_close_root_device(struct sysfs_root_device *root)
354 {
355         if (root != NULL) {
356                 if (root->devices != NULL)
357                         dlist_destroy(root->devices);
358                 if (root->directory != NULL)
359                         sysfs_close_directory(root->directory);
360                 free(root);
361         }
362 }
363
364 /**
365  * sysfs_get_root_devices: opens up all the devices under this root device
366  * @root: root device to open devices for
367  * returns dlist of devices with success and NULL with error
368  */
369 struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root)
370 {
371         struct sysfs_device *dev = NULL;
372         struct sysfs_directory *cur = NULL;
373
374         if (root == NULL) {
375                 errno = EINVAL;
376                 return NULL;
377         }
378         if (root->directory == NULL) {
379                 root->directory = open_device_dir(root->path);
380                 if (root->directory == NULL)
381                         return NULL;
382         }
383                 
384         if (root->directory->subdirs == NULL)
385                 return 0;
386
387         dlist_for_each_data(root->directory->subdirs, cur,
388                         struct sysfs_directory) {
389                 dev = sysfs_open_device_tree(cur->path);
390                 if (dev == NULL) {
391                         dprintf ("Error opening device at %s\n", cur->path);
392                         continue;
393                 }
394                 if (root->devices == NULL)
395                         root->devices = dlist_new_with_delete
396                                 (sizeof(struct sysfs_device), 
397                                 sysfs_close_dev_tree);
398                 dlist_unshift_sorted(root->devices, dev, sort_list);
399         }
400
401         return root->devices;
402 }
403
404 /**
405  * sysfs_open_root_device: opens sysfs devices root and all of its
406  *      devices.
407  * @name: name of /sys/devices/root to open
408  * returns struct sysfs_root_device if success and NULL with error
409  */
410 struct sysfs_root_device *sysfs_open_root_device(const char *name)
411 {
412         struct sysfs_root_device *root = NULL;
413         char rootpath[SYSFS_PATH_MAX];
414
415         if (name == NULL) {
416                 errno = EINVAL;
417                 return NULL;
418         }
419
420         memset(rootpath, 0, SYSFS_PATH_MAX);
421         if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) {
422                 dprintf ("Sysfs not supported on this system\n");
423                 return NULL;
424         }
425
426         safestrcat(rootpath, "/");
427         safestrcat(rootpath, SYSFS_DEVICES_NAME);
428         safestrcat(rootpath, "/");
429         safestrcat(rootpath, name);
430         if ((sysfs_path_is_dir(rootpath)) != 0) {
431                 errno = EINVAL;
432                 dprintf("Invalid root device: %s\n", name);
433                 return NULL;
434         }
435         root = (struct sysfs_root_device *)calloc
436                                         (1, sizeof(struct sysfs_root_device));
437         if (root == NULL) {
438                 dprintf("calloc failure\n");
439                 return NULL;
440         }
441         safestrcpy(root->name, name);
442         safestrcpy(root->path, rootpath);
443         if ((sysfs_remove_trailing_slash(root->path)) != 0) {
444                 dprintf("Invalid path to root device %s\n", root->path);
445                 sysfs_close_root_device(root);
446                 return NULL;
447         }
448         return root;
449 }
450
451 /**
452  * sysfs_get_device_attributes: returns a dlist of attributes corresponding to
453  *      the specific device
454  * @device: struct sysfs_device * for which attributes are to be returned
455  */
456 struct dlist *sysfs_get_device_attributes(struct sysfs_device *device)
457 {
458         if (device == NULL) {
459                 errno = EINVAL;
460                 return NULL;
461         }
462
463         if (device->directory == NULL) {
464                 device->directory = sysfs_open_directory(device->path);
465                 if (device->directory == NULL) 
466                         return NULL;
467         }
468         if (device->directory->attributes == NULL) {
469                 if ((sysfs_read_dir_attributes(device->directory)) != 0)
470                         return NULL;
471         }
472         return (device->directory->attributes);
473 }
474
475 /**
476  * sysfs_refresh_device_attributes: refreshes the device's list of attributes
477  * @device: sysfs_device whose attributes to refresh
478  *  
479  * NOTE: Upon return, prior references to sysfs_attributes for this device
480  *              _may_ not be valid
481  *
482  * Returns list of attributes on success and NULL on failure
483  */
484 struct dlist *sysfs_refresh_device_attributes(struct sysfs_device *device)
485 {
486         if (device == NULL) {
487                 errno = EINVAL;
488                 return NULL;
489         }
490
491         if (device->directory == NULL)
492                 return (sysfs_get_device_attributes(device));
493
494         if ((sysfs_refresh_dir_attributes(device->directory)) != 0) {
495                 dprintf("Error refreshing device attributes\n");
496                 return NULL;
497         }
498
499         return (device->directory->attributes);
500 }
501
502 /**
503  * sysfs_get_device_attr: searches dev's attributes by name
504  * @dev: device to look through
505  * @name: attribute name to get
506  * returns sysfs_attribute reference with success or NULL with error.
507  */
508 struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
509                                                 const char *name)
510 {
511         struct dlist *attrlist = NULL;
512
513         if (dev == NULL || name == NULL) {
514                 errno = EINVAL;
515                 return NULL;
516         }
517         
518         attrlist = sysfs_get_device_attributes(dev);
519         if (attrlist == NULL)
520                 return NULL;
521
522         return sysfs_get_directory_attribute(dev->directory, (char *)name);
523 }
524
525 /**
526  * get_device_absolute_path: looks up the bus the device is on, gets 
527  *              absolute path to the device
528  * @device: device for which path is needed
529  * @path: buffer to store absolute path
530  * @psize: size of "path"
531  * Returns 0 on success -1 on failure
532  */
533 static int get_device_absolute_path(const char *device, const char *bus, 
534                                 char *path, size_t psize)
535 {
536         char bus_path[SYSFS_PATH_MAX];
537
538         if (device == NULL || path == NULL) {
539                 errno = EINVAL;
540                 return -1;
541         }
542
543         memset(bus_path, 0, SYSFS_PATH_MAX);
544         if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) {
545                 dprintf ("Sysfs not supported on this system\n");
546                 return -1;
547         }
548         safestrcat(bus_path, "/");
549         safestrcat(bus_path, SYSFS_BUS_NAME);
550         safestrcat(bus_path, "/");
551         safestrcat(bus_path, bus);
552         safestrcat(bus_path, "/");
553         safestrcat(bus_path, SYSFS_DEVICES_NAME);
554         safestrcat(bus_path, "/");
555         safestrcat(bus_path, device);
556         /*
557          * We now are at /sys/bus/"bus_name"/devices/"device" which is a link.
558          * Now read this link to reach to the device.
559          */ 
560         if ((sysfs_get_link(bus_path, path, psize)) != 0) {
561                 dprintf("Error getting to device %s\n", device);
562                 return -1;
563         }
564         return 0;
565 }
566
567 /**
568  * sysfs_open_device: open a device by id (use the "bus" subsystem)
569  * @bus: bus the device belongs to
570  * @bus_id: bus_id of the device to open - has to be the "bus_id" in 
571  *              /sys/bus/xxx/devices
572  * returns struct sysfs_device if found, NULL otherwise
573  * NOTE: 
574  * 1. Use sysfs_close_device to close the device
575  * 2. Bus the device is on must be supplied
576  *      Use sysfs_find_device_bus to get the bus name
577  */
578 struct sysfs_device *sysfs_open_device(const char *bus, const char *bus_id)
579 {
580         char sysfs_path[SYSFS_PATH_MAX];
581         struct sysfs_device *device = NULL;
582
583         if (bus_id == NULL || bus == NULL) {
584                 errno = EINVAL;
585                 return NULL;
586         }
587         memset(sysfs_path, 0, SYSFS_PATH_MAX);
588         if ((get_device_absolute_path(bus_id, bus, sysfs_path, 
589                                                 SYSFS_PATH_MAX)) != 0) {
590                 dprintf("Error getting to device %s\n", bus_id);
591                 return NULL;
592         }
593         
594         device = sysfs_open_device_path(sysfs_path);
595         if (device == NULL) {
596                 dprintf("Error opening device %s\n", bus_id);
597                 return NULL;
598         }
599
600         return device;
601 }
602
603 /**
604  * sysfs_get_device_parent: opens up given device's parent and returns a 
605  *      reference to its sysfs_device
606  * @dev: sysfs_device whose parent is requested
607  * Returns sysfs_device of the parent on success and NULL on failure
608  */
609 struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev)
610 {
611         char ppath[SYSFS_PATH_MAX], *tmp = NULL;
612
613         if (dev == NULL) {
614                 errno = EINVAL;
615                 return NULL;
616         }
617
618         if (dev->parent != NULL)
619                 return (dev->parent);
620
621         memset(ppath, 0, SYSFS_PATH_MAX);
622         safestrcpy(ppath, dev->path);
623         tmp = strrchr(ppath, '/');
624         if (tmp == NULL) {
625                 dprintf("Invalid path to device %s\n", ppath);
626                 return NULL;
627         }
628         if (*(tmp + 1) == '\0') {
629                 *tmp = '\0';
630                 tmp = strrchr(tmp, '/');
631                 if (tmp == NULL) {
632                         dprintf("Invalid path to device %s\n", ppath);
633                         return NULL;
634                 }
635         }
636         *tmp = '\0';
637         
638         /*
639          * All "devices" have the "detach_state" attribute - validate here
640          */
641         safestrcat(ppath, "/detach_state");
642         if ((sysfs_path_is_file(ppath)) != 0) {
643                 dprintf("Device at %s does not have a parent\n", dev->path);
644                 return NULL;
645         }
646         tmp = strrchr(ppath, '/');
647         *tmp = '\0';
648         dev->parent = sysfs_open_device_path(ppath);
649         if (dev->parent == NULL) {
650                 dprintf("Error opening device %s's parent at %s\n", 
651                                         dev->bus_id, ppath);
652                 return NULL;
653         }
654         return (dev->parent);
655 }
656
657 /*
658  * sysfs_open_device_attr: open the given device's attribute
659  * @bus: Bus on which to look
660  * @dev_id: device for which attribute is required
661  * @attrname: name of the attribute to look for
662  * Returns struct sysfs_attribute on success and NULL on failure
663  * 
664  * NOTE:
665  *      A call to sysfs_close_attribute() is required to close
666  *      the attribute returned and free memory. 
667  */
668 struct sysfs_attribute *sysfs_open_device_attr(const char *bus,
669                 const char *bus_id, const char *attrib)
670 {
671         struct sysfs_attribute *attribute = NULL;
672         char devpath[SYSFS_PATH_MAX];
673         
674         if (bus == NULL || bus_id == NULL || attrib == NULL) {
675                 errno = EINVAL;
676                 return NULL;
677         }
678
679         memset(devpath, 0, SYSFS_PATH_MAX);
680         if ((get_device_absolute_path(bus_id, bus, devpath, 
681                                         SYSFS_PATH_MAX)) != 0) {
682                 dprintf("Error getting to device %s\n", bus_id);
683                 return NULL;
684         }
685         safestrcat(devpath, "/");
686         safestrcat(devpath, attrib);
687         attribute = sysfs_open_attribute(devpath);
688         if (attribute == NULL) {
689                 dprintf("Error opening attribute %s for device %s\n",
690                                 attrib, bus_id);
691                 return NULL;
692         }
693         if ((sysfs_read_attribute(attribute)) != 0) {
694                 dprintf("Error reading attribute %s for device %s\n",
695                                 attrib, bus_id);
696                 sysfs_close_attribute(attribute);
697                 return NULL;
698         }
699         return attribute;
700 }
701