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