chiark / gitweb /
use size definitions from libudev
[elogind.git] / udev / udev_sysfs.c
1 /*
2  * Copyright (C) 2005-2008 Kay Sievers <kay.sievers@vrfy.org>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <stddef.h>
21 #include <unistd.h>
22 #include <string.h>
23 #include <fcntl.h>
24 #include <ctype.h>
25 #include <errno.h>
26 #include <sys/stat.h>
27
28 #include "udev.h"
29
30 /* device cache */
31 static LIST_HEAD(dev_list);
32
33 /* attribute value cache */
34 static LIST_HEAD(attr_list);
35
36 struct sysfs_attr {
37         struct list_head node;
38         char path[UTIL_PATH_SIZE];
39         char *value;                    /* points to value_local if value is cached */
40         char value_local[UTIL_NAME_SIZE];
41 };
42
43 int sysfs_init(void)
44 {
45         INIT_LIST_HEAD(&dev_list);
46         INIT_LIST_HEAD(&attr_list);
47         return 0;
48 }
49
50 void sysfs_cleanup(void)
51 {
52         struct sysfs_attr *attr_loop;
53         struct sysfs_attr *attr_temp;
54         struct sysfs_device *dev_loop;
55         struct sysfs_device *dev_temp;
56
57         list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
58                 list_del(&attr_loop->node);
59                 free(attr_loop);
60         }
61
62         list_for_each_entry_safe(dev_loop, dev_temp, &dev_list, node) {
63                 list_del(&dev_loop->node);
64                 free(dev_loop);
65         }
66 }
67
68 void sysfs_device_set_values(struct udev *udev,
69                              struct sysfs_device *dev, const char *devpath,
70                              const char *subsystem, const char *driver)
71 {
72         char *pos;
73
74         util_strlcpy(dev->devpath, devpath, sizeof(dev->devpath));
75         if (subsystem != NULL)
76                 util_strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
77         if (driver != NULL)
78                 util_strlcpy(dev->driver, driver, sizeof(dev->driver));
79
80         /* set kernel name */
81         pos = strrchr(dev->devpath, '/');
82         if (pos == NULL)
83                 return;
84         util_strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel));
85         dbg(udev, "kernel='%s'\n", dev->kernel);
86
87         /* some devices have '!' in their name, change that to '/' */
88         pos = dev->kernel;
89         while (pos[0] != '\0') {
90                 if (pos[0] == '!')
91                         pos[0] = '/';
92                 pos++;
93         }
94
95         /* get kernel number */
96         pos = &dev->kernel[strlen(dev->kernel)];
97         while (isdigit(pos[-1]))
98                 pos--;
99         util_strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number));
100         dbg(udev, "kernel_number='%s'\n", dev->kernel_number);
101 }
102
103 struct sysfs_device *sysfs_device_get(struct udev *udev, const char *devpath)
104 {
105         char path[UTIL_PATH_SIZE];
106         char devpath_real[UTIL_PATH_SIZE];
107         struct sysfs_device *dev;
108         struct sysfs_device *dev_loop;
109         struct stat statbuf;
110         char link_path[UTIL_PATH_SIZE];
111         char link_target[UTIL_PATH_SIZE];
112         int len;
113         char *pos;
114
115         /* we handle only these devpathes */
116         if (devpath != NULL &&
117             strncmp(devpath, "/devices/", 9) != 0 &&
118             strncmp(devpath, "/subsystem/", 11) != 0 &&
119             strncmp(devpath, "/module/", 8) != 0 &&
120             strncmp(devpath, "/bus/", 5) != 0 &&
121             strncmp(devpath, "/class/", 7) != 0 &&
122             strncmp(devpath, "/block/", 7) != 0)
123                 return NULL;
124
125         dbg(udev, "open '%s'\n", devpath);
126         util_strlcpy(devpath_real, devpath, sizeof(devpath_real));
127         util_remove_trailing_chars(devpath_real, '/');
128         if (devpath[0] == '\0' )
129                 return NULL;
130
131         /* look for device already in cache (we never put an untranslated path in the cache) */
132         list_for_each_entry(dev_loop, &dev_list, node) {
133                 if (strcmp(dev_loop->devpath, devpath_real) == 0) {
134                         dbg(udev, "found in cache '%s'\n", dev_loop->devpath);
135                         return dev_loop;
136                 }
137         }
138
139         /* if we got a link, resolve it to the real device */
140         util_strlcpy(path, udev_get_sys_path(udev), sizeof(path));
141         util_strlcat(path, devpath_real, sizeof(path));
142         if (lstat(path, &statbuf) != 0) {
143                 dbg(udev, "stat '%s' failed: %s\n", path, strerror(errno));
144                 return NULL;
145         }
146         if (S_ISLNK(statbuf.st_mode)) {
147                 if (util_resolve_sys_link(udev, devpath_real, sizeof(devpath_real)) != 0)
148                         return NULL;
149
150                 /* now look for device in cache after path translation */
151                 list_for_each_entry(dev_loop, &dev_list, node) {
152                         if (strcmp(dev_loop->devpath, devpath_real) == 0) {
153                                 dbg(udev, "found in cache '%s'\n", dev_loop->devpath);
154                                 return dev_loop;
155                         }
156                 }
157         }
158
159         /* it is a new device */
160         dbg(udev, "new uncached device '%s'\n", devpath_real);
161         dev = malloc(sizeof(struct sysfs_device));
162         if (dev == NULL)
163                 return NULL;
164         memset(dev, 0x00, sizeof(struct sysfs_device));
165
166         sysfs_device_set_values(udev, dev, devpath_real, NULL, NULL);
167
168         /* get subsystem name */
169         util_strlcpy(link_path, udev_get_sys_path(udev), sizeof(link_path));
170         util_strlcat(link_path, dev->devpath, sizeof(link_path));
171         util_strlcat(link_path, "/subsystem", sizeof(link_path));
172         len = readlink(link_path, link_target, sizeof(link_target));
173         if (len > 0) {
174                 /* get subsystem from "subsystem" link */
175                 link_target[len] = '\0';
176                 dbg(udev, "subsystem link '%s' points to '%s'\n", link_path, link_target);
177                 pos = strrchr(link_target, '/');
178                 if (pos != NULL)
179                         util_strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
180         } else if (strstr(dev->devpath, "/drivers/") != NULL) {
181                 util_strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem));
182         } else if (strncmp(dev->devpath, "/module/", 8) == 0) {
183                 util_strlcpy(dev->subsystem, "module", sizeof(dev->subsystem));
184         } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) {
185                 pos = strrchr(dev->devpath, '/');
186                 if (pos == &dev->devpath[10])
187                         util_strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
188         } else if (strncmp(dev->devpath, "/class/", 7) == 0) {
189                 pos = strrchr(dev->devpath, '/');
190                 if (pos == &dev->devpath[6])
191                         util_strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
192         } else if (strncmp(dev->devpath, "/bus/", 5) == 0) {
193                 pos = strrchr(dev->devpath, '/');
194                 if (pos == &dev->devpath[4])
195                         util_strlcpy(dev->subsystem, "subsystem", sizeof(dev->subsystem));
196         }
197
198         /* get driver name */
199         util_strlcpy(link_path, udev_get_sys_path(udev), sizeof(link_path));
200         util_strlcat(link_path, dev->devpath, sizeof(link_path));
201         util_strlcat(link_path, "/driver", sizeof(link_path));
202         len = readlink(link_path, link_target, sizeof(link_target));
203         if (len > 0) {
204                 link_target[len] = '\0';
205                 dbg(udev, "driver link '%s' points to '%s'\n", link_path, link_target);
206                 pos = strrchr(link_target, '/');
207                 if (pos != NULL)
208                         util_strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
209         }
210
211         dbg(udev, "add to cache 'devpath=%s', subsystem='%s', driver='%s'\n", dev->devpath, dev->subsystem, dev->driver);
212         list_add(&dev->node, &dev_list);
213
214         return dev;
215 }
216
217 struct sysfs_device *sysfs_device_get_parent(struct udev *udev, struct sysfs_device *dev)
218 {
219         char parent_devpath[UTIL_PATH_SIZE];
220         char *pos;
221
222         dbg(udev, "open '%s'\n", dev->devpath);
223
224         /* look if we already know the parent */
225         if (dev->parent != NULL)
226                 return dev->parent;
227
228         util_strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
229         dbg(udev, "'%s'\n", parent_devpath);
230
231         /* strip last element */
232         pos = strrchr(parent_devpath, '/');
233         if (pos == NULL || pos == parent_devpath)
234                 return NULL;
235         pos[0] = '\0';
236
237         if (strncmp(parent_devpath, "/class", 6) == 0) {
238                 pos = strrchr(parent_devpath, '/');
239                 if (pos == &parent_devpath[6] || pos == parent_devpath) {
240                         dbg(udev, "/class top level, look for device link\n");
241                         goto device_link;
242                 }
243         }
244         if (strcmp(parent_devpath, "/block") == 0) {
245                 dbg(udev, "/block top level, look for device link\n");
246                 goto device_link;
247         }
248
249         /* are we at the top level? */
250         pos = strrchr(parent_devpath, '/');
251         if (pos == NULL || pos == parent_devpath)
252                 return NULL;
253
254         /* get parent and remember it */
255         dev->parent = sysfs_device_get(udev, parent_devpath);
256         return dev->parent;
257
258 device_link:
259         util_strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
260         util_strlcat(parent_devpath, "/device", sizeof(parent_devpath));
261         if (util_resolve_sys_link(udev, parent_devpath, sizeof(parent_devpath)) != 0)
262                 return NULL;
263
264         /* get parent and remember it */
265         dev->parent = sysfs_device_get(udev, parent_devpath);
266         return dev->parent;
267 }
268
269 struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct udev *udev, struct sysfs_device *dev, const char *subsystem)
270 {
271         struct sysfs_device *dev_parent;
272
273         dev_parent = sysfs_device_get_parent(udev, dev);
274         while (dev_parent != NULL) {
275                 if (strcmp(dev_parent->subsystem, subsystem) == 0)
276                         return dev_parent;
277                 dev_parent = sysfs_device_get_parent(udev, dev_parent);
278         }
279         return NULL;
280 }
281
282 char *sysfs_attr_get_value(struct udev *udev, const char *devpath, const char *attr_name)
283 {
284         char path_full[UTIL_PATH_SIZE];
285         const char *path;
286         char value[UTIL_NAME_SIZE];
287         struct sysfs_attr *attr_loop;
288         struct sysfs_attr *attr;
289         struct stat statbuf;
290         int fd;
291         ssize_t size;
292         size_t sysfs_len;
293
294         dbg(udev, "open '%s'/'%s'\n", devpath, attr_name);
295         sysfs_len = util_strlcpy(path_full, udev_get_sys_path(udev), sizeof(path_full));
296         if(sysfs_len >= sizeof(path_full))
297                 sysfs_len = sizeof(path_full) - 1;
298         path = &path_full[sysfs_len];
299         util_strlcat(path_full, devpath, sizeof(path_full));
300         util_strlcat(path_full, "/", sizeof(path_full));
301         util_strlcat(path_full, attr_name, sizeof(path_full));
302
303         /* look for attribute in cache */
304         list_for_each_entry(attr_loop, &attr_list, node) {
305                 if (strcmp(attr_loop->path, path) == 0) {
306                         dbg(udev, "found in cache '%s'\n", attr_loop->path);
307                         return attr_loop->value;
308                 }
309         }
310
311         /* store attribute in cache (also negatives are kept in cache) */
312         dbg(udev, "new uncached attribute '%s'\n", path_full);
313         attr = malloc(sizeof(struct sysfs_attr));
314         if (attr == NULL)
315                 return NULL;
316         memset(attr, 0x00, sizeof(struct sysfs_attr));
317         util_strlcpy(attr->path, path, sizeof(attr->path));
318         dbg(udev, "add to cache '%s'\n", path_full);
319         list_add(&attr->node, &attr_list);
320
321         if (lstat(path_full, &statbuf) != 0) {
322                 dbg(udev, "stat '%s' failed: %s\n", path_full, strerror(errno));
323                 goto out;
324         }
325
326         if (S_ISLNK(statbuf.st_mode)) {
327                 /* links return the last element of the target path */
328                 char link_target[UTIL_PATH_SIZE];
329                 int len;
330                 const char *pos;
331
332                 len = readlink(path_full, link_target, sizeof(link_target));
333                 if (len > 0) {
334                         link_target[len] = '\0';
335                         pos = strrchr(link_target, '/');
336                         if (pos != NULL) {
337                                 dbg(udev, "cache '%s' with link value '%s'\n", path_full, value);
338                                 util_strlcpy(attr->value_local, &pos[1], sizeof(attr->value_local));
339                                 attr->value = attr->value_local;
340                         }
341                 }
342                 goto out;
343         }
344
345         /* skip directories */
346         if (S_ISDIR(statbuf.st_mode))
347                 goto out;
348
349         /* skip non-readable files */
350         if ((statbuf.st_mode & S_IRUSR) == 0)
351                 goto out;
352
353         /* read attribute value */
354         fd = open(path_full, O_RDONLY);
355         if (fd < 0) {
356                 dbg(udev, "attribute '%s' can not be opened\n", path_full);
357                 goto out;
358         }
359         size = read(fd, value, sizeof(value));
360         close(fd);
361         if (size < 0)
362                 goto out;
363         if (size == sizeof(value))
364                 goto out;
365
366         /* got a valid value, store and return it */
367         value[size] = '\0';
368         util_remove_trailing_chars(value, '\n');
369         dbg(udev, "cache '%s' with attribute value '%s'\n", path_full, value);
370         util_strlcpy(attr->value_local, value, sizeof(attr->value_local));
371         attr->value = attr->value_local;
372
373 out:
374         return attr->value;
375 }
376
377 int sysfs_lookup_devpath_by_subsys_id(struct udev *udev, char *devpath_full, size_t len, const char *subsystem, const char *id)
378 {
379         size_t sysfs_len;
380         char path_full[UTIL_PATH_SIZE];
381         char *path;
382         struct stat statbuf;
383
384         sysfs_len = util_strlcpy(path_full, udev_get_sys_path(udev), sizeof(path_full));
385         path = &path_full[sysfs_len];
386
387         if (strcmp(subsystem, "subsystem") == 0) {
388                 util_strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
389                 util_strlcat(path, id, sizeof(path_full) - sysfs_len);
390                 if (stat(path_full, &statbuf) == 0)
391                         goto found;
392
393                 util_strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
394                 util_strlcat(path, id, sizeof(path_full) - sysfs_len);
395                 if (stat(path_full, &statbuf) == 0)
396                         goto found;
397                 goto out;
398
399                 util_strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
400                 util_strlcat(path, id, sizeof(path_full) - sysfs_len);
401                 if (stat(path_full, &statbuf) == 0)
402                         goto found;
403         }
404
405         if (strcmp(subsystem, "module") == 0) {
406                 util_strlcpy(path, "/module/", sizeof(path_full) - sysfs_len);
407                 util_strlcat(path, id, sizeof(path_full) - sysfs_len);
408                 if (stat(path_full, &statbuf) == 0)
409                         goto found;
410                 goto out;
411         }
412
413         if (strcmp(subsystem, "drivers") == 0) {
414                 char subsys[UTIL_NAME_SIZE];
415                 char *driver;
416
417                 util_strlcpy(subsys, id, sizeof(subsys));
418                 driver = strchr(subsys, ':');
419                 if (driver != NULL) {
420                         driver[0] = '\0';
421                         driver = &driver[1];
422                         util_strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
423                         util_strlcat(path, subsys, sizeof(path_full) - sysfs_len);
424                         util_strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
425                         util_strlcat(path, driver, sizeof(path_full) - sysfs_len);
426                         if (stat(path_full, &statbuf) == 0)
427                                 goto found;
428
429                         util_strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
430                         util_strlcat(path, subsys, sizeof(path_full) - sysfs_len);
431                         util_strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
432                         util_strlcat(path, driver, sizeof(path_full) - sysfs_len);
433                         if (stat(path_full, &statbuf) == 0)
434                                 goto found;
435                 }
436                 goto out;
437         }
438
439         util_strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
440         util_strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
441         util_strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
442         util_strlcat(path, id, sizeof(path_full) - sysfs_len);
443         if (stat(path_full, &statbuf) == 0)
444                 goto found;
445
446         util_strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
447         util_strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
448         util_strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
449         util_strlcat(path, id, sizeof(path_full) - sysfs_len);
450         if (stat(path_full, &statbuf) == 0)
451                 goto found;
452
453         util_strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
454         util_strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
455         util_strlcat(path, "/", sizeof(path_full) - sysfs_len);
456         util_strlcat(path, id, sizeof(path_full) - sysfs_len);
457         if (stat(path_full, &statbuf) == 0)
458                 goto found;
459 out:
460         return 0;
461 found:
462         if (S_ISLNK(statbuf.st_mode))
463                 util_resolve_sys_link(udev, path, sizeof(path_full) - sysfs_len);
464         util_strlcpy(devpath_full, path, len);
465         return 1;
466 }