5cd365a1ce126de097ca63693da933b70e6dde24
[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
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 char sysfs_path[PATH_SIZE];
41
42 int sysfs_init(char *path, size_t len)
43 {
44         if (path) {
45                 strlcpy(sysfs_path, path, len);
46                 remove_trailing_chars(sysfs_path, '/');
47         } else
48                 strlcpy(sysfs_path, "/sys", sizeof(sysfs_path));
49         dbg("sysfs_path='%s'", sysfs_path);
50
51         return 0;
52 }
53
54 int sysfs_resolve_link(char *devpath, size_t size)
55 {
56         char link_path[PATH_SIZE];
57         char link_target[PATH_SIZE];
58         int len;
59         int i;
60         int back;
61
62         strlcpy(link_path, sysfs_path, sizeof(link_path));
63         strlcat(link_path, devpath, sizeof(link_path));
64         len = readlink(link_path, link_target, sizeof(link_target));
65         if (len <= 0)
66                 return -1;
67         link_target[len] = '\0';
68         dbg("path link '%s' points to '%s'", devpath, link_target);
69
70         for (back = 0; strncmp(&link_target[back * 3], "../", 3) == 0; back++)
71                 ;
72         dbg("base '%s', tail '%s', back %i", devpath, &link_target[back * 3], back);
73         for (i = 0; i <= back; i++) {
74                 char *pos = strrchr(devpath, '/');
75
76                 if (pos == NULL)
77                         return -1;
78                 pos[0] = '\0';
79         }
80         dbg("after moving back '%s'", devpath);
81         strlcat(devpath, "/", size);
82         strlcat(devpath, &link_target[back * 3], size);
83         return 0;
84 }
85
86 size_t sysfs_attr_get_value(const char *devpath, const char *attr_name,
87                             char *attr_value, int attr_len)
88 {
89         char path_full[PATH_SIZE];
90         struct stat statbuf;
91         int fd;
92         ssize_t size;
93         size_t sysfs_len;
94
95         if (!attr_value || !attr_len)
96                 return 0;
97
98         attr_value[0] = '\0';
99         size = 0;
100
101         dbg("open '%s'/'%s'", devpath, attr_name);
102         sysfs_len = strlcpy(path_full, sysfs_path, sizeof(path_full));
103         if(sysfs_len >= sizeof(path_full))
104                 sysfs_len = sizeof(path_full) - 1;
105
106         strlcat(path_full, devpath, sizeof(path_full));
107         strlcat(path_full, "/", sizeof(path_full));
108         strlcat(path_full, attr_name, sizeof(path_full));
109
110         if (stat(path_full, &statbuf) != 0) {
111                 dbg("stat '%s' failed: %s", path_full, strerror(errno));
112                 goto out;
113         }
114
115         /* skip directories */
116         if (S_ISDIR(statbuf.st_mode))
117                 goto out;
118
119         /* skip non-readable files */
120         if ((statbuf.st_mode & S_IRUSR) == 0)
121                 goto out;
122
123         /* read attribute value */
124         fd = open(path_full, O_RDONLY);
125         if (fd < 0) {
126                 dbg("attribute '%s' can not be opened: %s",
127                     path_full, strerror(errno));
128                 goto out;
129         }
130         size = read(fd, attr_value, attr_len);
131         close(fd);
132         if (size < 0)
133                 goto out;
134         if (size == attr_len) {
135                 dbg("overflow in attribute '%s', truncating", path_full);
136                 size--;
137         }
138
139         /* got a valid value, store and return it */
140         attr_value[size] = '\0';
141         remove_trailing_chars(attr_value, '\n');
142
143 out:
144         return size;
145 }
146
147 ssize_t sysfs_attr_set_value(const char *devpath, const char *attr_name,
148                              const char *value, int value_len)
149 {
150         char path_full[PATH_SIZE];
151         struct stat statbuf;
152         int fd;
153         ssize_t size = -1;
154         size_t sysfs_len;
155
156         if (!attr_name || !value || !value_len)
157                 return 0;
158
159         dbg("open '%s'/'%s'", devpath, attr_name);
160         sysfs_len = snprintf(path_full, PATH_SIZE, "%s%s/%s", sysfs_path,
161                              devpath, attr_name);
162         if (sysfs_len >= PATH_SIZE || sysfs_len < 0) {
163                 if (sysfs_len < 0)
164                         dbg("cannot copy sysfs path %s%s/%s : %s", sysfs_path,
165                             devpath, attr_name, strerror(errno));
166                 else
167                         dbg("sysfs_path %s%s/%s too large", sysfs_path,
168                             devpath, attr_name);
169                 goto out;
170         }
171
172         if (stat(path_full, &statbuf) != 0) {
173                 dbg("stat '%s' failed: %s", path_full, strerror(errno));
174                 goto out;
175         }
176
177         /* skip directories */
178         if (S_ISDIR(statbuf.st_mode))
179                 goto out;
180
181         /* skip non-writeable files */
182         if ((statbuf.st_mode & S_IWUSR) == 0)
183                 goto out;
184
185         /* write attribute value */
186         fd = open(path_full, O_WRONLY);
187         if (fd < 0) {
188                 dbg("attribute '%s' can not be opened: %s",
189                     path_full, strerror(errno));
190                 goto out;
191         }
192         size = write(fd, value, value_len);
193         if (size < 0)
194                 dbg("write to %s failed: %s", path_full, strerror(errno));
195         else if (size < value_len) {
196                 dbg("tried to write %d to %s. Wrote %d\n", value_len,
197                     path_full, size);
198                 size = -1;
199         }
200
201         close(fd);
202 out:
203
204         return size;
205 }
206
207 int sysfs_check_holders(char * check_devt, char * new_devt)
208 {
209         unsigned int major, new_minor, table_minor;
210         char path[PATH_SIZE], check_dev[PATH_SIZE];
211         char * table_name;
212         DIR *dirfd;
213         struct dirent *holder;
214
215         if (sscanf(new_devt,"%d:%d", &major, &new_minor) != 2) {
216                 condlog(1, "invalid device number %s", new_devt);
217                 return 0;
218         }
219
220         if (devt2devname(check_dev, PATH_SIZE, check_devt))
221                 return 0;
222
223         condlog(3, "%s: checking holder", check_dev);
224
225         snprintf(path, PATH_SIZE, "/sys/block/%s/holders", check_dev);
226         dirfd = opendir(path);
227         if (dirfd == NULL) {
228                 condlog(3, "%s: failed to open directory %s (%d)",
229                         check_dev, path, errno);
230                 return 0;
231         }
232         while ((holder = readdir(dirfd)) != NULL) {
233                 if ((strcmp(holder->d_name,".") == 0) ||
234                     (strcmp(holder->d_name,"..") == 0))
235                         continue;
236
237                 if (sscanf(holder->d_name, "dm-%d", &table_minor) != 1) {
238                         condlog(3, "%s: %s is not a dm-device",
239                                 check_dev, holder->d_name);
240                         continue;
241                 }
242                 if (table_minor == new_minor) {
243                         condlog(3, "%s: holder already correct", check_dev);
244                         continue;
245                 }
246                 table_name = dm_mapname(major, table_minor);
247
248                 condlog(3, "%s: reassign table %s old %s new %s", check_dev,
249                         table_name, check_devt, new_devt);
250
251                 dm_reassign_table(table_name, check_devt, new_devt);
252                 FREE(table_name);
253         }
254         closedir(dirfd);
255
256         return 0;
257 }