summaryrefslogtreecommitdiff
path: root/usr/src/cmd/zfs
diff options
context:
space:
mode:
authorChris Williamson <chris.williamson@delphix.com>2017-06-23 15:56:06 -0700
committerMatthew Ahrens <mahrens@delphix.com>2017-06-26 06:45:06 -0700
commitdfc115332c94a2f62058ac7f2bce7631fbd20b3d (patch)
tree1bcc8a12ac56d03b394b32e32347d9f28f9af103 /usr/src/cmd/zfs
parent3d9b1a2a543845425f021c3f896a07b1deff87c9 (diff)
downloadillumos-gate-dfc115332c94a2f62058ac7f2bce7631fbd20b3d.tar.gz
7431 ZFS Channel Programs
Reviewed by: Matthew Ahrens <mahrens@delphix.com> Reviewed by: George Wilson <george.wilson@delphix.com> Reviewed by: John Kennedy <john.kennedy@delphix.com> Reviewed by: Dan Kimmel <dan.kimmel@delphix.com> Approved by: Garrett D'Amore <garrett@damore.org>
Diffstat (limited to 'usr/src/cmd/zfs')
-rw-r--r--usr/src/cmd/zfs/zfs_main.c206
1 files changed, 205 insertions, 1 deletions
diff --git a/usr/src/cmd/zfs/zfs_main.c b/usr/src/cmd/zfs/zfs_main.c
index e34a40a474..4f52949fba 100644
--- a/usr/src/cmd/zfs/zfs_main.c
+++ b/usr/src/cmd/zfs/zfs_main.c
@@ -21,7 +21,7 @@
/*
* Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
- * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
+ * Copyright (c) 2011, 2016 by Delphix. All rights reserved.
* Copyright 2012 Milan Jurik. All rights reserved.
* Copyright (c) 2012, Joyent, Inc. All rights reserved.
* Copyright (c) 2011-2012 Pawel Jakub Dawidek. All rights reserved.
@@ -50,6 +50,7 @@
#include <grp.h>
#include <pwd.h>
#include <signal.h>
+#include <sys/debug.h>
#include <sys/list.h>
#include <sys/mkdev.h>
#include <sys/mntent.h>
@@ -106,6 +107,7 @@ static int zfs_do_holds(int argc, char **argv);
static int zfs_do_release(int argc, char **argv);
static int zfs_do_diff(int argc, char **argv);
static int zfs_do_bookmark(int argc, char **argv);
+static int zfs_do_channel_program(int argc, char **argv);
/*
* Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
@@ -153,6 +155,7 @@ typedef enum {
HELP_RELEASE,
HELP_DIFF,
HELP_BOOKMARK,
+ HELP_CHANNEL_PROGRAM,
} zfs_help_t;
typedef struct zfs_command {
@@ -180,6 +183,7 @@ static zfs_command_t command_table[] = {
{ "promote", zfs_do_promote, HELP_PROMOTE },
{ "rename", zfs_do_rename, HELP_RENAME },
{ "bookmark", zfs_do_bookmark, HELP_BOOKMARK },
+ { "program", zfs_do_channel_program, HELP_CHANNEL_PROGRAM },
{ NULL },
{ "list", zfs_do_list, HELP_LIST },
{ NULL },
@@ -324,6 +328,10 @@ get_usage(zfs_help_t idx)
"[snapshot|filesystem]\n"));
case HELP_BOOKMARK:
return (gettext("\tbookmark <snapshot> <bookmark>\n"));
+ case HELP_CHANNEL_PROGRAM:
+ return (gettext("\tprogram [-t <instruction limit>] "
+ "[-m <memory limit (b)>] <pool> <program file> "
+ "[lua args...]\n"));
}
abort();
@@ -352,6 +360,18 @@ safe_malloc(size_t size)
return (data);
}
+void *
+safe_realloc(void *data, size_t size)
+{
+ void *newp;
+ if ((newp = realloc(data, size)) == NULL) {
+ free(data);
+ nomem();
+ }
+
+ return (newp);
+}
+
static char *
safe_strdup(char *str)
{
@@ -6979,6 +6999,190 @@ usage:
return (-1);
}
+static int
+zfs_do_channel_program(int argc, char **argv)
+{
+ int ret, fd;
+ char c;
+ char *progbuf, *filename, *poolname;
+ size_t progsize, progread;
+ nvlist_t *outnvl;
+ uint64_t instrlimit = ZCP_DEFAULT_INSTRLIMIT;
+ uint64_t memlimit = ZCP_DEFAULT_MEMLIMIT;
+ zpool_handle_t *zhp;
+
+ /* check options */
+ while (-1 !=
+ (c = getopt(argc, argv, "t:(instr-limit)m:(memory-limit)"))) {
+ switch (c) {
+ case 't':
+ case 'm': {
+ uint64_t arg;
+ char *endp;
+
+ errno = 0;
+ arg = strtoull(optarg, &endp, 0);
+ if (errno != 0 || *endp != '\0') {
+ (void) fprintf(stderr, gettext(
+ "invalid argument "
+ "'%s': expected integer\n"), optarg);
+ goto usage;
+ }
+
+ if (c == 't') {
+ if (arg > ZCP_MAX_INSTRLIMIT || arg == 0) {
+ (void) fprintf(stderr, gettext(
+ "Invalid instruction limit: "
+ "%s\n"), optarg);
+ return (1);
+ } else {
+ instrlimit = arg;
+ }
+ } else {
+ ASSERT3U(c, ==, 'm');
+ if (arg > ZCP_MAX_MEMLIMIT || arg == 0) {
+ (void) fprintf(stderr, gettext(
+ "Invalid memory limit: "
+ "%s\n"), optarg);
+ return (1);
+ } else {
+ memlimit = arg;
+ }
+ }
+ break;
+ }
+ case '?':
+ (void) fprintf(stderr, gettext("invalid option '%c'\n"),
+ optopt);
+ goto usage;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 2) {
+ (void) fprintf(stderr,
+ gettext("invalid number of arguments\n"));
+ goto usage;
+ }
+
+ poolname = argv[0];
+ filename = argv[1];
+ if (strcmp(filename, "-") == 0) {
+ fd = 0;
+ filename = "standard input";
+ } else if ((fd = open(filename, O_RDONLY)) < 0) {
+ (void) fprintf(stderr, gettext("cannot open '%s': %s\n"),
+ filename, strerror(errno));
+ return (1);
+ }
+
+ if ((zhp = zpool_open(g_zfs, poolname)) == NULL) {
+ (void) fprintf(stderr, gettext("cannot open pool '%s'"),
+ poolname);
+ return (1);
+ }
+ zpool_close(zhp);
+
+ /*
+ * Read in the channel program, expanding the program buffer as
+ * necessary.
+ */
+ progread = 0;
+ progsize = 1024;
+ progbuf = safe_malloc(progsize);
+ do {
+ ret = read(fd, progbuf + progread, progsize - progread);
+ progread += ret;
+ if (progread == progsize && ret > 0) {
+ progsize *= 2;
+ progbuf = safe_realloc(progbuf, progsize);
+ }
+ } while (ret > 0);
+
+ if (fd != 0)
+ (void) close(fd);
+ if (ret < 0) {
+ free(progbuf);
+ (void) fprintf(stderr,
+ gettext("cannot read '%s': %s\n"),
+ filename, strerror(errno));
+ return (1);
+ }
+ progbuf[progread] = '\0';
+
+ /*
+ * Any remaining arguments are passed as arguments to the lua script as
+ * a string array:
+ * {
+ * "argv" -> [ "arg 1", ... "arg n" ],
+ * }
+ */
+ nvlist_t *argnvl = fnvlist_alloc();
+ fnvlist_add_string_array(argnvl, ZCP_ARG_CLIARGV, argv + 2, argc - 2);
+
+ ret = lzc_channel_program(poolname, progbuf, instrlimit, memlimit,
+ argnvl, &outnvl);
+
+ if (ret != 0) {
+ /*
+ * On error, report the error message handed back by lua if one
+ * exists. Otherwise, generate an appropriate error message,
+ * falling back on strerror() for an unexpected return code.
+ */
+ char *errstring = NULL;
+ if (nvlist_exists(outnvl, ZCP_RET_ERROR)) {
+ (void) nvlist_lookup_string(outnvl,
+ ZCP_RET_ERROR, &errstring);
+ if (errstring == NULL)
+ errstring = strerror(ret);
+ } else {
+ switch (ret) {
+ case EINVAL:
+ errstring =
+ "Invalid instruction or memory limit.";
+ break;
+ case ENOMEM:
+ errstring = "Return value too large.";
+ break;
+ case ENOSPC:
+ errstring = "Memory limit exhausted.";
+ break;
+ case ETIME:
+ errstring = "Timed out.";
+ break;
+ case EPERM:
+ errstring = "Permission denied. Channel "
+ "programs must be run as root.";
+ break;
+ default:
+ errstring = strerror(ret);
+ }
+ }
+ (void) fprintf(stderr,
+ gettext("Channel program execution failed:\n%s\n"),
+ errstring);
+ } else {
+ (void) printf("Channel program fully executed ");
+ if (nvlist_empty(outnvl)) {
+ (void) printf("with no return value.\n");
+ } else {
+ (void) printf("with return value:\n");
+ dump_nvlist(outnvl, 4);
+ }
+ }
+
+ free(progbuf);
+ fnvlist_free(outnvl);
+ fnvlist_free(argnvl);
+ return (ret != 0);
+
+usage:
+ usage(B_FALSE);
+ return (-1);
+}
+
int
main(int argc, char **argv)
{