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