xref: /titanic_52/usr/src/cmd/dlmgmtd/dlmgmt_door.c (revision 327151705b7439cb7ab35c370f682cac7ef9523a)
1d62bc4baSyz147064 /*
2d62bc4baSyz147064  * CDDL HEADER START
3d62bc4baSyz147064  *
4d62bc4baSyz147064  * The contents of this file are subject to the terms of the
5d62bc4baSyz147064  * Common Development and Distribution License (the "License").
6d62bc4baSyz147064  * You may not use this file except in compliance with the License.
7d62bc4baSyz147064  *
8d62bc4baSyz147064  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz147064  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz147064  * See the License for the specific language governing permissions
11d62bc4baSyz147064  * and limitations under the License.
12d62bc4baSyz147064  *
13d62bc4baSyz147064  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz147064  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz147064  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz147064  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz147064  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz147064  *
19d62bc4baSyz147064  * CDDL HEADER END
20d62bc4baSyz147064  */
21d62bc4baSyz147064 
22d62bc4baSyz147064 /*
23*32715170SCathy Zhou  * Copyright (c) 2005, 2010, Oracle and/or its affiliates. All rights reserved.
24d62bc4baSyz147064  */
25d62bc4baSyz147064 
26d62bc4baSyz147064 /*
27d62bc4baSyz147064  * Main door handler functions used by dlmgmtd to process the different door
28d62bc4baSyz147064  * call requests. Door call requests can come from the user-land applications,
29024b0a25Sseb  * or from the kernel.
302b24ab6bSSebastien Roy  *
312b24ab6bSSebastien Roy  * Note on zones handling:
322b24ab6bSSebastien Roy  *
332b24ab6bSSebastien Roy  * There are two zoneid's associated with a link.  One is the zoneid of the
342b24ab6bSSebastien Roy  * zone in which the link was created (ll_zoneid in the dlmgmt_link_t), and
352b24ab6bSSebastien Roy  * the other is the zoneid of the zone where the link is currently assigned
362b24ab6bSSebastien Roy  * (the "zone" link property).  The two can be different if a datalink is
372b24ab6bSSebastien Roy  * created in the global zone and subsequently assigned to a non-global zone
382b24ab6bSSebastien Roy  * via zonecfg or via explicitly setting the "zone" link property.
392b24ab6bSSebastien Roy  *
402b24ab6bSSebastien Roy  * Door clients can see links that were created in their zone, and links that
412b24ab6bSSebastien Roy  * are currently assigned to their zone.  Door clients in a zone can only
422b24ab6bSSebastien Roy  * modify links that were created in their zone.
432b24ab6bSSebastien Roy  *
442b24ab6bSSebastien Roy  * The datalink ID space is global, while each zone has its own datalink name
452b24ab6bSSebastien Roy  * space.  This allows each zone to have complete freedom over the names that
462b24ab6bSSebastien Roy  * they assign to links created within the zone.
47d62bc4baSyz147064  */
48d62bc4baSyz147064 
49d62bc4baSyz147064 #include <assert.h>
50d62bc4baSyz147064 #include <alloca.h>
51024b0a25Sseb #include <errno.h>
52024b0a25Sseb #include <priv_utils.h>
53024b0a25Sseb #include <stdlib.h>
54d62bc4baSyz147064 #include <strings.h>
550dc974a9SCathy Zhou #include <syslog.h>
560dc974a9SCathy Zhou #include <sys/sysevent/eventdefs.h>
572b24ab6bSSebastien Roy #include <zone.h>
580dc974a9SCathy Zhou #include <libsysevent.h>
59d62bc4baSyz147064 #include <libdlmgmt.h>
600dc974a9SCathy Zhou #include <librcm.h>
61d62bc4baSyz147064 #include "dlmgmt_impl.h"
62d62bc4baSyz147064 
63*32715170SCathy Zhou typedef void dlmgmt_door_handler_t(void *, void *, size_t *, zoneid_t,
64*32715170SCathy Zhou     ucred_t *);
65024b0a25Sseb 
66024b0a25Sseb typedef struct dlmgmt_door_info_s {
67024b0a25Sseb 	uint_t			di_cmd;
68024b0a25Sseb 	size_t			di_reqsz;
69024b0a25Sseb 	size_t			di_acksz;
70024b0a25Sseb 	dlmgmt_door_handler_t	*di_handler;
71024b0a25Sseb } dlmgmt_door_info_t;
72024b0a25Sseb 
732b24ab6bSSebastien Roy /*
742b24ab6bSSebastien Roy  * Check if the caller has the required privileges to operate on a link of the
752b24ab6bSSebastien Roy  * given class.
762b24ab6bSSebastien Roy  */
772b24ab6bSSebastien Roy static int
782b24ab6bSSebastien Roy dlmgmt_checkprivs(datalink_class_t class, ucred_t *cred)
792b24ab6bSSebastien Roy {
802b24ab6bSSebastien Roy 	const priv_set_t *eset;
812b24ab6bSSebastien Roy 
822b24ab6bSSebastien Roy 	eset = ucred_getprivset(cred, PRIV_EFFECTIVE);
832b24ab6bSSebastien Roy 	if (eset != NULL && ((class == DATALINK_CLASS_IPTUN &&
842b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_IPTUN_CONFIG)) ||
852b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_DL_CONFIG) ||
862b24ab6bSSebastien Roy 	    priv_ismember(eset, PRIV_SYS_NET_CONFIG)))
872b24ab6bSSebastien Roy 		return (0);
882b24ab6bSSebastien Roy 	return (EACCES);
892b24ab6bSSebastien Roy }
90024b0a25Sseb 
91d62bc4baSyz147064 static dlmgmt_link_t *
922b24ab6bSSebastien Roy dlmgmt_getlink_by_dev(char *devname, zoneid_t zoneid)
93d62bc4baSyz147064 {
94d62bc4baSyz147064 	dlmgmt_link_t *linkp = avl_first(&dlmgmt_id_avl);
95d62bc4baSyz147064 
96d62bc4baSyz147064 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
972b24ab6bSSebastien Roy 		if (link_is_visible(linkp, zoneid) &&
982b24ab6bSSebastien Roy 		    (linkp->ll_class == DATALINK_CLASS_PHYS) &&
99d62bc4baSyz147064 		    linkattr_equal(&(linkp->ll_head), FDEVNAME, devname,
100d62bc4baSyz147064 		    strlen(devname) + 1)) {
101d62bc4baSyz147064 			return (linkp);
102d62bc4baSyz147064 		}
103d62bc4baSyz147064 	}
104d62bc4baSyz147064 	return (NULL);
105d62bc4baSyz147064 }
106d62bc4baSyz147064 
1070dc974a9SCathy Zhou /*
1080dc974a9SCathy Zhou  * Post the EC_DATALINK sysevent for the given linkid. This sysevent will
1090dc974a9SCathy Zhou  * be consumed by the datalink sysevent module.
1100dc974a9SCathy Zhou  */
1110dc974a9SCathy Zhou static void
1125093e103SCathy Zhou dlmgmt_post_sysevent(const char *subclass, datalink_id_t linkid,
1135093e103SCathy Zhou     boolean_t reconfigured)
1140dc974a9SCathy Zhou {
1150dc974a9SCathy Zhou 	nvlist_t	*nvl = NULL;
1160dc974a9SCathy Zhou 	sysevent_id_t	eid;
1170dc974a9SCathy Zhou 	int		err;
1180dc974a9SCathy Zhou 
1190dc974a9SCathy Zhou 	if (((err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0)) != 0) ||
1205093e103SCathy Zhou 	    ((err = nvlist_add_uint64(nvl, RCM_NV_LINKID, linkid)) != 0) ||
1215093e103SCathy Zhou 	    ((err = nvlist_add_boolean_value(nvl, RCM_NV_RECONFIGURED,
1225093e103SCathy Zhou 	    reconfigured)) != 0)) {
1230dc974a9SCathy Zhou 		goto done;
1240dc974a9SCathy Zhou 	}
1250dc974a9SCathy Zhou 
1260dc974a9SCathy Zhou 	if (sysevent_post_event(EC_DATALINK, (char *)subclass, SUNW_VENDOR,
1270dc974a9SCathy Zhou 	    (char *)progname, nvl, &eid) == -1) {
1280dc974a9SCathy Zhou 		err = errno;
1290dc974a9SCathy Zhou 	}
1300dc974a9SCathy Zhou 
1310dc974a9SCathy Zhou done:
1320dc974a9SCathy Zhou 	if (err != 0) {
1330dc974a9SCathy Zhou 		dlmgmt_log(LOG_WARNING, "dlmgmt_post_sysevent(%d) failed: %s",
1340dc974a9SCathy Zhou 		    linkid, strerror(err));
1350dc974a9SCathy Zhou 	}
1360dc974a9SCathy Zhou 	nvlist_free(nvl);
1370dc974a9SCathy Zhou }
1380dc974a9SCathy Zhou 
139*32715170SCathy Zhou /* ARGSUSED */
140d62bc4baSyz147064 static void
141*32715170SCathy Zhou dlmgmt_upcall_create(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
142*32715170SCathy Zhou     ucred_t *cred)
143d62bc4baSyz147064 {
144024b0a25Sseb 	dlmgmt_upcall_arg_create_t *create = argp;
145024b0a25Sseb 	dlmgmt_create_retval_t	*retvalp = retp;
146d62bc4baSyz147064 	datalink_class_t	class;
147d62bc4baSyz147064 	uint32_t		media;
148d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
149d62bc4baSyz147064 	char			link[MAXLINKNAMELEN];
150d62bc4baSyz147064 	uint32_t		flags;
1518a1c9a22SCathy Zhou 	int			err = 0;
152d62bc4baSyz147064 	boolean_t		created = B_FALSE;
1535093e103SCathy Zhou 	boolean_t		reconfigured = B_FALSE;
154d62bc4baSyz147064 
155d62bc4baSyz147064 	/*
156d62bc4baSyz147064 	 * Determine whether this link is persistent. Note that this request
157d62bc4baSyz147064 	 * is coming from kernel so this link must be active.
158d62bc4baSyz147064 	 */
159d62bc4baSyz147064 	flags = DLMGMT_ACTIVE | (create->ld_persist ? DLMGMT_PERSIST : 0);
160d62bc4baSyz147064 
161d62bc4baSyz147064 	class = create->ld_class;
162d62bc4baSyz147064 	media = create->ld_media;
163d62bc4baSyz147064 
164d62bc4baSyz147064 	/*
165d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
166d62bc4baSyz147064 	 */
167d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
168d62bc4baSyz147064 
1692b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(class, cred)) != 0)
1702b24ab6bSSebastien Roy 		goto done;
1712b24ab6bSSebastien Roy 
172d62bc4baSyz147064 	/*
173d62bc4baSyz147064 	 * Check to see whether this is the reattachment of an existing
174d62bc4baSyz147064 	 * physical link. If so, return its linkid.
175d62bc4baSyz147064 	 */
1762b24ab6bSSebastien Roy 	if ((class == DATALINK_CLASS_PHYS) && (linkp =
1772b24ab6bSSebastien Roy 	    dlmgmt_getlink_by_dev(create->ld_devname, zoneid)) != NULL) {
1788a1c9a22SCathy Zhou 		if (linkattr_equal(&(linkp->ll_head), FPHYMAJ,
1798a1c9a22SCathy Zhou 		    &create->ld_phymaj, sizeof (uint64_t)) &&
1808a1c9a22SCathy Zhou 		    linkattr_equal(&(linkp->ll_head), FPHYINST,
1818a1c9a22SCathy Zhou 		    &create->ld_phyinst, sizeof (uint64_t)) &&
1828a1c9a22SCathy Zhou 		    (linkp->ll_flags & flags) == flags) {
1838a1c9a22SCathy Zhou 			/*
1848a1c9a22SCathy Zhou 			 * If nothing has been changed, directly return.
1858a1c9a22SCathy Zhou 			 */
1868a1c9a22SCathy Zhou 			goto noupdate;
1878a1c9a22SCathy Zhou 		}
1888a1c9a22SCathy Zhou 
189d62bc4baSyz147064 		err = linkattr_set(&(linkp->ll_head), FPHYMAJ,
190d62bc4baSyz147064 		    &create->ld_phymaj, sizeof (uint64_t), DLADM_TYPE_UINT64);
191d62bc4baSyz147064 		if (err != 0)
192d62bc4baSyz147064 			goto done;
193d62bc4baSyz147064 
194d62bc4baSyz147064 		err = linkattr_set(&(linkp->ll_head), FPHYINST,
195d62bc4baSyz147064 		    &create->ld_phyinst, sizeof (uint64_t), DLADM_TYPE_UINT64);
196d62bc4baSyz147064 		if (err != 0)
197d62bc4baSyz147064 			goto done;
198d62bc4baSyz147064 
1995093e103SCathy Zhou 		/*
2005093e103SCathy Zhou 		 * This is a device that is dynamic reconfigured.
2015093e103SCathy Zhou 		 */
2025093e103SCathy Zhou 		if ((linkp->ll_flags & DLMGMT_ACTIVE) == 0)
2035093e103SCathy Zhou 			reconfigured = B_TRUE;
2045093e103SCathy Zhou 
2052b24ab6bSSebastien Roy 		if ((err = link_activate(linkp)) != 0)
2062b24ab6bSSebastien Roy 			goto done;
207d62bc4baSyz147064 		linkp->ll_flags |= flags;
208d62bc4baSyz147064 		linkp->ll_gen++;
2095093e103SCathy Zhou 
210d62bc4baSyz147064 		goto done;
211d62bc4baSyz147064 	}
212d62bc4baSyz147064 
213d62bc4baSyz147064 	if ((err = dlmgmt_create_common(create->ld_devname, class, media,
2142b24ab6bSSebastien Roy 	    zoneid, flags, &linkp)) == EEXIST) {
215d62bc4baSyz147064 		/*
216d62bc4baSyz147064 		 * The link name already exists. Return error if this is a
217d62bc4baSyz147064 		 * non-physical link (in that case, the link name must be
218d62bc4baSyz147064 		 * the same as the given name).
219d62bc4baSyz147064 		 */
220d62bc4baSyz147064 		if (class != DATALINK_CLASS_PHYS)
221d62bc4baSyz147064 			goto done;
222d62bc4baSyz147064 
223d62bc4baSyz147064 		/*
224d62bc4baSyz147064 		 * The physical link's name already exists, request
225d62bc4baSyz147064 		 * a suggested link name: net<nextppa>
226d62bc4baSyz147064 		 */
2272b24ab6bSSebastien Roy 		err = dlmgmt_generate_name("net", link, MAXLINKNAMELEN, zoneid);
228d62bc4baSyz147064 		if (err != 0)
229d62bc4baSyz147064 			goto done;
230d62bc4baSyz147064 
2312b24ab6bSSebastien Roy 		err = dlmgmt_create_common(link, class, media, zoneid, flags,
2322b24ab6bSSebastien Roy 		    &linkp);
233d62bc4baSyz147064 	}
234d62bc4baSyz147064 
235d62bc4baSyz147064 	if (err != 0)
236d62bc4baSyz147064 		goto done;
237d62bc4baSyz147064 
238d62bc4baSyz147064 	created = B_TRUE;
239d62bc4baSyz147064 
240d62bc4baSyz147064 	/*
241d62bc4baSyz147064 	 * This is a new link.  Only need to persist link attributes for
242d62bc4baSyz147064 	 * physical links.
243d62bc4baSyz147064 	 */
244d62bc4baSyz147064 	if (class == DATALINK_CLASS_PHYS &&
245d62bc4baSyz147064 	    (((err = linkattr_set(&linkp->ll_head, FDEVNAME, create->ld_devname,
246d62bc4baSyz147064 	    strlen(create->ld_devname) + 1, DLADM_TYPE_STR)) != 0) ||
247d62bc4baSyz147064 	    ((err = linkattr_set(&linkp->ll_head, FPHYMAJ, &create->ld_phymaj,
248d62bc4baSyz147064 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0) ||
249d62bc4baSyz147064 	    ((err = linkattr_set(&linkp->ll_head, FPHYINST, &create->ld_phyinst,
250d62bc4baSyz147064 	    sizeof (uint64_t), DLADM_TYPE_UINT64)) != 0))) {
251d62bc4baSyz147064 		(void) dlmgmt_destroy_common(linkp, flags);
252d62bc4baSyz147064 	}
253d62bc4baSyz147064 
254d62bc4baSyz147064 done:
2552b24ab6bSSebastien Roy 	if ((err == 0) && ((err = dlmgmt_write_db_entry(linkp->ll_link, linkp,
256d62bc4baSyz147064 	    linkp->ll_flags)) != 0) && created) {
257d62bc4baSyz147064 		(void) dlmgmt_destroy_common(linkp, flags);
258d62bc4baSyz147064 	}
259d62bc4baSyz147064 
2608a1c9a22SCathy Zhou noupdate:
261d62bc4baSyz147064 	if (err == 0)
262d62bc4baSyz147064 		retvalp->lr_linkid = linkp->ll_linkid;
263d62bc4baSyz147064 
264d62bc4baSyz147064 	dlmgmt_table_unlock();
2650dc974a9SCathy Zhou 
2668a1c9a22SCathy Zhou 	if ((err == 0) && (class == DATALINK_CLASS_PHYS)) {
2670dc974a9SCathy Zhou 		/*
2680dc974a9SCathy Zhou 		 * Post the ESC_DATALINK_PHYS_ADD sysevent. This sysevent
2690dc974a9SCathy Zhou 		 * is consumed by the datalink sysevent module which in
2700dc974a9SCathy Zhou 		 * turn generates the RCM_RESOURCE_LINK_NEW RCM event.
2710dc974a9SCathy Zhou 		 */
2725093e103SCathy Zhou 		dlmgmt_post_sysevent(ESC_DATALINK_PHYS_ADD,
2735093e103SCathy Zhou 		    retvalp->lr_linkid, reconfigured);
2740dc974a9SCathy Zhou 	}
2750dc974a9SCathy Zhou 
2760dc974a9SCathy Zhou 	retvalp->lr_err = err;
277d62bc4baSyz147064 }
278d62bc4baSyz147064 
279*32715170SCathy Zhou /* ARGSUSED */
280d62bc4baSyz147064 static void
281*32715170SCathy Zhou dlmgmt_upcall_update(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
282*32715170SCathy Zhou     ucred_t *cred)
283d62bc4baSyz147064 {
284024b0a25Sseb 	dlmgmt_upcall_arg_update_t	*update = argp;
285024b0a25Sseb 	dlmgmt_update_retval_t		*retvalp = retp;
286d62bc4baSyz147064 	uint32_t			media = update->ld_media;
287d62bc4baSyz147064 	dlmgmt_link_t			*linkp;
288d62bc4baSyz147064 	int				err = 0;
289d62bc4baSyz147064 
290d62bc4baSyz147064 	/*
291d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
292d62bc4baSyz147064 	 */
293d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
294d62bc4baSyz147064 
295d62bc4baSyz147064 	/*
296d62bc4baSyz147064 	 * Check to see whether this is the reattachment of an existing
297d62bc4baSyz147064 	 * physical link. If so, return its linkid.
298d62bc4baSyz147064 	 */
2992b24ab6bSSebastien Roy 	if ((linkp = dlmgmt_getlink_by_dev(update->ld_devname, zoneid)) ==
3002b24ab6bSSebastien Roy 	    NULL) {
301d62bc4baSyz147064 		err = ENOENT;
302d62bc4baSyz147064 		goto done;
303d62bc4baSyz147064 	}
304d62bc4baSyz147064 
3052b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
3062b24ab6bSSebastien Roy 		goto done;
3072b24ab6bSSebastien Roy 
308d62bc4baSyz147064 	retvalp->lr_linkid = linkp->ll_linkid;
309d62bc4baSyz147064 	retvalp->lr_media = media;
310d62bc4baSyz147064 	if (linkp->ll_media != media && linkp->ll_media != DL_OTHER) {
311d62bc4baSyz147064 		/*
312d62bc4baSyz147064 		 * Assume a DL_ETHER link ce0, a DL_WIFI link ath0
313d62bc4baSyz147064 		 * 1. # dladm rename-link ce0 net0
314d62bc4baSyz147064 		 * 2. DR out ce0. net0 is down.
315d62bc4baSyz147064 		 * 3. use rename-link to have the ath0 device inherit
316d62bc4baSyz147064 		 *    the configuration from net0
317d62bc4baSyz147064 		 *    # dladm rename-link ath0 net0
318d62bc4baSyz147064 		 * 4. DR in ath0.
319d62bc4baSyz147064 		 * As ath0 and ce0 do not have the same media type, ath0
320d62bc4baSyz147064 		 * cannot inherit the configuration of net0.
321d62bc4baSyz147064 		 */
322d62bc4baSyz147064 		err = EEXIST;
323d62bc4baSyz147064 
324d62bc4baSyz147064 		/*
325d62bc4baSyz147064 		 * Return the media type of the existing link to indicate the
326d62bc4baSyz147064 		 * reason for the name conflict.
327d62bc4baSyz147064 		 */
328d62bc4baSyz147064 		retvalp->lr_media = linkp->ll_media;
329d62bc4baSyz147064 		goto done;
330d62bc4baSyz147064 	}
331d62bc4baSyz147064 
332d62bc4baSyz147064 	if (update->ld_novanity &&
333d62bc4baSyz147064 	    (strcmp(update->ld_devname, linkp->ll_link) != 0)) {
334d62bc4baSyz147064 		/*
335d62bc4baSyz147064 		 * Return an error if this is a physical link that does not
336d62bc4baSyz147064 		 * support vanity naming, but the link name is not the same
337d62bc4baSyz147064 		 * as the given device name.
338d62bc4baSyz147064 		 */
339d62bc4baSyz147064 		err = EEXIST;
340d62bc4baSyz147064 		goto done;
341d62bc4baSyz147064 	}
342d62bc4baSyz147064 
3438a1c9a22SCathy Zhou 	if (linkp->ll_media != media) {
344d62bc4baSyz147064 		linkp->ll_media = media;
345d62bc4baSyz147064 		linkp->ll_gen++;
3462b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
3472b24ab6bSSebastien Roy 		    linkp->ll_flags);
3488a1c9a22SCathy Zhou 	}
349d62bc4baSyz147064 
350d62bc4baSyz147064 done:
351d62bc4baSyz147064 	dlmgmt_table_unlock();
352d62bc4baSyz147064 	retvalp->lr_err = err;
353d62bc4baSyz147064 }
354d62bc4baSyz147064 
355*32715170SCathy Zhou /* ARGSUSED */
356d62bc4baSyz147064 static void
357*32715170SCathy Zhou dlmgmt_upcall_destroy(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
358*32715170SCathy Zhou     ucred_t *cred)
359d62bc4baSyz147064 {
360024b0a25Sseb 	dlmgmt_upcall_arg_destroy_t	*destroy = argp;
361024b0a25Sseb 	dlmgmt_destroy_retval_t		*retvalp = retp;
362d62bc4baSyz147064 	datalink_id_t			linkid = destroy->ld_linkid;
363d62bc4baSyz147064 	dlmgmt_link_t			*linkp = NULL;
364d62bc4baSyz147064 	uint32_t			flags, dflags = 0;
365d62bc4baSyz147064 	int				err = 0;
366d62bc4baSyz147064 
367d62bc4baSyz147064 	flags = DLMGMT_ACTIVE | (destroy->ld_persist ? DLMGMT_PERSIST : 0);
368d62bc4baSyz147064 
369d62bc4baSyz147064 	/*
370d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
371d62bc4baSyz147064 	 */
372d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
373d62bc4baSyz147064 
3742b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
375d62bc4baSyz147064 		err = ENOENT;
376d62bc4baSyz147064 		goto done;
377d62bc4baSyz147064 	}
378d62bc4baSyz147064 
3792b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
3802b24ab6bSSebastien Roy 		goto done;
3812b24ab6bSSebastien Roy 
382a73e6fc1SCathy Zhou 	if (((linkp->ll_flags & flags) & DLMGMT_ACTIVE) != 0) {
3832b24ab6bSSebastien Roy 		if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE)) != 0)
384d62bc4baSyz147064 			goto done;
385a73e6fc1SCathy Zhou 		dflags |= DLMGMT_ACTIVE;
386d62bc4baSyz147064 	}
387d62bc4baSyz147064 
388a73e6fc1SCathy Zhou 	if (((linkp->ll_flags & flags) & DLMGMT_PERSIST) != 0) {
3892b24ab6bSSebastien Roy 		if ((err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST)) != 0)
390a73e6fc1SCathy Zhou 			goto done;
391d62bc4baSyz147064 		dflags |= DLMGMT_PERSIST;
392d62bc4baSyz147064 	}
393d62bc4baSyz147064 
394a73e6fc1SCathy Zhou 	err = dlmgmt_destroy_common(linkp, flags);
395a73e6fc1SCathy Zhou done:
396a73e6fc1SCathy Zhou 	if (err != 0 && dflags != 0)
3972b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp, dflags);
398d62bc4baSyz147064 
399d62bc4baSyz147064 	dlmgmt_table_unlock();
400d62bc4baSyz147064 	retvalp->lr_err = err;
401d62bc4baSyz147064 }
402d62bc4baSyz147064 
4032b24ab6bSSebastien Roy /* ARGSUSED */
404d62bc4baSyz147064 static void
405*32715170SCathy Zhou dlmgmt_getname(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
406*32715170SCathy Zhou     ucred_t *cred)
407d62bc4baSyz147064 {
408024b0a25Sseb 	dlmgmt_door_getname_t	*getname = argp;
409024b0a25Sseb 	dlmgmt_getname_retval_t	*retvalp = retp;
410d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
411d62bc4baSyz147064 	int			err = 0;
412d62bc4baSyz147064 
413d62bc4baSyz147064 	/*
414d62bc4baSyz147064 	 * Hold the reader lock to access the link
415d62bc4baSyz147064 	 */
416d62bc4baSyz147064 	dlmgmt_table_lock(B_FALSE);
4172b24ab6bSSebastien Roy 	if ((linkp = link_by_id(getname->ld_linkid, zoneid)) == NULL) {
418d62bc4baSyz147064 		err = ENOENT;
4192b24ab6bSSebastien Roy 	} else if (strlcpy(retvalp->lr_link, linkp->ll_link, MAXLINKNAMELEN) >=
420d62bc4baSyz147064 	    MAXLINKNAMELEN) {
421d62bc4baSyz147064 		err = ENOSPC;
4222b24ab6bSSebastien Roy 	} else {
423d62bc4baSyz147064 		retvalp->lr_flags = linkp->ll_flags;
424d62bc4baSyz147064 		retvalp->lr_class = linkp->ll_class;
425d62bc4baSyz147064 		retvalp->lr_media = linkp->ll_media;
4262b24ab6bSSebastien Roy 	}
427d62bc4baSyz147064 
428d62bc4baSyz147064 	dlmgmt_table_unlock();
429d62bc4baSyz147064 	retvalp->lr_err = err;
430d62bc4baSyz147064 }
431d62bc4baSyz147064 
4322b24ab6bSSebastien Roy /* ARGSUSED */
433d62bc4baSyz147064 static void
434*32715170SCathy Zhou dlmgmt_getlinkid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
435*32715170SCathy Zhou     ucred_t *cred)
436d62bc4baSyz147064 {
437024b0a25Sseb 	dlmgmt_door_getlinkid_t	*getlinkid = argp;
438024b0a25Sseb 	dlmgmt_getlinkid_retval_t *retvalp = retp;
439d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
440d62bc4baSyz147064 	int			err = 0;
441d62bc4baSyz147064 
442d62bc4baSyz147064 	/*
443d62bc4baSyz147064 	 * Hold the reader lock to access the link
444d62bc4baSyz147064 	 */
445d62bc4baSyz147064 	dlmgmt_table_lock(B_FALSE);
4462b24ab6bSSebastien Roy 
4472b24ab6bSSebastien Roy 	if ((linkp = link_by_name(getlinkid->ld_link, zoneid)) == NULL) {
448d62bc4baSyz147064 		/*
4492b24ab6bSSebastien Roy 		 * The link does not exist in this zone.
450d62bc4baSyz147064 		 */
451d62bc4baSyz147064 		err = ENOENT;
452d62bc4baSyz147064 		goto done;
453d62bc4baSyz147064 	}
454d62bc4baSyz147064 
455d62bc4baSyz147064 	retvalp->lr_linkid = linkp->ll_linkid;
456d62bc4baSyz147064 	retvalp->lr_flags = linkp->ll_flags;
457d62bc4baSyz147064 	retvalp->lr_class = linkp->ll_class;
458d62bc4baSyz147064 	retvalp->lr_media = linkp->ll_media;
459d62bc4baSyz147064 
460d62bc4baSyz147064 done:
461d62bc4baSyz147064 	dlmgmt_table_unlock();
462d62bc4baSyz147064 	retvalp->lr_err = err;
463d62bc4baSyz147064 }
464d62bc4baSyz147064 
4652b24ab6bSSebastien Roy /* ARGSUSED */
466d62bc4baSyz147064 static void
467*32715170SCathy Zhou dlmgmt_getnext(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
468*32715170SCathy Zhou     ucred_t *cred)
469d62bc4baSyz147064 {
470024b0a25Sseb 	dlmgmt_door_getnext_t	*getnext = argp;
471024b0a25Sseb 	dlmgmt_getnext_retval_t	*retvalp = retp;
472d62bc4baSyz147064 	dlmgmt_link_t		link, *linkp;
473d62bc4baSyz147064 	avl_index_t		where;
474d62bc4baSyz147064 	int			err = 0;
475d62bc4baSyz147064 
476d62bc4baSyz147064 	/*
477d62bc4baSyz147064 	 * Hold the reader lock to access the link
478d62bc4baSyz147064 	 */
479d62bc4baSyz147064 	dlmgmt_table_lock(B_FALSE);
480d62bc4baSyz147064 
4812b24ab6bSSebastien Roy 	link.ll_linkid = (getnext->ld_linkid + 1);
4822b24ab6bSSebastien Roy 	if ((linkp = avl_find(&dlmgmt_id_avl, &link, &where)) == NULL)
483d62bc4baSyz147064 		linkp = avl_nearest(&dlmgmt_id_avl, where, AVL_AFTER);
484d62bc4baSyz147064 
485d62bc4baSyz147064 	for (; linkp != NULL; linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
4862b24ab6bSSebastien Roy 		if (!link_is_visible(linkp, zoneid))
4872b24ab6bSSebastien Roy 			continue;
488d62bc4baSyz147064 		if ((linkp->ll_class & getnext->ld_class) &&
489d62bc4baSyz147064 		    (linkp->ll_flags & getnext->ld_flags) &&
490d62bc4baSyz147064 		    DATALINK_MEDIA_ACCEPTED(getnext->ld_dmedia,
491d62bc4baSyz147064 		    linkp->ll_media))
492d62bc4baSyz147064 			break;
493d62bc4baSyz147064 	}
494d62bc4baSyz147064 
495d62bc4baSyz147064 	if (linkp == NULL) {
496d62bc4baSyz147064 		err = ENOENT;
497d62bc4baSyz147064 	} else {
498d62bc4baSyz147064 		retvalp->lr_linkid = linkp->ll_linkid;
499d62bc4baSyz147064 		retvalp->lr_class = linkp->ll_class;
500d62bc4baSyz147064 		retvalp->lr_media = linkp->ll_media;
501d62bc4baSyz147064 		retvalp->lr_flags = linkp->ll_flags;
502d62bc4baSyz147064 	}
503d62bc4baSyz147064 
504d62bc4baSyz147064 	dlmgmt_table_unlock();
505d62bc4baSyz147064 	retvalp->lr_err = err;
506d62bc4baSyz147064 }
507d62bc4baSyz147064 
5082b24ab6bSSebastien Roy /* ARGSUSED */
509024b0a25Sseb static void
510*32715170SCathy Zhou dlmgmt_upcall_getattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
511*32715170SCathy Zhou     ucred_t *cred)
512d62bc4baSyz147064 {
513024b0a25Sseb 	dlmgmt_upcall_arg_getattr_t	*getattr = argp;
514024b0a25Sseb 	dlmgmt_getattr_retval_t		*retvalp = retp;
515d62bc4baSyz147064 	dlmgmt_link_t			*linkp;
516d62bc4baSyz147064 
517d62bc4baSyz147064 	/*
518d62bc4baSyz147064 	 * Hold the reader lock to access the link
519d62bc4baSyz147064 	 */
520d62bc4baSyz147064 	dlmgmt_table_lock(B_FALSE);
5212b24ab6bSSebastien Roy 	if ((linkp = link_by_id(getattr->ld_linkid, zoneid)) == NULL) {
522f1956ffeSCathy Zhou 		retvalp->lr_err = ENOENT;
5232b24ab6bSSebastien Roy 	} else {
5242b24ab6bSSebastien Roy 		retvalp->lr_err = dlmgmt_getattr_common(&linkp->ll_head,
5252b24ab6bSSebastien Roy 		    getattr->ld_attr, retvalp);
526d62bc4baSyz147064 	}
527d62bc4baSyz147064 	dlmgmt_table_unlock();
528d62bc4baSyz147064 }
529d62bc4baSyz147064 
530*32715170SCathy Zhou /* ARGSUSED */
531d62bc4baSyz147064 static void
532*32715170SCathy Zhou dlmgmt_createid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
533*32715170SCathy Zhou     ucred_t *cred)
534d62bc4baSyz147064 {
535024b0a25Sseb 	dlmgmt_door_createid_t	*createid = argp;
536024b0a25Sseb 	dlmgmt_createid_retval_t *retvalp = retp;
537d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
538d62bc4baSyz147064 	datalink_id_t		linkid = DATALINK_INVALID_LINKID;
539d62bc4baSyz147064 	char			link[MAXLINKNAMELEN];
540d62bc4baSyz147064 	int			err;
541d62bc4baSyz147064 
542d62bc4baSyz147064 	/*
543d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
544d62bc4baSyz147064 	 */
545d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
546d62bc4baSyz147064 
5472b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(createid->ld_class, cred)) != 0)
5482b24ab6bSSebastien Roy 		goto done;
5492b24ab6bSSebastien Roy 
550d62bc4baSyz147064 	if (createid->ld_prefix) {
551d62bc4baSyz147064 		err = dlmgmt_generate_name(createid->ld_link, link,
5522b24ab6bSSebastien Roy 		    MAXLINKNAMELEN, zoneid);
553d62bc4baSyz147064 		if (err != 0)
554d62bc4baSyz147064 			goto done;
555d62bc4baSyz147064 
556d62bc4baSyz147064 		err = dlmgmt_create_common(link, createid->ld_class,
5572b24ab6bSSebastien Roy 		    createid->ld_media, zoneid, createid->ld_flags, &linkp);
558d62bc4baSyz147064 	} else {
559d62bc4baSyz147064 		err = dlmgmt_create_common(createid->ld_link,
5602b24ab6bSSebastien Roy 		    createid->ld_class, createid->ld_media, zoneid,
5612b24ab6bSSebastien Roy 		    createid->ld_flags, &linkp);
562d62bc4baSyz147064 	}
563d62bc4baSyz147064 
564d62bc4baSyz147064 	if (err == 0) {
565d62bc4baSyz147064 		/*
566d62bc4baSyz147064 		 * Keep the active mapping.
567d62bc4baSyz147064 		 */
568d62bc4baSyz147064 		linkid = linkp->ll_linkid;
5692b24ab6bSSebastien Roy 		if (createid->ld_flags & DLMGMT_ACTIVE) {
5702b24ab6bSSebastien Roy 			(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
5712b24ab6bSSebastien Roy 			    DLMGMT_ACTIVE);
5722b24ab6bSSebastien Roy 		}
573d62bc4baSyz147064 	}
574d62bc4baSyz147064 
575d62bc4baSyz147064 done:
576d62bc4baSyz147064 	dlmgmt_table_unlock();
577d62bc4baSyz147064 	retvalp->lr_linkid = linkid;
578d62bc4baSyz147064 	retvalp->lr_err = err;
579d62bc4baSyz147064 }
580d62bc4baSyz147064 
581*32715170SCathy Zhou /* ARGSUSED */
582d62bc4baSyz147064 static void
583*32715170SCathy Zhou dlmgmt_destroyid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
584*32715170SCathy Zhou     ucred_t *cred)
585d62bc4baSyz147064 {
586024b0a25Sseb 	dlmgmt_door_destroyid_t	*destroyid = argp;
587024b0a25Sseb 	dlmgmt_destroyid_retval_t *retvalp = retp;
588d62bc4baSyz147064 	datalink_id_t		linkid = destroyid->ld_linkid;
589d62bc4baSyz147064 	uint32_t		flags = destroyid->ld_flags;
590d62bc4baSyz147064 	dlmgmt_link_t		*linkp = NULL;
591d62bc4baSyz147064 	int			err = 0;
592d62bc4baSyz147064 
593d62bc4baSyz147064 	/*
594d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
595d62bc4baSyz147064 	 */
596d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
5972b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
598d62bc4baSyz147064 		err = ENOENT;
599d62bc4baSyz147064 		goto done;
600d62bc4baSyz147064 	}
601d62bc4baSyz147064 
6022b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
603d62bc4baSyz147064 		goto done;
604d62bc4baSyz147064 
605d62bc4baSyz147064 	/*
606d62bc4baSyz147064 	 * Delete the active mapping.
607d62bc4baSyz147064 	 */
608d62bc4baSyz147064 	if (flags & DLMGMT_ACTIVE)
6092b24ab6bSSebastien Roy 		err = dlmgmt_delete_db_entry(linkp, DLMGMT_ACTIVE);
6102b24ab6bSSebastien Roy 	if (err == 0)
6112b24ab6bSSebastien Roy 		err = dlmgmt_destroy_common(linkp, flags);
612d62bc4baSyz147064 done:
613d62bc4baSyz147064 	dlmgmt_table_unlock();
614d62bc4baSyz147064 	retvalp->lr_err = err;
615d62bc4baSyz147064 }
616d62bc4baSyz147064 
617d62bc4baSyz147064 /*
618d62bc4baSyz147064  * Remap a linkid to a given link name, i.e., rename an existing link1
619d62bc4baSyz147064  * (ld_linkid) to a non-existent link2 (ld_link): rename link1's name to
620d62bc4baSyz147064  * the given link name.
621d62bc4baSyz147064  */
622*32715170SCathy Zhou /* ARGSUSED */
623d62bc4baSyz147064 static void
624*32715170SCathy Zhou dlmgmt_remapid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
625*32715170SCathy Zhou     ucred_t *cred)
626d62bc4baSyz147064 {
627024b0a25Sseb 	dlmgmt_door_remapid_t	*remapid = argp;
628024b0a25Sseb 	dlmgmt_remapid_retval_t	*retvalp = retp;
6292b24ab6bSSebastien Roy 	dlmgmt_link_t		*linkp;
6302b24ab6bSSebastien Roy 	char			oldname[MAXLINKNAMELEN];
6312b24ab6bSSebastien Roy 	boolean_t		renamed = B_FALSE;
632d62bc4baSyz147064 	int			err = 0;
633d62bc4baSyz147064 
634d62bc4baSyz147064 	if (!dladm_valid_linkname(remapid->ld_link)) {
635d62bc4baSyz147064 		retvalp->lr_err = EINVAL;
636d62bc4baSyz147064 		return;
637d62bc4baSyz147064 	}
638d62bc4baSyz147064 
639d62bc4baSyz147064 	/*
640d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
641d62bc4baSyz147064 	 */
642d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
6432b24ab6bSSebastien Roy 	if ((linkp = link_by_id(remapid->ld_linkid, zoneid)) == NULL) {
644d62bc4baSyz147064 		err = ENOENT;
645d62bc4baSyz147064 		goto done;
646d62bc4baSyz147064 	}
647d62bc4baSyz147064 
6482b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
6492b24ab6bSSebastien Roy 		goto done;
6502b24ab6bSSebastien Roy 
6512b24ab6bSSebastien Roy 	if (link_by_name(remapid->ld_link, linkp->ll_zoneid) != NULL) {
652d62bc4baSyz147064 		err = EEXIST;
653d62bc4baSyz147064 		goto done;
654d62bc4baSyz147064 	}
655d62bc4baSyz147064 
6562b24ab6bSSebastien Roy 	(void) strlcpy(oldname, linkp->ll_link, MAXLINKNAMELEN);
6572b24ab6bSSebastien Roy 	avl_remove(&dlmgmt_name_avl, linkp);
6582b24ab6bSSebastien Roy 	(void) strlcpy(linkp->ll_link, remapid->ld_link, MAXLINKNAMELEN);
6592b24ab6bSSebastien Roy 	avl_add(&dlmgmt_name_avl, linkp);
6602b24ab6bSSebastien Roy 	renamed = B_TRUE;
661d62bc4baSyz147064 
6622b24ab6bSSebastien Roy 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
6632b24ab6bSSebastien Roy 		err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_ACTIVE);
6642b24ab6bSSebastien Roy 		if (err != 0)
6652b24ab6bSSebastien Roy 			goto done;
6662b24ab6bSSebastien Roy 	}
6672b24ab6bSSebastien Roy 	if (linkp->ll_flags & DLMGMT_PERSIST) {
6682b24ab6bSSebastien Roy 		err = dlmgmt_write_db_entry(oldname, linkp, DLMGMT_PERSIST);
6692b24ab6bSSebastien Roy 		if (err != 0) {
6702b24ab6bSSebastien Roy 			if (linkp->ll_flags & DLMGMT_ACTIVE) {
6712b24ab6bSSebastien Roy 				(void) dlmgmt_write_db_entry(remapid->ld_link,
6722b24ab6bSSebastien Roy 				    linkp, DLMGMT_ACTIVE);
6732b24ab6bSSebastien Roy 			}
6742b24ab6bSSebastien Roy 			goto done;
6752b24ab6bSSebastien Roy 		}
6762b24ab6bSSebastien Roy 	}
6772b24ab6bSSebastien Roy 
6782b24ab6bSSebastien Roy 	dlmgmt_advance(linkp);
6792b24ab6bSSebastien Roy 	linkp->ll_gen++;
680d62bc4baSyz147064 done:
6812b24ab6bSSebastien Roy 	if (err != 0 && renamed) {
6822b24ab6bSSebastien Roy 		avl_remove(&dlmgmt_name_avl, linkp);
6832b24ab6bSSebastien Roy 		(void) strlcpy(linkp->ll_link, oldname, MAXLINKNAMELEN);
6842b24ab6bSSebastien Roy 		avl_add(&dlmgmt_name_avl, linkp);
6852b24ab6bSSebastien Roy 	}
686d62bc4baSyz147064 	dlmgmt_table_unlock();
687d62bc4baSyz147064 	retvalp->lr_err = err;
688d62bc4baSyz147064 }
689d62bc4baSyz147064 
690*32715170SCathy Zhou /* ARGSUSED */
691d62bc4baSyz147064 static void
692*32715170SCathy Zhou dlmgmt_upid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
693*32715170SCathy Zhou     ucred_t *cred)
694d62bc4baSyz147064 {
695024b0a25Sseb 	dlmgmt_door_upid_t	*upid = argp;
696024b0a25Sseb 	dlmgmt_upid_retval_t	*retvalp = retp;
697d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
698d62bc4baSyz147064 	int			err = 0;
699d62bc4baSyz147064 
700d62bc4baSyz147064 	/*
701d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
702d62bc4baSyz147064 	 */
703d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
7042b24ab6bSSebastien Roy 	if ((linkp = link_by_id(upid->ld_linkid, zoneid)) == NULL) {
705d62bc4baSyz147064 		err = ENOENT;
706d62bc4baSyz147064 		goto done;
707d62bc4baSyz147064 	}
708d62bc4baSyz147064 
7092b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
7102b24ab6bSSebastien Roy 		goto done;
7112b24ab6bSSebastien Roy 
712d62bc4baSyz147064 	if (linkp->ll_flags & DLMGMT_ACTIVE) {
713d62bc4baSyz147064 		err = EINVAL;
714d62bc4baSyz147064 		goto done;
715d62bc4baSyz147064 	}
716d62bc4baSyz147064 
7172b24ab6bSSebastien Roy 	if ((err = link_activate(linkp)) == 0) {
7182b24ab6bSSebastien Roy 		(void) dlmgmt_write_db_entry(linkp->ll_link, linkp,
7192b24ab6bSSebastien Roy 		    DLMGMT_ACTIVE);
7202b24ab6bSSebastien Roy 	}
721d62bc4baSyz147064 done:
722d62bc4baSyz147064 	dlmgmt_table_unlock();
723d62bc4baSyz147064 	retvalp->lr_err = err;
724d62bc4baSyz147064 }
725d62bc4baSyz147064 
726*32715170SCathy Zhou /* ARGSUSED */
727d62bc4baSyz147064 static void
728*32715170SCathy Zhou dlmgmt_createconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
729*32715170SCathy Zhou     ucred_t *cred)
730d62bc4baSyz147064 {
731024b0a25Sseb 	dlmgmt_door_createconf_t *createconf = argp;
732024b0a25Sseb 	dlmgmt_createconf_retval_t *retvalp = retp;
7332b24ab6bSSebastien Roy 	dlmgmt_dlconf_t		*dlconfp;
734d62bc4baSyz147064 	int			err;
735d62bc4baSyz147064 
736d62bc4baSyz147064 	/*
737d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
738d62bc4baSyz147064 	 */
739d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
740d62bc4baSyz147064 
7412b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(createconf->ld_class, cred)) != 0)
742d62bc4baSyz147064 		goto done;
743d62bc4baSyz147064 
7442b24ab6bSSebastien Roy 	err = dlconf_create(createconf->ld_link, createconf->ld_linkid,
7452b24ab6bSSebastien Roy 	    createconf->ld_class, createconf->ld_media, zoneid, &dlconfp);
7462b24ab6bSSebastien Roy 	if (err == 0) {
7472b24ab6bSSebastien Roy 		avl_add(&dlmgmt_dlconf_avl, dlconfp);
748d62bc4baSyz147064 		dlmgmt_advance_dlconfid(dlconfp);
749*32715170SCathy Zhou 		retvalp->lr_confid = dlconfp->ld_id;
7502b24ab6bSSebastien Roy 	}
751d62bc4baSyz147064 done:
752d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
753d62bc4baSyz147064 	retvalp->lr_err = err;
754d62bc4baSyz147064 }
755d62bc4baSyz147064 
756*32715170SCathy Zhou /* ARGSUSED */
757d62bc4baSyz147064 static void
758*32715170SCathy Zhou dlmgmt_setattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
759*32715170SCathy Zhou     ucred_t *cred)
760d62bc4baSyz147064 {
761024b0a25Sseb 	dlmgmt_door_setattr_t	*setattr = argp;
762024b0a25Sseb 	dlmgmt_setattr_retval_t	*retvalp = retp;
763d62bc4baSyz147064 	dlmgmt_dlconf_t		dlconf, *dlconfp;
764d62bc4baSyz147064 	int			err = 0;
765d62bc4baSyz147064 
766d62bc4baSyz147064 	/*
767d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
768d62bc4baSyz147064 	 */
769d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
770d62bc4baSyz147064 
771*32715170SCathy Zhou 	dlconf.ld_id = setattr->ld_confid;
772d62bc4baSyz147064 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
7732b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
774d62bc4baSyz147064 		err = ENOENT;
775d62bc4baSyz147064 		goto done;
776d62bc4baSyz147064 	}
777d62bc4baSyz147064 
7782b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
7792b24ab6bSSebastien Roy 		goto done;
7802b24ab6bSSebastien Roy 
781d62bc4baSyz147064 	err = linkattr_set(&(dlconfp->ld_head), setattr->ld_attr,
782d62bc4baSyz147064 	    &setattr->ld_attrval, setattr->ld_attrsz, setattr->ld_type);
783d62bc4baSyz147064 
784d62bc4baSyz147064 done:
785d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
786d62bc4baSyz147064 	retvalp->lr_err = err;
787d62bc4baSyz147064 }
788d62bc4baSyz147064 
789*32715170SCathy Zhou /* ARGSUSED */
790d62bc4baSyz147064 static void
791*32715170SCathy Zhou dlmgmt_unsetconfattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
792*32715170SCathy Zhou     ucred_t *cred)
793d62bc4baSyz147064 {
794024b0a25Sseb 	dlmgmt_door_unsetattr_t	*unsetattr = argp;
795024b0a25Sseb 	dlmgmt_unsetattr_retval_t *retvalp = retp;
796d62bc4baSyz147064 	dlmgmt_dlconf_t		dlconf, *dlconfp;
797d62bc4baSyz147064 	int			err = 0;
798d62bc4baSyz147064 
799d62bc4baSyz147064 	/*
800d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
801d62bc4baSyz147064 	 */
802d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
803d62bc4baSyz147064 
804*32715170SCathy Zhou 	dlconf.ld_id = unsetattr->ld_confid;
805d62bc4baSyz147064 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
8062b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
807d62bc4baSyz147064 		err = ENOENT;
808d62bc4baSyz147064 		goto done;
809d62bc4baSyz147064 	}
810d62bc4baSyz147064 
8112b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
8122b24ab6bSSebastien Roy 		goto done;
8132b24ab6bSSebastien Roy 
8142b24ab6bSSebastien Roy 	linkattr_unset(&(dlconfp->ld_head), unsetattr->ld_attr);
815d62bc4baSyz147064 
816d62bc4baSyz147064 done:
817d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
818d62bc4baSyz147064 	retvalp->lr_err = err;
819d62bc4baSyz147064 }
820d62bc4baSyz147064 
821d62bc4baSyz147064 /*
822*32715170SCathy Zhou  * Note that dlmgmt_openconf() returns a conf ID of a conf AVL tree entry,
823d62bc4baSyz147064  * which is managed by dlmgmtd.  The ID is used to find the conf entry when
824d62bc4baSyz147064  * dlmgmt_write_conf() is called.  The conf entry contains an ld_gen value
825d62bc4baSyz147064  * (which is the generation number - ll_gen) of the dlmgmt_link_t at the time
826*32715170SCathy Zhou  * of dlmgmt_openconf(), and ll_gen changes every time the dlmgmt_link_t
827d62bc4baSyz147064  * changes its attributes.  Therefore, dlmgmt_write_conf() can compare ld_gen
828d62bc4baSyz147064  * in the conf entry against the latest dlmgmt_link_t ll_gen value to see if
829*32715170SCathy Zhou  * anything has changed between the dlmgmt_openconf() and dlmgmt_writeconf()
830d62bc4baSyz147064  * calls.  If so, EAGAIN is returned.  This mechanism can ensures atomicity
831d62bc4baSyz147064  * across the pair of dladm_read_conf() and dladm_write_conf() calls.
832d62bc4baSyz147064  */
833*32715170SCathy Zhou /* ARGSUSED */
834d62bc4baSyz147064 static void
835*32715170SCathy Zhou dlmgmt_writeconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
836*32715170SCathy Zhou     ucred_t *cred)
837d62bc4baSyz147064 {
838024b0a25Sseb 	dlmgmt_door_writeconf_t	*writeconf = argp;
839024b0a25Sseb 	dlmgmt_writeconf_retval_t *retvalp = retp;
840d62bc4baSyz147064 	dlmgmt_dlconf_t		dlconf, *dlconfp;
841d62bc4baSyz147064 	dlmgmt_link_t		*linkp;
842d62bc4baSyz147064 	dlmgmt_linkattr_t	*attrp, *next;
843d62bc4baSyz147064 	int			err = 0;
844d62bc4baSyz147064 
845d62bc4baSyz147064 	/*
846*32715170SCathy Zhou 	 * Hold the lock to access the dlconf table.
847d62bc4baSyz147064 	 */
848d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
849d62bc4baSyz147064 
850*32715170SCathy Zhou 	dlconf.ld_id = writeconf->ld_confid;
851d62bc4baSyz147064 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
8522b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
853d62bc4baSyz147064 		err = ENOENT;
854d62bc4baSyz147064 		goto done;
855d62bc4baSyz147064 	}
856d62bc4baSyz147064 
8572b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
8582b24ab6bSSebastien Roy 		goto done;
8592b24ab6bSSebastien Roy 
860d62bc4baSyz147064 	/*
861d62bc4baSyz147064 	 * Hold the writer lock to update the link table.
862d62bc4baSyz147064 	 */
863d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
8642b24ab6bSSebastien Roy 	linkp = link_by_id(dlconfp->ld_linkid, zoneid);
865d62bc4baSyz147064 	if ((linkp == NULL) || (linkp->ll_class != dlconfp->ld_class) ||
866d62bc4baSyz147064 	    (linkp->ll_media != dlconfp->ld_media) ||
867d62bc4baSyz147064 	    (strcmp(linkp->ll_link, dlconfp->ld_link) != 0)) {
868d62bc4baSyz147064 		/*
869d62bc4baSyz147064 		 * The link does not exist.
870d62bc4baSyz147064 		 */
871d62bc4baSyz147064 		dlmgmt_table_unlock();
872d62bc4baSyz147064 		err = ENOENT;
873d62bc4baSyz147064 		goto done;
874d62bc4baSyz147064 	}
875d62bc4baSyz147064 
876d62bc4baSyz147064 	if (linkp->ll_gen != dlconfp->ld_gen) {
877d62bc4baSyz147064 		/*
878d62bc4baSyz147064 		 * Something has changed the link configuration; try again.
879d62bc4baSyz147064 		 */
880d62bc4baSyz147064 		dlmgmt_table_unlock();
881d62bc4baSyz147064 		err = EAGAIN;
882d62bc4baSyz147064 		goto done;
883d62bc4baSyz147064 	}
884d62bc4baSyz147064 
885d62bc4baSyz147064 	/*
886d62bc4baSyz147064 	 * Delete the old attribute list.
887d62bc4baSyz147064 	 */
888d62bc4baSyz147064 	for (attrp = linkp->ll_head; attrp != NULL; attrp = next) {
889d62bc4baSyz147064 		next = attrp->lp_next;
890d62bc4baSyz147064 		free(attrp->lp_val);
891d62bc4baSyz147064 		free(attrp);
892d62bc4baSyz147064 	}
893d62bc4baSyz147064 	linkp->ll_head = NULL;
894d62bc4baSyz147064 
895d62bc4baSyz147064 	/*
896d62bc4baSyz147064 	 * Set the new attribute.
897d62bc4baSyz147064 	 */
898d62bc4baSyz147064 	for (attrp = dlconfp->ld_head; attrp != NULL; attrp = attrp->lp_next) {
899d62bc4baSyz147064 		if ((err = linkattr_set(&(linkp->ll_head), attrp->lp_name,
900d62bc4baSyz147064 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
901d62bc4baSyz147064 			dlmgmt_table_unlock();
902d62bc4baSyz147064 			goto done;
903d62bc4baSyz147064 		}
904d62bc4baSyz147064 	}
905d62bc4baSyz147064 
906d62bc4baSyz147064 	linkp->ll_gen++;
9072b24ab6bSSebastien Roy 	err = dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_PERSIST);
908d62bc4baSyz147064 	dlmgmt_table_unlock();
909d62bc4baSyz147064 done:
910d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
911d62bc4baSyz147064 	retvalp->lr_err = err;
912d62bc4baSyz147064 }
913d62bc4baSyz147064 
914*32715170SCathy Zhou /* ARGSUSED */
915d62bc4baSyz147064 static void
916*32715170SCathy Zhou dlmgmt_removeconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
917*32715170SCathy Zhou     ucred_t *cred)
918d62bc4baSyz147064 {
919024b0a25Sseb 	dlmgmt_door_removeconf_t 	*removeconf = argp;
920024b0a25Sseb 	dlmgmt_removeconf_retval_t	*retvalp = retp;
9212b24ab6bSSebastien Roy 	dlmgmt_link_t			*linkp;
922d62bc4baSyz147064 	int				err;
923d62bc4baSyz147064 
924d62bc4baSyz147064 	dlmgmt_table_lock(B_TRUE);
9252b24ab6bSSebastien Roy 	if ((linkp = link_by_id(removeconf->ld_linkid, zoneid)) == NULL) {
9262b24ab6bSSebastien Roy 		err = ENOENT;
9272b24ab6bSSebastien Roy 		goto done;
9282b24ab6bSSebastien Roy 	}
9292b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID && linkp->ll_onloan) {
9302b24ab6bSSebastien Roy 		/*
9312b24ab6bSSebastien Roy 		 * A non-global zone cannot remove the persistent
9322b24ab6bSSebastien Roy 		 * configuration of a link that is on loan from the global
9332b24ab6bSSebastien Roy 		 * zone.
9342b24ab6bSSebastien Roy 		 */
9352b24ab6bSSebastien Roy 		err = EACCES;
9362b24ab6bSSebastien Roy 		goto done;
9372b24ab6bSSebastien Roy 	}
9382b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
9392b24ab6bSSebastien Roy 		goto done;
9402b24ab6bSSebastien Roy 
9412b24ab6bSSebastien Roy 	err = dlmgmt_delete_db_entry(linkp, DLMGMT_PERSIST);
9422b24ab6bSSebastien Roy done:
943d62bc4baSyz147064 	dlmgmt_table_unlock();
944d62bc4baSyz147064 	retvalp->lr_err = err;
945d62bc4baSyz147064 }
946d62bc4baSyz147064 
947*32715170SCathy Zhou /* ARGSUSED */
948d62bc4baSyz147064 static void
949*32715170SCathy Zhou dlmgmt_destroyconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
950*32715170SCathy Zhou     ucred_t *cred)
951d62bc4baSyz147064 {
952024b0a25Sseb 	dlmgmt_door_destroyconf_t	*destroyconf = argp;
953024b0a25Sseb 	dlmgmt_destroyconf_retval_t	*retvalp = retp;
954d62bc4baSyz147064 	dlmgmt_dlconf_t			dlconf, *dlconfp;
955d62bc4baSyz147064 	int				err = 0;
956d62bc4baSyz147064 
957d62bc4baSyz147064 	/*
958d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
959d62bc4baSyz147064 	 */
960d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
961d62bc4baSyz147064 
962*32715170SCathy Zhou 	dlconf.ld_id = destroyconf->ld_confid;
963d62bc4baSyz147064 	dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL);
9642b24ab6bSSebastien Roy 	if (dlconfp == NULL || zoneid != dlconfp->ld_zoneid) {
965d62bc4baSyz147064 		err = ENOENT;
966d62bc4baSyz147064 		goto done;
967d62bc4baSyz147064 	}
968d62bc4baSyz147064 
9692b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0)
9702b24ab6bSSebastien Roy 		goto done;
9712b24ab6bSSebastien Roy 
972d62bc4baSyz147064 	avl_remove(&dlmgmt_dlconf_avl, dlconfp);
973d62bc4baSyz147064 	dlconf_destroy(dlconfp);
974d62bc4baSyz147064 
975d62bc4baSyz147064 done:
976d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
977d62bc4baSyz147064 	retvalp->lr_err = err;
978d62bc4baSyz147064 }
979d62bc4baSyz147064 
980d62bc4baSyz147064 /*
981*32715170SCathy Zhou  * dlmgmt_openconf() returns a handle of the current configuration, which
982*32715170SCathy Zhou  * is then used to update the configuration by dlmgmt_writeconf(). Therefore,
983*32715170SCathy Zhou  * it requires privileges.
984*32715170SCathy Zhou  *
985*32715170SCathy Zhou  * Further, please see the comments above dladm_write_conf() to see how
986*32715170SCathy Zhou  * ld_gen is used to ensure atomicity across the {dlmgmt_openconf(),
987*32715170SCathy Zhou  * dlmgmt_writeconf()} pair.
988d62bc4baSyz147064  */
9892b24ab6bSSebastien Roy /* ARGSUSED */
990d62bc4baSyz147064 static void
991*32715170SCathy Zhou dlmgmt_openconf(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
992*32715170SCathy Zhou     ucred_t *cred)
993d62bc4baSyz147064 {
994*32715170SCathy Zhou 	dlmgmt_door_openconf_t	*openconf = argp;
995*32715170SCathy Zhou 	dlmgmt_openconf_retval_t *retvalp = retp;
996d62bc4baSyz147064 	dlmgmt_link_t 		*linkp;
997*32715170SCathy Zhou 	datalink_id_t		linkid = openconf->ld_linkid;
9982b24ab6bSSebastien Roy 	dlmgmt_dlconf_t		*dlconfp;
999d62bc4baSyz147064 	dlmgmt_linkattr_t	*attrp;
1000d62bc4baSyz147064 	int			err = 0;
1001d62bc4baSyz147064 
1002d62bc4baSyz147064 	/*
1003d62bc4baSyz147064 	 * Hold the writer lock to update the dlconf table.
1004d62bc4baSyz147064 	 */
1005d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_TRUE);
1006d62bc4baSyz147064 
1007d62bc4baSyz147064 	/*
1008d62bc4baSyz147064 	 * Hold the reader lock to access the link
1009d62bc4baSyz147064 	 */
1010d62bc4baSyz147064 	dlmgmt_table_lock(B_FALSE);
10112b24ab6bSSebastien Roy 	linkp = link_by_id(linkid, zoneid);
1012d62bc4baSyz147064 	if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) {
10132b24ab6bSSebastien Roy 		/* The persistent link configuration does not exist. */
1014d62bc4baSyz147064 		err = ENOENT;
1015d62bc4baSyz147064 		goto done;
1016d62bc4baSyz147064 	}
10172b24ab6bSSebastien Roy 	if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) {
10182b24ab6bSSebastien Roy 		/*
10192b24ab6bSSebastien Roy 		 * The caller is in a non-global zone and the persistent
10202b24ab6bSSebastien Roy 		 * configuration belongs to the global zone.
10212b24ab6bSSebastien Roy 		 */
10222b24ab6bSSebastien Roy 		err = EACCES;
10232b24ab6bSSebastien Roy 		goto done;
10242b24ab6bSSebastien Roy 	}
1025d62bc4baSyz147064 
1026*32715170SCathy Zhou 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
1027*32715170SCathy Zhou 		goto done;
1028*32715170SCathy Zhou 
1029d62bc4baSyz147064 	if ((err = dlconf_create(linkp->ll_link, linkp->ll_linkid,
10302b24ab6bSSebastien Roy 	    linkp->ll_class, linkp->ll_media, zoneid, &dlconfp)) != 0)
1031d62bc4baSyz147064 		goto done;
1032d62bc4baSyz147064 
1033d62bc4baSyz147064 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) {
1034d62bc4baSyz147064 		if ((err = linkattr_set(&(dlconfp->ld_head), attrp->lp_name,
1035d62bc4baSyz147064 		    attrp->lp_val, attrp->lp_sz, attrp->lp_type)) != 0) {
1036d62bc4baSyz147064 			dlconf_destroy(dlconfp);
1037d62bc4baSyz147064 			goto done;
1038d62bc4baSyz147064 		}
1039d62bc4baSyz147064 	}
1040d62bc4baSyz147064 	dlconfp->ld_gen = linkp->ll_gen;
10412b24ab6bSSebastien Roy 	avl_add(&dlmgmt_dlconf_avl, dlconfp);
1042d62bc4baSyz147064 	dlmgmt_advance_dlconfid(dlconfp);
1043d62bc4baSyz147064 
1044*32715170SCathy Zhou 	retvalp->lr_confid = dlconfp->ld_id;
1045d62bc4baSyz147064 done:
1046d62bc4baSyz147064 	dlmgmt_table_unlock();
1047d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
1048d62bc4baSyz147064 	retvalp->lr_err = err;
1049d62bc4baSyz147064 }
1050d62bc4baSyz147064 
1051d62bc4baSyz147064 /*
1052*32715170SCathy Zhou  * dlmgmt_getconfsnapshot() returns a read-only snapshot of all the
1053*32715170SCathy Zhou  * configuration, and requires no privileges.
1054*32715170SCathy Zhou  *
1055*32715170SCathy Zhou  * If the given size cannot hold all the configuration, set the size
1056*32715170SCathy Zhou  * that is needed, and return ENOSPC.
1057d62bc4baSyz147064  */
10582b24ab6bSSebastien Roy /* ARGSUSED */
1059024b0a25Sseb static void
1060*32715170SCathy Zhou dlmgmt_getconfsnapshot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1061*32715170SCathy Zhou     ucred_t *cred)
1062*32715170SCathy Zhou {
1063*32715170SCathy Zhou 	dlmgmt_door_getconfsnapshot_t	*snapshot = argp;
1064*32715170SCathy Zhou 	dlmgmt_getconfsnapshot_retval_t	*retvalp = retp;
1065*32715170SCathy Zhou 	dlmgmt_link_t 			*linkp;
1066*32715170SCathy Zhou 	datalink_id_t			linkid = snapshot->ld_linkid;
1067*32715170SCathy Zhou 	dlmgmt_linkattr_t		*attrp;
1068*32715170SCathy Zhou 	char				*buf;
1069*32715170SCathy Zhou 	size_t				nvlsz;
1070*32715170SCathy Zhou 	nvlist_t			*nvl = NULL;
1071*32715170SCathy Zhou 	int				err = 0;
1072*32715170SCathy Zhou 
1073*32715170SCathy Zhou 	assert(*sz >= sizeof (dlmgmt_getconfsnapshot_retval_t));
1074*32715170SCathy Zhou 
1075*32715170SCathy Zhou 	/*
1076*32715170SCathy Zhou 	 * Hold the reader lock to access the link
1077*32715170SCathy Zhou 	 */
1078*32715170SCathy Zhou 	dlmgmt_table_lock(B_FALSE);
1079*32715170SCathy Zhou 	linkp = link_by_id(linkid, zoneid);
1080*32715170SCathy Zhou 	if ((linkp == NULL) || !(linkp->ll_flags & DLMGMT_PERSIST)) {
1081*32715170SCathy Zhou 		/* The persistent link configuration does not exist. */
1082*32715170SCathy Zhou 		err = ENOENT;
1083*32715170SCathy Zhou 		goto done;
1084*32715170SCathy Zhou 	}
1085*32715170SCathy Zhou 	if (linkp->ll_onloan && zoneid != GLOBAL_ZONEID) {
1086*32715170SCathy Zhou 		/*
1087*32715170SCathy Zhou 		 * The caller is in a non-global zone and the persistent
1088*32715170SCathy Zhou 		 * configuration belongs to the global zone.
1089*32715170SCathy Zhou 		 */
1090*32715170SCathy Zhou 		err = EACCES;
1091*32715170SCathy Zhou 		goto done;
1092*32715170SCathy Zhou 	}
1093*32715170SCathy Zhou 
1094*32715170SCathy Zhou 	err = nvlist_alloc(&nvl, NV_UNIQUE_NAME_TYPE, 0);
1095*32715170SCathy Zhou 	if (err != 0)
1096*32715170SCathy Zhou 		goto done;
1097*32715170SCathy Zhou 
1098*32715170SCathy Zhou 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next) {
1099*32715170SCathy Zhou 		if ((err = nvlist_add_byte_array(nvl, attrp->lp_name,
1100*32715170SCathy Zhou 		    attrp->lp_val, attrp->lp_sz)) != 0) {
1101*32715170SCathy Zhou 			goto done;
1102*32715170SCathy Zhou 		}
1103*32715170SCathy Zhou 	}
1104*32715170SCathy Zhou 
1105*32715170SCathy Zhou 	if ((err = nvlist_size(nvl, &nvlsz, NV_ENCODE_NATIVE)) != 0)
1106*32715170SCathy Zhou 		goto done;
1107*32715170SCathy Zhou 
1108*32715170SCathy Zhou 	if (nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t) > *sz) {
1109*32715170SCathy Zhou 		*sz = nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t);
1110*32715170SCathy Zhou 		err = ENOSPC;
1111*32715170SCathy Zhou 		goto done;
1112*32715170SCathy Zhou 	}
1113*32715170SCathy Zhou 
1114*32715170SCathy Zhou 	/*
1115*32715170SCathy Zhou 	 * pack the the nvlist into the return value.
1116*32715170SCathy Zhou 	 */
1117*32715170SCathy Zhou 	*sz = nvlsz + sizeof (dlmgmt_getconfsnapshot_retval_t);
1118*32715170SCathy Zhou 	retvalp->lr_nvlsz = nvlsz;
1119*32715170SCathy Zhou 	buf = (char *)retvalp + sizeof (dlmgmt_getconfsnapshot_retval_t);
1120*32715170SCathy Zhou 	err = nvlist_pack(nvl, &buf, &nvlsz, NV_ENCODE_NATIVE, 0);
1121*32715170SCathy Zhou 
1122*32715170SCathy Zhou done:
1123*32715170SCathy Zhou 	dlmgmt_table_unlock();
1124*32715170SCathy Zhou 	nvlist_free(nvl);
1125*32715170SCathy Zhou 	retvalp->lr_err = err;
1126*32715170SCathy Zhou }
1127*32715170SCathy Zhou 
1128*32715170SCathy Zhou /* ARGSUSED */
1129*32715170SCathy Zhou static void
1130*32715170SCathy Zhou dlmgmt_getattr(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1131*32715170SCathy Zhou     ucred_t *cred)
1132d62bc4baSyz147064 {
1133024b0a25Sseb 	dlmgmt_door_getattr_t	*getattr = argp;
1134024b0a25Sseb 	dlmgmt_getattr_retval_t	*retvalp = retp;
1135d62bc4baSyz147064 	dlmgmt_dlconf_t		dlconf, *dlconfp;
1136*32715170SCathy Zhou 	int			err;
1137d62bc4baSyz147064 
1138d62bc4baSyz147064 	/*
1139024b0a25Sseb 	 * Hold the read lock to access the dlconf table.
1140d62bc4baSyz147064 	 */
1141d62bc4baSyz147064 	dlmgmt_dlconf_table_lock(B_FALSE);
1142d62bc4baSyz147064 
1143*32715170SCathy Zhou 	dlconf.ld_id = getattr->ld_confid;
11442b24ab6bSSebastien Roy 	if ((dlconfp = avl_find(&dlmgmt_dlconf_avl, &dlconf, NULL)) == NULL ||
11452b24ab6bSSebastien Roy 	    zoneid != dlconfp->ld_zoneid) {
1146024b0a25Sseb 		retvalp->lr_err = ENOENT;
11472b24ab6bSSebastien Roy 	} else {
1148*32715170SCathy Zhou 		if ((err = dlmgmt_checkprivs(dlconfp->ld_class, cred)) != 0) {
1149*32715170SCathy Zhou 			retvalp->lr_err = err;
1150*32715170SCathy Zhou 		} else {
1151*32715170SCathy Zhou 			retvalp->lr_err = dlmgmt_getattr_common(
1152*32715170SCathy Zhou 			    &dlconfp->ld_head, getattr->ld_attr, retvalp);
1153*32715170SCathy Zhou 		}
1154d62bc4baSyz147064 	}
1155d62bc4baSyz147064 
1156d62bc4baSyz147064 	dlmgmt_dlconf_table_unlock();
1157d62bc4baSyz147064 }
1158d62bc4baSyz147064 
1159*32715170SCathy Zhou /* ARGSUSED */
116030890389Sartem static void
1161*32715170SCathy Zhou dlmgmt_upcall_linkprop_init(void *argp, void *retp, size_t *sz,
1162*32715170SCathy Zhou     zoneid_t zoneid, ucred_t *cred)
116330890389Sartem {
116430890389Sartem 	dlmgmt_door_linkprop_init_t	*lip = argp;
116530890389Sartem 	dlmgmt_linkprop_init_retval_t	*retvalp = retp;
11662b24ab6bSSebastien Roy 	dlmgmt_link_t			*linkp;
11672b24ab6bSSebastien Roy 	int				err;
116830890389Sartem 
116930890389Sartem 	dlmgmt_table_lock(B_FALSE);
11702b24ab6bSSebastien Roy 	if ((linkp = link_by_id(lip->ld_linkid, zoneid)) == NULL)
11712b24ab6bSSebastien Roy 		err = ENOENT;
117230890389Sartem 	else
11732b24ab6bSSebastien Roy 		err = dlmgmt_checkprivs(linkp->ll_class, cred);
117430890389Sartem 	dlmgmt_table_unlock();
117530890389Sartem 
11768d4cf8d8S 	if (err == 0) {
11778d4cf8d8S 		dladm_status_t	s;
11788d4cf8d8S 		char		buf[DLADM_STRSIZE];
11798d4cf8d8S 
11808d4cf8d8S 		s = dladm_init_linkprop(dld_handle, lip->ld_linkid, B_TRUE);
11818d4cf8d8S 		if (s != DLADM_STATUS_OK) {
11828d4cf8d8S 			dlmgmt_log(LOG_WARNING,
11838d4cf8d8S 			    "linkprop initialization failed on link %d: %s",
11848d4cf8d8S 			    lip->ld_linkid, dladm_status2str(s, buf));
11858d4cf8d8S 			err = EINVAL;
11868d4cf8d8S 		}
11878d4cf8d8S 	}
11882b24ab6bSSebastien Roy 	retvalp->lr_err = err;
118930890389Sartem }
119030890389Sartem 
11912b24ab6bSSebastien Roy /* ARGSUSED */
119262ee1d25SArtem Kachitchkine static void
1193*32715170SCathy Zhou dlmgmt_setzoneid(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1194*32715170SCathy Zhou     ucred_t *cred)
11952b24ab6bSSebastien Roy {
11962b24ab6bSSebastien Roy 	dlmgmt_door_setzoneid_t	*setzoneid = argp;
11972b24ab6bSSebastien Roy 	dlmgmt_setzoneid_retval_t *retvalp = retp;
11982b24ab6bSSebastien Roy 	dlmgmt_link_t		*linkp;
11992b24ab6bSSebastien Roy 	datalink_id_t		linkid = setzoneid->ld_linkid;
12002b24ab6bSSebastien Roy 	zoneid_t		oldzoneid, newzoneid;
12012b24ab6bSSebastien Roy 	int			err = 0;
12022b24ab6bSSebastien Roy 
12032b24ab6bSSebastien Roy 	dlmgmt_table_lock(B_TRUE);
12042b24ab6bSSebastien Roy 
12052b24ab6bSSebastien Roy 	/* We currently only allow changing zoneid's from the global zone. */
12062b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
12072b24ab6bSSebastien Roy 		err = EACCES;
12082b24ab6bSSebastien Roy 		goto done;
12092b24ab6bSSebastien Roy 	}
12102b24ab6bSSebastien Roy 
12112b24ab6bSSebastien Roy 	if ((linkp = link_by_id(linkid, zoneid)) == NULL) {
12122b24ab6bSSebastien Roy 		err = ENOENT;
12132b24ab6bSSebastien Roy 		goto done;
12142b24ab6bSSebastien Roy 	}
12152b24ab6bSSebastien Roy 
12162b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(linkp->ll_class, cred)) != 0)
12172b24ab6bSSebastien Roy 		goto done;
12182b24ab6bSSebastien Roy 
12192b24ab6bSSebastien Roy 	/* We can only assign an active link to a zone. */
12202b24ab6bSSebastien Roy 	if (!(linkp->ll_flags & DLMGMT_ACTIVE)) {
12212b24ab6bSSebastien Roy 		err = EINVAL;
12222b24ab6bSSebastien Roy 		goto done;
12232b24ab6bSSebastien Roy 	}
12242b24ab6bSSebastien Roy 
12252b24ab6bSSebastien Roy 	oldzoneid = linkp->ll_zoneid;
12262b24ab6bSSebastien Roy 	newzoneid = setzoneid->ld_zoneid;
12272b24ab6bSSebastien Roy 
12282b24ab6bSSebastien Roy 	if (oldzoneid == newzoneid)
12292b24ab6bSSebastien Roy 		goto done;
12302b24ab6bSSebastien Roy 
12312b24ab6bSSebastien Roy 	/*
12322b24ab6bSSebastien Roy 	 * Before we remove the link from its current zone, make sure that
12332b24ab6bSSebastien Roy 	 * there isn't a link with the same name in the destination zone.
12342b24ab6bSSebastien Roy 	 */
12352b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID &&
12362b24ab6bSSebastien Roy 	    link_by_name(linkp->ll_link, newzoneid) != NULL) {
12372b24ab6bSSebastien Roy 		err = EEXIST;
12382b24ab6bSSebastien Roy 		goto done;
12392b24ab6bSSebastien Roy 	}
12402b24ab6bSSebastien Roy 
12412b24ab6bSSebastien Roy 	if (oldzoneid != GLOBAL_ZONEID) {
12422b24ab6bSSebastien Roy 		if (zone_remove_datalink(oldzoneid, linkid) != 0) {
12432b24ab6bSSebastien Roy 			err = errno;
12442b24ab6bSSebastien Roy 			dlmgmt_log(LOG_WARNING, "unable to remove link %d from "
12452b24ab6bSSebastien Roy 			    "zone %d: %s", linkid, oldzoneid, strerror(err));
12462b24ab6bSSebastien Roy 			goto done;
12472b24ab6bSSebastien Roy 		}
12482b24ab6bSSebastien Roy 		avl_remove(&dlmgmt_loan_avl, linkp);
12492b24ab6bSSebastien Roy 		linkp->ll_onloan = B_FALSE;
12502b24ab6bSSebastien Roy 	}
12512b24ab6bSSebastien Roy 	if (newzoneid != GLOBAL_ZONEID) {
12522b24ab6bSSebastien Roy 		if (zone_add_datalink(newzoneid, linkid) != 0) {
12532b24ab6bSSebastien Roy 			err = errno;
12542b24ab6bSSebastien Roy 			dlmgmt_log(LOG_WARNING, "unable to add link %d to zone "
12552b24ab6bSSebastien Roy 			    "%d: %s", linkid, newzoneid, strerror(err));
12562b24ab6bSSebastien Roy 			(void) zone_add_datalink(oldzoneid, linkid);
12572b24ab6bSSebastien Roy 			goto done;
12582b24ab6bSSebastien Roy 		}
12592b24ab6bSSebastien Roy 		avl_add(&dlmgmt_loan_avl, linkp);
12602b24ab6bSSebastien Roy 		linkp->ll_onloan = B_TRUE;
12612b24ab6bSSebastien Roy 	}
12622b24ab6bSSebastien Roy 
12632b24ab6bSSebastien Roy 	avl_remove(&dlmgmt_name_avl, linkp);
12642b24ab6bSSebastien Roy 	linkp->ll_zoneid = newzoneid;
12652b24ab6bSSebastien Roy 	avl_add(&dlmgmt_name_avl, linkp);
12662b24ab6bSSebastien Roy 
12672b24ab6bSSebastien Roy done:
12682b24ab6bSSebastien Roy 	dlmgmt_table_unlock();
12692b24ab6bSSebastien Roy 	retvalp->lr_err = err;
12702b24ab6bSSebastien Roy }
12712b24ab6bSSebastien Roy 
1272*32715170SCathy Zhou /* ARGSUSED */
12732b24ab6bSSebastien Roy static void
1274*32715170SCathy Zhou dlmgmt_zoneboot(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1275*32715170SCathy Zhou     ucred_t *cred)
12762b24ab6bSSebastien Roy {
12772b24ab6bSSebastien Roy 	int			err;
12782b24ab6bSSebastien Roy 	dlmgmt_door_zoneboot_t	*zoneboot = argp;
12792b24ab6bSSebastien Roy 	dlmgmt_zoneboot_retval_t *retvalp = retp;
12802b24ab6bSSebastien Roy 
12812b24ab6bSSebastien Roy 	dlmgmt_table_lock(B_TRUE);
12822b24ab6bSSebastien Roy 
12832b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(0, cred)) != 0)
12842b24ab6bSSebastien Roy 		goto done;
12852b24ab6bSSebastien Roy 
12862b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
12872b24ab6bSSebastien Roy 		err = EACCES;
12882b24ab6bSSebastien Roy 		goto done;
12892b24ab6bSSebastien Roy 	}
12902b24ab6bSSebastien Roy 	if (zoneboot->ld_zoneid == GLOBAL_ZONEID) {
12912b24ab6bSSebastien Roy 		err = EINVAL;
12922b24ab6bSSebastien Roy 		goto done;
12932b24ab6bSSebastien Roy 	}
12942b24ab6bSSebastien Roy 
12952b24ab6bSSebastien Roy 	if ((err = dlmgmt_elevate_privileges()) == 0) {
12962b24ab6bSSebastien Roy 		err = dlmgmt_zone_init(zoneboot->ld_zoneid);
12972b24ab6bSSebastien Roy 		(void) dlmgmt_drop_privileges();
12982b24ab6bSSebastien Roy 	}
12992b24ab6bSSebastien Roy done:
13002b24ab6bSSebastien Roy 	dlmgmt_table_unlock();
13012b24ab6bSSebastien Roy 	retvalp->lr_err = err;
13022b24ab6bSSebastien Roy }
13032b24ab6bSSebastien Roy 
1304*32715170SCathy Zhou /* ARGSUSED */
13052b24ab6bSSebastien Roy static void
1306*32715170SCathy Zhou dlmgmt_zonehalt(void *argp, void *retp, size_t *sz, zoneid_t zoneid,
1307*32715170SCathy Zhou     ucred_t *cred)
13082b24ab6bSSebastien Roy {
13092b24ab6bSSebastien Roy 	int			err = 0;
13102b24ab6bSSebastien Roy 	dlmgmt_door_zonehalt_t	*zonehalt = argp;
13112b24ab6bSSebastien Roy 	dlmgmt_zonehalt_retval_t *retvalp = retp;
13122b24ab6bSSebastien Roy 
13132b24ab6bSSebastien Roy 	if ((err = dlmgmt_checkprivs(0, cred)) == 0) {
13142b24ab6bSSebastien Roy 		if (zoneid != GLOBAL_ZONEID) {
13152b24ab6bSSebastien Roy 			err = EACCES;
13162b24ab6bSSebastien Roy 		} else if (zonehalt->ld_zoneid == GLOBAL_ZONEID) {
13172b24ab6bSSebastien Roy 			err = EINVAL;
13182b24ab6bSSebastien Roy 		} else {
13192b24ab6bSSebastien Roy 			dlmgmt_table_lock(B_TRUE);
13202b24ab6bSSebastien Roy 			dlmgmt_db_fini(zonehalt->ld_zoneid);
13212b24ab6bSSebastien Roy 			dlmgmt_table_unlock();
13222b24ab6bSSebastien Roy 		}
13232b24ab6bSSebastien Roy 	}
13242b24ab6bSSebastien Roy 	retvalp->lr_err = err;
13252b24ab6bSSebastien Roy }
13262b24ab6bSSebastien Roy 
1327024b0a25Sseb static dlmgmt_door_info_t i_dlmgmt_door_info_tbl[] = {
13282b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_CREATE, sizeof (dlmgmt_upcall_arg_create_t),
1329024b0a25Sseb 	    sizeof (dlmgmt_create_retval_t), dlmgmt_upcall_create },
13302b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_GETATTR, sizeof (dlmgmt_upcall_arg_getattr_t),
1331024b0a25Sseb 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_upcall_getattr },
13322b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_DESTROY, sizeof (dlmgmt_upcall_arg_destroy_t),
1333024b0a25Sseb 	    sizeof (dlmgmt_destroy_retval_t), dlmgmt_upcall_destroy },
13342b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETNAME, sizeof (dlmgmt_door_getname_t),
1335024b0a25Sseb 	    sizeof (dlmgmt_getname_retval_t), dlmgmt_getname },
13362b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETLINKID, sizeof (dlmgmt_door_getlinkid_t),
1337024b0a25Sseb 	    sizeof (dlmgmt_getlinkid_retval_t), dlmgmt_getlinkid },
13382b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETNEXT, sizeof (dlmgmt_door_getnext_t),
1339024b0a25Sseb 	    sizeof (dlmgmt_getnext_retval_t), dlmgmt_getnext },
13402b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DLS_UPDATE, sizeof (dlmgmt_upcall_arg_update_t),
1341024b0a25Sseb 	    sizeof (dlmgmt_update_retval_t), dlmgmt_upcall_update },
13422b24ab6bSSebastien Roy 	{ DLMGMT_CMD_CREATE_LINKID, sizeof (dlmgmt_door_createid_t),
1343024b0a25Sseb 	    sizeof (dlmgmt_createid_retval_t), dlmgmt_createid },
13442b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DESTROY_LINKID, sizeof (dlmgmt_door_destroyid_t),
1345024b0a25Sseb 	    sizeof (dlmgmt_destroyid_retval_t), dlmgmt_destroyid },
13462b24ab6bSSebastien Roy 	{ DLMGMT_CMD_REMAP_LINKID, sizeof (dlmgmt_door_remapid_t),
1347024b0a25Sseb 	    sizeof (dlmgmt_remapid_retval_t), dlmgmt_remapid },
13482b24ab6bSSebastien Roy 	{ DLMGMT_CMD_CREATECONF, sizeof (dlmgmt_door_createconf_t),
1349024b0a25Sseb 	    sizeof (dlmgmt_createconf_retval_t), dlmgmt_createconf },
1350*32715170SCathy Zhou 	{ DLMGMT_CMD_OPENCONF, sizeof (dlmgmt_door_openconf_t),
1351*32715170SCathy Zhou 	    sizeof (dlmgmt_openconf_retval_t), dlmgmt_openconf },
13522b24ab6bSSebastien Roy 	{ DLMGMT_CMD_WRITECONF, sizeof (dlmgmt_door_writeconf_t),
1353024b0a25Sseb 	    sizeof (dlmgmt_writeconf_retval_t), dlmgmt_writeconf },
13542b24ab6bSSebastien Roy 	{ DLMGMT_CMD_UP_LINKID, sizeof (dlmgmt_door_upid_t),
1355024b0a25Sseb 	    sizeof (dlmgmt_upid_retval_t), dlmgmt_upid },
13562b24ab6bSSebastien Roy 	{ DLMGMT_CMD_SETATTR, sizeof (dlmgmt_door_setattr_t),
1357024b0a25Sseb 	    sizeof (dlmgmt_setattr_retval_t), dlmgmt_setattr },
13582b24ab6bSSebastien Roy 	{ DLMGMT_CMD_UNSETATTR, sizeof (dlmgmt_door_unsetattr_t),
1359024b0a25Sseb 	    sizeof (dlmgmt_unsetattr_retval_t), dlmgmt_unsetconfattr },
13602b24ab6bSSebastien Roy 	{ DLMGMT_CMD_REMOVECONF, sizeof (dlmgmt_door_removeconf_t),
1361024b0a25Sseb 	    sizeof (dlmgmt_removeconf_retval_t), dlmgmt_removeconf },
13622b24ab6bSSebastien Roy 	{ DLMGMT_CMD_DESTROYCONF, sizeof (dlmgmt_door_destroyconf_t),
1363024b0a25Sseb 	    sizeof (dlmgmt_destroyconf_retval_t), dlmgmt_destroyconf },
13642b24ab6bSSebastien Roy 	{ DLMGMT_CMD_GETATTR, sizeof (dlmgmt_door_getattr_t),
136530890389Sartem 	    sizeof (dlmgmt_getattr_retval_t), dlmgmt_getattr },
1366*32715170SCathy Zhou 	{ DLMGMT_CMD_GETCONFSNAPSHOT, sizeof (dlmgmt_door_getconfsnapshot_t),
1367*32715170SCathy Zhou 	    sizeof (dlmgmt_getconfsnapshot_retval_t), dlmgmt_getconfsnapshot },
13682b24ab6bSSebastien Roy 	{ DLMGMT_CMD_LINKPROP_INIT, sizeof (dlmgmt_door_linkprop_init_t),
136930890389Sartem 	    sizeof (dlmgmt_linkprop_init_retval_t),
137062ee1d25SArtem Kachitchkine 	    dlmgmt_upcall_linkprop_init },
13712b24ab6bSSebastien Roy 	{ DLMGMT_CMD_SETZONEID, sizeof (dlmgmt_door_setzoneid_t),
13722b24ab6bSSebastien Roy 	    sizeof (dlmgmt_setzoneid_retval_t), dlmgmt_setzoneid },
13732b24ab6bSSebastien Roy 	{ DLMGMT_CMD_ZONEBOOT, sizeof (dlmgmt_door_zoneboot_t),
13742b24ab6bSSebastien Roy 	    sizeof (dlmgmt_zoneboot_retval_t), dlmgmt_zoneboot },
13752b24ab6bSSebastien Roy 	{ DLMGMT_CMD_ZONEHALT, sizeof (dlmgmt_door_zonehalt_t),
13762b24ab6bSSebastien Roy 	    sizeof (dlmgmt_zonehalt_retval_t), dlmgmt_zonehalt },
13772b24ab6bSSebastien Roy 	{ 0, 0, 0, NULL }
1378024b0a25Sseb };
1379d62bc4baSyz147064 
13802b24ab6bSSebastien Roy static dlmgmt_door_info_t *
13812b24ab6bSSebastien Roy dlmgmt_getcmdinfo(int cmd)
13822b24ab6bSSebastien Roy {
13832b24ab6bSSebastien Roy 	dlmgmt_door_info_t	*infop = i_dlmgmt_door_info_tbl;
13842b24ab6bSSebastien Roy 
13852b24ab6bSSebastien Roy 	while (infop->di_handler != NULL) {
13862b24ab6bSSebastien Roy 		if (infop->di_cmd == cmd)
13872b24ab6bSSebastien Roy 			break;
13882b24ab6bSSebastien Roy 		infop++;
13892b24ab6bSSebastien Roy 	}
13902b24ab6bSSebastien Roy 	return (infop);
13912b24ab6bSSebastien Roy }
1392d62bc4baSyz147064 
1393d62bc4baSyz147064 /* ARGSUSED */
1394d62bc4baSyz147064 void
1395d62bc4baSyz147064 dlmgmt_handler(void *cookie, char *argp, size_t argsz, door_desc_t *dp,
1396d62bc4baSyz147064     uint_t n_desc)
1397d62bc4baSyz147064 {
13982b24ab6bSSebastien Roy 	dlmgmt_door_arg_t	*door_arg = (dlmgmt_door_arg_t *)(void *)argp;
1399024b0a25Sseb 	dlmgmt_door_info_t	*infop = NULL;
1400024b0a25Sseb 	dlmgmt_retval_t		retval;
14012b24ab6bSSebastien Roy 	ucred_t			*cred = NULL;
14022b24ab6bSSebastien Roy 	zoneid_t		zoneid;
1403*32715170SCathy Zhou 	void			*retvalp = NULL;
1404*32715170SCathy Zhou 	size_t			sz, acksz;
1405eae72b5bSSebastien Roy 	int			err = 0;
1406d62bc4baSyz147064 
14072b24ab6bSSebastien Roy 	infop = dlmgmt_getcmdinfo(door_arg->ld_cmd);
1408024b0a25Sseb 	if (infop == NULL || argsz != infop->di_reqsz) {
1409024b0a25Sseb 		err = EINVAL;
14102b24ab6bSSebastien Roy 		goto done;
1411024b0a25Sseb 	}
1412024b0a25Sseb 
14132b24ab6bSSebastien Roy 	if (door_ucred(&cred) != 0 || (zoneid = ucred_getzoneid(cred)) == -1) {
1414024b0a25Sseb 		err = errno;
14152b24ab6bSSebastien Roy 		goto done;
1416024b0a25Sseb 	}
1417024b0a25Sseb 
1418024b0a25Sseb 	/*
1419*32715170SCathy Zhou 	 * Note that malloc() cannot be used here because door_return
1420*32715170SCathy Zhou 	 * never returns, and memory allocated by malloc() would get leaked.
1421*32715170SCathy Zhou 	 * Use alloca() instead.
1422024b0a25Sseb 	 */
1423*32715170SCathy Zhou 	acksz = infop->di_acksz;
1424*32715170SCathy Zhou 
1425*32715170SCathy Zhou again:
1426*32715170SCathy Zhou 	retvalp = alloca(acksz);
1427*32715170SCathy Zhou 	sz = acksz;
1428*32715170SCathy Zhou 	infop->di_handler(argp, retvalp, &acksz, zoneid, cred);
1429*32715170SCathy Zhou 	if (acksz > sz) {
1430*32715170SCathy Zhou 		/*
1431*32715170SCathy Zhou 		 * If the specified buffer size is not big enough to hold the
1432*32715170SCathy Zhou 		 * return value, reallocate the buffer and try to get the
1433*32715170SCathy Zhou 		 * result one more time.
1434*32715170SCathy Zhou 		 */
1435*32715170SCathy Zhou 		assert(((dlmgmt_retval_t *)retvalp)->lr_err == ENOSPC);
1436*32715170SCathy Zhou 		goto again;
1437*32715170SCathy Zhou 	}
1438024b0a25Sseb 
14392b24ab6bSSebastien Roy done:
14402b24ab6bSSebastien Roy 	if (cred != NULL)
14412b24ab6bSSebastien Roy 		ucred_free(cred);
14422b24ab6bSSebastien Roy 	if (err == 0) {
1443*32715170SCathy Zhou 		(void) door_return(retvalp, acksz, NULL, 0);
14442b24ab6bSSebastien Roy 	} else {
1445024b0a25Sseb 		retval.lr_err = err;
1446024b0a25Sseb 		(void) door_return((char *)&retval, sizeof (retval), NULL, 0);
1447024b0a25Sseb 	}
14482b24ab6bSSebastien Roy }
1449