libmultipath: Fix logic in should_multipath
[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(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         pthread_cleanup_push(put_multipath_config, conf);
96         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
97         pthread_cleanup_pop(1);
98         if (fd < 0)
99                 goto out;
100         if (!can_write) {
101                 condlog(0, "cannot replace wwids. wwids file is read-only");
102                 goto out_file;
103         }
104         if (ftruncate(fd, 0) < 0) {
105                 condlog(0, "cannot truncate wwids file : %s", strerror(errno));
106                 goto out_file;
107         }
108         if (lseek(fd, 0, SEEK_SET) < 0) {
109                 condlog(0, "cannot seek to the start of the file : %s",
110                         strerror(errno));
111                 goto out_file;
112         }
113         len = strlen(WWIDS_FILE_HEADER);
114         if (write(fd, WWIDS_FILE_HEADER, len) != len) {
115                 condlog(0, "Can't write wwid file header : %s",
116                         strerror(errno));
117                 /* cleanup partially written header */
118                 if (ftruncate(fd, 0) < 0)
119                         condlog(0, "Cannot truncate header : %s",
120                                 strerror(errno));
121                 goto out_file;
122         }
123         if (!mp || !mp->allocated) {
124                 ret = 0;
125                 goto out_file;
126         }
127         vector_foreach_slot(mp, mpp, i) {
128                 if (write_out_wwid(fd, mpp->wwid) < 0)
129                         goto out_file;
130         }
131         ret = 0;
132 out_file:
133         close(fd);
134 out:
135         return ret;
136 }
137
138 int
139 do_remove_wwid(int fd, char *str) {
140         char buf[4097];
141         char *ptr;
142         off_t start = 0;
143         int bytes;
144
145         while (1) {
146                 if (lseek(fd, start, SEEK_SET) < 0) {
147                         condlog(0, "wwid file read lseek failed : %s",
148                                 strerror(errno));
149                         return -1;
150                 }
151                 bytes = read(fd, buf, 4096);
152                 if (bytes < 0) {
153                         if (errno == EINTR || errno == EAGAIN)
154                                 continue;
155                         condlog(0, "failed to read from wwids file : %s",
156                                 strerror(errno));
157                         return -1;
158                 }
159                 if (!bytes) /* didn't find wwid to remove */
160                         return 1;
161                 buf[bytes] = '\0';
162                 ptr = strstr(buf, str);
163                 if (ptr != NULL) {
164                         condlog(3, "found '%s'", str);
165                         if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
166                                 condlog(0, "write lseek failed : %s",
167                                                 strerror(errno));
168                                 return -1;
169                         }
170                         while (1) {
171                                 if (write(fd, "#", 1) < 0) {
172                                         if (errno == EINTR || errno == EAGAIN)
173                                                 continue;
174                                         condlog(0, "failed to write to wwids file : %s", strerror(errno));
175                                         return -1;
176                                 }
177                                 return 0;
178                         }
179                 }
180                 ptr = strrchr(buf, '\n');
181                 if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
182                         condlog(4, "couldn't find newline, assuming end of file");
183                         return 1;
184                 }
185                 start = start + (ptr - buf) + 1;
186         }
187 }
188
189
190 int
191 remove_wwid(char *wwid) {
192         int fd, len, can_write;
193         char *str;
194         int ret = -1;
195         struct config *conf;
196
197         len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
198         str = malloc(len);
199         if (str == NULL) {
200                 condlog(0, "can't allocate memory to remove wwid : %s",
201                         strerror(errno));
202                 return -1;
203         }
204         if (snprintf(str, len, "/%s/\n", wwid) >= len) {
205                 condlog(0, "string overflow trying to remove wwid");
206                 goto out;
207         }
208         condlog(3, "removing line '%s' from wwids file", str);
209         conf = get_multipath_config();
210         pthread_cleanup_push(put_multipath_config, conf);
211         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
212         pthread_cleanup_pop(1);
213         if (fd < 0)
214                 goto out;
215         if (!can_write) {
216                 condlog(0, "cannot remove wwid. wwids file is read-only");
217                 goto out_file;
218         }
219         ret = do_remove_wwid(fd, str);
220
221 out_file:
222         close(fd);
223 out:
224         free(str);
225         return ret;
226 }
227
228 int
229 check_wwids_file(char *wwid, int write_wwid)
230 {
231         int fd, can_write, found, ret;
232         FILE *f;
233         struct config *conf;
234
235         conf = get_multipath_config();
236         pthread_cleanup_push(put_multipath_config, conf);
237         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
238         pthread_cleanup_pop(1);
239         if (fd < 0)
240                 return -1;
241
242         f = fdopen(fd, "r");
243         if (!f) {
244                 condlog(0,"can't fdopen wwids file : %s", strerror(errno));
245                 close(fd);
246                 return -1;
247         }
248         found = lookup_wwid(f, wwid);
249         if (found) {
250                 ret = 0;
251                 goto out;
252         }
253         if (!write_wwid) {
254                 ret = -1;
255                 goto out;
256         }
257         if (!can_write) {
258                 condlog(0, "wwids file is read-only. Can't write wwid");
259                 ret = -1;
260                 goto out;
261         }
262
263         if (fflush(f) != 0) {
264                 condlog(0, "cannot fflush wwids file stream : %s",
265                         strerror(errno));
266                 ret = -1;
267                 goto out;
268         }
269
270         ret = write_out_wwid(fd, wwid);
271 out:
272         fclose(f);
273         return ret;
274 }
275
276 int
277 should_multipath(struct path *pp1, vector pathvec)
278 {
279         int i, ignore_new_devs, find_multipaths;
280         struct path *pp2;
281         struct config *conf;
282
283         conf = get_multipath_config();
284         ignore_new_devs = conf->ignore_new_devs;
285         find_multipaths = conf->find_multipaths;
286         put_multipath_config(conf);
287         if (!find_multipaths && !ignore_new_devs)
288                 return 1;
289
290         condlog(4, "checking if %s should be multipathed", pp1->dev);
291         if (!ignore_new_devs) {
292                 vector_foreach_slot(pathvec, pp2, i) {
293                         if (pp1->dev == pp2->dev)
294                                 continue;
295                         if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) {
296                                 condlog(3, "found multiple paths with wwid %s, "
297                                         "multipathing %s", pp1->wwid, pp1->dev);
298                                 return 1;
299                         }
300                 }
301         }
302         if (check_wwids_file(pp1->wwid, 0) < 0) {
303                 condlog(3, "wwid %s not in wwids file, skipping %s",
304                         pp1->wwid, pp1->dev);
305                 return 0;
306         }
307         condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid,
308                 pp1->dev);
309         return 1;
310 }
311
312 int
313 remember_wwid(char *wwid)
314 {
315         int ret = check_wwids_file(wwid, 1);
316         if (ret < 0){
317                 condlog(3, "failed writing wwid %s to wwids file", wwid);
318                 return -1;
319         }
320         if (ret == 1)
321                 condlog(3, "wrote wwid %s to wwids file", wwid);
322         else
323                 condlog(4, "wwid %s already in wwids file", wwid);
324         return 0;
325 }