--- old/usr/src/lib/libzfs/common/libzfs_util.c	Mon Aug  5 17:16:43 2019
+++ new/usr/src/lib/libzfs/common/libzfs_util.c	Mon Aug  5 17:16:43 2019
@@ -22,6 +22,7 @@
 /*
  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
+ * Copyright 2015 Nexenta Systems, Inc. All rights reserved.
  * Copyright (c) 2011, 2015 by Delphix. All rights reserved.
  * Copyright 2016 Igor Kozhukhov <ikozhukhov@gmail.com>
  * Copyright (c) 2017 Datto Inc.
@@ -235,9 +236,15 @@
 		return (dgettext(TEXT_DOMAIN, "invalid diff data"));
 	case EZFS_POOLREADONLY:
 		return (dgettext(TEXT_DOMAIN, "pool is read-only"));
-	case EZFS_NO_PENDING:
-		return (dgettext(TEXT_DOMAIN, "operation is not "
-		    "in progress"));
+	case EZFS_PROPNOTSUP:
+		return (dgettext(TEXT_DOMAIN, "property is not supported"));
+	case EZFS_COSNOTFOUND:
+		return (dgettext(TEXT_DOMAIN, "CoS descriptor not found"));
+	case EZFS_COSEXIST:
+		return (dgettext(TEXT_DOMAIN, "CoS descriptor already exists"));
+	case EZFS_COSREF:
+		return (dgettext(TEXT_DOMAIN,
+			"CoS descriptor is still referenced"));
 	case EZFS_UNKNOWN:
 		return (dgettext(TEXT_DOMAIN, "unknown error"));
 	default:
@@ -389,6 +396,9 @@
 	case EROFS:
 		zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
 		break;
+	case EINVAL:
+		zfs_verror(hdl, EZFS_INVALIDNAME, fmt, ap);
+		break;
 	case ENAMETOOLONG:
 		zfs_verror(hdl, EZFS_NAMETOOLONG, fmt, ap);
 		break;
@@ -411,6 +421,50 @@
 }
 
 int
+zpool_vprop_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
+{
+	return (zpool_vprop_standard_error_fmt(hdl, error, "%s", msg));
+}
+
+/*PRINTFLIKE3*/
+int
+zpool_vprop_standard_error_fmt(libzfs_handle_t *hdl, int error,
+    const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+
+	if (zfs_common_error(hdl, error, fmt, ap) != 0) {
+		va_end(ap);
+		return (-1);
+	}
+
+	switch (error) {
+	case ENOENT:
+		zfs_verror(hdl, EZFS_COSNOTFOUND, fmt, ap);
+		break;
+	case ENOTSUP:
+		zfs_verror(hdl, EZFS_PROPNOTSUP, fmt, ap);
+		break;
+
+	case EEXIST:
+		zfs_verror(hdl, EZFS_COSEXIST, fmt, ap);
+		break;
+	case EBUSY:
+		zfs_verror(hdl, EZFS_COSREF, fmt, ap);
+		break;
+	default:
+		zfs_error_aux(hdl, strerror(error));
+		zfs_verror(hdl, EZFS_UNKNOWN, fmt, ap);
+		break;
+	}
+
+	va_end(ap);
+	return (-1);
+}
+
+int
 zpool_standard_error(libzfs_handle_t *hdl, int error, const char *msg)
 {
 	return (zpool_standard_error_fmt(hdl, error, "%s", msg));
@@ -447,7 +501,8 @@
 		break;
 
 	case EBUSY:
-		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN, "pool is busy"));
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "pool or device is busy"));
 		zfs_verror(hdl, EZFS_BUSY, fmt, ap);
 		break;
 
@@ -483,10 +538,6 @@
 	case EROFS:
 		zfs_verror(hdl, EZFS_POOLREADONLY, fmt, ap);
 		break;
-	/* There is no pending operation to cancel */
-	case ENOTACTIVE:
-		zfs_verror(hdl, EZFS_NO_PENDING, fmt, ap);
-		break;
 
 	default:
 		zfs_error_aux(hdl, strerror(error));
@@ -622,6 +673,8 @@
 	zfs_prop_init();
 	zpool_prop_init();
 	zpool_feature_init();
+	vdev_prop_init();
+	cos_prop_init();
 	libzfs_mnttab_init(hdl);
 
 	if (getenv("ZFS_PROP_DEBUG") != NULL) {
@@ -639,6 +692,8 @@
 		(void) fclose(hdl->libzfs_mnttab);
 	if (hdl->libzfs_sharetab)
 		(void) fclose(hdl->libzfs_sharetab);
+	if (hdl->libzfs_log_str)
+		free(hdl->libzfs_log_str);
 	zfs_uninit_libshare(hdl);
 	zpool_free_handles(hdl);
 	libzfs_fru_clear(hdl, B_TRUE);
@@ -807,10 +862,24 @@
 	return (0);
 }
 
+#pragma weak libzfs_log_event = libzfs_log_event_stub
+
+/* ARGSUSED hdl zc */
+void
+libzfs_log_event_stub(libzfs_handle_t *hdl, const char *zc)
+{
+}
+
 int
 zfs_ioctl(libzfs_handle_t *hdl, int request, zfs_cmd_t *zc)
 {
-	return (ioctl(hdl->libzfs_fd, request, zc));
+	int error;
+
+	error = ioctl(hdl->libzfs_fd, request, zc);
+	if (error == 0)
+		libzfs_log_event(hdl, zc->zc_name);
+
+	return (error);
 }
 
 /*
@@ -1046,8 +1115,9 @@
 			break;
 	}
 	if (i == strlen(ends)) {
-		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-		    "invalid numeric suffix '%s'"), buf);
+		if (hdl)
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "invalid numeric suffix '%s'"), buf);
 		return (-1);
 	}
 
@@ -1059,8 +1129,9 @@
 	    toupper(buf[0]) != 'B'))
 		return (10*i);
 
-	zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
-	    "invalid numeric suffix '%s'"), buf);
+	if (hdl)
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "invalid numeric suffix '%s'"), buf);
 	return (-1);
 }
 
@@ -1160,10 +1231,20 @@
 	char *value;
 	boolean_t isnone = B_FALSE;
 
-	if (type == ZFS_TYPE_POOL) {
+	switch (type) {
+	case ZFS_TYPE_POOL:
 		proptype = zpool_prop_get_type(prop);
 		propname = zpool_prop_to_name(prop);
-	} else {
+		break;
+	case ZFS_TYPE_VDEV:
+		proptype = vdev_prop_get_type(prop);
+		propname = vdev_prop_to_name(prop);
+		break;
+	case ZFS_TYPE_COS:
+		proptype = cos_prop_get_type(prop);
+		propname = cos_prop_to_name(prop);
+		break;
+	default:
 		proptype = zfs_prop_get_type(prop);
 		propname = zfs_prop_to_name(prop);
 	}
@@ -1491,6 +1572,438 @@
 	return (zprop_iter_common(func, cb, show_all, ordered, type));
 }
 
+int
+vdev_get_proplist(libzfs_handle_t *hdl, char *props, zprop_list_t **listp)
+{
+	*listp = NULL;
+
+	/*
+	 * If 'all' is specified, return a NULL list.
+	 */
+	if (strcmp(props, "all") == 0) {
+		vdev_prop_t prop;
+		for (prop = VDEV_PROP_PATH; prop < VDEV_NUM_PROPS; prop++) {
+			const char *propname = vdev_prop_to_name(prop);
+			if (addlist(hdl, (char *)propname, listp,
+			    ZFS_TYPE_VDEV))
+				return (-1);
+			listp = &(*listp)->pl_next;
+		}
+
+		return (0);
+	}
+
+	/*
+	 * If no props were specified, return an error.
+	 */
+	if (props[0] == '\0') {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "no properties specified"));
+		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
+		    "bad property list")));
+	}
+
+	/*
+	 * It would be nice to use getsubopt() here, but the inclusion of column
+	 * aliases makes this more effort than it's worth.
+	 */
+	while (*props != '\0') {
+		size_t len;
+		char *p;
+		char c;
+
+		if ((p = strchr(props, ',')) == NULL) {
+			len = strlen(props);
+			p = props + len;
+		} else {
+			len = p - props;
+		}
+
+		/*
+		 * Check for empty options.
+		 */
+		if (len == 0) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "empty property name"));
+			return (zfs_error(hdl, EZFS_BADPROP,
+			    dgettext(TEXT_DOMAIN, "bad property list")));
+		}
+
+		/*
+		 * Check all regular property names.
+		 */
+		c = props[len];
+		props[len] = '\0';
+
+		/*
+		 * Make sure we're looking at a valid prop.
+		 */
+		if (vdev_name_to_prop(props) == ZPROP_INVAL) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "invalid property '%s'"), props);
+			return (zfs_error(hdl, EZFS_BADPROP,
+			    dgettext(TEXT_DOMAIN, "bad property list")));
+		}
+
+		if (addlist(hdl, props, listp, ZFS_TYPE_VDEV))
+			return (-1);
+		listp = &(*listp)->pl_next;
+
+		props = p;
+		if (c == ',')
+			props++;
+	}
+
+	return (0);
+}
+
+int
+cos_get_proplist(libzfs_handle_t *hdl, char *props, zprop_list_t **listp)
+{
+	*listp = NULL;
+
+	/*
+	 * If 'all' is specified, return a NULL list.
+	 */
+	if (strcmp(props, "all") == 0) {
+		cos_prop_t prop;
+		for (prop = COS_PROP_GUID; prop < COS_NUM_PROPS; prop++) {
+			const char *propname = cos_prop_to_name(prop);
+			if (addlist(hdl, (char *)propname, listp,
+			    ZFS_TYPE_COS))
+				return (-1);
+			listp = &(*listp)->pl_next;
+		}
+
+		return (0);
+	}
+
+	/*
+	 * If no props were specified, return an error.
+	 */
+	if (props[0] == '\0') {
+		zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+		    "no properties specified"));
+		return (zfs_error(hdl, EZFS_BADPROP, dgettext(TEXT_DOMAIN,
+		    "bad property list")));
+	}
+
+	/*
+	 * It would be nice to use getsubopt() here, but the inclusion of column
+	 * aliases makes this more effort than it's worth.
+	 */
+	while (*props != '\0') {
+		size_t len;
+		char *p;
+		char c;
+
+		if ((p = strchr(props, ',')) == NULL) {
+			len = strlen(props);
+			p = props + len;
+		} else {
+			len = p - props;
+		}
+
+		/*
+		 * Check for empty options.
+		 */
+		if (len == 0) {
+			zfs_error_aux(hdl, dgettext(TEXT_DOMAIN,
+			    "empty property name"));
+			return (zfs_error(hdl, EZFS_BADPROP,
+			    dgettext(TEXT_DOMAIN, "bad property list")));
+		}
+
+		/*
+		 * Check all regular property names.
+		 */
+		c = props[len];
+		props[len] = '\0';
+
+		if (addlist(hdl, props, listp, ZFS_TYPE_COS))
+			return (-1);
+		listp = &(*listp)->pl_next;
+
+		props = p;
+		if (c == ',')
+			props++;
+	}
+
+	return (0);
+}
+
+void
+vdev_print_headers(zprop_get_cbdata_t *cbp)
+{
+	zprop_list_t *pl = cbp->cb_proplist;
+	int i;
+	char *title;
+	size_t len;
+
+	cbp->cb_first = B_FALSE;
+	if (cbp->cb_scripted)
+		return;
+
+	/*
+	 * Start with the length of the column headers.
+	 */
+	cbp->cb_colwidths[GET_COL_NAME] = strlen(dgettext(TEXT_DOMAIN,
+	    "POOLNAME"));
+	cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
+	    "c0t0d0s0"));
+	cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(dgettext(TEXT_DOMAIN,
+	    "PROPERTY"));
+	cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
+	    "VALUE"));
+
+	for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
+		/*
+		 * 'PROPERTY' column
+		 */
+		const char *propname = vdev_prop_to_name(pl->pl_prop);
+
+		len = strlen(propname);
+		if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
+			cbp->cb_colwidths[GET_COL_PROPERTY] = len;
+
+		/*
+		 * 'VALUE' column.
+		 */
+		if (pl != cbp->cb_proplist &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
+			cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
+
+		/*
+		 * 'NAME'
+		 */
+		if (pl->pl_prop == 0 &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
+			cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
+		}
+		/*
+		 * 'SOURCE'
+		 */
+		if (pl->pl_prop == 0 &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_SOURCE]) {
+			cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width;
+		}
+	}
+
+	/*
+	 * Now go through and print the headers.
+	 */
+	for (i = 0; i < ZFS_GET_NCOLS-1; i++) {
+		switch (cbp->cb_columns[i]) {
+		case GET_COL_NAME:
+			title = dgettext(TEXT_DOMAIN, "POOLNAME");
+			break;
+		case GET_COL_SOURCE:
+			title = dgettext(TEXT_DOMAIN, "VDEV");
+			break;
+		case GET_COL_PROPERTY:
+			title = dgettext(TEXT_DOMAIN, "PROPERTY");
+			break;
+		case GET_COL_VALUE:
+			title = dgettext(TEXT_DOMAIN, "VALUE");
+			break;
+		default:
+			title = NULL;
+		}
+
+		if (title != NULL) {
+			if (i == (ZFS_GET_NCOLS - 1) ||
+			    cbp->cb_columns[i + 1] == GET_COL_NONE)
+				(void) printf("%s", title);
+			else
+				(void) printf("%-*s  ",
+				    cbp->cb_colwidths[cbp->cb_columns[i]],
+				    title);
+		}
+	}
+	(void) printf("\n");
+}
+
+void
+cos_print_headers(zprop_get_cbdata_t *cbp)
+{
+	zprop_list_t *pl = cbp->cb_proplist;
+	int i;
+	char *title;
+	size_t len;
+
+	cbp->cb_first = B_FALSE;
+	if (cbp->cb_scripted)
+		return;
+
+	/*
+	 * Start with the length of the column headers.
+	 */
+	cbp->cb_colwidths[GET_COL_NAME] = strlen(dgettext(TEXT_DOMAIN,
+	    "POOLNAME"));
+	cbp->cb_colwidths[GET_COL_SOURCE] = strlen(dgettext(TEXT_DOMAIN,
+	    "c0t0d0s0"));
+	cbp->cb_colwidths[GET_COL_PROPERTY] = strlen(dgettext(TEXT_DOMAIN,
+	    "PROPERTY"));
+	cbp->cb_colwidths[GET_COL_VALUE] = strlen(dgettext(TEXT_DOMAIN,
+	    "VALUE"));
+
+	for (pl = cbp->cb_proplist; pl != NULL; pl = pl->pl_next) {
+		/*
+		 * 'PROPERTY' column
+		 */
+		const char *propname = cos_prop_to_name(pl->pl_prop);
+
+		len = strlen(propname);
+		if (len > cbp->cb_colwidths[GET_COL_PROPERTY])
+			cbp->cb_colwidths[GET_COL_PROPERTY] = len;
+
+		/*
+		 * 'VALUE' column.
+		 */
+		if (pl != cbp->cb_proplist &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_VALUE])
+			cbp->cb_colwidths[GET_COL_VALUE] = pl->pl_width;
+
+		/*
+		 * 'NAME'
+		 */
+		if (pl->pl_prop == 0 &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_NAME]) {
+			cbp->cb_colwidths[GET_COL_NAME] = pl->pl_width;
+		}
+		/*
+		 * 'SOURCE'
+		 */
+		if (pl->pl_prop == 0 &&
+		    pl->pl_width > cbp->cb_colwidths[GET_COL_SOURCE]) {
+			cbp->cb_colwidths[GET_COL_SOURCE] = pl->pl_width;
+		}
+	}
+
+	/*
+	 * Now go through and print the headers.
+	 */
+	for (i = 0; i < ZFS_GET_NCOLS-1; i++) {
+		switch (cbp->cb_columns[i]) {
+		case GET_COL_NAME:
+			title = dgettext(TEXT_DOMAIN, "POOLNAME");
+			break;
+		case GET_COL_SOURCE:
+			title = dgettext(TEXT_DOMAIN, "COS");
+			break;
+		case GET_COL_PROPERTY:
+			title = dgettext(TEXT_DOMAIN, "PROPERTY");
+			break;
+		case GET_COL_VALUE:
+			title = dgettext(TEXT_DOMAIN, "VALUE");
+			break;
+		default:
+			title = NULL;
+		}
+
+		if (title != NULL) {
+			if (i == (ZFS_GET_NCOLS - 1) ||
+			    cbp->cb_columns[i + 1] == GET_COL_NONE)
+				(void) printf("%s", title);
+			else
+				(void) printf("%-*s  ",
+				    cbp->cb_colwidths[cbp->cb_columns[i]],
+				    title);
+		}
+	}
+	(void) printf("\n");
+}
+
+void
+vdev_print_one_property(const char *poolname, const char *vdevname,
+    zprop_get_cbdata_t *cbp, const char *propname, const char *value)
+{
+	int i;
+	const char *str;
+
+	if (cbp->cb_first)
+		vdev_print_headers(cbp);
+
+	for (i = 0; i < ZFS_GET_NCOLS; i++) {
+		switch (cbp->cb_columns[i]) {
+		case GET_COL_NAME:
+			str = poolname;
+			break;
+
+		case GET_COL_SOURCE:
+			str = vdevname;
+			break;
+
+		case GET_COL_PROPERTY:
+			str = propname;
+			break;
+
+		case GET_COL_VALUE:
+			str = value;
+			break;
+
+		default:
+			continue;
+		}
+
+		if (cbp->cb_columns[i + 1] == GET_COL_NONE)
+			(void) printf("%s", str);
+		else if (cbp->cb_scripted)
+			(void) printf("%s\t", str);
+		else
+			(void) printf("%-*s  ",
+			    cbp->cb_colwidths[cbp->cb_columns[i]],
+			    str);
+	}
+
+	(void) printf("\n");
+}
+
+void
+cos_print_one_property(const char *poolname, const char *cosname,
+    zprop_get_cbdata_t *cbp, const char *propname, const char *value)
+{
+	int i;
+	const char *str;
+
+	if (cbp->cb_first)
+		cos_print_headers(cbp);
+
+	for (i = 0; i < ZFS_GET_NCOLS; i++) {
+		switch (cbp->cb_columns[i]) {
+		case GET_COL_NAME:
+			str = poolname;
+			break;
+
+		case GET_COL_SOURCE:
+			str = cosname;
+			break;
+
+		case GET_COL_PROPERTY:
+			str = propname;
+			break;
+
+		case GET_COL_VALUE:
+			str = value;
+			break;
+
+		default:
+			continue;
+		}
+
+		if (cbp->cb_columns[i + 1] == GET_COL_NONE)
+			(void) printf("%s", str);
+		else if (cbp->cb_scripted)
+			(void) printf("%s\t", str);
+		else
+			(void) printf("%-*s  ",
+			    cbp->cb_colwidths[cbp->cb_columns[i]],
+			    str);
+	}
+
+	(void) printf("\n");
+}
+
 /*
  * zfs_get_hole_count retrieves the number of holes (blocks which are
  * zero-filled) in the specified file using the _FIO_COUNT_FILLED ioctl. It
