efbe7c1a2f09e0067fa7d8518e0d1f4eb6da1219
[multipath-tools/.git] / libmultipath / sysfs.c
1 /*
2  * Copyright (C) 2005-2006 Kay Sievers <kay.sievers@vrfy.org>
3  *
4  *      This program is free software; you can redistribute it and/or modify it
5  *      under the terms of the GNU General Public License as published by the
6  *      Free Software Foundation version 2 of the License.
7  *
8  *      This program is distributed in the hope that it will be useful, but
9  *      WITHOUT ANY WARRANTY; without even the implied warranty of
10  *      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  *      General Public License for more details.
12  *
13  *      You should have received a copy of the GNU General Public License along
14  *      with this program; if not, write to the Free Software Foundation, Inc.,
15  *      51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
16  *
17  */
18
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stddef.h>
23 #include <unistd.h>
24 #include <fcntl.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <sys/stat.h>
28 #include <string.h>
29
30 #include "checkers.h"
31 #include "vector.h"
32 #include "structs.h"
33 #include "sysfs.h"
34 #include "list.h"
35 #include "util.h"
36 #include "debug.h"
37
38 char sysfs_path[PATH_SIZE];
39
40 /* attribute value cache */
41 static LIST_HEAD(attr_list);
42 struct sysfs_attr {
43         struct list_head node;
44         char path[PATH_SIZE];
45         char *value;                    /* points to value_local if value is cached */
46         char value_local[NAME_SIZE];
47 };
48
49 /* list of sysfs devices */
50 static LIST_HEAD(sysfs_dev_list);
51 struct sysfs_dev {
52         struct list_head node;
53         struct sysfs_device dev;
54 };
55
56 int sysfs_init(char *path, size_t len)
57 {
58         if (path) {
59                 strlcpy(sysfs_path, path, len);
60                 remove_trailing_chars(sysfs_path, '/');
61         } else
62                 strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
63         dbg("sysfs_path='%s'", sysfs_path);
64
65         INIT_LIST_HEAD(&attr_list);
66         INIT_LIST_HEAD(&sysfs_dev_list);
67         return 0;
68 }
69
70 void sysfs_cleanup(void)
71 {
72         struct sysfs_attr *attr_loop;
73         struct sysfs_attr *attr_temp;
74
75         struct sysfs_dev *sysdev_loop;
76         struct sysfs_dev *sysdev_temp;
77
78         list_for_each_entry_safe(attr_loop, attr_temp, &attr_list, node) {
79                 list_del(&attr_loop->node);
80                 free(attr_loop);
81         }
82
83         list_for_each_entry_safe(sysdev_loop, sysdev_temp, &sysfs_dev_list, node) {
84                 list_del(&sysdev_loop->node);
85                 free(sysdev_loop);
86         }
87 }
88
89 void sysfs_device_set_values(struct sysfs_device *dev, const char *devpath,
90                              const char *subsystem, const char *driver)
91 {
92         char *pos;
93
94         strlcpy(dev->devpath, devpath, sizeof(dev->devpath));
95         if (subsystem != NULL)
96                 strlcpy(dev->subsystem, subsystem, sizeof(dev->subsystem));
97         if (driver != NULL)
98                 strlcpy(dev->driver, driver, sizeof(dev->driver));
99
100         /* set kernel name */
101         pos = strrchr(dev->devpath, '/');
102         if (pos == NULL)
103                 return;
104         strlcpy(dev->kernel, &pos[1], sizeof(dev->kernel));
105         dbg("kernel='%s'", dev->kernel);
106
107         /* some devices have '!' in their name, change that to '/' */
108         pos = dev->kernel;
109         while (pos[0] != '\0') {
110                 if (pos[0] == '!')
111                         pos[0] = '/';
112                 pos++;
113         }
114
115         /* get kernel number */
116         pos = &dev->kernel[strlen(dev->kernel)];
117         while (isdigit(pos[-1]))
118                 pos--;
119         strlcpy(dev->kernel_number, pos, sizeof(dev->kernel_number));
120         dbg("kernel_number='%s'", dev->kernel_number);
121 }
122
123 int sysfs_resolve_link(char *devpath, size_t size)
124 {
125         char link_path[PATH_SIZE];
126         char link_target[PATH_SIZE];
127         int len;
128         int i;
129         int back;
130
131         strlcpy(link_path, sysfs_path, sizeof(link_path));
132         strlcat(link_path, devpath, sizeof(link_path));
133         len = readlink(link_path, link_target, sizeof(link_target));
134         if (len <= 0)
135                 return -1;
136         link_target[len] = '\0';
137         dbg("path link '%s' points to '%s'", devpath, link_target);
138
139         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
140                 ;
141         dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
142         for (i = 0; i <= back; i++) {
143                 char *pos = strrchr(devpath, '/');
144
145                 if (pos == NULL)
146                         return -1;
147                 pos[0] = '\0';
148         }
149         dbg("after moving back '%s'", devpath);
150         strlcat(devpath, "/", size);
151         strlcat(devpath, &link_target[back * 3], size);
152         return 0;
153 }
154
155 struct sysfs_device *sysfs_device_get(const char *devpath)
156 {
157         char path[PATH_SIZE];
158         char devpath_real[PATH_SIZE];
159         struct sysfs_device *dev = NULL;
160         struct sysfs_dev *sysdev_loop, *sysdev;
161         struct stat statbuf;
162         char link_path[PATH_SIZE];
163         char link_target[PATH_SIZE];
164         int len;
165         char *pos;
166
167         /* we handle only these devpathes */
168         if (devpath != NULL &&
169             strncmp(devpath, "/devices/", 9) != 0 &&
170             strncmp(devpath, "/subsystem/", 11) != 0 &&
171             strncmp(devpath, "/module/", 8) != 0 &&
172             strncmp(devpath, "/bus/", 5) != 0 &&
173             strncmp(devpath, "/class/", 7) != 0 &&
174             strncmp(devpath, "/block/", 7) != 0) {
175                 dbg("invalid devpath '%s'", devpath);
176                 return NULL;
177         }
178
179         dbg("open '%s'", devpath);
180         strlcpy(devpath_real, devpath, sizeof(devpath_real));
181         remove_trailing_chars(devpath_real, '/');
182         if (devpath[0] == '\0' )
183                 return NULL;
184
185         /* if we got a link, resolve it to the real device */
186         strlcpy(path, sysfs_path, sizeof(path));
187         strlcat(path, devpath_real, sizeof(path));
188         if (lstat(path, &statbuf) != 0) {
189                 /* if stat fails look in the cache */
190                 dbg("stat '%s' failed: %s", path, strerror(errno));
191                 list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
192                         if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) {
193                                 dbg("found vanished dev in cache '%s'",
194                                     sysdev_loop->dev.devpath);
195                                 return &sysdev_loop->dev;
196                         }
197                 }
198                 return NULL;
199         }
200
201         if (S_ISLNK(statbuf.st_mode)) {
202                 if (sysfs_resolve_link(devpath_real, sizeof(devpath_real)) != 0)
203                         return NULL;
204         }
205
206         list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
207                 if (strcmp(sysdev_loop->dev.devpath, devpath_real) == 0) {
208                         dbg("found dev in cache '%s'", sysdev_loop->dev.devpath);
209                         dev = &sysdev_loop->dev;
210                 }
211         }
212
213         if(!dev) {
214                 /* it is a new device */
215                 dbg("new device '%s'", devpath_real);
216                 sysdev = malloc(sizeof(struct sysfs_dev));
217                 if (sysdev == NULL)
218                         return NULL;
219                 memset(sysdev, 0x00, sizeof(struct sysfs_dev));
220                 list_add(&sysdev->node, &sysfs_dev_list);
221                 dev = &sysdev->dev;
222         }
223
224         sysfs_device_set_values(dev, devpath_real, NULL, NULL);
225
226         /* get subsystem name */
227         strlcpy(link_path, sysfs_path, sizeof(link_path));
228         strlcat(link_path, dev->devpath, sizeof(link_path));
229         strlcat(link_path, "/subsystem", sizeof(link_path));
230         len = readlink(link_path, link_target, sizeof(link_target));
231         if (len > 0) {
232                 /* get subsystem from "subsystem" link */
233                 link_target[len] = '\0';
234                 dbg("subsystem link '%s' points to '%s'", link_path, link_target);
235                 pos = strrchr(link_target, '/');
236                 if (pos != NULL)
237                         strlcpy(dev->subsystem, &pos[1], sizeof(dev->subsystem));
238         } else if (strstr(dev->devpath, "/drivers/") != NULL) {
239                 strlcpy(dev->subsystem, "drivers", sizeof(dev->subsystem));
240         } else if (strncmp(dev->devpath, "/module/", 8) == 0) {
241                 strlcpy(dev->subsystem, "module", sizeof(dev->subsystem));
242         } else if (strncmp(dev->devpath, "/subsystem/", 11) == 0) {
243                 pos = strrchr(dev->devpath, '/');
244                 if (pos == &dev->devpath[10])
245                         strlcpy(dev->subsystem, "subsystem",
246                                 sizeof(dev->subsystem));
247         } else if (strncmp(dev->devpath, "/class/", 7) == 0) {
248                 pos = strrchr(dev->devpath, '/');
249                 if (pos == &dev->devpath[6])
250                         strlcpy(dev->subsystem, "subsystem",
251                                 sizeof(dev->subsystem));
252         } else if (strncmp(dev->devpath, "/bus/", 5) == 0) {
253                 pos = strrchr(dev->devpath, '/');
254                 if (pos == &dev->devpath[4])
255                         strlcpy(dev->subsystem, "subsystem",
256                                 sizeof(dev->subsystem));
257         }
258
259         /* get driver name */
260         strlcpy(link_path, sysfs_path, sizeof(link_path));
261         strlcat(link_path, dev->devpath, sizeof(link_path));
262         strlcat(link_path, "/driver", sizeof(link_path));
263         len = readlink(link_path, link_target, sizeof(link_target));
264         if (len > 0) {
265                 link_target[len] = '\0';
266                 dbg("driver link '%s' points to '%s'", link_path, link_target);
267                 pos = strrchr(link_target, '/');
268                 if (pos != NULL)
269                         strlcpy(dev->driver, &pos[1], sizeof(dev->driver));
270         }
271
272         return dev;
273 }
274
275 struct sysfs_device *sysfs_device_get_parent(struct sysfs_device *dev)
276 {
277         char parent_devpath[PATH_SIZE];
278         char *pos;
279
280         dbg("open '%s'", dev->devpath);
281
282         /* look if we already know the parent */
283         if (dev->parent != NULL)
284                 return dev->parent;
285
286         strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
287         dbg("'%s'", parent_devpath);
288
289         /* strip last element */
290         pos = strrchr(parent_devpath, '/');
291         if (pos == NULL || pos == parent_devpath)
292                 return NULL;
293         pos[0] = '\0';
294
295         if (strncmp(parent_devpath, "/class", 6) == 0) {
296                 pos = strrchr(parent_devpath, '/');
297                 if (pos == &parent_devpath[6] || pos == parent_devpath) {
298                         dbg("/class top level, look for device link");
299                         goto device_link;
300                 }
301         }
302         if (strcmp(parent_devpath, "/block") == 0) {
303                 dbg("/block top level, look for device link");
304                 goto device_link;
305         }
306
307         /* are we at the top level? */
308         pos = strrchr(parent_devpath, '/');
309         if (pos == NULL || pos == parent_devpath)
310                 return NULL;
311
312         /* get parent and remember it */
313         dev->parent = sysfs_device_get(parent_devpath);
314         return dev->parent;
315
316 device_link:
317         strlcpy(parent_devpath, dev->devpath, sizeof(parent_devpath));
318         strlcat(parent_devpath, "/device", sizeof(parent_devpath));
319         if (sysfs_resolve_link(parent_devpath, sizeof(parent_devpath)) != 0)
320                 return NULL;
321
322         /* get parent and remember it */
323         dev->parent = sysfs_device_get(parent_devpath);
324         return dev->parent;
325 }
326
327 struct sysfs_device *sysfs_device_get_parent_with_subsystem(struct sysfs_device *dev, const char *subsystem)
328 {
329         struct sysfs_device *dev_parent;
330
331         dev_parent = sysfs_device_get_parent(dev);
332         while (dev_parent != NULL) {
333                 if (strcmp(dev_parent->subsystem, subsystem) == 0)
334                         return dev_parent;
335                 dev_parent = sysfs_device_get_parent(dev_parent);
336         }
337         return NULL;
338 }
339
340 void sysfs_device_put(struct sysfs_device *dev)
341 {
342         struct sysfs_dev *sysdev_loop;
343
344         list_for_each_entry(sysdev_loop, &sysfs_dev_list, node) {
345                 if (&sysdev_loop->dev == dev) {
346                         dbg("removed dev '%s' from cache",
347                             sysdev_loop->dev.devpath);
348                         list_del(&sysdev_loop->node);
349                         free(sysdev_loop);
350                         return;
351                 }
352         }
353         dbg("dev '%s' not found in cache",
354             sysdev_loop->dev.devpath);
355
356         return;
357 }
358
359 int
360 sysfs_attr_set_value(const char *devpath, const char *attr_name,
361                      const char *value)
362 {
363         char path_full[PATH_SIZE];
364         int sysfs_len;
365         struct stat statbuf;
366         int fd, value_len, ret = -1;
367
368         dbg("open '%s'/'%s'", devpath, attr_name);
369         sysfs_len = snprintf(path_full, PATH_SIZE, "%s%s/%s", sysfs_path,
370                              devpath, attr_name);
371         if (sysfs_len >= PATH_SIZE || sysfs_len < 0) {
372                 if (sysfs_len < 0)
373                         dbg("cannot copy sysfs path %s%s/%s : %s", sysfs_path,
374                             devpath, attr_name, strerror(errno));
375                 else
376                         dbg("sysfs_path %s%s/%s too large", sysfs_path,
377                             devpath, attr_name);
378                 goto out;
379         }
380
381         if (stat(path_full, &statbuf) != 0) {
382                 dbg("stat '%s' failed: %s" path_full, strerror(errno));
383                 goto out;
384         }
385
386         /* skip directories */
387         if (S_ISDIR(statbuf.st_mode))
388                 goto out;
389
390         if ((statbuf.st_mode & S_IWUSR) == 0)
391                 goto out;
392
393         fd = open(path_full, O_WRONLY);
394         if (fd < 0) {
395                 dbg("attribute '%s' can not be opened: %s",
396                     path_full, strerror(errno));
397                 goto out;
398         }
399         value_len = strlen(value) + 1;
400         ret = write(fd, value, value_len);
401         if (ret == value_len)
402                 ret = 0;
403         else if (ret < 0)
404                 dbg("write to %s failed: %s", path_full, strerror(errno));
405         else {
406                 dbg("tried to write %d to %s. Wrote %d\n", value_len,
407                     path_full, ret);
408                 ret = -1;
409         }
410         close(fd);
411 out:
412         return ret;
413 }
414
415
416 char *sysfs_attr_get_value(const char *devpath, const char *attr_name)
417 {
418         char path_full[PATH_SIZE];
419         const char *path;
420         char value[NAME_SIZE];
421         struct sysfs_attr *attr_loop;
422         struct sysfs_attr *attr = NULL;
423         struct stat statbuf;
424         int fd;
425         ssize_t size;
426         size_t sysfs_len;
427
428         dbg("open '%s'/'%s'", devpath, attr_name);
429         sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
430         if(sysfs_len >= sizeof(path_full))
431                 sysfs_len = sizeof(path_full) - 1;
432         path = &path_full[sysfs_len];
433         strlcat(path_full, devpath, sizeof(path_full));
434         strlcat(path_full, "/", sizeof(path_full));
435         strlcat(path_full, attr_name, sizeof(path_full));
436
437         /* look for attribute in cache */
438         list_for_each_entry(attr_loop, &attr_list, node) {
439                 if (strcmp(attr_loop->path, path) == 0) {
440                         dbg("found in cache '%s'", attr_loop->path);
441                         attr = attr_loop;
442                 }
443         }
444         if (!attr) {
445                 /* store attribute in cache */
446                 dbg("new uncached attribute '%s'", path_full);
447                 attr = malloc(sizeof(struct sysfs_attr));
448                 if (attr == NULL)
449                         return NULL;
450                 memset(attr, 0x00, sizeof(struct sysfs_attr));
451                 strlcpy(attr->path, path, sizeof(attr->path));
452                 dbg("add to cache '%s'", path_full);
453                 list_add(&attr->node, &attr_list);
454         } else {
455                 /* clear old value */
456                 if(attr->value)
457                         memset(attr->value, 0x00, sizeof(attr->value));
458         }
459
460         if (lstat(path_full, &statbuf) != 0) {
461                 dbg("stat '%s' failed: %s", path_full, strerror(errno));
462                 goto out;
463         }
464
465         if (S_ISLNK(statbuf.st_mode)) {
466                 /* links return the last element of the target path */
467                 char link_target[PATH_SIZE];
468                 int len;
469                 const char *pos;
470
471                 len = readlink(path_full, link_target, sizeof(link_target));
472                 if (len > 0) {
473                         link_target[len] = '\0';
474                         pos = strrchr(link_target, '/');
475                         if (pos != NULL) {
476                                 dbg("cache '%s' with link value '%s'",
477                                     path_full, value);
478                                 strlcpy(attr->value_local, &pos[1],
479                                         sizeof(attr->value_local));
480                                 attr->value = attr->value_local;
481                         }
482                 }
483                 goto out;
484         }
485
486         /* skip directories */
487         if (S_ISDIR(statbuf.st_mode))
488                 goto out;
489
490         /* skip non-readable files */
491         if ((statbuf.st_mode & S_IRUSR) == 0)
492                 goto out;
493
494         /* read attribute value */
495         fd = open(path_full, O_RDONLY);
496         if (fd < 0) {
497                 dbg("attribute '%s' can not be opened: %s",
498                     path_full, strerror(errno));
499                 goto out;
500         }
501         size = read(fd, value, sizeof(value));
502         close(fd);
503         if (size < 0)
504                 goto out;
505         if (size == sizeof(value)) {
506                 dbg("overflow in attribute '%s', truncating", path_full);
507                 size--;
508         }
509
510         /* got a valid value, store and return it */
511         value[size] = '\0';
512         remove_trailing_chars(value, '\n');
513         dbg("cache '%s' with attribute value '%s'", path_full, value);
514         strlcpy(attr->value_local, value, sizeof(attr->value_local));
515         attr->value = attr->value_local;
516
517 out:
518         return attr && attr->value && strlen(attr->value) ? attr->value : NULL;
519 }
520
521 int sysfs_lookup_devpath_by_subsys_id(char *devpath_full, size_t len,
522                                       const char *subsystem, const char *id)
523 {
524         size_t sysfs_len;
525         char path_full[PATH_SIZE];
526         char *path;
527         struct stat statbuf;
528
529         sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
530         path = &path_full[sysfs_len];
531
532         if (strcmp(subsystem, "subsystem") == 0) {
533                 strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
534                 strlcat(path, id, sizeof(path_full) - sysfs_len);
535                 if (stat(path_full, &statbuf) == 0)
536                         goto found;
537
538                 strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
539                 strlcat(path, id, sizeof(path_full) - sysfs_len);
540                 if (stat(path_full, &statbuf) == 0)
541                         goto found;
542                 goto out;
543
544                 strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
545                 strlcat(path, id, sizeof(path_full) - sysfs_len);
546                 if (stat(path_full, &statbuf) == 0)
547                         goto found;
548         }
549
550         if (strcmp(subsystem, "module") == 0) {
551                 strlcpy(path, "/module/", sizeof(path_full) - sysfs_len);
552                 strlcat(path, id, sizeof(path_full) - sysfs_len);
553                 if (stat(path_full, &statbuf) == 0)
554                         goto found;
555                 goto out;
556         }
557
558         if (strcmp(subsystem, "drivers") == 0) {
559                 char subsys[NAME_SIZE];
560                 char *driver;
561
562                 strlcpy(subsys, id, sizeof(subsys));
563                 driver = strchr(subsys, ':');
564                 if (driver != NULL) {
565                         driver[0] = '\0';
566                         driver = &driver[1];
567                         strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
568                         strlcat(path, subsys, sizeof(path_full) - sysfs_len);
569                         strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
570                         strlcat(path, driver, sizeof(path_full) - sysfs_len);
571                         if (stat(path_full, &statbuf) == 0)
572                                 goto found;
573
574                         strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
575                         strlcat(path, subsys, sizeof(path_full) - sysfs_len);
576                         strlcat(path, "/drivers/", sizeof(path_full) - sysfs_len);
577                         strlcat(path, driver, sizeof(path_full) - sysfs_len);
578                         if (stat(path_full, &statbuf) == 0)
579                                 goto found;
580                 }
581                 goto out;
582         }
583
584         strlcpy(path, "/subsystem/", sizeof(path_full) - sysfs_len);
585         strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
586         strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
587         strlcat(path, id, sizeof(path_full) - sysfs_len);
588         if (stat(path_full, &statbuf) == 0)
589                 goto found;
590
591         strlcpy(path, "/bus/", sizeof(path_full) - sysfs_len);
592         strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
593         strlcat(path, "/devices/", sizeof(path_full) - sysfs_len);
594         strlcat(path, id, sizeof(path_full) - sysfs_len);
595         if (stat(path_full, &statbuf) == 0)
596                 goto found;
597
598         strlcpy(path, "/class/", sizeof(path_full) - sysfs_len);
599         strlcat(path, subsystem, sizeof(path_full) - sysfs_len);
600         strlcat(path, "/", sizeof(path_full) - sysfs_len);
601         strlcat(path, id, sizeof(path_full) - sysfs_len);
602         if (stat(path_full, &statbuf) == 0)
603                 goto found;
604 out:
605         return 0;
606 found:
607         if (S_ISLNK(statbuf.st_mode))
608                 sysfs_resolve_link(path, sizeof(path_full) - sysfs_len);
609         strlcpy(devpath_full, path, len);
610         return 1;
611 }