#include #include #include #include #include #include #include #include #include "util.h" #include "checkers.h" #include "vector.h" #include "structs.h" #include "debug.h" #include "uxsock.h" #include "file.h" #include "wwids.h" #include "defaults.h" #include "config.h" #include "devmapper.h" /* * Copyright (c) 2010 Benjamin Marzinski, Redhat */ static int lookup_wwid(FILE *f, char *wwid) { int c; char buf[LINE_MAX]; int count; while ((c = fgetc(f)) != EOF){ if (c != '/') { if (fgets(buf, LINE_MAX, f) == NULL) return 0; else continue; } count = 0; while ((c = fgetc(f)) != '/') { if (c == EOF) return 0; if (count >= WWID_SIZE - 1) goto next; if (wwid[count] == '\0') goto next; if (c != wwid[count++]) goto next; } if (wwid[count] == '\0') return 1; next: if (fgets(buf, LINE_MAX, f) == NULL) return 0; } return 0; } static int write_out_wwid(int fd, char *wwid) { int ret; off_t offset; char buf[WWID_SIZE + 3]; ret = snprintf(buf, WWID_SIZE + 3, "/%s/\n", wwid); if (ret >= (WWID_SIZE + 3) || ret < 0){ condlog(0, "can't format wwid for writing (%d) : %s", ret, strerror(errno)); return -1; } offset = lseek(fd, 0, SEEK_END); if (offset < 0) { condlog(0, "can't seek to the end of wwids file : %s", strerror(errno)); return -1; } if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf)) { condlog(0, "cannot write wwid to wwids file : %s", strerror(errno)); if (ftruncate(fd, offset)) condlog(0, "cannot truncate failed wwid write : %s", strerror(errno)); return -1; } return 1; } int replace_wwids(vector mp) { int i, can_write; long fd; struct multipath * mpp; size_t len; int ret = -1; struct config *conf; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); pthread_cleanup_pop(1); if (fd < 0) goto out; pthread_cleanup_push(close_fd, (void*)fd); if (!can_write) { condlog(0, "cannot replace wwids. wwids file is read-only"); goto out_file; } if (ftruncate(fd, 0) < 0) { condlog(0, "cannot truncate wwids file : %s", strerror(errno)); goto out_file; } if (lseek(fd, 0, SEEK_SET) < 0) { condlog(0, "cannot seek to the start of the file : %s", strerror(errno)); goto out_file; } len = strlen(WWIDS_FILE_HEADER); if (write(fd, WWIDS_FILE_HEADER, len) != (ssize_t)len) { condlog(0, "Can't write wwid file header : %s", strerror(errno)); /* cleanup partially written header */ if (ftruncate(fd, 0) < 0) condlog(0, "Cannot truncate header : %s", strerror(errno)); goto out_file; } if (!mp || !mp->allocated) { ret = 0; goto out_file; } vector_foreach_slot(mp, mpp, i) { if (write_out_wwid(fd, mpp->wwid) < 0) goto out_file; } ret = 0; out_file: pthread_cleanup_pop(1); out: return ret; } int do_remove_wwid(int fd, char *str) { char buf[4097]; char *ptr; off_t start = 0; int bytes; while (1) { if (lseek(fd, start, SEEK_SET) < 0) { condlog(0, "wwid file read lseek failed : %s", strerror(errno)); return -1; } bytes = read(fd, buf, 4096); if (bytes < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to read from wwids file : %s", strerror(errno)); return -1; } if (!bytes) /* didn't find wwid to remove */ return 1; buf[bytes] = '\0'; ptr = strstr(buf, str); if (ptr != NULL) { condlog(3, "found '%s'", str); if (lseek(fd, start + (ptr - buf), SEEK_SET) < 0) { condlog(0, "write lseek failed : %s", strerror(errno)); return -1; } while (1) { if (write(fd, "#", 1) < 0) { if (errno == EINTR || errno == EAGAIN) continue; condlog(0, "failed to write to wwids file : %s", strerror(errno)); return -1; } return 0; } } ptr = strrchr(buf, '\n'); if (ptr == NULL) { /* shouldn't happen, assume it is EOF */ condlog(4, "couldn't find newline, assuming end of file"); return 1; } start = start + (ptr - buf) + 1; } } int remove_wwid(char *wwid) { long fd; int len, can_write; char *str; int ret = -1; struct config *conf; len = strlen(wwid) + 4; /* two slashes the newline and a zero byte */ str = malloc(len); if (str == NULL) { condlog(0, "can't allocate memory to remove wwid : %s", strerror(errno)); return -1; } pthread_cleanup_push(free, str); if (snprintf(str, len, "/%s/\n", wwid) >= len) { condlog(0, "string overflow trying to remove wwid"); ret = -1; goto out; } condlog(3, "removing line '%s' from wwids file", str); conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); pthread_cleanup_pop(1); if (fd < 0) { ret = -1; goto out; } pthread_cleanup_push(close_fd, (void*)fd); if (!can_write) { ret = -1; condlog(0, "cannot remove wwid. wwids file is read-only"); } else ret = do_remove_wwid(fd, str); pthread_cleanup_pop(1); out: /* free(str) */ pthread_cleanup_pop(1); return ret; } int check_wwids_file(char *wwid, int write_wwid) { int fd, can_write, found, ret; FILE *f; struct config *conf; conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); fd = open_file(conf->wwids_file, &can_write, WWIDS_FILE_HEADER); pthread_cleanup_pop(1); if (fd < 0) return -1; f = fdopen(fd, "r"); if (!f) { condlog(0,"can't fdopen wwids file : %s", strerror(errno)); close(fd); return -1; } found = lookup_wwid(f, wwid); if (found) { ret = 0; goto out; } if (!write_wwid) { ret = -1; goto out; } if (!can_write) { condlog(0, "wwids file is read-only. Can't write wwid"); ret = -1; goto out; } if (fflush(f) != 0) { condlog(0, "cannot fflush wwids file stream : %s", strerror(errno)); ret = -1; goto out; } ret = write_out_wwid(fd, wwid); out: fclose(f); return ret; } int should_multipath(struct path *pp1, vector pathvec, vector mpvec) { int i, ignore_new_devs, find_multipaths; struct path *pp2; struct config *conf; conf = get_multipath_config(); ignore_new_devs = ignore_new_devs_on(conf); find_multipaths = find_multipaths_on(conf); put_multipath_config(conf); if (!find_multipaths && !ignore_new_devs) return 1; condlog(4, "checking if %s should be multipathed", pp1->dev); if (!ignore_new_devs) { char tmp_wwid[WWID_SIZE]; struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid); if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid, WWID_SIZE) == 0 && !strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) { condlog(3, "wwid %s is already multipathed, keeping it", pp1->wwid); return 1; } vector_foreach_slot(pathvec, pp2, i) { if (pp1->dev == pp2->dev) continue; if (strncmp(pp1->wwid, pp2->wwid, WWID_SIZE) == 0) { condlog(3, "found multiple paths with wwid %s, " "multipathing %s", pp1->wwid, pp1->dev); return 1; } } } if (check_wwids_file(pp1->wwid, 0) < 0) { condlog(3, "wwid %s not in wwids file, skipping %s", pp1->wwid, pp1->dev); return 0; } condlog(3, "found wwid %s in wwids file, multipathing %s", pp1->wwid, pp1->dev); return 1; } int remember_wwid(char *wwid) { int ret = check_wwids_file(wwid, 1); if (ret < 0){ condlog(3, "failed writing wwid %s to wwids file", wwid); return -1; } if (ret == 1) condlog(3, "wrote wwid %s to wwids file", wwid); else condlog(4, "wwid %s already in wwids file", wwid); return ret; } static const char shm_dir[] = MULTIPATH_SHM_BASE "failed_wwids"; static const char shm_lock[] = ".lock"; static const char shm_header[] = "multipath shm lock file, don't edit"; static char _shm_lock_path[sizeof(shm_dir)+sizeof(shm_lock)]; static const char *shm_lock_path = &_shm_lock_path[0]; static void init_shm_paths(void) { snprintf(_shm_lock_path, sizeof(_shm_lock_path), "%s/%s", shm_dir, shm_lock); } static pthread_once_t shm_path_once = PTHREAD_ONCE_INIT; static int multipath_shm_open(bool rw) { int fd; int can_write; pthread_once(&shm_path_once, init_shm_paths); fd = open_file(shm_lock_path, &can_write, shm_header); if (fd >= 0 && rw && !can_write) { close(fd); condlog(1, "failed to open %s for writing", shm_dir); return -1; } return fd; } static void multipath_shm_close(void *arg) { long fd = (long)arg; close(fd); unlink(shm_lock_path); } static int _failed_wwid_op(const char *wwid, bool rw, int (*func)(const char *), const char *msg) { char path[PATH_MAX]; long lockfd; int r = -1; if (safe_sprintf(path, "%s/%s", shm_dir, wwid)) { condlog(1, "%s: path name overflow", __func__); return -1; } lockfd = multipath_shm_open(rw); if (lockfd == -1) return -1; pthread_cleanup_push(multipath_shm_close, (void *)lockfd); r = func(path); pthread_cleanup_pop(1); if (r == WWID_FAILED_ERROR) condlog(1, "%s: %s: %s", msg, wwid, strerror(errno)); else if (r == WWID_FAILED_CHANGED) condlog(3, "%s: %s", msg, wwid); else if (!rw) condlog(4, "%s: %s is %s", msg, wwid, r == WWID_IS_FAILED ? "failed" : "good"); return r; } static int _is_failed(const char *path) { struct stat st; if (lstat(path, &st) == 0) return WWID_IS_FAILED; else if (errno == ENOENT) return WWID_IS_NOT_FAILED; else return WWID_FAILED_ERROR; } static int _mark_failed(const char *path) { /* Called from _failed_wwid_op: we know that shm_lock_path exists */ if (_is_failed(path) == WWID_IS_FAILED) return WWID_FAILED_UNCHANGED; return (link(shm_lock_path, path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR); } static int _unmark_failed(const char *path) { if (_is_failed(path) == WWID_IS_NOT_FAILED) return WWID_FAILED_UNCHANGED; return (unlink(path) == 0 ? WWID_FAILED_CHANGED : WWID_FAILED_ERROR); } #define declare_failed_wwid_op(op, rw) \ int op ## _wwid(const char *wwid) \ { \ return _failed_wwid_op(wwid, (rw), _ ## op, #op); \ } declare_failed_wwid_op(is_failed, false) declare_failed_wwid_op(mark_failed, true) declare_failed_wwid_op(unmark_failed, true) int remember_cmdline_wwid(void) { FILE *f = NULL; char buf[LINE_MAX], *next, *ptr; int ret = 0; f = fopen("/proc/cmdline", "re"); if (!f) { condlog(0, "can't open /proc/cmdline : %s", strerror(errno)); return -1; } if (!fgets(buf, sizeof(buf), f)) { if (ferror(f)) condlog(0, "read of /proc/cmdline failed : %s", strerror(errno)); else condlog(0, "couldn't read /proc/cmdline"); fclose(f); return -1; } fclose(f); next = buf; while((ptr = strstr(next, "mpath.wwid="))) { ptr += 11; next = strpbrk(ptr, " \t\n"); if (next) { *next = '\0'; next++; } if (strlen(ptr)) { if (remember_wwid(ptr) != 0) ret = -1; } else { condlog(0, "empty mpath.wwid kernel command line option"); ret = -1; } if (!next) break; } return ret; }