chiark / gitweb /
libsysfs: remove brute-force "bus", "driver" searching for old kernels
[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-2005
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 
28  * Returns 0 on SUCCESS and -1 on error
29  */
30 static int get_dev_driver(struct sysfs_device *dev)
31 {
32         char path[SYSFS_PATH_MAX];
33         char devpath[SYSFS_PATH_MAX];
34
35         if (!dev) {
36                 errno = EINVAL;
37                 return -1;
38         }
39         memset(path, 0, SYSFS_PATH_MAX);
40         memset(devpath, 0, SYSFS_PATH_MAX);
41         safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
42         safestrcatmax(path, "/driver", SYSFS_PATH_MAX);
43         if (!sysfs_path_is_link(path)) {
44                 if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
45                         if (sysfs_get_name_from_path(devpath, 
46                                         dev->driver_name, SYSFS_NAME_LEN))
47                                 return -1;
48                 }
49                 return 0;
50         }
51         return -1;
52 }
53
54 /**
55  * sysfs_get_device_bus: retrieves the bus name the device is on, checks path 
56  *      to bus' link to make sure it has correct device.
57  * @dev: device to get busname.
58  * returns 0 with success and -1 with error.
59  */
60 int sysfs_get_device_bus(struct sysfs_device *dev)
61 {
62         char devpath[SYSFS_PATH_MAX];
63         char path[SYSFS_PATH_MAX];
64
65         if (!dev) {
66                 errno = EINVAL;
67                 return -1;
68         }
69
70         memset(path, 0, SYSFS_PATH_MAX);
71         memset(devpath, 0, SYSFS_PATH_MAX);
72         safestrcpymax(path, dev->path, SYSFS_PATH_MAX);
73         safestrcatmax(path, "/bus", SYSFS_PATH_MAX);
74         if (!sysfs_path_is_link(path)) {
75                 if (!sysfs_get_link(path, devpath, SYSFS_PATH_MAX)) {
76                         if (sysfs_get_name_from_path(devpath, 
77                                         dev->bus, SYSFS_NAME_LEN))
78                                 return -1;
79                 }
80                 return 0;
81         }
82         return -1;
83 }
84
85 /**
86  * sysfs_close_device_tree: closes every device in the supplied tree, 
87  *      closing children only.
88  * @devroot: device root of tree.
89  */
90 void sysfs_close_device_tree(struct sysfs_device *devroot)
91 {
92         if (devroot) {
93                 if (devroot->children) {
94                         struct sysfs_device *child = NULL;
95
96                         dlist_for_each_data(devroot->children, child,
97                                         struct sysfs_device) {
98                                 sysfs_close_device_tree(child);
99                         }
100                 }
101                 sysfs_close_device(devroot);
102         }
103 }
104
105 /**
106  * sysfs_close_device: closes and cleans up a device
107  * @dev = device to clean up
108  */
109 void sysfs_close_device(struct sysfs_device *dev)
110 {
111         if (dev) {
112                 if (dev->parent)
113                         sysfs_close_device(dev->parent);
114                 if (dev->children && dev->children->count)
115                         dlist_destroy(dev->children);
116                 if (dev->attrlist)
117                         dlist_destroy(dev->attrlist);
118                 free(dev);
119         }
120 }
121
122 /**
123  * alloc_device: allocates and initializes device structure
124  * returns struct sysfs_device
125  */
126 static struct sysfs_device *alloc_device(void)
127 {
128         return (struct sysfs_device *) calloc(1, sizeof(struct sysfs_device));
129 }
130
131 /**
132  * sysfs_open_device_path: opens and populates device structure
133  * @path: path to device, this is the /sys/devices/ path
134  * returns sysfs_device structure with success or NULL with error
135  */
136 struct sysfs_device *sysfs_open_device_path(const char *path)
137 {
138         struct sysfs_device *dev;
139
140         if (!path) {
141                 errno = EINVAL;
142                 return NULL;
143         }
144         if (sysfs_path_is_dir(path)) {
145                 dprintf("Incorrect path to device: %s\n", path);
146                 return NULL;
147         }
148         dev = alloc_device();
149         if (!dev) {
150                 dprintf("Error allocating device at %s\n", path);
151                 return NULL;
152         }
153         if (sysfs_get_name_from_path(path, dev->bus_id, SYSFS_NAME_LEN)) {
154                 errno = EINVAL;
155                 dprintf("Error getting device bus_id\n");
156                 sysfs_close_device(dev);
157                 return NULL;
158         }
159         safestrcpy(dev->path, path);
160         if (sysfs_remove_trailing_slash(dev->path)) {
161                 dprintf("Invalid path to device %s\n", dev->path);
162                 sysfs_close_device(dev);
163                 return NULL;
164         }
165         /*
166          * The "name" attribute no longer exists... return the device's
167          * sysfs representation instead, in the "dev->name" field, which
168          * implies that the dev->name and dev->bus_id contain same data.
169          */
170         safestrcpy(dev->name, dev->bus_id);
171
172         if (sysfs_get_device_bus(dev))
173                 dprintf("Could not get device bus\n");
174
175         if (get_dev_driver(dev)) {
176                 dprintf("Could not get device %s's driver\n", dev->bus_id);
177                 safestrcpy(dev->driver_name, SYSFS_UNKNOWN);
178         }
179
180         return dev;
181 }
182
183 /**
184  * sysfs_get_device_attr: searches dev's attributes by name
185  * @dev: device to look through
186  * @name: attribute name to get
187  * returns sysfs_attribute reference with success or NULL with error.
188  */
189 struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
190                                                 const char *name)
191 {
192         if (!dev || !name) {
193                 errno = EINVAL;
194                 return NULL;
195         }
196         return get_attribute(dev, (char *)name);
197 }
198
199 /**
200  * sysfs_get_device_attributes: gets list of device attributes
201  * @dev: device whose attributes list is needed
202  * returns dlist of attributes on success or NULL on error
203  */
204 struct dlist *sysfs_get_device_attributes(struct sysfs_device *dev)
205 {
206         if (!dev) {
207                 errno = EINVAL;
208                 return NULL;
209         }
210         return get_attributes_list(dev);
211 }
212
213 /**
214  * get_device_absolute_path: looks up the bus the device is on, gets 
215  *              absolute path to the device
216  * @device: device for which path is needed
217  * @path: buffer to store absolute path
218  * @psize: size of "path"
219  * Returns 0 on success -1 on failure
220  */
221 static int get_device_absolute_path(const char *device, const char *bus, 
222                                 char *path, size_t psize)
223 {
224         char bus_path[SYSFS_PATH_MAX];
225
226         if (!device || !path) {
227                 errno = EINVAL;
228                 return -1;
229         }
230
231         memset(bus_path, 0, SYSFS_PATH_MAX);
232         if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX)) {
233                 dprintf ("Sysfs not supported on this system\n");
234                 return -1;
235         }
236         safestrcat(bus_path, "/");
237         safestrcat(bus_path, SYSFS_BUS_NAME);
238         safestrcat(bus_path, "/");
239         safestrcat(bus_path, bus);
240         safestrcat(bus_path, "/");
241         safestrcat(bus_path, SYSFS_DEVICES_NAME);
242         safestrcat(bus_path, "/");
243         safestrcat(bus_path, device);
244         /*
245          * We now are at /sys/bus/"bus_name"/devices/"device" which is a link.
246          * Now read this link to reach to the device.
247          */ 
248         if (sysfs_get_link(bus_path, path, psize)) {
249                 dprintf("Error getting to device %s\n", device);
250                 return -1;
251         }
252         return 0;
253 }
254
255 /**
256  * sysfs_open_device: open a device by id (use the "bus" subsystem)
257  * @bus: bus the device belongs to
258  * @bus_id: bus_id of the device to open - has to be the "bus_id" in 
259  *              /sys/bus/xxx/devices
260  * returns struct sysfs_device if found, NULL otherwise
261  * NOTE: 
262  * 1. Use sysfs_close_device to close the device
263  * 2. Bus the device is on must be supplied
264  *      Use sysfs_find_device_bus to get the bus name
265  */
266 struct sysfs_device *sysfs_open_device(const char *bus, const char *bus_id)
267 {
268         char sysfs_path[SYSFS_PATH_MAX];
269         struct sysfs_device *device;
270
271         if (!bus_id || !bus) {
272                 errno = EINVAL;
273                 return NULL;
274         }
275         memset(sysfs_path, 0, SYSFS_PATH_MAX);
276         if (get_device_absolute_path(bus_id, bus, sysfs_path, 
277                                 SYSFS_PATH_MAX)) {
278                 dprintf("Error getting to device %s\n", bus_id);
279                 return NULL;
280         }
281         
282         device = sysfs_open_device_path(sysfs_path);
283         if (!device) {
284                 dprintf("Error opening device %s\n", bus_id);
285                 return NULL;
286         }
287         return device;
288 }
289
290 /**
291  * sysfs_get_device_parent: opens up given device's parent and returns a 
292  *      reference to its sysfs_device
293  * @dev: sysfs_device whose parent is requested
294  * Returns sysfs_device of the parent on success and NULL on failure
295  */
296 struct sysfs_device *sysfs_get_device_parent(struct sysfs_device *dev)
297 {
298         char ppath[SYSFS_PATH_MAX], dpath[SYSFS_PATH_MAX], *tmp;
299
300         if (!dev) {
301                 errno = EINVAL;
302                 return NULL;
303         }
304
305         if (dev->parent)
306                 return (dev->parent);
307
308         memset(ppath, 0, SYSFS_PATH_MAX);
309         memset(dpath, 0, SYSFS_PATH_MAX);
310         safestrcpy(ppath, dev->path);
311         tmp = strrchr(ppath, '/');
312         if (!tmp) {
313                 dprintf("Invalid path to device %s\n", ppath);
314                 return NULL;
315         }
316         if (*(tmp + 1) == '\0') {
317                 *tmp = '\0';
318                 tmp = strrchr(tmp, '/');
319                 if (tmp == NULL) {
320                         dprintf("Invalid path to device %s\n", ppath);
321                         return NULL;
322                 }
323         }
324         *tmp = '\0';
325
326         /* Make sure we're not at the top of the device tree */
327         sysfs_get_mnt_path(dpath, SYSFS_PATH_MAX);
328         safestrcat(dpath, "/" SYSFS_DEVICES_NAME);
329         if (strcmp(dpath, ppath) == 0) {
330                 dprintf("Device at %s does not have a parent\n", dev->path);
331                 return NULL;
332         }
333
334         dev->parent = sysfs_open_device_path(ppath);
335         if (!dev->parent) {
336                 dprintf("Error opening device %s's parent at %s\n", 
337                                         dev->bus_id, ppath);
338                 return NULL;
339         }
340         return (dev->parent);
341 }