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