/* * GPL HEADER START * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 only, * as published by the Free Software Foundation. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License version 2 for more details. A copy is * included in the COPYING file that accompanied this code. * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * GPL HEADER END */ /* * Copyright (c) 2017, Intel Corporation. * * lustre/utils/lsnapshot.c * * Author: Fan, Yong */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "obdctl.h" #define SNAPSHOT_CONF_DIR "/etc/lsnapshot" #define LDEV_CONF "/etc/ldev.conf" #define SNAPSHOT_LOG "/var/log/lsnapshot.log" #define SNAPSHOT_MAGIC "0x14F711B9" #define MAX_BUF_SIZE 4096 enum snapshot_role { SR_MGS = 0x0001, SR_MDT = 0x0002, SR_OST = 0x0004, }; struct snapshot_target { struct list_head st_list; /* Target node name. */ char *st_host; /* Where the pool is */ char *st_dir; /* The target pool name on the target node. */ char *st_pool; /* The backend filesystem name against the target pool. */ char *st_filesystem; int st_role; unsigned int st_index; unsigned int st_gen; int st_line; int st_status; pid_t st_pid; bool st_ignored; }; struct snapshot_instance { struct list_head si_mdts_list; struct list_head si_osts_list; struct snapshot_target *si_mgs; struct snapshot_target *si_mdt0; FILE *si_log_fp; char *si_rsh; char *si_fsname; char *si_ssname; char *si_new_ssname; char *si_comment; int si_conf_fd; int si_timeout; bool si_barrier; bool si_detail; bool si_force; }; static const char snapshot_rsh_default[] = "ssh"; static char snapshot_path[MAX_BUF_SIZE]; static char *snapshot_role2name(char *name, enum snapshot_role role, __u32 index) { if (role & SR_MDT) snprintf(name, 8, "MDT%04x", index); else if (role & SR_MGS) snprintf(name, 4, "MGS"); else snprintf(name, 8, "OST%04x", index); return name; } #define SNAPSHOT_ADD_LOG(si, format, ...) \ do { \ char buf[MAX_BUF_SIZE]; \ char *ptr; \ time_t tt; \ \ memset(buf, 0, sizeof(buf)); \ time(&tt); \ snprintf(buf, sizeof(buf) - 1, "%s", ctime(&tt)); \ ptr = strrchr(buf, '\n'); \ if (ptr) \ *ptr = '\0'; \ \ fprintf(si->si_log_fp, "%s (%d:%s:%d:%s:%s): "format, buf, \ getpid(), __func__, __LINE__, si->si_fsname, \ si->si_rsh, ## __VA_ARGS__); \ } while (0) #define DRSH "%s %s" #define DFSNAME "%s/%s" #define DSSNAME "%s/%s@%s" #define DZFS "%s zfs" #define DIMPORT "%s zpool import -d %s %s > /dev/null 2>&1" #define PRSH(si, st) (si)->si_rsh, (st)->st_host #define PFSNAME(st) (st)->st_pool, (st)->st_filesystem #define PSSNAME(si, st) PFSNAME(st), (si)->si_ssname #define PSS_NEW(si, st) PFSNAME(st), (si)->si_new_ssname #define PZFS(st) snapshot_path #define PIMPORT(st) snapshot_path, \ (st)->st_dir ? (st)->st_dir : "/dev -d /tmp", (st)->st_pool char *snapshot_fgets(FILE *fp, char *buf, int buflen) { char *ptr; memset(buf, 0, buflen); if (fgets(buf, buflen, fp) == NULL) return NULL; ptr = strchr(buf, '\n'); if (ptr) *ptr = '\0'; return buf; } static int snapshot_exec(const char *cmd) { int rc; errno = 0; /* system() return value depends on both the system() general framework, * such as whether fork()/exec() success or fail, and the real @cmd exec * result. Especially, if the @cmd is remote command, we may cannot know * the real failure. */ rc = system(cmd); /* fork()/exec() error */ if (rc == -1) return errno != 0 ? -errno : -1; if (WIFEXITED(rc)) { rc = WEXITSTATUS(rc); if (rc > 0) rc = -rc; } else if (WIFSIGNALED(rc)) { rc = -EINTR; } else { /* all other known or unknown cases. */ rc = -EFAULT; } return rc; } static int snapshot_load_conf_ldev(struct snapshot_instance *si, char *buf, struct snapshot_target *st, char **role) { char *label = NULL; char *device = NULL; char *ignore = NULL; char *ptr; char *ptr1; int len; int rc; rc = sscanf(buf, "%ms %ms %ms %ms", &st->st_host, &ignore, &label, &device); if (rc < 4) { rc = -EINVAL; goto out; } free(ignore); /* Format of device: * [md|zfs:][pool_dir/]/ */ ptr = strchr(device, ':'); if (ptr) { ptr++; if (strncmp(device, "zfs:", strlen("zfs:")) != 0) { rc = -EINVAL; goto out; } } else { ptr = device; } ptr1 = strrchr(ptr, '/'); /* "ptr1 - ptr + 1 == strlen(ptr)" means '/' is at the tail. */ if (!ptr1 || ptr1 == ptr || ptr1 - ptr + 1 == strlen(ptr)) { rc = -EINVAL; goto out; } len = strlen(ptr1); st->st_filesystem = malloc(len); if (!st->st_filesystem) { rc = -ENOMEM; goto out; } *ptr1 = '\0'; strncpy(st->st_filesystem, ptr1 + 1, len - 1); st->st_filesystem[len - 1] = '\0'; if (*ptr == '/') { ptr1 = strrchr(ptr, '/'); *ptr1 = '\0'; st->st_dir = strdup(ptr); if (!st->st_dir) { rc = -ENOMEM; goto out; } ptr = ptr1 + 1; } st->st_pool = strdup(ptr); if (!st->st_pool) { rc = -ENOMEM; goto out; } /* Format of label: * fsname- or */ ptr = strrchr(label, '-'); if (ptr) { if (strncmp(si->si_fsname, label, ptr - label) != 0) { /* This line is NOT for current filesystem .*/ rc = -EAGAIN; goto out; } ptr++; } else { ptr = label; } if (strlen(ptr) < 3 || strlen(ptr) > 7) { rc = -EINVAL; goto out; } *role = malloc(4); if (!*role) { rc = -ENOMEM; goto out; } strncpy(*role, ptr, 3); (*role)[3] = 0; ptr += 3; len = 0; while (isxdigit(ptr[len])) { if (isdigit(ptr[len])) st->st_index = st->st_index * 16 + ptr[len] - '0'; else if (isupper(ptr[len])) st->st_index = st->st_index * 16 + ptr[len] - 'A' + 10; else st->st_index = st->st_index * 16 + ptr[len] - 'a' + 10; len++; } if (len == 0) { if (strncasecmp(*role, "MGS", 3) != 0) rc = -EINVAL; else rc = 0; goto out; } if (!isxdigit(ptr[len]) && ptr[len] != '\0') { rc = -EINVAL; goto out; } out: if (label) free(label); if (device) free(device); return rc; } /** * For old snasphot tools, the configration is in /etc/lsnapshot/${fsname}.conf, * the format is: * * * For example: * * host-mdt1 /tmp myfs-mdt1 mdt1 MGS,MDT 0 * host-mdt2 /tmp myfs-mdt2 mdt2 MDT 1 * host-ost1 /tmp myfs-ost1 ost1 OST 0 * host-ost2 /tmp myfs-ost2 ost2 OST 1 * * * For new snasphot tools, the configration is in /etc/ldev.conf, which is not * only for snapshot, but also for other purpose. The format is: * foreign/-