예제 #1
0
파일: inetadm.c 프로젝트: andreiw/polaris
/*
 * Add the proto list contained in array 'plist' to entry 'entry', storing
 * aside the scf_value_t's created and added to the entry in a list that the
 * pointer referenced by sv_list is made to point at.
 */
static void
add_proto_list(scf_transaction_entry_t *entry, scf_handle_t *hdl,
    char **plist, uu_list_t **sv_list)
{
	scf_val_el_t		*sv_el;
	int			i;

	static uu_list_pool_t	*sv_pool = NULL;

	if ((sv_pool == NULL) &&
	    ((sv_pool = uu_list_pool_create("sv_pool",
	    sizeof (scf_val_el_t), offsetof(scf_val_el_t, link), NULL,
	    UU_LIST_POOL_DEBUG)) == NULL))
		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));

	if ((*sv_list = uu_list_create(sv_pool, NULL, 0)) == NULL)
		uu_die(gettext("Error: %s.\n"), uu_strerror(uu_error()));

	for (i = 0; plist[i] != NULL; i++) {
		if ((sv_el = malloc(sizeof (scf_val_el_t))) == NULL)
			uu_die(gettext("Error:"));

		if (((sv_el->val = scf_value_create(hdl)) == NULL) ||
		    (scf_value_set_astring(sv_el->val, plist[i]) != 0) ||
		    (scf_entry_add_value(entry, sv_el->val) != 0))
			scfdie();

		uu_list_node_init(sv_el, &sv_el->link, sv_pool);
		(void) uu_list_insert_after(*sv_list, NULL, sv_el);
	}
}
예제 #2
0
static void
add_prop(char *property)
{
	svcprop_prop_node_t *p, *last;
	char *slash;

	const char * const invalid_component_emsg =
	    gettext("Invalid component name `%s'.\n");

	/* FMRI syntax knowledge. */
	slash = strchr(property, '/');
	if (slash != NULL) {
		if (strchr(slash + 1, '/') != NULL) {
			uu_warn(gettext("-p argument `%s' has too many "
			    "components.\n"), property);
			usage();
		}
	}

	if (slash != NULL)
		*slash = '\0';

	p = safe_malloc(sizeof (svcprop_prop_node_t));
	uu_list_node_init(p, &p->spn_list_node, prop_pool);

	p->spn_comp1 = property;
	p->spn_comp2 = (slash == NULL) ? NULL : slash + 1;

	if (uu_check_name(p->spn_comp1, UU_NAME_DOMAIN) == -1)
		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp1);
	if (p->spn_comp2 != NULL &&
	    uu_check_name(p->spn_comp2, UU_NAME_DOMAIN) == -1)
		uu_xdie(UU_EXIT_USAGE, invalid_component_emsg, p->spn_comp2);

	last = uu_list_last(prop_list);
	if (last != NULL) {
		if ((last->spn_comp2 == NULL) ^ (p->spn_comp2 == NULL)) {
			/*
			 * The -p options have mixed numbers of components.
			 * If they both turn out to be valid, then the
			 * single-component ones will specify property groups,
			 * so we need to turn on types to keep the output of
			 * display_prop() consistent with display_pg().
			 */
			types = 1;
		}
	}

	(void) uu_list_insert_after(prop_list, NULL, p);
}
예제 #3
0
파일: tlx.c 프로젝트: alhazred/onarm
/* Add 'call' to the connection indication queue 'queue'. */
int
queue_conind(uu_list_t *queue, struct t_call *call)
{
	tlx_conn_ind_t *ci;

	if ((ci = malloc(sizeof (tlx_conn_ind_t))) == NULL) {
		error_msg(strerror(errno));
		return (-1);
	}

	ci->call = call;
	uu_list_node_init(ci, &ci->link, conn_ind_pool);
	(void) uu_list_insert_after(queue, NULL, ci);

	return (0);
}
예제 #4
0
/*
 * valid_props validates all the properties in an array of inetd_prop_t's,
 * marking each property as valid or invalid.  If any properties are invalid,
 * it returns B_FALSE, otherwise it returns B_TRUE.  Note that some properties
 * are interdependent, so if one is invalid, it leaves others in an
 * indeterminate state (such as ISRPC and SVC_NAME).  In this case, the
 * indeterminate property will be marked valid.  IE, the only properties
 * marked invalid are those that are KNOWN to be invalid.
 *
 * Piggy-backed onto this validation if 'fmri' is non-NULL is the construction
 * of a structured configuration, a basic_cfg_t,  which is used by inetd.
 * If 'fmri' is set then the latter three parameters need to be set to
 * non-NULL values, and if the configuration is valid, the storage referenced
 * by cfgpp is set to point at an initialized basic_cfg_t.
 */
boolean_t
valid_props(inetd_prop_t *prop, const char *fmri, basic_cfg_t **cfgpp,
    uu_list_pool_t *proto_info_pool, uu_list_pool_t *tlx_ci_pool)
{
	char			*bufp, *cp;
	boolean_t		ret = B_TRUE;
	int			i;
	long			uidl;
	boolean_t		isrpc;
	int			sock_type_id;
	int			rpc_pnum;
	int			rpc_lv, rpc_hv;
	basic_cfg_t		*cfg;
	char			*proto = NULL;
	int			pi;
	char			**netids = NULL;
	int			ni = 0;

	if (fmri != NULL)
		assert((cfgpp != NULL) && (proto_info_pool != NULL) &&
		    (tlx_ci_pool != NULL));

	/*
	 * Set all checkable properties to valid as a baseline.  We'll be
	 * marking all invalid properties.
	 */
	for (i = 0; prop[i].ip_name != NULL; i++) {
		if (prop[i].ip_error != IVE_UNSET)
			prop[i].ip_error = IVE_VALID;
	}

	if (((cfg = calloc(1, sizeof (basic_cfg_t))) == NULL) ||
	    ((fmri != NULL) &&
	    ((cfg->proto_list = uu_list_create(proto_info_pool, NULL, 0)) ==
	    NULL))) {
		free(cfg);
		return (B_FALSE);
	}

	/* Check a service name was supplied */
	if ((prop[PT_SVC_NAME_INDEX].ip_error == IVE_UNSET) ||
	    ((cfg->svc_name =
	    strdup(prop[PT_SVC_NAME_INDEX].ip_value.iv_string)) == NULL))
		prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;

	/* Check that iswait and isrpc have valid boolean values */

	if ((prop[PT_ISWAIT_INDEX].ip_error == IVE_UNSET) ||
	    (((cfg->iswait = prop[PT_ISWAIT_INDEX].ip_value.iv_boolean) !=
	    B_TRUE) && (cfg->iswait != B_FALSE)))
		prop[PT_ISWAIT_INDEX].ip_error = IVE_INVALID;

	if ((prop[PT_ISRPC_INDEX].ip_error == IVE_UNSET) ||
	    (((isrpc = prop[PT_ISRPC_INDEX].ip_value.iv_boolean) != B_TRUE) &&
	    (isrpc != B_FALSE))) {
		prop[PT_ISRPC_INDEX].ip_error = IVE_INVALID;
	} else if (isrpc) {
		/*
		 * This is an RPC service, so ensure that the RPC version
		 * numbers are zero or greater, that the low version isn't
		 * greater than the high version and a valid program name
		 * is supplied.
		 */

		if ((prop[PT_RPC_LW_VER_INDEX].ip_error == IVE_UNSET) ||
		    ((rpc_lv = prop[PT_RPC_LW_VER_INDEX].ip_value.iv_int) <
		    0))
			prop[PT_RPC_LW_VER_INDEX].ip_error = IVE_INVALID;

		if ((prop[PT_RPC_HI_VER_INDEX].ip_error == IVE_UNSET) ||
		    ((rpc_hv = prop[PT_RPC_HI_VER_INDEX].ip_value.iv_int) <
		    0))
			prop[PT_RPC_HI_VER_INDEX].ip_error = IVE_INVALID;

		if ((prop[PT_RPC_LW_VER_INDEX].ip_error != IVE_INVALID) &&
		    (prop[PT_RPC_HI_VER_INDEX].ip_error != IVE_INVALID) &&
		    (rpc_lv > rpc_hv)) {
			prop[PT_RPC_LW_VER_INDEX].ip_error = IVE_INVALID;
			prop[PT_RPC_HI_VER_INDEX].ip_error = IVE_INVALID;
		}

		if ((cfg->svc_name != NULL) &&
		    ((rpc_pnum = get_rpc_prognum(cfg->svc_name)) == -1))
			prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;
	}

	/* Check that the socket type is one of the acceptable values. */
	cfg->istlx = B_FALSE;
	if ((prop[PT_SOCK_TYPE_INDEX].ip_error == IVE_UNSET) ||
	    ((sock_type_id = get_sock_type_id(
	    prop[PT_SOCK_TYPE_INDEX].ip_value.iv_string)) == -1) &&
	    !(cfg->istlx = is_tlx_service(prop)))
		prop[PT_SOCK_TYPE_INDEX].ip_error = IVE_INVALID;

	/* Get the bind address */
	if (!cfg->istlx && prop[PT_BIND_ADDR_INDEX].ip_error != IVE_UNSET &&
	    (cfg->bind_addr =
	    strdup(prop[PT_BIND_ADDR_INDEX].ip_value.iv_string)) == NULL)
		prop[PT_BIND_ADDR_INDEX].ip_error = IVE_INVALID;

	/*
	 * Iterate through all the different protos/netids resulting from the
	 * proto property and check that they're valid and perform checks on
	 * other fields that are tied-in with the proto.
	 */

	pi = 0;
	do {
		socket_info_t		*si = NULL;
		tlx_info_t		*ti = NULL;
		proto_info_t		*p_inf = NULL;
		boolean_t		v6only = B_FALSE;
		char			*only;
		boolean_t		invalid_proto = B_FALSE;
		char			**protos;
		struct protoent		pe;
		char			gpbuf[1024];
		struct netconfig	*nconf = NULL;

		/*
		 * If we don't know whether it's an rpc service or its
		 * endpoint type, we can't do any of the proto checks as we
		 * have no context; break out.
		 */
		if ((prop[PT_ISRPC_INDEX].ip_error != IVE_VALID) ||
		    (prop[PT_SOCK_TYPE_INDEX].ip_error != IVE_VALID))
			break;

		/* skip proto specific processing if the proto isn't set. */
		if (prop[PT_PROTO_INDEX].ip_error == IVE_UNSET) {
			invalid_proto = B_TRUE;
			goto past_proto_processing;
		}
		protos = prop[PT_PROTO_INDEX].ip_value.iv_string_list;

		/*
		 * Get the next netid/proto.
		 */

		if (!cfg->istlx || !isrpc) {
			proto = protos[pi++];
		/*
		 * This is a TLI/RPC service, so get the next netid, expanding
		 * any supplied nettype.
		 */
		} else if ((netids == NULL) ||
		    ((proto = netids[ni++]) == NULL)) {
			/*
			 * Either this is the first time around or
			 * we've exhausted the last set of netids, so
			 * try and get the next set using the currently
			 * indexed proto entry.
			 */

			if (netids != NULL) {
				destroy_strings(netids);
				netids = NULL;
			}

			if (protos[pi] != NULL) {
				if ((netids = get_netids(protos[pi++])) ==
				    NULL) {
					invalid_proto = B_TRUE;
					proto = protos[pi - 1];
				} else {
					ni = 0;
					proto = netids[ni++];
				}
			} else {
				proto = NULL;
			}
		}

		if (proto == NULL)
			break;

		if (invalid_proto)
			goto past_proto_processing;

		/* strip a trailing only to simplify further processing */
		only = proto + strlen(proto) - (sizeof ("6only") - 1);
		if ((only > proto) && (strcmp(only, "6only") == 0)) {
			*++only = '\0';
			v6only = B_TRUE;
		}

		/* validate the proto/netid */

		if (!cfg->istlx) {
			if (!valid_socket_proto(proto))
				invalid_proto = B_TRUE;
		} else {
			/*
			 * Check if we've got a valid netid. If
			 * getnetconfigent() fails, we check to see whether
			 * we've got a v6 netid that may have been rejected
			 * because no IPv6 interface was configured before
			 * flagging 'proto' as invalid. If the latter condition
			 * holds, we don't flag the proto as invalid, and
			 * leave inetd to handle the value appropriately
			 * when it tries to listen on behalf of the service.
			 */
			if (((nconf = getnetconfigent(proto)) == NULL) &&
			    !v6_proto(proto))
				invalid_proto = B_TRUE;
		}
		if (invalid_proto)
			goto past_proto_processing;

		/*
		 * dissallow datagram type nowait services
		 */
		if ((prop[PT_ISWAIT_INDEX].ip_error == IVE_VALID) &&
		    !cfg->iswait) {
			if (strncmp(proto, SOCKET_PROTO_UDP,
			    sizeof (SOCKET_PROTO_UDP) - 1) == 0) {
				invalid_proto = B_TRUE;
			} else if (cfg->istlx && (nconf != NULL) &&
			    (nconf->nc_semantics == NC_TPI_CLTS)) {
					invalid_proto = B_TRUE;
			}
			if (invalid_proto) {
				prop[PT_ISWAIT_INDEX].ip_error = IVE_INVALID;
				goto past_proto_processing;
			}
		}

		/*
		 * We're running in validate only mode. Don't bother creating
		 * any proto structures (they don't do any further validation).
		 */
		if (fmri == NULL)
			goto past_proto_processing;

		/*
		 * Create the apropriate transport info structure.
		 */
		if (cfg->istlx) {
			if ((ti = create_tlx_info(proto, tlx_ci_pool)) != NULL)
				p_inf = (proto_info_t *)ti;
		} else {
			struct sockaddr_storage *ss;

			if ((si = calloc(1, sizeof (socket_info_t))) != NULL) {
				p_inf = (proto_info_t *)si;
				si->type = sock_type_id;
				ss = &si->local_addr;

				if (v6_socket_proto(proto)) {
					ss->ss_family = AF_INET6;
					/* already in network order */
					((struct sockaddr_in6 *)ss)->sin6_addr =
					    in6addr_any;
				} else {
					ss->ss_family = AF_INET;
					((struct sockaddr_in *)ss)->sin_addr.
					    s_addr = htonl(INADDR_ANY);
				}
				if (set_bind_addr(ss, cfg->bind_addr) != 0) {
					prop[PT_BIND_ADDR_INDEX].ip_error =
					    IVE_INVALID;
				}
			}
		}
		if (p_inf == NULL) {
			invalid_proto = B_TRUE;
			goto past_proto_processing;
		}

		p_inf->v6only = v6only;

		/*
		 * Store the supplied proto string for error reporting,
		 * re-attaching the 'only' suffix if one was taken off.
		 */
		if ((p_inf->proto = malloc(strlen(proto) + 5)) == NULL) {
			invalid_proto = B_TRUE;
			goto past_proto_processing;
		} else {
			(void) strlcpy(p_inf->proto, proto, strlen(proto) + 5);
			if (v6only)
				(void) strlcat(p_inf->proto, "only",
				    strlen(proto) + 5);
		}

		/*
		 * Validate and setup RPC/non-RPC specifics.
		 */

		if (isrpc) {
			rpc_info_t *ri;

			if ((rpc_pnum != -1) && (rpc_lv != -1) &&
			    (rpc_hv != -1)) {
				if ((ri = create_rpc_info(proto, rpc_pnum,
				    rpc_lv, rpc_hv)) == NULL) {
					invalid_proto = B_TRUE;
				} else {
					p_inf->ri = ri;
				}
			}
		}

past_proto_processing:
		/* validate non-RPC service name */
		if (!isrpc && (cfg->svc_name != NULL)) {
			struct servent	se;
			char		gsbuf[NSS_BUFLEN_SERVICES];
			char		*gsproto = proto;

			if (invalid_proto) {
				/*
				 * Make getservbyname_r do its lookup without a
				 * proto.
				 */
				gsproto = NULL;
			} else if (gsproto != NULL) {
				/*
				 * Since getservbyname & getprotobyname don't
				 * support tcp6, udp6 or sctp6 take off the 6
				 * digit from protocol.
				 */
				if (v6_socket_proto(gsproto))
					gsproto[strlen(gsproto) - 1] = '\0';
			}

			if (getservbyname_r(cfg->svc_name, gsproto, &se, gsbuf,
			    sizeof (gsbuf)) == NULL) {
				if (gsproto != NULL)
					invalid_proto = B_TRUE;
				prop[PT_SVC_NAME_INDEX].ip_error = IVE_INVALID;
			} else if (cfg->istlx && (ti != NULL)) {
				/* LINTED E_BAD_PTR_CAST_ALIGN */
				SS_SETPORT(*(struct sockaddr_storage *)
				    ti->local_addr.buf, se.s_port);
			} else if (!cfg->istlx && (si != NULL)) {
				if ((gsproto != NULL) &&
				    getprotobyname_r(gsproto, &pe, gpbuf,
				    sizeof (gpbuf)) == NULL) {
					invalid_proto = B_TRUE;
				} else {
					si->protocol = pe.p_proto;
				}
				SS_SETPORT(si->local_addr, se.s_port);
			}

		}

		if (p_inf != NULL) {
			p_inf->listen_fd = -1;

			/* add new proto entry to proto_list */
			uu_list_node_init(p_inf, &p_inf->link, proto_info_pool);
			(void) uu_list_insert_after(cfg->proto_list, NULL,
			    p_inf);
		}

		if (nconf != NULL)
			freenetconfigent(nconf);
		if (invalid_proto)
			prop[PT_PROTO_INDEX].ip_error = IVE_INVALID;
	} while (proto != NULL);	/* while just processed a proto */

	/*
	 * Check that the exec string for the start method actually exists and
	 * that the user is either a valid username or uid. Note we don't
	 * mandate the setting of these fields, and don't do any checks
	 * for arg0, hence its absence.
	 */

	if (prop[PT_EXEC_INDEX].ip_error != IVE_UNSET) {
		/* Don't pass any arguments to access() */
		if ((bufp = strdup(
		    prop[PT_EXEC_INDEX].ip_value.iv_string)) == NULL) {
			prop[PT_EXEC_INDEX].ip_error = IVE_INVALID;
		} else {
			if ((cp = strpbrk(bufp, " \t")) != NULL)
				*cp = '\0';

			if ((access(bufp, F_OK) == -1) && (errno == ENOENT))
				prop[PT_EXEC_INDEX].ip_error = IVE_INVALID;
			free(bufp);
		}
	}

	if (prop[PT_USER_INDEX].ip_error != IVE_UNSET) {
		char		pw_buf[NSS_BUFLEN_PASSWD];
		struct passwd	pw;

		if (getpwnam_r(prop[PT_USER_INDEX].ip_value.iv_string, &pw,
		    pw_buf, NSS_BUFLEN_PASSWD) == NULL) {
			errno = 0;
			uidl = strtol(prop[PT_USER_INDEX].ip_value.iv_string,
			    &bufp, 10);
			if ((errno != 0) || (*bufp != '\0') ||
			    (getpwuid_r(uidl, &pw, pw_buf,
			    NSS_BUFLEN_PASSWD) == NULL))
				prop[PT_USER_INDEX].ip_error = IVE_INVALID;
		}
	}

	/*
	 * Iterate through the properties in the array verifying that any
	 * default properties are valid, and setting the return boolean
	 * according to whether any properties were marked invalid.
	 */

	for (i = 0; prop[i].ip_name != NULL; i++) {
		if (prop[i].ip_error == IVE_UNSET)
			continue;

		if (prop[i].ip_default &&
		    !valid_default_prop(prop[i].ip_name, &prop[i].ip_value))
			prop[i].ip_error = IVE_INVALID;

		if (prop[i].ip_error == IVE_INVALID)
			ret = B_FALSE;
	}

	/* pass back the basic_cfg_t if requested and it's a valid config */
	if ((cfgpp != NULL) && ret) {
		*cfgpp = cfg;
	} else {
		destroy_basic_cfg(cfg);
	}

	return (ret);
}
예제 #5
0
/*
 * Given a ZFS handle and a property, construct a complete list of datasets
 * that need to be modified as part of this process.  For anything but the
 * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
 * Otherwise, we iterate over all children and look for any datasets that
 * inherit the property.  For each such dataset, we add it to the list and
 * mark whether it was shared beforehand.
 */
prop_changelist_t *
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int gather_flags,
    int mnt_flags)
{
	prop_changelist_t *clp;
	prop_changenode_t *cn;
	zfs_handle_t *temp;
	char property[ZFS_MAXPROPLEN];
	uu_compare_fn_t *compare = NULL;
	boolean_t legacy = B_FALSE;

	if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
		return (NULL);

	/*
	 * For mountpoint-related tasks, we want to sort everything by
	 * mountpoint, so that we mount and unmount them in the appropriate
	 * order, regardless of their position in the hierarchy.
	 */
	if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
	    prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS ||
	    prop == ZFS_PROP_SHARESMB) {

		if (zfs_prop_get(zhp, ZFS_PROP_MOUNTPOINT,
		    property, sizeof (property),
		    NULL, NULL, 0, B_FALSE) == 0 &&
		    (strcmp(property, "legacy") == 0 ||
		    strcmp(property, "none") == 0)) {

			legacy = B_TRUE;
		}
		if (!legacy) {
			compare = compare_mountpoints;
			clp->cl_sorted = B_TRUE;
		}
	}

	clp->cl_pool = uu_list_pool_create("changelist_pool",
	    sizeof (prop_changenode_t),
	    offsetof(prop_changenode_t, cn_listnode),
	    compare, 0);
	if (clp->cl_pool == NULL) {
		assert(uu_error() == UU_ERROR_NO_MEMORY);
		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
		changelist_free(clp);
		return (NULL);
	}

	clp->cl_list = uu_list_create(clp->cl_pool, NULL,
	    clp->cl_sorted ? UU_LIST_SORTED : 0);
	clp->cl_gflags = gather_flags;
	clp->cl_mflags = mnt_flags;

	if (clp->cl_list == NULL) {
		assert(uu_error() == UU_ERROR_NO_MEMORY);
		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * If this is a rename or the 'zoned' property, we pretend we're
	 * changing the mountpoint and flag it so we can catch all children in
	 * change_one().
	 *
	 * Flag cl_alldependents to catch all children plus the dependents
	 * (clones) that are not in the hierarchy.
	 */
	if (prop == ZFS_PROP_NAME) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
		clp->cl_alldependents = B_TRUE;
	} else if (prop == ZFS_PROP_ZONED) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
		clp->cl_allchildren = B_TRUE;
	} else if (prop == ZFS_PROP_CANMOUNT) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
	} else if (prop == ZFS_PROP_VOLSIZE) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
	} else {
		clp->cl_prop = prop;
	}
	clp->cl_realprop = prop;

	if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
	    clp->cl_prop != ZFS_PROP_SHARENFS &&
	    clp->cl_prop != ZFS_PROP_SHARESMB)
		return (clp);

	/*
	 * If watching SHARENFS or SHARESMB then
	 * also watch its companion property.
	 */
	if (clp->cl_prop == ZFS_PROP_SHARENFS)
		clp->cl_shareprop = ZFS_PROP_SHARESMB;
	else if (clp->cl_prop == ZFS_PROP_SHARESMB)
		clp->cl_shareprop = ZFS_PROP_SHARENFS;

	if (clp->cl_alldependents) {
		if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
			changelist_free(clp);
			return (NULL);
		}
	} else if (zfs_iter_children(zhp, change_one, clp) != 0) {
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * We have to re-open ourselves because we auto-close all the handles
	 * and can't tell the difference.
	 */
	if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
	    ZFS_TYPE_DATASET)) == NULL) {
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * Always add ourself to the list.  We add ourselves to the end so that
	 * we're the last to be unmounted.
	 */
	if ((cn = zfs_alloc(zhp->zfs_hdl,
	    sizeof (prop_changenode_t))) == NULL) {
		zfs_close(temp);
		changelist_free(clp);
		return (NULL);
	}

	cn->cn_handle = temp;
	cn->cn_mounted = (clp->cl_gflags & CL_GATHER_MOUNT_ALWAYS) ||
	    zfs_is_mounted(temp, NULL);
	cn->cn_shared = zfs_is_shared(temp);
	cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
	cn->cn_needpost = B_TRUE;

	uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
	if (clp->cl_sorted) {
		uu_list_index_t idx;
		(void) uu_list_find(clp->cl_list, cn, NULL, &idx);
		uu_list_insert(clp->cl_list, cn, idx);
	} else {
		/*
		 * Add the target dataset to the end of the list.
		 * The list is not really unsorted. The list will be
		 * in reverse dataset name order. This is necessary
		 * when the original mountpoint is legacy or none.
		 */
		verify(uu_list_insert_after(clp->cl_list,
		    uu_list_last(clp->cl_list), cn) == 0);
	}

	/*
	 * If the mountpoint property was previously 'legacy', or 'none',
	 * record it as the behavior of changelist_postfix() will be different.
	 */
	if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) && legacy) {
		/*
		 * do not automatically mount ex-legacy datasets if
		 * we specifically set canmount to noauto
		 */
		if (zfs_prop_get_int(zhp, ZFS_PROP_CANMOUNT) !=
		    ZFS_CANMOUNT_NOAUTO)
			clp->cl_waslegacy = B_TRUE;
	}

	return (clp);
}
예제 #6
0
/*
 * Given a ZFS handle and a property, construct a complete list of datasets
 * that need to be modified as part of this process.  For anything but the
 * 'mountpoint' and 'sharenfs' properties, this just returns an empty list.
 * Otherwise, we iterate over all children and look for any datasets that
 * inherit the property.  For each such dataset, we add it to the list and
 * mark whether it was shared beforehand.
 */
prop_changelist_t *
changelist_gather(zfs_handle_t *zhp, zfs_prop_t prop, int flags)
{
	prop_changelist_t *clp;
	prop_changenode_t *cn;
	zfs_handle_t *temp;
	char property[ZFS_MAXPROPLEN];
	uu_compare_fn_t *compare = NULL;

	if ((clp = zfs_alloc(zhp->zfs_hdl, sizeof (prop_changelist_t))) == NULL)
		return (NULL);

	/*
	 * For mountpoint-related tasks, we want to sort everything by
	 * mountpoint, so that we mount and unmount them in the appropriate
	 * order, regardless of their position in the hierarchy.
	 */
	if (prop == ZFS_PROP_NAME || prop == ZFS_PROP_ZONED ||
	    prop == ZFS_PROP_MOUNTPOINT || prop == ZFS_PROP_SHARENFS) {
		compare = compare_mountpoints;
		clp->cl_sorted = B_TRUE;
	}

	clp->cl_pool = uu_list_pool_create("changelist_pool",
	    sizeof (prop_changenode_t),
	    offsetof(prop_changenode_t, cn_listnode),
	    compare, 0);
	if (clp->cl_pool == NULL) {
		assert(uu_error() == UU_ERROR_NO_MEMORY);
		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
		changelist_free(clp);
		return (NULL);
	}

	clp->cl_list = uu_list_create(clp->cl_pool, NULL,
	    clp->cl_sorted ? UU_LIST_SORTED : 0);
	clp->cl_flags = flags;

	if (clp->cl_list == NULL) {
		assert(uu_error() == UU_ERROR_NO_MEMORY);
		(void) zfs_error(zhp->zfs_hdl, EZFS_NOMEM, "internal error");
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * If this is a rename or the 'zoned' property, we pretend we're
	 * changing the mountpoint and flag it so we can catch all children in
	 * change_one().
	 *
	 * Flag cl_alldependents to catch all children plus the dependents
	 * (clones) that are not in the hierarchy.
	 */
	if (prop == ZFS_PROP_NAME) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
		clp->cl_alldependents = B_TRUE;
	} else if (prop == ZFS_PROP_ZONED) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
		clp->cl_allchildren = B_TRUE;
	} else if (prop == ZFS_PROP_CANMOUNT) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
	} else if (prop == ZFS_PROP_VOLSIZE) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
	} else if (prop == ZFS_PROP_VERSION) {
		clp->cl_prop = ZFS_PROP_MOUNTPOINT;
	} else {
		clp->cl_prop = prop;
	}
	clp->cl_realprop = prop;

	if (clp->cl_prop != ZFS_PROP_MOUNTPOINT &&
	    clp->cl_prop != ZFS_PROP_SHARENFS &&
	    clp->cl_prop != ZFS_PROP_SHAREISCSI)
		return (clp);

	if (clp->cl_alldependents) {
		if (zfs_iter_dependents(zhp, B_TRUE, change_one, clp) != 0) {
			changelist_free(clp);
			return (NULL);
		}
	} else if (zfs_iter_children(zhp, change_one, clp) != 0) {
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * We have to re-open ourselves because we auto-close all the handles
	 * and can't tell the difference.
	 */
	if ((temp = zfs_open(zhp->zfs_hdl, zfs_get_name(zhp),
	    ZFS_TYPE_ANY)) == NULL) {
		changelist_free(clp);
		return (NULL);
	}

	/*
	 * Always add ourself to the list.  We add ourselves to the end so that
	 * we're the last to be unmounted.
	 */
	if ((cn = zfs_alloc(zhp->zfs_hdl,
	    sizeof (prop_changenode_t))) == NULL) {
		zfs_close(temp);
		changelist_free(clp);
		return (NULL);
	}

	cn->cn_handle = temp;
	cn->cn_mounted = zfs_is_mounted(temp, NULL);
	cn->cn_shared = zfs_is_shared(temp);

#ifndef	__APPLE__
	cn->cn_zoned = zfs_prop_get_int(zhp, ZFS_PROP_ZONED);
#endif	/*!__APPLE__*/

	uu_list_node_init(cn, &cn->cn_listnode, clp->cl_pool);
	if (clp->cl_sorted) {
		uu_list_index_t idx;
		(void) uu_list_find(clp->cl_list, cn, NULL, &idx);
		uu_list_insert(clp->cl_list, cn, idx);
	} else {
		verify(uu_list_insert_after(clp->cl_list,
		    uu_list_last(clp->cl_list), cn) == 0);
	}

	/*
	 * If the mountpoint property was previously 'legacy', or 'none',
	 * record it as the behavior of changelist_postfix() will be different.
	 */
	if ((clp->cl_prop == ZFS_PROP_MOUNTPOINT) &&
	    (zfs_prop_get(zhp, prop, property, sizeof (property),
	    NULL, NULL, 0, B_FALSE) == 0 &&
	    (strcmp(property, "legacy") == 0 || strcmp(property, "none") == 0)))
		clp->cl_waslegacy = B_TRUE;

	return (clp);
}
예제 #7
0
/*
 * Registers the attributes of a running method passed as arguments so that
 * the method's termination is noticed and any further processing of the
 * associated instance is carried out. The function also sets up any
 * necessary timers so we can detect hung methods.
 * Returns -1 if either it failed to open the /proc psinfo file which is used
 * to monitor the method process, it failed to setup a required timer or
 * memory allocation failed; else 0.
 */
int
register_method(instance_t *ins, pid_t pid, ctid_t cid, instance_method_t mthd,
    char *proto_name)
{
	char		path[MAXPATHLEN];
	int		fd;
	method_el_t	*me;

	/* open /proc psinfo file of process to listen for POLLHUP events on */
	(void) snprintf(path, sizeof (path), "/proc/%u/psinfo", pid);
	for (;;) {
		if ((fd = open(path, O_RDONLY)) >= 0) {
			break;
		} else if (errno != EINTR) {
			/*
			 * Don't output an error for ENOENT; we get this
			 * if a method has gone away whilst we were stopped,
			 * and we're now trying to re-listen for it.
			 */
			if (errno != ENOENT) {
				error_msg(gettext("Failed to open %s: %s"),
				    path, strerror(errno));
			}
			return (-1);
		}
	}

	/* add method record to in-memory list */
	if ((me = calloc(1, sizeof (method_el_t))) == NULL) {
		error_msg(strerror(errno));
		(void) close(fd);
		return (-1);
	}
	me->fd = fd;
	me->inst = (instance_t *)ins;
	me->method = mthd;
	me->pid = pid;
	me->cid = cid;
	if (proto_name != NULL) {
		if ((me->proto_name = strdup(proto_name)) == NULL) {
			error_msg(strerror(errno));
			free(me);
			(void) close(fd);
			return (-1);
		}
	} else
		me->proto_name = NULL;

	/* register a timeout for the method, if required */
	if (mthd != IM_START) {
		method_info_t *mi = ins->config->methods[mthd];

		if (mi->timeout > 0) {
			assert(ins->timer_id == -1);
			ins->timer_id = iu_schedule_timer(timer_queue,
			    mi->timeout, method_timeout, me);
			if (ins->timer_id == -1) {
				error_msg(gettext(
				    "Failed to schedule method timeout"));
				if (me->proto_name != NULL)
					free(me->proto_name);
				free(me);
				(void) close(fd);
				return (-1);
			}
		}
	}

	/*
	 * Add fd of psinfo file to poll set, but pass 0 for events to
	 * poll for, so we should only get a POLLHUP event on the fd.
	 */
	if (set_pollfd(fd, 0) == -1) {
		cancel_inst_timer(ins);
		if (me->proto_name != NULL)
			free(me->proto_name);
		free(me);
		(void) close(fd);
		return (-1);
	}

	uu_list_node_init(me, &me->link, method_pool);
	(void) uu_list_insert_after(method_list, NULL, me);

	return (0);
}