Read wwid from sysfs vpg_pg83 attribute
[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 #include <dirent.h>
30 #include <libudev.h>
31
32 #include "checkers.h"
33 #include "vector.h"
34 #include "structs.h"
35 #include "sysfs.h"
36 #include "list.h"
37 #include "util.h"
38 #include "debug.h"
39 #include "devmapper.h"
40
41 /*
42  * When we modify an attribute value we cannot rely on libudev for now,
43  * as libudev lacks the capability to update an attribute value.
44  * So for modified attributes we need to implement our own function.
45  */
46 ssize_t sysfs_attr_get_value(struct udev_device *dev, const char *attr_name,
47                              char * value, size_t value_len)
48 {
49         char devpath[PATH_SIZE];
50         struct stat statbuf;
51         int fd;
52         ssize_t size = -1;
53
54         if (!dev || !attr_name || !value)
55                 return 0;
56
57         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
58                  attr_name);
59         condlog(4, "open '%s'", devpath);
60         if (stat(devpath, &statbuf) != 0) {
61                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
62                 return -ENXIO;
63         }
64
65         /* skip directories */
66         if (S_ISDIR(statbuf.st_mode)) {
67                 condlog(4, "%s is a directory", devpath);
68                 return -EISDIR;
69         }
70
71         /* skip non-writeable files */
72         if ((statbuf.st_mode & S_IRUSR) == 0) {
73                 condlog(4, "%s is not readable", devpath);
74                 return -EPERM;
75         }
76
77         /* read attribute value */
78         fd = open(devpath, O_RDONLY);
79         if (fd < 0) {
80                 condlog(4, "attribute '%s' can not be opened: %s",
81                         devpath, strerror(errno));
82                 return -errno;
83         }
84         size = read(fd, value, value_len);
85         if (size < 0) {
86                 condlog(4, "read from %s failed: %s", devpath, strerror(errno));
87                 size = -errno;
88         } else if (size == value_len) {
89                 condlog(4, "overflow while reading from %s", devpath);
90                 size = 0;
91         } else {
92                 value[size] = '\0';
93         }
94
95         close(fd);
96         if (size > 0)
97                 size = strchop(value);
98         return size;
99 }
100
101 ssize_t sysfs_bin_attr_get_value(struct udev_device *dev, const char *attr_name,
102                                  unsigned char * value, size_t value_len)
103 {
104         char devpath[PATH_SIZE];
105         struct stat statbuf;
106         int fd;
107         ssize_t size = -1;
108
109         if (!dev || !attr_name || !value)
110                 return 0;
111
112         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
113                  attr_name);
114         condlog(4, "open '%s'", devpath);
115         if (stat(devpath, &statbuf) != 0) {
116                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
117                 return -ENXIO;
118         }
119
120         /* skip directories */
121         if (S_ISDIR(statbuf.st_mode)) {
122                 condlog(4, "%s is a directory", devpath);
123                 return -EISDIR;
124         }
125
126         /* skip non-writeable files */
127         if ((statbuf.st_mode & S_IRUSR) == 0) {
128                 condlog(4, "%s is not readable", devpath);
129                 return -EPERM;
130         }
131
132         /* read attribute value */
133         fd = open(devpath, O_RDONLY);
134         if (fd < 0) {
135                 condlog(4, "attribute '%s' can not be opened: %s",
136                         devpath, strerror(errno));
137                 return -errno;
138         }
139         size = read(fd, value, value_len);
140         if (size < 0) {
141                 condlog(4, "read from %s failed: %s", devpath, strerror(errno));
142                 size = -errno;
143         } else if (size == value_len) {
144                 condlog(4, "overflow while reading from %s", devpath);
145                 size = 0;
146         }
147
148         close(fd);
149         return size;
150 }
151
152 ssize_t sysfs_attr_set_value(struct udev_device *dev, const char *attr_name,
153                              char * value, size_t value_len)
154 {
155         char devpath[PATH_SIZE];
156         struct stat statbuf;
157         int fd;
158         ssize_t size = -1;
159
160         if (!dev || !attr_name || !value || !value_len)
161                 return 0;
162
163         snprintf(devpath, PATH_SIZE, "%s/%s", udev_device_get_syspath(dev),
164                  attr_name);
165         condlog(4, "open '%s'", devpath);
166         if (stat(devpath, &statbuf) != 0) {
167                 condlog(4, "stat '%s' failed: %s", devpath, strerror(errno));
168                 return -errno;
169         }
170
171         /* skip directories */
172         if (S_ISDIR(statbuf.st_mode)) {
173                 condlog(4, "%s is a directory", devpath);
174                 return -EISDIR;
175         }
176
177         /* skip non-writeable files */
178         if ((statbuf.st_mode & S_IWUSR) == 0) {
179                 condlog(4, "%s is not writeable", devpath);
180                 return -EPERM;
181         }
182
183         /* write attribute value */
184         fd = open(devpath, O_WRONLY);
185         if (fd < 0) {
186                 condlog(4, "attribute '%s' can not be opened: %s",
187                         devpath, strerror(errno));
188                 return -errno;
189         }
190         size = write(fd, value, value_len);
191         if (size < 0) {
192                 condlog(4, "write to %s failed: %s", devpath, strerror(errno));
193                 size = -errno;
194         } else if (size < value_len) {
195                 condlog(4, "tried to write %ld to %s. Wrote %ld",
196                         (long)value_len, devpath, (long)size);
197                 size = 0;
198         }
199
200         close(fd);
201         return size;
202 }
203
204 int
205 sysfs_get_size (struct path *pp, unsigned long long * size)
206 {
207         char attr[255];
208         int r;
209
210         if (!pp->udev || !size)
211                 return 1;
212
213         attr[0] = '\0';
214         if (sysfs_attr_get_value(pp->udev, "size", attr, 255) <= 0) {
215                 condlog(3, "%s: No size attribute in sysfs", pp->dev);
216                 return 1;
217         }
218
219         r = sscanf(attr, "%llu\n", size);
220
221         if (r != 1) {
222                 condlog(3, "%s: Cannot parse size attribute", pp->dev);
223                 *size = 0;
224                 return 1;
225         }
226
227         return 0;
228 }
229
230 int sysfs_check_holders(char * check_devt, char * new_devt)
231 {
232         unsigned int major, new_minor, table_minor;
233         char path[PATH_SIZE], check_dev[PATH_SIZE];
234         char * table_name;
235         DIR *dirfd;
236         struct dirent *holder;
237
238         if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) {
239                 condlog(1, "invalid device number %s", new_devt);
240                 return 0;
241         }
242
243         if (devt2devname(check_dev, PATH_SIZE, check_devt)) {
244                 condlog(1, "can't get devname for %s", check_devt);
245                 return 0;
246         }
247
248         condlog(3, "%s: checking holder", check_dev);
249
250         snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev);
251         dirfd = opendir(path);
252         if (dirfd == NULL) {
253                 condlog(3, "%s: failed to open directory %s (%d)",
254                         check_dev, path, errno);
255                 return 0;
256         }
257         while ((holder = readdir(dirfd)) != NULL) {
258                 if ((strcmp(holder->d_name,".") == 0) ||
259                     (strcmp(holder->d_name,"..") == 0))
260                         continue;
261
262                 if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) {
263                         condlog(3, "%s: %s is not a dm-device",
264                                 check_dev, holder->d_name);
265                         continue;
266                 }
267                 if (table_minor == new_minor) {
268                         condlog(3, "%s: holder already correct", check_dev);
269                         continue;
270                 }
271                 table_name = dm_mapname(major, table_minor);
272
273                 condlog(0, "%s: reassign table %s old %s new %s", check_dev,
274                         table_name, check_devt, new_devt);
275
276                 dm_reassign_table(table_name, check_devt, new_devt);
277                 FREE(table_name);
278         }
279         closedir(dirfd);
280
281         return 0;
282 }