multipathd: fix reservation_key check
[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 #include <sys/stat.h>
9
10 #include "checkers.h"
11 #include "vector.h"
12 #include "structs.h"
13 #include "debug.h"
14 #include "uxsock.h"
15 #include "file.h"
16 #include "wwids.h"
17 #include "defaults.h"
18 #include "config.h"
19 #include "devmapper.h"
20
21 /*
22  * Copyright (c) 2010 Benjamin Marzinski, Redhat
23  */
24
25 static int
26 lookup_wwid(FILE *f, char *wwid) {
27         int c;
28         char buf[LINE_MAX];
29         int count;
30
31         while ((c = fgetc(f)) != EOF){
32                 if (c != '/') {
33                         if (fgets(buf, LINE_MAX, f) == NULL)
34                                 return 0;
35                         else
36                                 continue;
37                 }
38                 count = 0;
39                 while ((c = fgetc(f)) != '/') {
40                         if (c == EOF)
41                                 return 0;
42                         if (count >= WWID_SIZE - 1)
43                                 goto next;
44                         if (wwid[count] == '\0')
45                                 goto next;
46                         if (c != wwid[count++])
47                                 goto next;
48                 }
49                 if (wwid[count] == '\0')
50                         return 1;
51 next:
52                 if (fgets(buf, LINE_MAX, f) == NULL)
53                         return 0;
54         }
55         return 0;
56 }
57
58 static int
59 write_out_wwid(int fd, char *wwid) {
60         int ret;
61         off_t offset;
62         char buf[WWID_SIZE + 3];
63
64         ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid);
65         if (ret >= (WWID_SIZE + 3) || ret < 0){
66                 condlog(0, "can't format wwid for writing (%d) : %s",
67                         ret, strerror(errno));
68                 return -1;
69         }
70         offset = lseek(fd, 0, SEEK_END);
71         if (offset < 0) {
72                 condlog(0, "can't seek to the end of wwids file : %s",
73                         strerror(errno));
74                 return -1;
75         }
76         if (write(fd, buf, strlen(buf)) != strlen(buf)) {
77                 condlog(0, "cannot write wwid to wwids file : %s",
78                         strerror(errno));
79                 if (ftruncate(fd, offset))
80                         condlog(0, "cannot truncate failed wwid write : %s",
81                                 strerror(errno));
82                 return -1;
83         }
84         return 1;
85 }
86
87 int
88 replace_wwids(vector mp)
89 {
90         int i, fd, can_write;
91         struct multipath * mpp;
92         size_t len;
93         int ret = -1;
94         struct config *conf;
95
96         conf = get_multipath_config();
97         pthread_cleanup_push(put_multipath_config, conf);
98         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
99         pthread_cleanup_pop(1);
100         if (fd < 0)
101                 goto out;
102         if (!can_write) {
103                 condlog(0, "cannot replace wwids. wwids file is read-only");
104                 goto out_file;
105         }
106         if (ftruncate(fd, 0) < 0) {
107                 condlog(0, "cannot truncate wwids file : %s", strerror(errno));
108                 goto out_file;
109         }
110         if (lseek(fd, 0, SEEK_SET) < 0) {
111                 condlog(0, "cannot seek to the start of the file : %s",
112                         strerror(errno));
113                 goto out_file;
114         }
115         len = strlen(WWIDS_FILE_HEADER);
116         if (write(fd, WWIDS_FILE_HEADER, len) != len) {
117                 condlog(0, "Can't write wwid file header : %s",
118                         strerror(errno));
119                 /* cleanup partially written header */
120                 if (ftruncate(fd, 0) < 0)
121                         condlog(0, "Cannot truncate header : %s",
122                                 strerror(errno));
123                 goto out_file;
124         }
125         if (!mp || !mp->allocated) {
126                 ret = 0;
127                 goto out_file;
128         }
129         vector_foreach_slot(mp, mpp, i) {
130                 if (write_out_wwid(fd, mpp->wwid) < 0)
131                         goto out_file;
132         }
133         ret = 0;
134 out_file:
135         close(fd);
136 out:
137         return ret;
138 }
139
140 int
141 do_remove_wwid(int fd, char *str) {
142         char buf[4097];
143         char *ptr;
144         off_t start = 0;
145         int bytes;
146
147         while (1) {
148                 if (lseek(fd, start, SEEK_SET) < 0) {
149                         condlog(0, "wwid file read lseek failed : %s",
150                                 strerror(errno));
151                         return -1;
152                 }
153                 bytes = read(fd, buf, 4096);
154                 if (bytes < 0) {
155                         if (errno == EINTR || errno == EAGAIN)
156                                 continue;
157                         condlog(0, "failed to read from wwids file : %s",
158                                 strerror(errno));
159                         return -1;
160                 }
161                 if (!bytes) /* didn't find wwid to remove */
162                         return 1;
163                 buf[bytes] = '\0';
164                 ptr = strstr(buf, str);
165                 if (ptr != NULL) {
166                         condlog(3, "found '%s'", str);
167                         if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) {
168                                 condlog(0, "write lseek failed : %s",
169                                                 strerror(errno));
170                                 return -1;
171                         }
172                         while (1) {
173                                 if (write(fd, "#", 1) < 0) {
174                                         if (errno == EINTR || errno == EAGAIN)
175                                                 continue;
176                                         condlog(0, "failed to write to wwids file : %s", strerror(errno));
177                                         return -1;
178                                 }
179                                 return 0;
180                         }
181                 }
182                 ptr = strrchr(buf, '\n');
183                 if (ptr == NULL) { /* shouldn't happen, assume it is EOF */
184                         condlog(4, "couldn't find newline, assuming end of file");
185                         return 1;
186                 }
187                 start = start + (ptr - buf) + 1;
188         }
189 }
190
191
192 int
193 remove_wwid(char *wwid) {
194         int fd, len, can_write;
195         char *str;
196         int ret = -1;
197         struct config *conf;
198
199         len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */
200         str = malloc(len);
201         if (str == NULL) {
202                 condlog(0, "can't allocate memory to remove wwid : %s",
203                         strerror(errno));
204                 return -1;
205         }
206         if (snprintf(str, len, "/%s/\n", wwid) >= len) {
207                 condlog(0, "string overflow trying to remove wwid");
208                 goto out;
209         }
210         condlog(3, "removing line '%s' from wwids file", str);
211         conf = get_multipath_config();
212         pthread_cleanup_push(put_multipath_config, conf);
213         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
214         pthread_cleanup_pop(1);
215         if (fd < 0)
216                 goto out;
217         if (!can_write) {
218                 condlog(0, "cannot remove wwid. wwids file is read-only");
219                 goto out_file;
220         }
221         ret = do_remove_wwid(fd, str);
222
223 out_file:
224         close(fd);
225 out:
226         free(str);
227         return ret;
228 }
229
230 int
231 check_wwids_file(char *wwid, int write_wwid)
232 {
233         int fd, can_write, found, ret;
234         FILE *f;
235         struct config *conf;
236
237         conf = get_multipath_config();
238         pthread_cleanup_push(put_multipath_config, conf);
239         fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER);
240         pthread_cleanup_pop(1);
241         if (fd < 0)
242                 return -1;
243
244         f = fdopen(fd, "r");
245         if (!f) {
246                 condlog(0,"can't fdopen wwids file : %s", strerror(errno));
247                 close(fd);
248                 return -1;
249         }
250         found = lookup_wwid(f, wwid);
251         if (found) {
252                 ret = 0;
253                 goto out;
254         }
255         if (!write_wwid) {
256                 ret = -1;
257                 goto out;
258         }
259         if (!can_write) {
260                 condlog(0, "wwids file is read-only. Can't write wwid");
261                 ret = -1;
262                 goto out;
263         }
264
265         if (fflush(f) != 0) {
266                 condlog(0, "cannot fflush wwids file stream : %s",
267                         strerror(errno));
268                 ret = -1;
269                 goto out;
270         }
271
272         ret = write_out_wwid(fd, wwid);
273 out:
274         fclose(f);
275         return ret;
276 }
277
278 int
279 should_multipath(struct path *pp1, vector pathvec, vector mpvec)
280 {
281         int i, ignore_new_devs, find_multipaths;
282         struct path *pp2;
283         struct config *conf;
284
285         conf = get_multipath_config();
286         ignore_new_devs = ignore_new_devs_on(conf);
287         find_multipaths = find_multipaths_on(conf);
288         put_multipath_config(conf);
289         if (!find_multipaths && !ignore_new_devs)
290                 return 1;
291
292         condlog(4, "checking if %s should be multipathed", pp1->dev);
293         if (!ignore_new_devs) {
294                 char tmp_wwid[WWID_SIZE];
295                 struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid);
296
297                 if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid) == 0 &&
298                     !strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) {
299                         condlog(3, "wwid %s is already multipathed, keeping it",
300                                 pp1->wwid);
301                         return 1;
302                 }
303                 vector_foreach_slot(pathvec, pp2, i) {
304                         if (pp1->dev == pp2->dev)
305                                 continue;
306                         if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) {
307                                 condlog(3, "found multiple paths with wwid %s, "
308                                         "multipathing %s", pp1->wwid, pp1->dev);
309                                 return 1;
310                         }
311                 }
312         }
313         if (check_wwids_file(pp1->wwid, 0) < 0) {
314                 condlog(3, "wwid %s not in wwids file, skipping %s",
315                         pp1->wwid, pp1->dev);
316                 return 0;
317         }
318         condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid,
319                 pp1->dev);
320         return 1;
321 }
322
323 int
324 remember_wwid(char *wwid)
325 {
326         int ret = check_wwids_file(wwid, 1);
327         if (ret < 0){
328                 condlog(3, "failed writing wwid %s to wwids file", wwid);
329                 return -1;
330         }
331         if (ret == 1)
332                 condlog(3, "wrote wwid %s to wwids file", wwid);
333         else
334                 condlog(4, "wwid %s already in wwids file", wwid);
335         return ret;
336 }
337
338 static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids";
339 static const char shm_lock[] = ".lock";
340 static const char shm_header[] = "multipath shm lock file, don't edit";
341 static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)];
342 static const char *shm_lock_path = &_shm_lock_path[0];
343
344 static void init_shm_paths(void)
345 {
346         snprintf(_shm_lock_path, sizeof(_shm_lock_path),
347                  "%s/%s", shm_dir, shm_lock);
348 }
349
350 static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT;
351
352 static int multipath_shm_open(bool rw)
353 {
354         int fd;
355         int can_write;
356
357         pthread_once(&shm_path_once, init_shm_paths);
358         fd = open_file(shm_lock_path, &can_write, shm_header);
359
360         if (fd >= 0 && rw && !can_write) {
361                 close(fd);
362                 condlog(1, "failed to open %s for writing", shm_dir);
363                 return -1;
364         }
365
366         return fd;
367 }
368
369 static void multipath_shm_close(void *arg)
370 {
371         long fd = (long)arg;
372
373         close(fd);
374         unlink(shm_lock_path);
375 }
376
377 static int _failed_wwid_op(const char *wwid, bool rw,
378                            int (*func)(const char *), const char *msg)
379 {
380         char path[PATH_MAX];
381         long lockfd;
382         int r = -1;
383
384         if (snprintf(path, sizeof(path), "%s/%s", shm_dir, wwid)
385             >= sizeof(path)) {
386                 condlog(1, "%s: path name overflow", __func__);
387                 return -1;
388         }
389
390         lockfd = multipath_shm_open(rw);
391         if (lockfd == -1)
392                 return -1;
393
394         pthread_cleanup_push(multipath_shm_close, (void *)lockfd);
395         r = func(path);
396         pthread_cleanup_pop(1);
397
398         if (r == WWID_FAILED_ERROR)
399                 condlog(1, "%s: %s: %s", msg, wwid, strerror(errno));
400         else if (r == WWID_FAILED_CHANGED)
401                 condlog(3, "%s: %s", msg, wwid);
402         else if (!rw)
403                 condlog(4, "%s: %s is %s", msg, wwid,
404                         r == WWID_IS_FAILED ? "failed" : "good");
405
406         return r;
407 }
408
409 static int _is_failed(const char *path)
410 {
411         struct stat st;
412
413         if (lstat(path, &st) == 0)
414                 return WWID_IS_FAILED;
415         else if (errno == ENOENT)
416                 return WWID_IS_NOT_FAILED;
417         else
418                 return WWID_FAILED_ERROR;
419 }
420
421 static int _mark_failed(const char *path)
422 {
423         /* Called from _failed_wwid_op: we know that shm_lock_path exists */
424         if (_is_failed(path) == WWID_IS_FAILED)
425                 return WWID_FAILED_UNCHANGED;
426         return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED :
427                 WWID_FAILED_ERROR);
428 }
429
430 static int _unmark_failed(const char *path)
431 {
432         if (_is_failed(path) == WWID_IS_NOT_FAILED)
433                 return WWID_FAILED_UNCHANGED;
434         return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR);
435 }
436
437 #define declare_failed_wwid_op(op, rw) \
438 int op ## _wwid(const char *wwid) \
439 { \
440         return _failed_wwid_op(wwid, (rw), _ ## op, #op); \
441 }
442
443 declare_failed_wwid_op(is_failed, false)
444 declare_failed_wwid_op(mark_failed, true)
445 declare_failed_wwid_op(unmark_failed, true)