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