Imported Upstream version 0.6.3
[platform/upstream/multipath-tools.git] / libmultipath / wwids.c
1 #include <stdlib.h>
2 #include <errno.h>
3 #include <unistd.h>
4 #include <string.h>
5 #include <limits.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8
9 #include "checkers.h"
10 #include "vector.h"
11 #include "structs.h"
12 #include "debug.h"
13 #include "uxsock.h"
14 #include "file.h"
15 #include "wwids.h"
16 #include "defaults.h"
17 #include "config.h"
18
19 /*
20  * Copyright (c) 2010 Benjamin Marzinski, Redhat
21  */
22
23 static int
24 lookup_wwid(FILE *f, char *wwid) {
25         int c;
26         char buf[LINE_MAX];
27         int count;
28
29         while ((c = fgetc(f)) != EOF){
30                 if (c != '/') {
31                         if (fgets(buf, LINE_MAX, f) == NULL)
32                                 return 0;
33                         else
34                                 continue;
35                 }
36                 count = 0;
37                 while ((c = fgetc(f)) != '/') {
38                         if (c == EOF)
39                                 return 0;
40                         if (count >= WWID_SIZE - 1)
41                                 goto next;
42                         if (wwid[count] == '\0')
43                                 goto next;
44                         if (c != wwid[count++])
45                                 goto next;
46                 }
47                 if (wwid[count] == '\0')
48                         return 1;
49 next:
50                 if (fgets(buf, LINE_MAX, f) == NULL)
51                         return 0;
52         }
53         return 0;
54 }
55
56 static int
57 write_out_wwid(int fd, char *wwid) {
58         int ret;
59         off_t offset;
60         char buf[WWID_SIZE + 3];
61
62         ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid);
63         if (ret >= (WWID_SIZE + 3) || ret < 0){
64                 condlog(0, "can't format wwid for writing (%d) : %s",
65                         ret, strerror(errno));
66                 return -1;
67         }
68         offset = lseek(fd, 0, SEEK_END);
69         if (offset < 0) {
70                 condlog(0, "can't seek to the end of wwids file : %s",
71                         strerror(errno));
72                 return -1;
73         }
74         if (write_all(fd, buf, strlen(buf)) != strlen(buf)) {
75                 condlog(0, "cannot write wwid to wwids file : %s",
76                         strerror(errno));
77                 if (ftruncate(fd, offset))
78                         condlog(0, "cannot truncate failed wwid write : %s",
79                                 strerror(errno));
80                 return -1;
81         }
82         return 1;
83 }
84
85 int
86 replace_wwids(vector mp)
87 {
88         int i, fd, can_write;
89         struct multipath * mpp;
90         size_t len;
91         int ret = -1;
92         struct config *conf;
93
94         conf = get_multipath_config();
95         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
96         put_multipath_config(conf);
97         if (fd < 0)
98                 goto out;
99         if (!can_write) {
100                 condlog(0, "cannot replace wwids. wwids file is read-only");
101                 goto out_file;
102         }
103         if (ftruncate(fd, 0) < 0) {
104                 condlog(0, "cannot truncate wwids file : %s", strerror(errno));
105                 goto out_file;
106         }
107         if (lseek(fd, 0, SEEK_SET) < 0) {
108                 condlog(0, "cannot seek to the start of the file : %s",
109                         strerror(errno));
110                 goto out_file;
111         }
112         len = strlen(WWIDS_FILE_HEADER);
113         if (write_all(fd, WWIDS_FILE_HEADER, len) != len) {
114                 condlog(0, "Can't write wwid file header : %s",
115                         strerror(errno));
116                 /* cleanup partially written header */
117                 if (ftruncate(fd, 0) < 0)
118                         condlog(0, "Cannot truncate header : %s",
119                                 strerror(errno));
120                 goto out_file;
121         }
122         if (!mp || !mp->allocated) {
123                 ret = 0;
124                 goto out_file;
125         }
126         vector_foreach_slot(mp, mpp, i) {
127                 if (write_out_wwid(fd, mpp->wwid) < 0)
128                         goto out_file;
129         }
130         ret = 0;
131 out_file:
132         close(fd);
133 out:
134         return ret;
135 }
136
137 int
138 do_remove_wwid(int fd, char *str) {
139         char buf[4097];
140         char *ptr;
141         off_t start = 0;
142         int bytes;
143
144         while (1) {
145                 if (lseek(fd, start, SEEK_SET) < 0) {
146                         condlog(0, "wwid file read lseek failed : %s",
147                                 strerror(errno));
148                         return -1;
149                 }
150                 bytes = read(fd, buf, 4096);
151                 if (bytes < 0) {
152                         if (errno == EINTR || errno == EAGAIN)
153                                 continue;
154                         condlog(0, "failed to read from wwids file : %s",
155                                 strerror(errno));
156                         return -1;
157                 }
158                 if (!bytes) /* didn't find wwid to remove */
159                         return 1;
160                 buf[bytes] = '\0';
161                 ptr = strstr(buf, str);
162                 if (ptr != NULL) {
163                         condlog(3, "found '%s'", str);
164                         if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
165                                 condlog(0, "write lseek failed : %s",
166                                                 strerror(errno));
167                                 return -1;
168                         }
169                         while (1) {
170                                 if (write(fd, "#", 1) < 0) {
171                                         if (errno == EINTR || errno == EAGAIN)
172                                                 continue;
173                                         condlog(0, "failed to write to wwids file : %s", strerror(errno));
174                                         return -1;
175                                 }
176                                 return 0;
177                         }
178                 }
179                 ptr = strrchr(buf, '\n');
180                 if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
181                         condlog(4, "couldn't find newline, assuming end of file");
182                         return 1;
183                 }
184                 start = start + (ptr - buf) + 1;
185         }
186 }
187
188
189 int
190 remove_wwid(char *wwid) {
191         int fd, len, can_write;
192         char *str;
193         int ret = -1;
194         struct config *conf;
195
196         len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
197         str = malloc(len);
198         if (str == NULL) {
199                 condlog(0, "can't allocate memory to remove wwid : %s",
200                         strerror(errno));
201                 return -1;
202         }
203         if (snprintf(str, len, "/%s/\n", wwid) >= len) {
204                 condlog(0, "string overflow trying to remove wwid");
205                 goto out;
206         }
207         condlog(3, "removing line '%s' from wwids file", str);
208         conf = get_multipath_config();
209         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
210         put_multipath_config(conf);
211         if (fd < 0)
212                 goto out;
213         if (!can_write) {
214                 condlog(0, "cannot remove wwid. wwids file is read-only");
215                 goto out_file;
216         }
217         ret = do_remove_wwid(fd, str);
218
219 out_file:
220         close(fd);
221 out:
222         free(str);
223         return ret;
224 }
225
226 int
227 check_wwids_file(char *wwid, int write_wwid)
228 {
229         int fd, can_write, found, ret;
230         FILE *f;
231         struct config *conf;
232
233         conf = get_multipath_config();
234         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
235         put_multipath_config(conf);
236         if (fd < 0)
237                 return -1;
238
239         f = fdopen(fd, "r");
240         if (!f) {
241                 condlog(0,"can't fdopen wwids file : %s", strerror(errno));
242                 close(fd);
243                 return -1;
244         }
245         found = lookup_wwid(f, wwid);
246         if (found) {
247                 ret = 0;
248                 goto out;
249         }
250         if (!write_wwid) {
251                 ret = -1;
252                 goto out;
253         }
254         if (!can_write) {
255                 condlog(0, "wwids file is read-only. Can't write wwid");
256                 ret = -1;
257                 goto out;
258         }
259
260         if (fflush(f) != 0) {
261                 condlog(0, "cannot fflush wwids file stream : %s",
262                         strerror(errno));
263                 ret = -1;
264                 goto out;
265         }
266
267         ret = write_out_wwid(fd, wwid);
268 out:
269         fclose(f);
270         return ret;
271 }
272
273 int
274 should_multipath(struct path *pp1, vector pathvec)
275 {
276         int i, ignore_new_devs;
277         struct path *pp2;
278         struct config *conf;
279
280         conf = get_multipath_config();
281         ignore_new_devs = conf->ignore_new_devs;
282         if (!conf->find_multipaths && !ignore_new_devs) {
283                 put_multipath_config(conf);
284                 return 1;
285         }
286         put_multipath_config(conf);
287
288         condlog(4, "checking if %s should be multipathed", pp1->dev);
289         if (!ignore_new_devs) {
290                 vector_foreach_slot(pathvec, pp2, i) {
291                         if (pp1->dev == pp2->dev)
292                                 continue;
293                         if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) {
294                                 condlog(3, "found multiple paths with wwid %s, "
295                                         "multipathing %s", pp1->wwid, pp1->dev);
296                                 return 1;
297                         }
298                 }
299         }
300         if (check_wwids_file(pp1->wwid, 0) < 0) {
301                 condlog(3, "wwid %s not in wwids file, skipping %s",
302                         pp1->wwid, pp1->dev);
303                 return 0;
304         }
305         condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid,
306                 pp1->dev);
307         return 1;
308 }
309
310 int
311 remember_wwid(char *wwid)
312 {
313         int ret = check_wwids_file(wwid, 1);
314         if (ret < 0){
315                 condlog(3, "failed writing wwid %s to wwids file", wwid);
316                 return -1;
317         }
318         if (ret == 1)
319                 condlog(3, "wrote wwid %s to wwids file", wwid);
320         else
321                 condlog(4, "wwid %s already in wwids file", wwid);
322         return 0;
323 }