From 8a597933be3389e99e7a05f01266bbb0e8be0515 Mon Sep 17 00:00:00 2001 From: Chunwei Chen Date: Fri, 5 Aug 2016 17:48:43 -0700 Subject: [PATCH] Prototype for systemd and fstab integration Signed-off-by: Chunwei Chen --- cmd/Makefile.am | 2 +- cmd/zpool/zpool_main.c | 12 +- cmd/zready/.gitignore | 1 + cmd/zready/Makefile.am | 19 ++ cmd/zready/zready.c | 272 +++++++++++++++++++++++++++ configure.ac | 1 + etc/systemd/system/.gitignore | 1 + etc/systemd/system/Makefile.am | 6 +- etc/systemd/system/zpool@.path.in | 7 + etc/systemd/system/zpool@.service.in | 15 ++ udev/rules.d/90-zfs.rules.in | 2 +- 11 files changed, 334 insertions(+), 4 deletions(-) create mode 100644 cmd/zready/.gitignore create mode 100644 cmd/zready/Makefile.am create mode 100644 cmd/zready/zready.c create mode 100644 etc/systemd/system/zpool@.path.in create mode 100644 etc/systemd/system/zpool@.service.in diff --git a/cmd/Makefile.am b/cmd/Makefile.am index 04aa7c6333da..372763b1dda2 100644 --- a/cmd/Makefile.am +++ b/cmd/Makefile.am @@ -1,3 +1,3 @@ SUBDIRS = zfs zpool zdb zhack zinject zstreamdump ztest zpios SUBDIRS += mount_zfs fsck_zfs zvol_id vdev_id arcstat dbufstat zed -SUBDIRS += arc_summary raidz_test +SUBDIRS += arc_summary raidz_test zready diff --git a/cmd/zpool/zpool_main.c b/cmd/zpool/zpool_main.c index 9041f9c33e15..f20efd75c062 100644 --- a/cmd/zpool/zpool_main.c +++ b/cmd/zpool/zpool_main.c @@ -2191,6 +2191,7 @@ zpool_do_import(int argc, char **argv) boolean_t do_rewind = B_FALSE; boolean_t xtreme_rewind = B_FALSE; boolean_t do_scan = B_FALSE; + boolean_t do_imported = B_FALSE; uint64_t pool_state, txg = -1ULL; char *cachefile = NULL; importargs_t idata = { 0 }; @@ -2221,6 +2222,9 @@ zpool_do_import(int argc, char **argv) case 'D': do_destroyed = B_TRUE; break; + case 'E': + do_imported = B_TRUE; + break; case 'f': flags |= ZFS_IMPORT_ANY_HOST; break; @@ -2420,6 +2424,11 @@ zpool_do_import(int argc, char **argv) if (pools != NULL && idata.exists && (argc == 1 || strcmp(argv[0], argv[1]) == 0)) { + if (do_imported) { + err = 0; + log_history = B_FALSE; + goto exit_early; + } (void) fprintf(stderr, gettext("cannot import '%s': " "a pool with that name already exists\n"), argv[0]); @@ -2443,12 +2452,13 @@ zpool_do_import(int argc, char **argv) } if (err == 1) { +exit_early: if (searchdirs != NULL) free(searchdirs); if (envdup != NULL) free(envdup); nvlist_free(policy); - return (1); + return (err); } /* diff --git a/cmd/zready/.gitignore b/cmd/zready/.gitignore new file mode 100644 index 000000000000..72515059e3df --- /dev/null +++ b/cmd/zready/.gitignore @@ -0,0 +1 @@ +/zready diff --git a/cmd/zready/Makefile.am b/cmd/zready/Makefile.am new file mode 100644 index 000000000000..ddb9381c70c5 --- /dev/null +++ b/cmd/zready/Makefile.am @@ -0,0 +1,19 @@ +include $(top_srcdir)/config/Rules.am + +AM_CPPFLAGS += -DDEBUG + +DEFAULT_INCLUDES += \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/lib/libspl/include + +sbin_PROGRAMS = zready + +zready_SOURCES = \ + zready.c + +zready_LDADD = \ + $(top_builddir)/lib/libnvpair/libnvpair.la \ + $(top_builddir)/lib/libuutil/libuutil.la \ + $(top_builddir)/lib/libzpool/libzpool.la \ + $(top_builddir)/lib/libzfs/libzfs.la \ + $(top_builddir)/lib/libzfs_core/libzfs_core.la diff --git a/cmd/zready/zready.c b/cmd/zready/zready.c new file mode 100644 index 000000000000..5a238253cc19 --- /dev/null +++ b/cmd/zready/zready.c @@ -0,0 +1,272 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define ZPOOL_PATH "/dev/zpool" + +static nvlist_t * +get_config(const char *dev) +{ + int fd; + vdev_label_t label; + char *path, *buf = label.vl_vdev_phys.vp_nvlist; + size_t buflen = sizeof (label.vl_vdev_phys.vp_nvlist); + struct stat64 statbuf; + uint64_t psize; + int l; + int found = 0; + nvlist_t *best = NULL; + uint64_t best_txg = 0; + uint64_t state; + uint64_t pguid = 0; + + path = strdup(dev); + + if ((fd = open64(path, O_RDONLY)) < 0) { + (void) printf("cannot open '%s': %s\n", path, strerror(errno)); + free(path); + exit(1); + } + + if (fstat64_blk(fd, &statbuf) != 0) { + (void) printf("failed to stat '%s': %s\n", path, + strerror(errno)); + free(path); + (void) close(fd); + exit(1); + } + + psize = statbuf.st_size; + psize = P2ALIGN(psize, (uint64_t)sizeof (vdev_label_t)); + + for (l = 0; l < VDEV_LABELS; l++) { + uint64_t g, txg; + nvlist_t *config = NULL; + + if (pread64(fd, &label, sizeof (label), + vdev_label_offset(psize, l, 0)) != sizeof (label)) { + (void) fprintf(stderr, "failed to read label %d\n", l); + continue; + } + + if (nvlist_unpack(buf, buflen, &config, 0) != 0) { + (void) fprintf(stderr, "failed to unpack label %d\n", l); + } else { + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_TXG, &txg) != 0) + continue; + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &g) != 0) + continue; + if (nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_STATE, &state) != 0) + continue; + + if (state == POOL_STATE_DESTROYED) + continue; + if (pguid == 0) + pguid = g; + else if (pguid != g) { + fprintf(stderr, "pool guid mismatch between labels\n"); + exit(1); + } + if (txg > best_txg) { + if (best) + nvlist_free(best); + best = config; + best_txg = txg; + } else { + nvlist_free(config); + } + found++; + } + } + + if (found < 3) + exit(1); + + free(path); + (void) close(fd); + return best; +} + +static int +build_vdev(nvlist_t *vdev, uint64_t vguid, uint64_t dguid) +{ + char dir[64]; + char *type; + uint64_t cguid; + nvlist_t **child; + uint_t nc; + int i, ready = 0; + int ret = 0; + + sprintf(dir, "%llu", (u_longlong_t)vguid); + if (mkdir(dir, 0755) < 0 && errno != EEXIST) { + fprintf(stderr, "failed to mkdir %s: %s\n", dir, strerror(errno)); + exit(1); + } + if (chdir(dir) < 0) { + fprintf(stderr, "failed to enter %s: %s\n", dir, strerror(errno)); + exit(1); + } + + if (nvlist_lookup_string(vdev, ZPOOL_CONFIG_TYPE, &type) != 0) { + fprintf(stderr, "fail to get vdev type\n"); + exit(1); + } + + if (dguid == vguid) { + ready = 1; + ret = 1; + } else if (strcmp(type, "disk") != 0 && strcmp(type, "file") != 0) { + int found = 0; + + if (nvlist_lookup_nvlist_array(vdev, ZPOOL_CONFIG_CHILDREN, &child, &nc) != 0) { + fprintf(stderr, "fail to get child vdev\n"); + exit(1); + } + for (i = 0; i < nc; i++) { + if (nvlist_lookup_uint64(child[i], ZPOOL_CONFIG_GUID, &cguid) != 0) { + fprintf(stderr, "failed to get child guid\n"); + exit(1); + } + if ((ret = build_vdev(child[i], cguid, dguid))) + break; + } + if (ret) { + for (i = 0; i < nc; i++) { + struct stat sbuf; + char path[64]; + if (nvlist_lookup_uint64(child[i], ZPOOL_CONFIG_GUID, &cguid) != 0) { + fprintf(stderr, "failed to get child guid\n"); + exit(1); + } + sprintf(path, "%llu/ready", (u_longlong_t)cguid); + if (stat(path, &sbuf) == 0) + found++; + } + if (found == nc) + ready = 1; + } + } + + if (ready && close(open("ready", O_WRONLY|O_CREAT, 0644)) < 0) { + fprintf(stderr, "failed to create \"ready\"\n"); + exit(1); + } + chdir(".."); + return ret; +} + +static void +build_tree(nvlist_t *config) +{ + DIR *dfd; + struct dirent *dirent; + nvlist_t *vdev; + uint64_t pguid, vguid, dguid, nc; + char *name; + int found = 0; + int fd; + if (nvlist_lookup_nvlist(config, ZPOOL_CONFIG_VDEV_TREE, &vdev) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_VDEV_CHILDREN, &nc) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_POOL_GUID, &pguid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_TOP_GUID, &vguid) != 0 || + nvlist_lookup_uint64(config, ZPOOL_CONFIG_GUID, &dguid) != 0 || + nvlist_lookup_string(config, ZPOOL_CONFIG_POOL_NAME, &name) != 0) { + fprintf(stderr, "fail to parse pool config\n"); + exit(1); + } + + /* make pool root dir and cd into it */ + if (mkdir(name, 0755) < 0 && errno != EEXIST) { + fprintf(stderr, "failed to mkdir %s: %s\n", name, strerror(errno)); + exit(1); + } + if (chdir(name) < 0) { + fprintf(stderr, "failed to enter %s: %s\n", name, strerror(errno)); + exit(1); + } + + /* write pool_guid, TODO: we need to check pool_guid is correct */ + if ((fd = open("pool_guid", O_WRONLY|O_CREAT|O_EXCL, 0644)) > 0) { + char u[64]; + sprintf(u, "%llu", (u_longlong_t)pguid); + write(fd, u, strlen(u)); + close(fd); + } + + /* decend into top level vdev */ + build_vdev(vdev, vguid, dguid); + + dfd = opendir("."); + while ((dirent = readdir(dfd)) != NULL) { + struct stat sbuf; + char path[64]; + if (strcmp(dirent->d_name, ".") == 0 || strcmp(dirent->d_name, "..") == 0) + continue; + if (snprintf(path, 64, "%s/ready", dirent->d_name) >= 64) + continue; + if (stat(path, &sbuf) == 0) + found++; + } + closedir(dfd); + + if ((found == nc) && close(open("ready", O_WRONLY|O_CREAT, 0644)) < 0) { + fprintf(stderr, "failed to create \"ready\"\n"); + exit(1); + } +} + +int +main(int argc, char **argv) +{ + nvlist_t *config; + if (argc < 2) { + fprintf(stderr, "Usage: %s \n", argv[0]); + exit(1); + } + config = get_config(argv[1]); + if (!config) + return (0); + + if (mkdir(ZPOOL_PATH, 0755) < 0 && errno != EEXIST) { + fprintf(stderr, "failed to mkdir %s: %s\n", ZPOOL_PATH, strerror(errno)); + exit(1); + } + if (chdir(ZPOOL_PATH) < 0) { + fprintf(stderr, "failed to enter %s: %s\n", ZPOOL_PATH, strerror(errno)); + exit(1); + } + build_tree(config); + nvlist_free(config); + return (0); +} diff --git a/configure.ac b/configure.ac index 9a8d6d24b836..143ee0360142 100644 --- a/configure.ac +++ b/configure.ac @@ -113,6 +113,7 @@ AC_CONFIG_FILES([ cmd/arc_summary/Makefile cmd/zed/Makefile cmd/raidz_test/Makefile + cmd/zready/Makefile contrib/Makefile contrib/bash_completion.d/Makefile contrib/dracut/Makefile diff --git a/etc/systemd/system/.gitignore b/etc/systemd/system/.gitignore index efada54ad932..97f460bf42c1 100644 --- a/etc/systemd/system/.gitignore +++ b/etc/systemd/system/.gitignore @@ -1,3 +1,4 @@ *.service *.target *.preset +*.path diff --git a/etc/systemd/system/Makefile.am b/etc/systemd/system/Makefile.am index b097497e050f..490eb4a213a7 100644 --- a/etc/systemd/system/Makefile.am +++ b/etc/systemd/system/Makefile.am @@ -7,7 +7,9 @@ systemdunit_DATA = \ zfs-import-scan.service \ zfs-mount.service \ zfs-share.service \ - zfs.target + zfs.target \ + zpool@.path \ + zpool@.service EXTRA_DIST = \ $(top_srcdir)/etc/systemd/system/zfs-zed.service.in \ @@ -16,6 +18,8 @@ EXTRA_DIST = \ $(top_srcdir)/etc/systemd/system/zfs-mount.service.in \ $(top_srcdir)/etc/systemd/system/zfs-share.service.in \ $(top_srcdir)/etc/systemd/system/zfs.target.in \ + $(top_srcdir)/etc/systemd/system/zpool@.path.in \ + $(top_srcdir)/etc/systemd/system/zpool@.service.in \ $(top_srcdir)/etc/systemd/system/50-zfs.preset.in $(systemdunit_DATA) $(systemdpreset_DATA):%:%.in diff --git a/etc/systemd/system/zpool@.path.in b/etc/systemd/system/zpool@.path.in new file mode 100644 index 000000000000..acb8e3e94ddf --- /dev/null +++ b/etc/systemd/system/zpool@.path.in @@ -0,0 +1,7 @@ +[Unit] +Description=zpool ready path +DefaultDependencies=no + +[Path] +PathExists=/dev/zpool/%i/ready +Unit=zpool@%i.service diff --git a/etc/systemd/system/zpool@.service.in b/etc/systemd/system/zpool@.service.in new file mode 100644 index 000000000000..b51f8c3f399e --- /dev/null +++ b/etc/systemd/system/zpool@.service.in @@ -0,0 +1,15 @@ +[Unit] +Description="Import pool" +DefaultDependencies=no +Requires=zpool@%i.path +After=zpool@%i.path + +[Service] +Type=oneshot +RemainAfterExit=yes +ExecStartPre=/sbin/modprobe zfs +ExecStart=@sbindir@/zpool import -EN %i +ExecStop=@sbindir@/zpool export %i + +[Install] +WantedBy=local-fs.target diff --git a/udev/rules.d/90-zfs.rules.in b/udev/rules.d/90-zfs.rules.in index 855c154040f5..1afd31a4f835 100644 --- a/udev/rules.d/90-zfs.rules.in +++ b/udev/rules.d/90-zfs.rules.in @@ -2,7 +2,7 @@ SUBSYSTEM!="block|misc", GOTO="zfs_end" ACTION!="add|change", GOTO="zfs_end" ENV{ID_FS_TYPE}=="zfs", RUN+="/sbin/modprobe zfs" -ENV{ID_FS_TYPE}=="zfs_member", RUN+="/sbin/modprobe zfs" +ENV{ID_FS_TYPE}=="zfs_member", RUN+="/sbin/modprobe zfs", RUN+="@sbindir@/zready /dev/%k" KERNEL=="null", SYMLINK+="root" SYMLINK=="null", SYMLINK+="root"