chiark / gitweb /
82b54719ff7ef679bbce3dec09365e5f09ba125b
[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_device_bus: retrieves the bus name the device is on, checks path to
28  *      bus' link to make sure it has correct device.
29  * @dev: device to get busname.
30  * returns 0 with success and -1 with error.
31  */
32 static int get_device_bus(struct sysfs_device *dev)
33 {
34         unsigned char subsys[SYSFS_NAME_LEN], path[SYSFS_PATH_MAX];
35         unsigned char target[SYSFS_PATH_MAX], *bus = NULL, *c = NULL;
36         struct dlist *buslist = NULL;
37
38         if (dev == NULL) {
39                 errno = EINVAL;
40                 return -1;
41         }
42
43         memset(subsys, 0, SYSFS_NAME_LEN);
44         strcat(subsys, "/");
45         strcpy(subsys, SYSFS_BUS_NAME);  /* subsys = /bus */
46         buslist = sysfs_open_subsystem_list(subsys);
47         if (buslist != NULL) {
48                 dlist_for_each_data(buslist, bus, char) {
49                         memset(path, 0, SYSFS_PATH_MAX);
50                         strcpy(path, dev->path);
51                         c = strstr(path, "/devices");
52                         if (c == NULL) {
53                                 dprintf("Invalid path to device %s\n", path);
54                                 sysfs_close_list(buslist);
55                                 return -1;
56                         }
57                         *c = '\0';
58                         strcat(path, "/");
59                         strcat(path, SYSFS_BUS_NAME);
60                         strcat(path, "/");
61                         strcat(path, bus);
62                         strcat(path, "/");
63                         strcat(path, SYSFS_DEVICES_NAME);
64                         strcat(path, "/");
65                         strcat(path, dev->bus_id);
66                         if ((sysfs_path_is_link(path)) == 0) {
67                                 memset(target, 0, SYSFS_PATH_MAX);
68                                 if ((sysfs_get_link(path, target, 
69                                                         SYSFS_PATH_MAX)) != 0) {
70                                         dprintf("Error getting link target\n");
71                                         sysfs_close_list(buslist);
72                                         return -1;
73                                 }
74                                 if (!(strncmp(target, dev->path, 
75                                                         SYSFS_PATH_MAX))) {
76                                         strcpy(dev->bus, bus);
77                                         sysfs_close_list(buslist);
78                                         return 0;
79                                 }
80                         }
81                 }
82                 sysfs_close_list(buslist);
83         }
84         return -1;
85 }
86
87 /**
88  * sysfs_close_device_tree: closes every device in the supplied tree, 
89  *      closing children only.
90  * @devroot: device root of tree.
91  */
92 static void sysfs_close_device_tree(struct sysfs_device *devroot)
93 {
94         if (devroot != NULL) {
95                 if (devroot->children != NULL) {
96                         struct sysfs_device *child = NULL;
97
98                         dlist_for_each_data(devroot->children, child,
99                                         struct sysfs_device) {
100                                 sysfs_close_device_tree(child);
101                         }
102                 }
103                 sysfs_close_device(devroot);
104         }
105 }
106
107 /**
108  * sysfs_del_device: routine for dlist integration
109  */
110 static void sysfs_del_device(void *dev)
111 {
112         sysfs_close_device((struct sysfs_device *)dev);
113 }
114
115 /**
116  * sysfs_close_dev_tree: routine for dlist integration
117  */
118 static void sysfs_close_dev_tree(void *dev)
119 {
120         sysfs_close_device_tree((struct sysfs_device *)dev);
121 }
122
123 /**
124  * sysfs_close_device: closes and cleans up a device
125  * @dev = device to clean up
126  */
127 void sysfs_close_device(struct sysfs_device *dev)
128 {
129         if (dev != NULL) {
130                 if (dev->directory != NULL)
131                         sysfs_close_directory(dev->directory);
132                 if (dev->children != NULL && dev->children->count == 0)
133                         dlist_destroy(dev->children);
134                 free(dev);
135         }
136 }
137
138 /**
139  * alloc_device: allocates and initializes device structure
140  * returns struct sysfs_device
141  */
142 static struct sysfs_device *alloc_device(void)
143 {
144         return (struct sysfs_device *)calloc(1, sizeof(struct sysfs_device));
145 }
146
147 /**
148  * open_device_dir: opens up sysfs_directory for specific root dev
149  * @name: name of root
150  * returns struct sysfs_directory with success and NULL with error
151  */
152 static struct sysfs_directory *open_device_dir(const unsigned char *path)
153 {
154         struct sysfs_directory *rdir = NULL;
155
156         if (path == NULL) {
157                 errno = EINVAL;
158                 return NULL;
159         }
160
161         rdir = sysfs_open_directory(path);
162         if (rdir == NULL) {
163                 errno = EINVAL;
164                 dprintf ("Device %s not supported on this system\n", path);
165                 return NULL;
166         }
167         if ((sysfs_read_directory(rdir)) != 0) {
168                 dprintf ("Error reading device at dir %s\n", path);
169                 sysfs_close_directory(rdir);
170                 return NULL;
171         }
172         
173         return rdir;
174 }
175
176 /**
177  * sysfs_open_device: opens and populates device structure
178  * @path: path to device, this is the /sys/devices/ path
179  * returns sysfs_device structure with success or NULL with error
180  */
181 struct sysfs_device *sysfs_open_device(const unsigned char *path)
182 {
183         struct sysfs_device *dev = NULL;
184
185         if (path == NULL) {
186                 errno = EINVAL;
187                 return NULL;
188         }
189         if ((sysfs_path_is_dir(path)) != 0) {
190                 dprintf("Incorrect path to device: %s\n", path);
191                 return NULL;
192         }
193         dev = alloc_device();   
194         if (dev == NULL) {
195                 dprintf("Error allocating device at %s\n", path);
196                 return NULL;
197         }
198         if ((sysfs_get_name_from_path(path, dev->bus_id, 
199                                         SYSFS_NAME_LEN)) != 0) {
200                 errno = EINVAL;
201                 dprintf("Error getting device bus_id\n");
202                 sysfs_close_device(dev);
203                 return NULL;
204         }
205         strcpy(dev->path, path);
206         /* 
207          * The "name" attribute no longer exists... return the device's
208          * sysfs representation instead, in the "dev->name" field, which
209          * implies that the dev->name and dev->bus_id contain same data.
210          */
211         strncpy(dev->name, dev->bus_id, SYSFS_NAME_LEN);
212         
213         if (get_device_bus(dev) != 0)
214                 strcpy(dev->bus, SYSFS_UNKNOWN);
215
216         return dev;
217 }
218
219 /**
220  * sysfs_open_device_tree: opens root device and all of its children,
221  *      creating a tree of devices. Only opens children.
222  * @path: sysfs path to devices
223  * returns struct sysfs_device and its children with success or NULL with
224  *      error.
225  */
226 static struct sysfs_device *sysfs_open_device_tree(const unsigned char *path)
227 {
228         struct sysfs_device *rootdev = NULL, *new = NULL;
229         struct sysfs_directory *cur = NULL;
230
231         if (path == NULL) {
232                 errno = EINVAL;
233                 return NULL;
234         }
235         rootdev = sysfs_open_device(path);
236         if (rootdev == NULL) {
237                 dprintf("Error opening root device at %s\n", path);
238                 return NULL;
239         }
240         if (rootdev->directory == NULL) {
241                 rootdev->directory = open_device_dir(rootdev->path);
242                 if (rootdev->directory == NULL) 
243                         return NULL;
244         }
245         if (rootdev->directory->subdirs != NULL) {
246                 dlist_for_each_data(rootdev->directory->subdirs, cur,
247                                 struct sysfs_directory) {
248                         new = sysfs_open_device_tree(cur->path);
249                         if (new == NULL) {
250                                 dprintf("Error opening device tree at %s\n",
251                                         cur->path);
252                                 sysfs_close_device_tree(rootdev);
253                                 return NULL;
254                         }
255                         if (rootdev->children == NULL)
256                                 rootdev->children = dlist_new_with_delete
257                                         (sizeof(struct sysfs_device),
258                                         sysfs_del_device);
259                         dlist_unshift(rootdev->children, new);
260                 }
261         }
262
263         return rootdev;
264 }
265
266 /**
267  * sysfs_close_root_device: closes root and all devices
268  * @root: root device to close
269  */
270 void sysfs_close_root_device(struct sysfs_root_device *root)
271 {
272         if (root != NULL) {
273                 if (root->devices != NULL)
274                         dlist_destroy(root->devices);
275                 if (root->directory != NULL)
276                         sysfs_close_directory(root->directory);
277                 free(root);
278         }
279 }
280
281 /**
282  * sysfs_get_root_devices: opens up all the devices under this root device
283  * @root: root device to open devices for
284  * returns dlist of devices with success and NULL with error
285  */
286 struct dlist *sysfs_get_root_devices(struct sysfs_root_device *root)
287 {
288         struct sysfs_device *dev = NULL;
289         struct sysfs_directory *cur = NULL;
290
291         if (root == NULL) {
292                 errno = EINVAL;
293                 return NULL;
294         }
295         if (root->directory == NULL) {
296                 root->directory = open_device_dir(root->path);
297                 if (root->directory == NULL)
298                         return NULL;
299         }
300                 
301         if (root->directory->subdirs == NULL)
302                 return 0;
303
304         dlist_for_each_data(root->directory->subdirs, cur,
305                         struct sysfs_directory) {
306                 dev = sysfs_open_device_tree(cur->path);
307                 if (dev == NULL) {
308                         dprintf ("Error opening device at %s\n", cur->path);
309                         continue;
310                 }
311                 if (root->devices == NULL)
312                         root->devices = dlist_new_with_delete
313                                 (sizeof(struct sysfs_device), 
314                                 sysfs_close_dev_tree);
315                 dlist_unshift(root->devices, dev);
316         }
317
318         return root->devices;
319 }
320
321 /**
322  * sysfs_open_root_device: opens sysfs devices root and all of its
323  *      devices.
324  * @name: name of /sys/devices/root to open
325  * returns struct sysfs_root_device if success and NULL with error
326  */
327 struct sysfs_root_device *sysfs_open_root_device(const unsigned char *name)
328 {
329         struct sysfs_root_device *root = NULL;
330         unsigned char rootpath[SYSFS_PATH_MAX];
331
332         if (name == NULL) {
333                 errno = EINVAL;
334                 return NULL;
335         }
336
337         memset(rootpath, 0, SYSFS_PATH_MAX);
338         if (sysfs_get_mnt_path(rootpath, SYSFS_PATH_MAX) != 0) {
339                 dprintf ("Sysfs not supported on this system\n");
340                 return NULL;
341         }
342
343         if (sysfs_trailing_slash(rootpath) == 0)
344                 strcat(rootpath, "/");
345
346         strcat(rootpath, SYSFS_DEVICES_NAME);
347         strcat(rootpath, "/");
348         strcat(rootpath, name);
349         if ((sysfs_path_is_dir(rootpath)) != 0) {
350                 errno = EINVAL;
351                 dprintf("Invalid root device: %s\n", name);
352                 return NULL;
353         }
354         root = (struct sysfs_root_device *)calloc
355                                         (1, sizeof(struct sysfs_root_device));
356         if (root == NULL) {
357                 dprintf("calloc failure\n");
358                 return NULL;
359         }
360         strcpy(root->path, rootpath);
361         return root;
362 }
363
364 /**
365  * sysfs_get_device_attributes: returns a dlist of attributes corresponding to
366  *      the specific device
367  * @device: struct sysfs_device * for which attributes are to be returned
368  */
369 struct dlist *sysfs_get_device_attributes(struct sysfs_device *device)
370 {
371         if (device == NULL) 
372                 return NULL;
373
374         if (device->directory == NULL) {
375                 device->directory = sysfs_open_directory(device->path);
376                 if (device->directory == NULL) 
377                         return NULL;
378         }
379         if (device->directory->attributes == NULL) {
380                 if ((sysfs_read_dir_attributes(device->directory)) != 0)
381                         return NULL;
382         } else {
383                 if ((sysfs_path_is_dir(device->path)) != 0) {
384                         dprintf("Device at %s no longer exists", device->path);
385                         return NULL;
386                 }
387                 if ((sysfs_refresh_attributes
388                                 (device->directory->attributes)) != 0) {
389                         dprintf("Error refreshing device attributes\n");
390                         return NULL;
391                 }
392         }
393         return (device->directory->attributes);
394 }
395
396 /**
397  * sysfs_get_device_attr: searches dev's attributes by name
398  * @dev: device to look through
399  * @name: attribute name to get
400  * returns sysfs_attribute reference with success or NULL with error.
401  */
402 struct sysfs_attribute *sysfs_get_device_attr(struct sysfs_device *dev,
403                                                 const unsigned char *name)
404 {
405         struct sysfs_attribute *cur = NULL;
406         struct dlist *attrlist = NULL;
407
408         if (dev == NULL || name == NULL) {
409                 errno = EINVAL;
410                 return NULL;
411         }
412
413         attrlist = sysfs_get_device_attributes(dev);
414         if (attrlist == NULL)
415                 return NULL;
416
417         cur = sysfs_get_directory_attribute(dev->directory, 
418                         (unsigned char *)name);
419         if (cur != NULL)
420                 return cur;
421
422         return NULL;
423 }
424
425 /**
426  * get_device_absolute_path: looks up the bus the device is on, gets 
427  *              absolute path to the device
428  * @device: device for which path is needed
429  * @path: buffer to store absolute path
430  * @psize: size of "path"
431  * Returns 0 on success -1 on failure
432  */
433 static int get_device_absolute_path(const unsigned char *device,
434                 const unsigned char *bus, unsigned char *path, size_t psize)
435 {
436         unsigned char bus_path[SYSFS_PATH_MAX];
437
438         if (device == NULL || path == NULL) {
439                 errno = EINVAL;
440                 return -1;
441         }
442
443         memset(bus_path, 0, SYSFS_PATH_MAX);
444         if (sysfs_get_mnt_path(bus_path, SYSFS_PATH_MAX) != 0) {
445                 dprintf ("Sysfs not supported on this system\n");
446                 return -1;
447         }
448
449         if (sysfs_trailing_slash(bus_path) == 0)
450                 strcat(bus_path, "/");
451
452         strcat(bus_path, SYSFS_BUS_NAME);
453         strcat(bus_path, "/");
454         strcat(bus_path, bus);
455         strcat(bus_path, "/");
456         strcat(bus_path, SYSFS_DEVICES_NAME);
457         strcat(bus_path, "/");
458         strcat(bus_path, device);
459         /*
460          * We now are at /sys/bus/"bus_name"/devices/"device" which is a link.
461          * Now read this link to reach to the device.
462          */ 
463         if ((sysfs_get_link(bus_path, path, SYSFS_PATH_MAX)) != 0) {
464                 dprintf("Error getting to device %s\n", device);
465                 return -1;
466         }
467         return 0;
468 }
469
470 /**
471  * sysfs_open_device_by_id: open a device by id (use the "bus" subsystem)
472  * @bus_id: bus_id of the device to open - has to be the "bus_id" in 
473  *              /sys/bus/xxx/devices
474  * @bus: bus the device belongs to
475  * returns struct sysfs_device if found, NULL otherwise
476  * NOTE: 
477  * 1. Use sysfs_close_device to close the device
478  * 2. Bus the device is on must be supplied
479  *      Use sysfs_find_device_bus to get the bus name
480  */
481 struct sysfs_device *sysfs_open_device_by_id(const unsigned char *bus_id, 
482                                                 const unsigned char *bus)
483 {
484         char sysfs_path[SYSFS_PATH_MAX];
485         struct sysfs_device *device = NULL;
486
487         if (bus_id == NULL || bus == NULL) {
488                 errno = EINVAL;
489                 return NULL;
490         }
491         memset(sysfs_path, 0, SYSFS_PATH_MAX);
492         if ((get_device_absolute_path(bus_id, bus, sysfs_path, 
493                                                 SYSFS_PATH_MAX)) != 0) {
494                 dprintf("Error getting to device %s\n", bus_id);
495                 return NULL;
496         }
497         
498         device = sysfs_open_device(sysfs_path);
499         if (device == NULL) {
500                 dprintf("Error opening device %s\n", bus_id);
501                 return NULL;
502         }
503
504         return device;
505 }
506
507 /*
508  * sysfs_open_device_attr: open the given device's attribute
509  * @bus: Bus on which to look
510  * @dev_id: device for which attribute is required
511  * @attrname: name of the attribute to look for
512  * Returns struct sysfs_attribute on success and NULL on failure
513  * 
514  * NOTE:
515  *      A call to sysfs_close_attribute() is required to close
516  *      the attribute returned and free memory. 
517  */
518 struct sysfs_attribute *sysfs_open_device_attr(const unsigned char *bus,
519                 const unsigned char *bus_id, const unsigned char *attrib)
520 {
521         struct sysfs_attribute *attribute = NULL;
522         unsigned char devpath[SYSFS_PATH_MAX];
523         
524         if (bus == NULL || bus_id == NULL || attrib == NULL) {
525                 errno = EINVAL;
526                 return NULL;
527         }
528
529         memset(devpath, 0, SYSFS_PATH_MAX);
530         if ((get_device_absolute_path(bus_id, bus, devpath, 
531                                         SYSFS_PATH_MAX)) != 0) {
532                 dprintf("Error getting to device %s\n", bus_id);
533                 return NULL;
534         }
535         strcat(devpath, "/");
536         strcat(devpath, attrib);
537         attribute = sysfs_open_attribute(devpath);
538         if (attribute == NULL) {
539                 dprintf("Error opening attribute %s for device %s\n",
540                                 attrib, bus_id);
541                 return NULL;
542         }
543         if ((sysfs_read_attribute(attribute)) != 0) {
544                 dprintf("Error reading attribute %s for device %s\n",
545                                 attrib, bus_id);
546                 sysfs_close_attribute(attribute);
547                 return NULL;
548         }
549         return attribute;
550 }
551