chiark / gitweb /
[PATCH] If a CALLOUT rule has a BUS id, then we must check to see if the device is...
[elogind.git] / libsysfs / sysfs_class.c
1 /*
2  * sysfs_class.c
3  *
4  * Generic class 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 static void sysfs_close_cls_dev(void *dev)
27 {
28         sysfs_close_class_device((struct sysfs_class_device *)dev);
29 }
30
31 /**
32  * class_name_equal: compares class_devices' name
33  * @a: class_name looking for
34  * @b: sysfs_class_device being compared
35  */
36 static int class_name_equal(void *a, void *b)
37 {
38         if (a == NULL || b == NULL)
39                 return 0;
40
41         if (strcmp(((unsigned char *)a), ((struct sysfs_class_device *)b)->name)
42                 == 0)
43                 return 1;
44
45         return 0;
46 }
47
48 /**
49  * sysfs_close_class_device: closes a single class device.
50  * @dev: class device to close.
51  */
52 void sysfs_close_class_device(struct sysfs_class_device *dev)
53 {
54         if (dev != NULL) {
55                 if (dev->directory != NULL)
56                         sysfs_close_directory(dev->directory);
57                 if (dev->sysdevice != NULL)
58                         sysfs_close_device(dev->sysdevice);
59                 if (dev->driver != NULL)
60                         sysfs_close_driver(dev->driver);
61                 if (dev->parent != NULL)
62                         sysfs_close_class_device(dev->parent);
63                 free(dev);
64         }
65 }
66
67 /**
68  * sysfs_close_class: close single class
69  * @class: class structure
70  */
71 void sysfs_close_class(struct sysfs_class *cls)
72 {
73         if (cls != NULL) {
74                 if (cls->directory != NULL)
75                         sysfs_close_directory(cls->directory);
76                 if (cls->devices != NULL) 
77                         dlist_destroy(cls->devices);
78                 free(cls);
79         }
80 }
81
82 /**
83  * alloc_class_device: mallocs and initializes new class device struct.
84  * returns sysfs_class_device or NULL.
85  */
86 static struct sysfs_class_device *alloc_class_device(void)
87 {
88         return (struct sysfs_class_device *)
89                                 calloc(1, sizeof(struct sysfs_class_device));
90 }
91
92 /**
93  * alloc_class: mallocs new class structure
94  * returns sysfs_class struct or NULL
95  */
96 static struct sysfs_class *alloc_class(void)
97 {
98         return (struct sysfs_class *)calloc(1, sizeof(struct sysfs_class));
99 }
100
101 /** 
102  * set_classdev_classname: Grabs classname from path
103  * @cdev: class device to set
104  * Returns nothing
105  */
106 static void set_classdev_classname(struct sysfs_class_device *cdev)
107 {
108         unsigned char *c = NULL, *e = NULL;
109         int count = 0;
110
111         c = strstr(cdev->path, SYSFS_CLASS_NAME);
112         if (c == NULL) {
113                 c = strstr(cdev->path, SYSFS_BLOCK_NAME);
114         } else {
115                 c = strstr(c, "/");
116         }
117
118         if (c == NULL)
119                 strcpy(cdev->classname, SYSFS_UNKNOWN);
120         else {
121                 if (*c == '/')
122                         c++;
123                 e = c;
124                 while (e != NULL && *e != '/' && *e != '\0') {
125                         e++;
126                         count++;
127                 }
128                 strncpy(cdev->classname, c, count);
129         }
130 }
131
132 /**
133  * sysfs_open_class_device_path: Opens and populates class device
134  * @path: path to class device.
135  * returns struct sysfs_class_device with success and NULL with error.
136  */
137 struct sysfs_class_device *sysfs_open_class_device_path
138                                         (const unsigned char *path)
139 {
140         struct sysfs_class_device *cdev = NULL;
141
142         if (path == NULL) {
143                 errno = EINVAL;
144                 return NULL;
145         }
146         if ((sysfs_path_is_dir(path)) != 0) {
147                 dprintf("%s is not a valid path to a class device\n", path);
148                 return NULL;
149         }
150         cdev = alloc_class_device();
151         if (cdev == NULL) {
152                 dprintf("calloc failed\n");
153                 return NULL;
154         }
155         if ((sysfs_get_name_from_path(path, cdev->name, SYSFS_NAME_LEN)) != 0) {
156                 errno = EINVAL;
157                 dprintf("Error getting class device name\n");
158                 sysfs_close_class_device(cdev);
159                 return NULL;
160         }
161
162         strcpy(cdev->path, path);
163         set_classdev_classname(cdev);
164
165         return cdev;
166 }
167
168 /**
169  * sysfs_get_class_devices: gets all devices for class
170  * @class: class to get devices for
171  * returns dlist of class_devices with success and NULL with error
172  */
173 struct dlist *sysfs_get_class_devices(struct sysfs_class *cls)
174 {
175         struct sysfs_class_device *dev = NULL;
176         struct sysfs_directory *cur = NULL;
177
178         if (cls == NULL) {
179                 errno = EINVAL;
180                 return NULL;
181         }
182         if (cls->directory == NULL) {
183                 cls->directory = sysfs_open_directory(cls->path);
184                 if (cls->directory == NULL) 
185                         return NULL;
186         }
187
188         if ((sysfs_read_dir_subdirs(cls->directory)) != 0) 
189                 return NULL;
190
191         if (cls->directory->subdirs != NULL) {
192                 dlist_for_each_data(cls->directory->subdirs, cur, 
193                                                 struct sysfs_directory) {
194                         dev = sysfs_open_class_device_path(cur->path);
195                         if (dev == NULL) {
196                                 dprintf("Error opening device at %s\n", 
197                                                                 cur->path);
198                                 continue;
199                         }
200                         if (cls->devices == NULL)
201                                 cls->devices = dlist_new_with_delete
202                                         (sizeof(struct sysfs_class_device),
203                                                         sysfs_close_cls_dev);
204                         dlist_unshift(cls->devices, dev);
205                 }
206         }
207         return cls->devices;
208 }
209
210 /**
211  * sysfs_open_class: opens specific class and all its devices on system
212  * returns sysfs_class structure with success or NULL with error.
213  */
214 struct sysfs_class *sysfs_open_class(const unsigned char *name)
215 {
216         struct sysfs_class *cls = NULL;
217         unsigned char classpath[SYSFS_PATH_MAX];
218
219         if (name == NULL) {
220                 errno = EINVAL;
221                 return NULL;
222         }
223
224         memset(classpath, 0, SYSFS_PATH_MAX);
225         if ((sysfs_get_mnt_path(classpath, SYSFS_PATH_MAX)) != 0) {
226                 dprintf("Sysfs not supported on this system\n");
227                 return NULL;
228         }
229         if (sysfs_trailing_slash(classpath) == 0)
230                 strcat(classpath, "/");
231
232         /* 
233          * We shall now treat "block" also as a class. Hence, check here
234          * if "name" is "block" and proceed accordingly
235          */
236         if (strcmp(name, SYSFS_BLOCK_NAME) == 0) {
237                 strcat(classpath, SYSFS_BLOCK_NAME);
238         } else {
239                 strcat(classpath, SYSFS_CLASS_NAME);
240                 strcat(classpath, "/");
241                 strcat(classpath, name);
242         }
243         if ((sysfs_path_is_dir(classpath)) != 0) {
244                 dprintf("Class %s not found on the system\n", name);
245                 return NULL;
246         }
247
248         cls = alloc_class();
249         if (cls == NULL) {
250                 dprintf("calloc failed\n");
251                 return NULL;
252         }
253         strcpy(cls->name, name);        
254         strcpy(cls->path, classpath);
255         
256         return cls;
257 }
258
259 /**
260  * sysfs_get_class_device: Get specific class device using the device's id
261  * @class: class to find device on
262  * @name: class name of the device
263  */ 
264 struct sysfs_class_device *sysfs_get_class_device(struct sysfs_class *class,
265                                         unsigned char *name)
266 {
267         struct dlist *devlist = NULL;
268         
269         if (class == NULL || name == NULL) {
270                 errno = EINVAL;
271                 return NULL;
272         }
273
274         if (class->devices == NULL) {
275                 class->devices = sysfs_get_class_devices(class);
276                 if (devlist == NULL) 
277                         return NULL;
278         }
279         return (struct sysfs_class_device *)dlist_find_custom(class->devices,
280                         name, class_name_equal);
281 }
282
283 /**
284  * sysfs_get_classdev_device: returns the sysfs_device corresponding to
285  *              sysfs_class_device, if present
286  * @clsdev: class device whose sysfs_device is required
287  * Returns sysfs_device on success, NULL on error or if device is not
288  * implemented
289  */ 
290 struct sysfs_device *sysfs_get_classdev_device
291                         (struct sysfs_class_device *clsdev)
292 {
293         struct sysfs_link *devlink = NULL;
294         
295         if (clsdev == NULL) {
296                 errno = EINVAL;
297                 return NULL;
298         }
299         
300         if (clsdev->sysdevice != NULL)
301                 return (clsdev->sysdevice);
302         
303         if (clsdev->directory == NULL) {
304                 clsdev->directory = sysfs_open_directory(clsdev->path);
305                 if (clsdev->directory == NULL)
306                         return NULL;
307         }
308         devlink = sysfs_get_directory_link(clsdev->directory, "device");
309         if (devlink == NULL) 
310                 return NULL;
311
312         clsdev->sysdevice = sysfs_open_device_path(devlink->target);
313         if (clsdev->sysdevice == NULL)
314                 return NULL;
315         if (clsdev->driver != NULL) 
316                 strcpy(clsdev->sysdevice->driver_name, clsdev->driver->name);
317
318         return (clsdev->sysdevice);
319 }
320                                 
321 /**
322  * sysfs_get_classdev_driver: returns the sysfs_driver corresponding to
323  *              sysfs_class_device, if present
324  * @clsdev: class device whose sysfs_device is required
325  * Returns sysfs_driver on success, NULL on error or if driver is not
326  * implemented
327  */ 
328 struct sysfs_driver *sysfs_get_classdev_driver
329                         (struct sysfs_class_device *clsdev)
330 {
331         struct sysfs_link *drvlink = NULL;
332         
333         if (clsdev == NULL) {
334                 errno = EINVAL;
335                 return NULL;
336         }
337         
338         if (clsdev->driver != NULL)
339                 return (clsdev->driver);
340         
341         if (clsdev->directory == NULL) {
342                 clsdev->directory = sysfs_open_directory(clsdev->path);
343                 if (clsdev->directory == NULL)
344                         return NULL;
345         }
346         drvlink = sysfs_get_directory_link(clsdev->directory, "driver");
347         if (drvlink != NULL) {
348                 clsdev->driver = sysfs_open_driver_path(drvlink->target);
349                 if (clsdev->driver == NULL)
350                         return NULL;
351                         
352         }
353         return (clsdev->driver);
354 }
355         
356 /* 
357  * get_blockdev_parent: Get the parent class device for a "block" subsystem 
358  *              device if present
359  * @clsdev: block subsystem class device whose parent needs to be found
360  * Returns 0 on success and 1 on error
361  */
362 static int get_blockdev_parent(struct sysfs_class_device *clsdev)
363 {
364         unsigned char parent_path[SYSFS_PATH_MAX], value[256], *c = NULL;
365         
366         memset(parent_path, 0, SYSFS_PATH_MAX);
367         strcpy(parent_path, clsdev->path);
368
369         c = strstr(parent_path, SYSFS_BLOCK_NAME);
370         if (c == NULL) {
371                 dprintf("Class device %s does not belong to BLOCK subsystem",
372                                 clsdev->name);
373                 return 1;
374         }
375         
376         c += strlen(SYSFS_BLOCK_NAME);
377         if (*c == '/')
378                 c++;
379         else
380                 goto errout;
381         
382         /* validate whether the given class device is a partition or not */ 
383         if ((strncmp(c, clsdev->name, strlen(clsdev->name))) == 0) {
384                 dprintf("%s not a partition\n", clsdev->name);
385                 return 1;
386         }
387         c = strchr(c, '/');
388         if (c == NULL) 
389                 goto errout;
390         *c = '\0';
391         
392         /* Now validate if the parent has the "dev" attribute */
393         memset(value, 0, 256);
394         strcat(parent_path, "/dev");
395         if ((sysfs_read_attribute_value(parent_path, value, 256)) != 0) {
396                 dprintf("Block device %s does not have a parent\n", 
397                                                         clsdev->name);
398                 return 1;
399         }
400                 
401         c = strrchr(parent_path, '/');
402         if (c == NULL)
403                 goto errout;
404
405         *c = '\0';
406         clsdev->parent = sysfs_open_class_device_path(parent_path);
407         if (clsdev->parent == NULL) {
408                 dprintf("Error opening the parent class device at %s\n", 
409                                                                 parent_path);
410                 return 1;
411         }
412         return 0;
413
414 errout:
415         dprintf("Invalid path %s\n", clsdev->path);
416         return 1;
417 }
418
419 /**
420  * sysfs_get_classdev_parent: Retrieves the parent of a class device. 
421  *      eg., when working with hda1, this function can be used to retrieve the
422  *              sysfs_class_device for hda
423  *              
424  * @clsdev: class device whose parent details are required.
425  * Returns sysfs_class_device of the parent on success, NULL on failure
426  */ 
427 struct sysfs_class_device *sysfs_get_classdev_parent
428                                 (struct sysfs_class_device *clsdev)
429 {
430         if (clsdev == NULL) {
431                 errno = EINVAL;
432                 return NULL;
433         }
434         if (clsdev->parent != NULL)
435                 return (clsdev->parent);
436         
437         /* 
438          * As of now, only block devices have a parent child heirarchy in sysfs
439          * We do not know, if, in the future, more classes will have a similar
440          * structure. Hence, we now call a specialized function for block and
441          * later we can add support functions for other subsystems as required.
442          */ 
443         if (!(strcmp(clsdev->classname, SYSFS_BLOCK_NAME))) {
444                 if ((get_blockdev_parent(clsdev)) == 0) 
445                         return (clsdev->parent);
446         }
447         return NULL;
448 }
449
450 /**
451  * get_classdev_path: given the class and a device in the class, return the
452  *              absolute path to the device
453  * @classname: name of the class
454  * @clsdev: the class device
455  * @path: buffer to return path
456  * @psize: size of "path"
457  * Returns 0 on SUCCESS or -1 on error
458  */
459 static int get_classdev_path(const unsigned char *classname, 
460                 const unsigned char *clsdev, unsigned char *path, size_t len)
461 {
462         if (classname == NULL || clsdev == NULL || path == NULL) {
463                 errno = EINVAL;
464                 return -1;
465         }
466         if (sysfs_get_mnt_path(path, len) != 0) {
467                 dprintf("Error getting sysfs mount path\n");
468                 return -1;
469         }
470
471         if (sysfs_trailing_slash(path) == 0)
472                 strcat(path, "/");
473
474         if (strcmp(classname, SYSFS_BLOCK_NAME) == 0) {
475                 strcat(path, SYSFS_BLOCK_NAME);
476         } else {
477                 strcat(path, SYSFS_CLASS_NAME);
478                 strcat(path, "/");
479                 strcat(path, classname);
480         }
481         strcat(path, "/");
482         strcat(path, clsdev);
483         return 0;
484 }
485
486 /**
487  * sysfs_open_class_device: Locates a specific class_device and returns it.
488  * Class_device must be closed using sysfs_close_class_device
489  * @classname: Class to search
490  * @name: name of the class_device
491  * 
492  * NOTE:
493  *      Call sysfs_close_class_device() to close the class device
494  */
495 struct sysfs_class_device *sysfs_open_class_device
496                 (const unsigned char *classname, const unsigned char *name)
497 {
498         unsigned char devpath[SYSFS_PATH_MAX];
499         struct sysfs_class_device *cdev = NULL;
500
501         if (classname == NULL || name == NULL) {
502                 errno = EINVAL;
503                 return NULL;
504         }
505         
506         memset(devpath, 0, SYSFS_PATH_MAX);
507         if ((get_classdev_path(classname, name, devpath, 
508                                         SYSFS_PATH_MAX)) != 0) {
509                 dprintf("Error getting to device %s on class %s\n",
510                                                         name, classname);
511                 return NULL;
512         }
513         
514         cdev = sysfs_open_class_device_path(devpath);
515         if (cdev == NULL) {
516                 dprintf("Error getting class device %s from class %s\n",
517                                 name, classname);
518                 return NULL;
519         }
520         return cdev;
521 }
522
523 /**
524  * sysfs_get_classdev_attributes: returns a dlist of attributes for
525  *      the requested class_device
526  * @cdev: sysfs_class_dev for which attributes are needed
527  * returns a dlist of attributes if exists, NULL otherwise
528  */
529 struct dlist *sysfs_get_classdev_attributes(struct sysfs_class_device *cdev)
530 {
531         if (cdev == NULL)
532                 return NULL;
533
534         if (cdev->directory == NULL) {
535                 cdev->directory = sysfs_open_directory(cdev->path);
536                 if (cdev->directory == NULL) 
537                         return NULL;
538         }
539         if (cdev->directory->attributes == NULL) {
540                 if ((sysfs_read_dir_attributes(cdev->directory)) != 0) {
541                         dprintf("Error reading attributes for directory %s\n",
542                                                         cdev->directory->path);
543                         return NULL;
544                 }
545         } else {
546                 if ((sysfs_path_is_dir(cdev->path)) != 0) {
547                         dprintf("Class device at %s no longer exists\n", 
548                                                         cdev->path);
549                         return NULL;
550                 }
551                 if ((sysfs_refresh_attributes
552                                         (cdev->directory->attributes)) != 0) {
553                         dprintf("Error refreshing classdev attributes\n");
554                         return NULL;
555                 }
556         }
557         return (cdev->directory->attributes);
558 }
559
560 /**
561  * sysfs_get_classdev_attr: searches class device's attributes by name
562  * @clsdev: class device to look through
563  * @name: attribute name to get
564  * returns sysfs_attribute reference with success or NULL with error
565  */
566 struct sysfs_attribute *sysfs_get_classdev_attr
567                 (struct sysfs_class_device *clsdev, const unsigned char *name)
568 {
569         struct sysfs_attribute *cur = NULL;
570         struct sysfs_directory *sdir = NULL;
571         struct dlist *attrlist = NULL;
572         
573         if (clsdev == NULL || name == NULL) {
574                 errno = EINVAL;
575                 return NULL;
576         }
577         
578         /* 
579          * First, see if it's in the current directory. Then look at 
580          * subdirs since class devices can have subdirs of attributes.
581          */ 
582         attrlist = sysfs_get_classdev_attributes(clsdev);
583         if (attrlist != NULL) {
584                 cur = sysfs_get_directory_attribute(clsdev->directory,
585                                                 (unsigned char *)name);
586                 if (cur != NULL)
587                         return cur;
588         }
589
590         if (clsdev->directory->subdirs == NULL) 
591                 if ((sysfs_read_dir_subdirs(clsdev->directory)) != 0 ||
592                     clsdev->directory->subdirs == NULL) 
593                         return NULL;
594
595         if (clsdev->directory->subdirs != NULL) {
596                 dlist_for_each_data(clsdev->directory->subdirs, sdir,
597                                                 struct sysfs_directory) {
598                         if ((sysfs_path_is_dir(sdir->path)) != 0) 
599                                 continue;
600                         if (sdir->attributes == NULL) {
601                                 cur = sysfs_get_directory_attribute(sdir,
602                                                         (unsigned char *)name);
603                         } else {
604                                 if ((sysfs_refresh_attributes
605                                                 (sdir->attributes)) == 0)
606                                 cur = sysfs_get_directory_attribute(sdir, 
607                                                         (unsigned char *)name);
608                         }
609                 }
610         }
611         return cur;
612 }
613
614 /**
615  * sysfs_open_classdev_attr: read an attribute for a given class device
616  * @classname: name of the class on which to look
617  * @dev: class device name for which the attribute has to be read
618  * @attrib: attribute to read
619  * Returns sysfs_attribute * on SUCCESS and NULL on error
620  * 
621  * NOTE:
622  *      A call to sysfs_close_attribute() is required to close the
623  *      attribute returned and to free memory
624  */
625 struct sysfs_attribute *sysfs_open_classdev_attr(const unsigned char *classname,
626                 const unsigned char *dev, const unsigned char *attrib)
627 {
628         struct sysfs_attribute *attribute = NULL;
629         unsigned char path[SYSFS_PATH_MAX];
630
631         if (classname == NULL || dev == NULL || attrib == NULL) {
632                 errno = EINVAL;
633                 return NULL;
634         }
635         memset(path, 0, SYSFS_PATH_MAX);
636         if ((get_classdev_path(classname, dev, path, SYSFS_PATH_MAX)) != 0) {
637                 dprintf("Error getting to device %s on class %s\n",
638                                                 dev, classname);
639                 return NULL;
640         }
641         strcat(path, "/");
642         strcat(path, attrib);
643         attribute = sysfs_open_attribute(path);
644         if (attribute == NULL) {
645                 dprintf("Error opening attribute %s on class device %s\n",
646                                 attrib, dev);
647                 return NULL;
648         }
649         if ((sysfs_read_attribute(attribute)) != 0) {
650                 dprintf("Error reading attribute %s for class device %s\n",
651                                 attrib, dev);
652                 sysfs_close_attribute(attribute);
653                 return NULL;
654         }
655         return attribute;
656 }
657