Open up libzfs_run_process/libzfs_load_module
[zfs.git] / lib / libzfs / libzfs_util.c
index 259f5fd..37dba8d 100644 (file)
@@ -19,8 +19,7 @@
  * CDDL HEADER END
  */
 /*
- * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
- * Use is subject to license terms.
+ * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  */
 
 /*
 #include <unistd.h>
 #include <ctype.h>
 #include <math.h>
+#include <sys/stat.h>
 #include <sys/mnttab.h>
 #include <sys/mntent.h>
 #include <sys/types.h>
+#include <wait.h>
 
 #include <libzfs.h>
 
@@ -70,7 +71,7 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_BADPROP:
                return (dgettext(TEXT_DOMAIN, "invalid property value"));
        case EZFS_PROPREADONLY:
-               return (dgettext(TEXT_DOMAIN, "read only property"));
+               return (dgettext(TEXT_DOMAIN, "read-only property"));
        case EZFS_PROPTYPE:
                return (dgettext(TEXT_DOMAIN, "property doesn't apply to "
                    "datasets of this type"));
@@ -90,12 +91,10 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_BADSTREAM:
                return (dgettext(TEXT_DOMAIN, "invalid backup stream"));
        case EZFS_DSREADONLY:
-               return (dgettext(TEXT_DOMAIN, "dataset is read only"));
+               return (dgettext(TEXT_DOMAIN, "dataset is read-only"));
        case EZFS_VOLTOOBIG:
                return (dgettext(TEXT_DOMAIN, "volume size exceeds limit for "
                    "this system"));
-       case EZFS_VOLHASDATA:
-               return (dgettext(TEXT_DOMAIN, "volume has data"));
        case EZFS_INVALIDNAME:
                return (dgettext(TEXT_DOMAIN, "invalid name"));
        case EZFS_BADRESTORE:
@@ -138,16 +137,12 @@ libzfs_error_description(libzfs_handle_t *hdl)
                return (dgettext(TEXT_DOMAIN, "smb remove share failed"));
        case EZFS_SHARESMBFAILED:
                return (dgettext(TEXT_DOMAIN, "smb add share failed"));
-       case EZFS_ISCSISVCUNAVAIL:
-               return (dgettext(TEXT_DOMAIN,
-                   "iscsitgt service need to be enabled by "
-                   "a privileged user"));
-       case EZFS_DEVLINKS:
-               return (dgettext(TEXT_DOMAIN, "failed to create /dev links"));
        case EZFS_PERM:
                return (dgettext(TEXT_DOMAIN, "permission denied"));
        case EZFS_NOSPC:
                return (dgettext(TEXT_DOMAIN, "out of space"));
+       case EZFS_FAULT:
+               return (dgettext(TEXT_DOMAIN, "bad address"));
        case EZFS_IO:
                return (dgettext(TEXT_DOMAIN, "I/O error"));
        case EZFS_INTR:
@@ -161,12 +156,6 @@ libzfs_error_description(libzfs_handle_t *hdl)
                return (dgettext(TEXT_DOMAIN, "recursive dataset dependency"));
        case EZFS_NOHISTORY:
                return (dgettext(TEXT_DOMAIN, "no history available"));
-       case EZFS_UNSHAREISCSIFAILED:
-               return (dgettext(TEXT_DOMAIN,
-                   "iscsitgtd failed request to unshare"));
-       case EZFS_SHAREISCSIFAILED:
-               return (dgettext(TEXT_DOMAIN,
-                   "iscsitgtd failed request to share"));
        case EZFS_POOLPROPS:
                return (dgettext(TEXT_DOMAIN, "failed to retrieve "
                    "pool properties"));
@@ -194,9 +183,6 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_NODELEGATION:
                return (dgettext(TEXT_DOMAIN, "delegated administration is "
                    "disabled on pool"));
-       case EZFS_PERMRDONLY:
-               return (dgettext(TEXT_DOMAIN, "snapshot permissions cannot be"
-                   " modified"));
        case EZFS_BADCACHE:
                return (dgettext(TEXT_DOMAIN, "invalid or missing cache file"));
        case EZFS_ISL2CACHE:
@@ -210,6 +196,34 @@ libzfs_error_description(libzfs_handle_t *hdl)
        case EZFS_ACTIVE_SPARE:
                return (dgettext(TEXT_DOMAIN, "pool has active shared spare "
                    "device"));
+       case EZFS_UNPLAYED_LOGS:
+               return (dgettext(TEXT_DOMAIN, "log device has unplayed intent "
+                   "logs"));
+       case EZFS_REFTAG_RELE:
+               return (dgettext(TEXT_DOMAIN, "no such tag on this dataset"));
+       case EZFS_REFTAG_HOLD:
+               return (dgettext(TEXT_DOMAIN, "tag already exists on this "
+                   "dataset"));
+       case EZFS_TAGTOOLONG:
+               return (dgettext(TEXT_DOMAIN, "tag too long"));
+       case EZFS_PIPEFAILED:
+               return (dgettext(TEXT_DOMAIN, "pipe create failed"));
+       case EZFS_THREADCREATEFAILED:
+               return (dgettext(TEXT_DOMAIN, "thread create failed"));
+       case EZFS_POSTSPLIT_ONLINE:
+               return (dgettext(TEXT_DOMAIN, "disk was split from this pool "
+                   "into a new one"));
+       case EZFS_SCRUBBING:
+               return (dgettext(TEXT_DOMAIN, "currently scrubbing; "
+                   "use 'zpool scrub -s' to cancel current scrub"));
+       case EZFS_NO_SCRUB:
+               return (dgettext(TEXT_DOMAIN, "there is no active scrub"));
+       case EZFS_DIFF:
+               return (dgettext(TEXT_DOMAIN, "unable to generate diffs"));
+       case EZFS_DIFFDATA:
+               return (dgettext(TEXT_DOMAIN, "invalid diff data"));
+       case EZFS_POOLREADONLY:
+               return (dgettext(TEXT_DOMAIN, "pool is read-only"));
        case EZFS_UNKNOWN:
                return (dgettext(TEXT_DOMAIN, "unknown error"));
        default:
@@ -298,6 +312,10 @@ zfs_common_error(libzfs_handle_t *hdl, int error, const char *fmt,
                zfs_verror(hdl, EZFS_IO, fmt, ap);
                return (-1);
 
+       case EFAULT:
+               zfs_verror(hdl, EZFS_FAULT, fmt, ap);
+               return (-1);
+
        case EINTR:
                zfs_verror(hdl, EZFS_INTR, fmt, ap);
                return (-1);
@@ -354,9 +372,7 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
                zfs_verror(hdl, EZFS_BUSY, fmt, ap);
                break;
        case EROFS:
-               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-                   "snapshot permissions cannot be modified"));
-               zfs_verror(hdl, EZFS_PERMRDONLY, fmt, ap);
+               zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
                break;
        case ENAMETOOLONG:
                zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
@@ -364,8 +380,13 @@ zfs_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
        case ENOTSUP:
                zfs_verror(hdl, EZFS_BADVERSION, fmt, ap);
                break;
+       case EAGAIN:
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "pool I/O is currently suspended"));
+               zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
+               break;
        default:
-               zfs_error_aux(hdl, strerror(errno));
+               zfs_error_aux(hdl, strerror(error));
                zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
                break;
        }
@@ -438,6 +459,16 @@ zpool_standard_error_fmt(libzfs_handle_t *hdl, int error, const char *fmt, ...)
                zfs_verror(hdl, EZFS_NOSPC, fmt, ap);
                return (-1);
 
+       case EAGAIN:
+               zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+                   "pool I/O is currently suspended"));
+               zfs_verror(hdl, EZFS_POOLUNAVAIL, fmt, ap);
+               break;
+
+       case EROFS:
+               zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
+               break;
+
        default:
                zfs_error_aux(hdl, strerror(error));
                zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
@@ -471,6 +502,29 @@ zfs_alloc(libzfs_handle_t *hdl, size_t size)
 }
 
 /*
+ * A safe form of asprintf() which will die if the allocation fails.
+ */
+/*PRINTFLIKE2*/
+char *
+zfs_asprintf(libzfs_handle_t *hdl, const char *fmt, ...)
+{
+       va_list ap;
+       char *ret;
+       int err;
+
+       va_start(ap, fmt);
+
+       err = vasprintf(&ret, fmt, ap);
+
+       va_end(ap);
+
+       if (err < 0)
+               (void) no_memory(hdl);
+
+       return (ret);
+}
+
+/*
  * A safe form of realloc(), which also zeroes newly allocated space.
  */
 void *
@@ -519,13 +573,13 @@ zfs_nicenum(uint64_t num, char *buf, size_t buflen)
        u = " KMGTPE"[index];
 
        if (index == 0) {
-               (void) snprintf(buf, buflen, "%llu", n);
+               (void) snprintf(buf, buflen, "%llu", (u_longlong_t) n);
        } else if ((num & ((1ULL << 10 * index) - 1)) == 0) {
                /*
                 * If this is an even multiple of the base, always display
                 * without any decimal precision.
                 */
-               (void) snprintf(buf, buflen, "%llu%c", n, u);
+               (void) snprintf(buf, buflen, "%llu%c", (u_longlong_t) n, u);
        } else {
                /*
                 * We want to choose a precision that reflects the best choice
@@ -551,21 +605,102 @@ libzfs_print_on_error(libzfs_handle_t *hdl, boolean_t printerr)
        hdl->libzfs_printerr = printerr;
 }
 
+static int
+libzfs_module_loaded(const char *module)
+{
+       FILE *f;
+       int result = 0;
+       char name[256];
+
+       f = fopen("/proc/modules", "r");
+       if (f == NULL)
+               return -1;
+
+       while (fgets(name, sizeof(name), f)) {
+               char *c = strchr(name, ' ');
+               if (!c)
+                       continue;
+               *c = 0;
+               if (strcmp(module, name) == 0) {
+                       result = 1;
+                       break;
+               }
+       }
+       fclose(f);
+
+       return result;
+}
+
+int
+libzfs_run_process(const char *path, char *argv[])
+{
+       pid_t pid;
+       int rc;
+
+       pid = vfork();
+       if (pid == 0) {
+               close(1);
+               close(2);
+               (void) execvp(path, argv);
+               _exit(-1);
+       } else if (pid > 0) {
+               int status;
+
+               while ((rc = waitpid(pid, &status, 0)) == -1 &&
+                       errno == EINTR);
+               if (rc < 0 || !WIFEXITED(status))
+                       return -1;
+
+               return WEXITSTATUS(status);
+       }
+
+       return -1;
+}
+
+int
+libzfs_load_module(const char *module)
+{
+       char *argv[4] = {"/sbin/modprobe", "-q", (char *)module, (char *)0};
+
+       if (libzfs_module_loaded(module))
+               return 0;
+
+       return libzfs_run_process("/sbin/modprobe", argv);
+}
+
 libzfs_handle_t *
 libzfs_init(void)
 {
        libzfs_handle_t *hdl;
 
-       if ((hdl = calloc(sizeof (libzfs_handle_t), 1)) == NULL) {
+       if (libzfs_load_module("zfs") != 0) {
+               (void) fprintf(stderr, gettext("Failed to load ZFS module "
+                              "stack.\nLoad the module manually by running "
+                              "'insmod <location>/zfs.ko' as root.\n"));
+               return (NULL);
+       }
+
+       if ((hdl = calloc(1, sizeof (libzfs_handle_t))) == NULL) {
                return (NULL);
        }
 
        if ((hdl->libzfs_fd = open(ZFS_DEV, O_RDWR)) < 0) {
+               (void) fprintf(stderr, gettext("Unable to open %s: %s.\n"),
+                              ZFS_DEV, strerror(errno));
+               if (errno == ENOENT)
+                       (void) fprintf(stderr,
+                            gettext("Verify the ZFS module stack is "
+                            "loaded by running '/sbin/modprobe zfs'.\n"));
+
                free(hdl);
                return (NULL);
        }
 
+#ifdef HAVE_SETMNTENT
+       if ((hdl->libzfs_mnttab = setmntent(MNTTAB, "r")) == NULL) {
+#else
        if ((hdl->libzfs_mnttab = fopen(MNTTAB, "r")) == NULL) {
+#endif
                (void) close(hdl->libzfs_fd);
                free(hdl);
                return (NULL);
@@ -575,6 +710,7 @@ libzfs_init(void)
 
        zfs_prop_init();
        zpool_prop_init();
+       libzfs_mnttab_init(hdl);
 
        return (hdl);
 }
@@ -584,14 +720,22 @@ libzfs_fini(libzfs_handle_t *hdl)
 {
        (void) close(hdl->libzfs_fd);
        if (hdl->libzfs_mnttab)
+#ifdef HAVE_SETMNTENT
+               (void) endmntent(hdl->libzfs_mnttab);
+#else
                (void) fclose(hdl->libzfs_mnttab);
+#endif
        if (hdl->libzfs_sharetab)
                (void) fclose(hdl->libzfs_sharetab);
+#ifdef HAVE_ZPL
        zfs_uninit_libshare(hdl);
+#endif
        if (hdl->libzfs_log_str)
                (void) free(hdl->libzfs_log_str);
        zpool_free_handles(hdl);
+       libzfs_fru_clear(hdl, B_TRUE);
        namespace_clear(hdl);
+       libzfs_mnttab_fini(hdl);
        free(hdl);
 }
 
@@ -659,6 +803,46 @@ zfs_path_to_zhandle(libzfs_handle_t *hdl, char *path, zfs_type_t argtype)
 }
 
 /*
+ * Given a shorthand device name, check if a file by that name exists in a list
+ * of directories under /dev.  If one is found, store its full path in the
+ * buffer pointed to by the path argument and return 0, else return -1.  The
+ * path buffer must be allocated by the caller.
+ */
+int
+zfs_resolve_shortname(const char *name, char *path, size_t pathlen)
+{
+       int i, err;
+       char dirs[5][9] = {"by-id", "by-label", "by-path", "by-uuid", "zpool"};
+
+       (void) snprintf(path, pathlen, "%s/%s", DISK_ROOT, name);
+       err = access(path, F_OK);
+       for (i = 0; i < 5 && err < 0; i++) {
+               (void) snprintf(path, pathlen, "%s/%s/%s",
+                   UDISK_ROOT, dirs[i], name);
+               err = access(path, F_OK);
+       }
+       return err;
+}
+
+/*
+ * Append partition suffix to a device path.  This should be used to generate
+ * the name of a whole disk as it is stored in the vdev label.  The
+ * user-visible names of whole disks do not contain the partition information.
+ * Modifies buf which must be allocated by the caller.
+ */
+void
+zfs_append_partition(const char *path, char *buf, size_t buflen)
+{
+       if (strncmp(path, UDISK_ROOT, strlen(UDISK_ROOT)) == 0)
+               (void) snprintf(buf, buflen, "%s%s%s", path, "-part",
+                       FIRST_SLICE);
+       else
+               (void) snprintf(buf, buflen, "%s%s%s", path,
+                       isdigit(path[strlen(path)-1]) ?  "p" : "",
+                       FIRST_SLICE);
+}
+
+/*
  * Initialize the zc_nvlist_dst member to prepare for receiving an nvlist from
  * an ioctl().
  */
@@ -666,10 +850,10 @@ int
 zcmd_alloc_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc, size_t len)
 {
        if (len == 0)
-               len = 2048;
+               len = 16 * 1024;
        zc->zc_nvlist_dst_size = len;
        if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
-           zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == NULL)
+           zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
                return (-1);
 
        return (0);
@@ -685,8 +869,7 @@ zcmd_expand_dst_nvlist(libzfs_handle_t *hdl, zfs_cmd_t *zc)
 {
        free((void *)(uintptr_t)zc->zc_nvlist_dst);
        if ((zc->zc_nvlist_dst = (uint64_t)(uintptr_t)
-           zfs_alloc(hdl, zc->zc_nvlist_dst_size))
-           == NULL)
+           zfs_alloc(hdl, zc->zc_nvlist_dst_size)) == 0)
                return (-1);
 
        return (0);
@@ -792,6 +975,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
            "PROPERTY"));
        cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
            "VALUE"));
+       cbp->cb_colwidths[GET_COL_RECVD] = strlen(dgettext(TEXT_DOMAIN,
+           "RECEIVED"));
        cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
            "SOURCE"));
 
@@ -805,7 +990,7 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
         * inheriting from the longest name.  This is acceptable because in the
         * majority of cases 'SOURCE' is the last column displayed, and we don't
         * use the width anyway.  Note that the 'VALUE' column can be oversized,
-        * if the name of the property is much longer the any values we find.
+        * if the name of the property is much longer than any values we find.
         */
        for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
                /*
@@ -836,6 +1021,11 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
                    pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
                        cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
 
+               /* 'RECEIVED' column. */
+               if (pl != cbp->cb_proplist &&
+                   pl->pl_recvd_width > cbp->cb_colwidths[GET_COL_RECVD])
+                       cbp->cb_colwidths[GET_COL_RECVD] = pl->pl_recvd_width;
+
                /*
                 * 'NAME' and 'SOURCE' columns
                 */
@@ -851,7 +1041,7 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
        /*
         * Now go through and print the headers.
         */
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < ZFS_GET_NCOLS; i++) {
                switch (cbp->cb_columns[i]) {
                case GET_COL_NAME:
                        title = dgettext(TEXT_DOMAIN, "NAME");
@@ -862,6 +1052,9 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
                case GET_COL_VALUE:
                        title = dgettext(TEXT_DOMAIN, "VALUE");
                        break;
+               case GET_COL_RECVD:
+                       title = dgettext(TEXT_DOMAIN, "RECEIVED");
+                       break;
                case GET_COL_SOURCE:
                        title = dgettext(TEXT_DOMAIN, "SOURCE");
                        break;
@@ -870,7 +1063,8 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
                }
 
                if (title != NULL) {
-                       if (i == 3 || cbp->cb_columns[i + 1] == 0)
+                       if (i == (ZFS_GET_NCOLS - 1) ||
+                           cbp->cb_columns[i + 1] == GET_COL_NONE)
                                (void) printf("%s", title);
                        else
                                (void) printf("%-*s  ",
@@ -888,10 +1082,10 @@ zprop_print_headers(zprop_get_cbdata_t *cbp, zfs_type_t type)
 void
 zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
     const char *propname, const char *value, zprop_source_t sourcetype,
-    const char *source)
+    const char *source, const char *recvd_value)
 {
        int i;
-       const char *str;
+       const char *str = NULL;
        char buf[128];
 
        /*
@@ -903,7 +1097,7 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
        if (cbp->cb_first)
                zprop_print_headers(cbp, cbp->cb_type);
 
-       for (i = 0; i < 4; i++) {
+       for (i = 0; i < ZFS_GET_NCOLS; i++) {
                switch (cbp->cb_columns[i]) {
                case GET_COL_NAME:
                        str = name;
@@ -940,14 +1134,21 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
                                    "inherited from %s", source);
                                str = buf;
                                break;
+                       case ZPROP_SRC_RECEIVED:
+                               str = "received";
+                               break;
                        }
                        break;
 
+               case GET_COL_RECVD:
+                       str = (recvd_value == NULL ? "-" : recvd_value);
+                       break;
+
                default:
                        continue;
                }
 
-               if (cbp->cb_columns[i + 1] == 0)
+               if (cbp->cb_columns[i + 1] == GET_COL_NONE)
                        (void) printf("%s", str);
                else if (cbp->cb_scripted)
                        (void) printf("%s\t", str);
@@ -955,7 +1156,6 @@ zprop_print_one_property(const char *name, zprop_get_cbdata_t *cbp,
                        (void) printf("%-*s  ",
                            cbp->cb_colwidths[cbp->cb_columns[i]],
                            str);
-
        }
 
        (void) printf("\n");
@@ -1017,7 +1217,7 @@ zfs_nicestrtonum(libzfs_handle_t *hdl, const char *value, uint64_t *num)
                return (-1);
        }
 
-       /* Rely on stroull() to process the numeric portion.  */
+       /* Rely on strtoull() to process the numeric portion.  */
        errno = 0;
        *num = strtoull(value, &end, 10);
 
@@ -1209,7 +1409,7 @@ addlist(libzfs_handle_t *hdl, char *propname, zprop_list_t **listp,
         * dataset property,
         */
        if (prop == ZPROP_INVAL && (type == ZFS_TYPE_POOL ||
-           !zfs_prop_user(propname))) {
+           (!zfs_prop_user(propname) && !zfs_prop_userquota(propname)))) {
                zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
                    "invalid property '%s'"), propname);
                return (zfs_error(hdl, EZFS_BADPROP,