17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5ba2e4443Sseb  * Common Development and Distribution License (the "License").
6ba2e4443Sseb  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  */
217c478bd9Sstevel@tonic-gate /*
22ba2e4443Sseb  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
237c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
247c478bd9Sstevel@tonic-gate  */
257c478bd9Sstevel@tonic-gate 
267c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
277c478bd9Sstevel@tonic-gate 
287c478bd9Sstevel@tonic-gate #include <unistd.h>
297c478bd9Sstevel@tonic-gate #include <stropts.h>
307c478bd9Sstevel@tonic-gate #include <errno.h>
310ba2cbe9Sxc #include <fcntl.h>
320ba2cbe9Sxc #include <strings.h>
330ba2cbe9Sxc #include <dirent.h>
347c478bd9Sstevel@tonic-gate #include <net/if.h>
350ba2cbe9Sxc #include <sys/stat.h>
360ba2cbe9Sxc #include <sys/dld.h>
370ba2cbe9Sxc #include <libdlpi.h>
380ba2cbe9Sxc #include <libdevinfo.h>
390ba2cbe9Sxc #include <libdladm_impl.h>
40*33343a97Smeem #include <libintl.h>
417c478bd9Sstevel@tonic-gate 
42aae21359Skrgopi typedef struct dladm_dev {
43aae21359Skrgopi 	char			dd_name[IFNAMSIZ];
44aae21359Skrgopi 	struct dladm_dev	*dd_next;
45aae21359Skrgopi } dladm_dev_t;
46aae21359Skrgopi 
47aae21359Skrgopi typedef struct dladm_walk {
48aae21359Skrgopi 	dladm_dev_t		*dw_dev_list;
49aae21359Skrgopi } dladm_walk_t;
50aae21359Skrgopi 
510ba2cbe9Sxc static char		dladm_rootdir[MAXPATHLEN] = "/";
520ba2cbe9Sxc 
537c478bd9Sstevel@tonic-gate /*
547c478bd9Sstevel@tonic-gate  * Issue an ioctl to the specified file descriptor attached to the
557c478bd9Sstevel@tonic-gate  * DLD control driver interface.
567c478bd9Sstevel@tonic-gate  */
570ba2cbe9Sxc int
58210db224Sericheng i_dladm_ioctl(int fd, int ic_cmd, void *ic_dp, int ic_len)
597c478bd9Sstevel@tonic-gate {
607c478bd9Sstevel@tonic-gate 	struct strioctl	iocb;
617c478bd9Sstevel@tonic-gate 
627c478bd9Sstevel@tonic-gate 	iocb.ic_cmd = ic_cmd;
637c478bd9Sstevel@tonic-gate 	iocb.ic_timout = 0;
647c478bd9Sstevel@tonic-gate 	iocb.ic_len = ic_len;
65210db224Sericheng 	iocb.ic_dp = (char *)ic_dp;
667c478bd9Sstevel@tonic-gate 
677c478bd9Sstevel@tonic-gate 	return (ioctl(fd, I_STR, &iocb));
687c478bd9Sstevel@tonic-gate }
697c478bd9Sstevel@tonic-gate 
707c478bd9Sstevel@tonic-gate /*
717c478bd9Sstevel@tonic-gate  * Return the attributes of the specified datalink from the DLD driver.
727c478bd9Sstevel@tonic-gate  */
737c478bd9Sstevel@tonic-gate static int
747c478bd9Sstevel@tonic-gate i_dladm_info(int fd, const char *name, dladm_attr_t *dap)
757c478bd9Sstevel@tonic-gate {
767c478bd9Sstevel@tonic-gate 	dld_ioc_attr_t	dia;
777c478bd9Sstevel@tonic-gate 
787c478bd9Sstevel@tonic-gate 	if (strlen(name) >= IFNAMSIZ) {
797c478bd9Sstevel@tonic-gate 		errno = EINVAL;
807c478bd9Sstevel@tonic-gate 		return (-1);
817c478bd9Sstevel@tonic-gate 	}
827c478bd9Sstevel@tonic-gate 
837c478bd9Sstevel@tonic-gate 	(void) strlcpy(dia.dia_name, name, IFNAMSIZ);
847c478bd9Sstevel@tonic-gate 
85210db224Sericheng 	if (i_dladm_ioctl(fd, DLDIOCATTR, &dia, sizeof (dia)) < 0)
867c478bd9Sstevel@tonic-gate 		return (-1);
877c478bd9Sstevel@tonic-gate 
887c478bd9Sstevel@tonic-gate 	(void) strlcpy(dap->da_dev, dia.dia_dev, MAXNAMELEN);
89210db224Sericheng 	dap->da_max_sdu = dia.dia_max_sdu;
907c478bd9Sstevel@tonic-gate 	dap->da_vid = dia.dia_vid;
917c478bd9Sstevel@tonic-gate 
927c478bd9Sstevel@tonic-gate 	return (0);
937c478bd9Sstevel@tonic-gate }
947c478bd9Sstevel@tonic-gate 
957c478bd9Sstevel@tonic-gate /*
967c478bd9Sstevel@tonic-gate  * Adds a datalink to the array corresponding to arg.
977c478bd9Sstevel@tonic-gate  */
987c478bd9Sstevel@tonic-gate static void
997c478bd9Sstevel@tonic-gate i_dladm_nt_net_add(void *arg, char *name)
1007c478bd9Sstevel@tonic-gate {
101aae21359Skrgopi 	dladm_walk_t	*dwp = arg;
102aae21359Skrgopi 	dladm_dev_t	*ddp = dwp->dw_dev_list;
103aae21359Skrgopi 	dladm_dev_t	**lastp = &dwp->dw_dev_list;
104aae21359Skrgopi 
105aae21359Skrgopi 	while (ddp) {
106aae21359Skrgopi 		/*
107aae21359Skrgopi 		 * Skip duplicates.
108aae21359Skrgopi 		 */
109aae21359Skrgopi 		if (strcmp(ddp->dd_name, name) == 0)
1107c478bd9Sstevel@tonic-gate 			return;
111aae21359Skrgopi 
112aae21359Skrgopi 		lastp = &ddp->dd_next;
113aae21359Skrgopi 		ddp = ddp->dd_next;
1147c478bd9Sstevel@tonic-gate 	}
1157c478bd9Sstevel@tonic-gate 
116aae21359Skrgopi 	if ((ddp = malloc(sizeof (*ddp))) == NULL)
117aae21359Skrgopi 		return;
118aae21359Skrgopi 
119aae21359Skrgopi 	(void) strlcpy(ddp->dd_name, name, IFNAMSIZ);
120aae21359Skrgopi 	ddp->dd_next = NULL;
121aae21359Skrgopi 	*lastp = ddp;
1227c478bd9Sstevel@tonic-gate }
1237c478bd9Sstevel@tonic-gate 
1247c478bd9Sstevel@tonic-gate /*
1257c478bd9Sstevel@tonic-gate  * Walker callback invoked for each DDI_NT_NET node.
1267c478bd9Sstevel@tonic-gate  */
1277c478bd9Sstevel@tonic-gate static int
1287c478bd9Sstevel@tonic-gate i_dladm_nt_net_walk(di_node_t node, di_minor_t minor, void *arg)
1297c478bd9Sstevel@tonic-gate {
1307c478bd9Sstevel@tonic-gate 	dl_info_ack_t	dlia;
1317c478bd9Sstevel@tonic-gate 	char		name[IFNAMSIZ];
1327c478bd9Sstevel@tonic-gate 	int		fd;
1337c478bd9Sstevel@tonic-gate 	char		*provider;
1347c478bd9Sstevel@tonic-gate 	uint_t		ppa;
1357c478bd9Sstevel@tonic-gate 
1367c478bd9Sstevel@tonic-gate 	provider = di_minor_name(minor);
1377c478bd9Sstevel@tonic-gate 
1387c478bd9Sstevel@tonic-gate 	if ((fd = dlpi_open(provider)) < 0)
1397c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1407c478bd9Sstevel@tonic-gate 
1417c478bd9Sstevel@tonic-gate 	if (dlpi_info(fd, -1, &dlia, NULL, NULL, NULL, NULL, NULL, NULL) < 0) {
1427c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1437c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1447c478bd9Sstevel@tonic-gate 	}
1457c478bd9Sstevel@tonic-gate 
1467c478bd9Sstevel@tonic-gate 	if (dlia.dl_provider_style == DL_STYLE1) {
1477c478bd9Sstevel@tonic-gate 		i_dladm_nt_net_add(arg, provider);
1487c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1497c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 
1527c478bd9Sstevel@tonic-gate 	ppa = di_instance(node);
1537c478bd9Sstevel@tonic-gate 
1547c478bd9Sstevel@tonic-gate 	if (dlpi_attach(fd, -1, ppa) < 0) {
1557c478bd9Sstevel@tonic-gate 		(void) dlpi_close(fd);
1567c478bd9Sstevel@tonic-gate 		return (DI_WALK_CONTINUE);
1577c478bd9Sstevel@tonic-gate 	}
1587c478bd9Sstevel@tonic-gate 	(void) snprintf(name, IFNAMSIZ - 1, "%s%d", provider, ppa);
1597c478bd9Sstevel@tonic-gate 	i_dladm_nt_net_add(arg, name);
1607c478bd9Sstevel@tonic-gate 	(void) dlpi_close(fd);
1617c478bd9Sstevel@tonic-gate 	return (DI_WALK_CONTINUE);
1627c478bd9Sstevel@tonic-gate }
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate /*
1657c478bd9Sstevel@tonic-gate  * Invoke the specified callback function for each active DDI_NT_NET
1667c478bd9Sstevel@tonic-gate  * node.
1677c478bd9Sstevel@tonic-gate  */
1687c478bd9Sstevel@tonic-gate int
1697c478bd9Sstevel@tonic-gate dladm_walk(void (*fn)(void *, const char *), void *arg)
1707c478bd9Sstevel@tonic-gate {
1717c478bd9Sstevel@tonic-gate 	di_node_t	root;
172aae21359Skrgopi 	dladm_walk_t	dw;
173aae21359Skrgopi 	dladm_dev_t	*ddp, *last_ddp;
1747c478bd9Sstevel@tonic-gate 
1757c478bd9Sstevel@tonic-gate 	if ((root = di_init("/", DINFOCACHE)) == DI_NODE_NIL) {
1767c478bd9Sstevel@tonic-gate 		errno = EFAULT;
1777c478bd9Sstevel@tonic-gate 		return (-1);
1787c478bd9Sstevel@tonic-gate 	}
179aae21359Skrgopi 	dw.dw_dev_list = NULL;
1807c478bd9Sstevel@tonic-gate 
181aae21359Skrgopi 	(void) di_walk_minor(root, DDI_NT_NET, DI_CHECK_ALIAS, &dw,
1827c478bd9Sstevel@tonic-gate 	    i_dladm_nt_net_walk);
183aae21359Skrgopi 
1847c478bd9Sstevel@tonic-gate 	di_fini(root);
1857c478bd9Sstevel@tonic-gate 
186aae21359Skrgopi 	ddp = dw.dw_dev_list;
187aae21359Skrgopi 	while (ddp) {
188aae21359Skrgopi 		fn(arg, ddp->dd_name);
1896e61f8d9Skrgopi 		(void) dladm_walk_vlan(fn, arg, ddp->dd_name);
190aae21359Skrgopi 		last_ddp = ddp;
191aae21359Skrgopi 		ddp = ddp->dd_next;
192aae21359Skrgopi 		free(last_ddp);
1937c478bd9Sstevel@tonic-gate 	}
1947c478bd9Sstevel@tonic-gate 
195aae21359Skrgopi 	return (0);
1967c478bd9Sstevel@tonic-gate }
1977c478bd9Sstevel@tonic-gate 
1987c478bd9Sstevel@tonic-gate /*
199210db224Sericheng  * Invoke the specified callback function for each vlan managed by dld
2007c478bd9Sstevel@tonic-gate  */
2017c478bd9Sstevel@tonic-gate int
202aae21359Skrgopi dladm_walk_vlan(void (*fn)(void *, const char *), void *arg, const char *name)
2037c478bd9Sstevel@tonic-gate {
204aae21359Skrgopi 	int		fd, bufsize, i;
205aae21359Skrgopi 	int		nvlan = 4094;
206210db224Sericheng 	dld_ioc_vlan_t	*iocp = NULL;
207210db224Sericheng 	dld_vlan_info_t	*dvip;
2087c478bd9Sstevel@tonic-gate 
209210db224Sericheng 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
2107c478bd9Sstevel@tonic-gate 		return (-1);
2117c478bd9Sstevel@tonic-gate 
212aae21359Skrgopi 	bufsize = sizeof (dld_ioc_vlan_t) + nvlan * sizeof (dld_vlan_info_t);
2137c478bd9Sstevel@tonic-gate 
214aae21359Skrgopi 	if ((iocp = (dld_ioc_vlan_t *)calloc(1, bufsize)) == NULL)
215aae21359Skrgopi 		return (-1);
2167c478bd9Sstevel@tonic-gate 
217ba2e4443Sseb 	(void) strlcpy((char *)iocp->div_name, name, IFNAMSIZ);
218aae21359Skrgopi 	if (i_dladm_ioctl(fd, DLDIOCVLAN, iocp, bufsize) == 0) {
219aae21359Skrgopi 		dvip = (dld_vlan_info_t *)(iocp + 1);
220aae21359Skrgopi 		for (i = 0; i < iocp->div_count; i++)
221210db224Sericheng 			(*fn)(arg, dvip[i].dvi_name);
2227c478bd9Sstevel@tonic-gate 	}
223aae21359Skrgopi 	/*
224aae21359Skrgopi 	 * Note: Callers of dladm_walk_vlan() ignore the return
225aae21359Skrgopi 	 * value of this routine. So ignoring ioctl failure case
226aae21359Skrgopi 	 * and just returning 0.
227aae21359Skrgopi 	 */
228210db224Sericheng 	free(iocp);
2297c478bd9Sstevel@tonic-gate 	(void) close(fd);
2307c478bd9Sstevel@tonic-gate 	return (0);
2317c478bd9Sstevel@tonic-gate }
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 
2347c478bd9Sstevel@tonic-gate /*
2357c478bd9Sstevel@tonic-gate  * Returns the current attributes of the specified datalink.
2367c478bd9Sstevel@tonic-gate  */
2377c478bd9Sstevel@tonic-gate int
2387c478bd9Sstevel@tonic-gate dladm_info(const char *name, dladm_attr_t *dap)
2397c478bd9Sstevel@tonic-gate {
2407c478bd9Sstevel@tonic-gate 	int		fd;
2417c478bd9Sstevel@tonic-gate 
2427c478bd9Sstevel@tonic-gate 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
2437c478bd9Sstevel@tonic-gate 		return (-1);
2447c478bd9Sstevel@tonic-gate 
2457c478bd9Sstevel@tonic-gate 	if (i_dladm_info(fd, name, dap) < 0)
2467c478bd9Sstevel@tonic-gate 		goto failed;
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 	(void) close(fd);
2497c478bd9Sstevel@tonic-gate 	return (0);
2507c478bd9Sstevel@tonic-gate 
2517c478bd9Sstevel@tonic-gate failed:
2527c478bd9Sstevel@tonic-gate 	(void) close(fd);
2537c478bd9Sstevel@tonic-gate 	return (-1);
2547c478bd9Sstevel@tonic-gate }
2550ba2cbe9Sxc 
2560ba2cbe9Sxc const char *
2570ba2cbe9Sxc dladm_status2str(dladm_status_t status, char *buf)
2580ba2cbe9Sxc {
2590ba2cbe9Sxc 	const char	*s;
2600ba2cbe9Sxc 
2610ba2cbe9Sxc 	switch (status) {
2620ba2cbe9Sxc 	case DLADM_STATUS_OK:
2630ba2cbe9Sxc 		s = "ok";
2640ba2cbe9Sxc 		break;
2650ba2cbe9Sxc 	case DLADM_STATUS_BADARG:
2660ba2cbe9Sxc 		s = "invalid argument";
2670ba2cbe9Sxc 		break;
2680ba2cbe9Sxc 	case DLADM_STATUS_FAILED:
2690ba2cbe9Sxc 		s = "operation failed";
2700ba2cbe9Sxc 		break;
2710ba2cbe9Sxc 	case DLADM_STATUS_TOOSMALL:
2720ba2cbe9Sxc 		s = "buffer size too small";
2730ba2cbe9Sxc 		break;
2740ba2cbe9Sxc 	case DLADM_STATUS_NOTSUP:
2750ba2cbe9Sxc 		s = "operation not supported";
2760ba2cbe9Sxc 		break;
2770ba2cbe9Sxc 	case DLADM_STATUS_NOTFOUND:
2780ba2cbe9Sxc 		s = "object not found";
2790ba2cbe9Sxc 		break;
2800ba2cbe9Sxc 	case DLADM_STATUS_BADVAL:
2810ba2cbe9Sxc 		s = "invalid value";
2820ba2cbe9Sxc 		break;
2830ba2cbe9Sxc 	case DLADM_STATUS_NOMEM:
2840ba2cbe9Sxc 		s = "insufficient memory";
2850ba2cbe9Sxc 		break;
2860ba2cbe9Sxc 	case DLADM_STATUS_EXIST:
2870ba2cbe9Sxc 		s = "object already exists";
2880ba2cbe9Sxc 		break;
2890ba2cbe9Sxc 	case DLADM_STATUS_LINKINVAL:
2900ba2cbe9Sxc 		s = "invalid link";
2910ba2cbe9Sxc 		break;
2920ba2cbe9Sxc 	case DLADM_STATUS_PROPRDONLY:
2930ba2cbe9Sxc 		s = "read-only property";
2940ba2cbe9Sxc 		break;
2950ba2cbe9Sxc 	case DLADM_STATUS_BADVALCNT:
2960ba2cbe9Sxc 		s = "invalid number of values";
2970ba2cbe9Sxc 		break;
2980ba2cbe9Sxc 	case DLADM_STATUS_DBNOTFOUND:
2990ba2cbe9Sxc 		s = "database not found";
3000ba2cbe9Sxc 		break;
3010ba2cbe9Sxc 	case DLADM_STATUS_DENIED:
3020ba2cbe9Sxc 		s = "permission denied";
3030ba2cbe9Sxc 		break;
3040ba2cbe9Sxc 	case DLADM_STATUS_IOERR:
3050ba2cbe9Sxc 		s = "I/O error";
3060ba2cbe9Sxc 		break;
3070ba2cbe9Sxc 	default:
308*33343a97Smeem 		s = "<unknown error>";
309*33343a97Smeem 		break;
3100ba2cbe9Sxc 	}
311*33343a97Smeem 	(void) snprintf(buf, DLADM_STRSIZE, "%s", dgettext(TEXT_DOMAIN, s));
3120ba2cbe9Sxc 	return (buf);
3130ba2cbe9Sxc }
3140ba2cbe9Sxc 
3150ba2cbe9Sxc /*
3160ba2cbe9Sxc  * Convert a unix errno to a dladm_status_t.
3170ba2cbe9Sxc  * We only convert errnos that are likely to be encountered. All others
3180ba2cbe9Sxc  * are mapped to DLADM_STATUS_FAILED.
3190ba2cbe9Sxc  */
3200ba2cbe9Sxc dladm_status_t
3210ba2cbe9Sxc dladm_errno2status(int err)
3220ba2cbe9Sxc {
3230ba2cbe9Sxc 	switch (err) {
3240ba2cbe9Sxc 	case EINVAL:
3250ba2cbe9Sxc 		return (DLADM_STATUS_BADARG);
3260ba2cbe9Sxc 	case EEXIST:
3270ba2cbe9Sxc 		return (DLADM_STATUS_EXIST);
3280ba2cbe9Sxc 	case ENOENT:
3290ba2cbe9Sxc 		return (DLADM_STATUS_NOTFOUND);
3300ba2cbe9Sxc 	case ENOSPC:
3310ba2cbe9Sxc 		return (DLADM_STATUS_TOOSMALL);
3320ba2cbe9Sxc 	case ENOMEM:
3330ba2cbe9Sxc 		return (DLADM_STATUS_NOMEM);
3340ba2cbe9Sxc 	case ENOTSUP:
3350ba2cbe9Sxc 		return (DLADM_STATUS_NOTSUP);
3360ba2cbe9Sxc 	case EACCES:
3370ba2cbe9Sxc 		return (DLADM_STATUS_DENIED);
3380ba2cbe9Sxc 	case EIO:
3390ba2cbe9Sxc 		return (DLADM_STATUS_IOERR);
3400ba2cbe9Sxc 	default:
3410ba2cbe9Sxc 		return (DLADM_STATUS_FAILED);
3420ba2cbe9Sxc 	}
3430ba2cbe9Sxc }
3440ba2cbe9Sxc 
3450ba2cbe9Sxc /*
3460ba2cbe9Sxc  * These are the uid and gid of the user 'dladm'.
3470ba2cbe9Sxc  * The directory /etc/dladm and all files under it are owned by this user.
3480ba2cbe9Sxc  */
3490ba2cbe9Sxc #define	DLADM_DB_OWNER		15
3500ba2cbe9Sxc #define	DLADM_DB_GROUP		3
3510ba2cbe9Sxc #define	LOCK_DB_PERMS		S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH
3520ba2cbe9Sxc 
3530ba2cbe9Sxc static int
3540ba2cbe9Sxc i_dladm_lock_db(const char *lock_file, short type)
3550ba2cbe9Sxc {
3560ba2cbe9Sxc 	int	lock_fd;
3570ba2cbe9Sxc 	struct  flock lock;
3580ba2cbe9Sxc 
3590ba2cbe9Sxc 	if ((lock_fd = open(lock_file, O_RDWR | O_CREAT | O_TRUNC,
3600ba2cbe9Sxc 	    LOCK_DB_PERMS)) < 0)
3610ba2cbe9Sxc 		return (-1);
3620ba2cbe9Sxc 
3630ba2cbe9Sxc 	lock.l_type = type;
3640ba2cbe9Sxc 	lock.l_whence = SEEK_SET;
3650ba2cbe9Sxc 	lock.l_start = 0;
3660ba2cbe9Sxc 	lock.l_len = 0;
3670ba2cbe9Sxc 
3680ba2cbe9Sxc 	if (fcntl(lock_fd, F_SETLKW, &lock) < 0) {
3690ba2cbe9Sxc 		int err = errno;
3700ba2cbe9Sxc 
3710ba2cbe9Sxc 		(void) close(lock_fd);
3720ba2cbe9Sxc 		(void) unlink(lock_file);
3730ba2cbe9Sxc 		errno = err;
3740ba2cbe9Sxc 		return (-1);
3750ba2cbe9Sxc 	}
3760ba2cbe9Sxc 	return (lock_fd);
3770ba2cbe9Sxc }
3780ba2cbe9Sxc 
3790ba2cbe9Sxc static void
3800ba2cbe9Sxc i_dladm_unlock_db(const char *lock_file, int fd)
3810ba2cbe9Sxc {
3820ba2cbe9Sxc 	struct flock lock;
3830ba2cbe9Sxc 
3840ba2cbe9Sxc 	if (fd < 0)
3850ba2cbe9Sxc 		return;
3860ba2cbe9Sxc 
3870ba2cbe9Sxc 	lock.l_type = F_UNLCK;
3880ba2cbe9Sxc 	lock.l_whence = SEEK_SET;
3890ba2cbe9Sxc 	lock.l_start = 0;
3900ba2cbe9Sxc 	lock.l_len = 0;
3910ba2cbe9Sxc 
3920ba2cbe9Sxc 	(void) fcntl(fd, F_SETLKW, &lock);
3930ba2cbe9Sxc 	(void) close(fd);
3940ba2cbe9Sxc 	(void) unlink(lock_file);
3950ba2cbe9Sxc }
3960ba2cbe9Sxc 
3970ba2cbe9Sxc dladm_status_t
3980ba2cbe9Sxc i_dladm_rw_db(const char *db_file, mode_t db_perms,
3990ba2cbe9Sxc     dladm_status_t (*process_db)(void *, FILE *, FILE *),
4000ba2cbe9Sxc     void *arg, boolean_t writeop)
4010ba2cbe9Sxc {
4020ba2cbe9Sxc 	dladm_status_t	status = DLADM_STATUS_OK;
4030ba2cbe9Sxc 	FILE		*fp, *nfp = NULL;
4040ba2cbe9Sxc 	char		lock[MAXPATHLEN];
4050ba2cbe9Sxc 	char		file[MAXPATHLEN];
4060ba2cbe9Sxc 	char		newfile[MAXPATHLEN];
4070ba2cbe9Sxc 	char		*db_basename;
4080ba2cbe9Sxc 	int		nfd, lock_fd;
4090ba2cbe9Sxc 
4100ba2cbe9Sxc 	/*
4110ba2cbe9Sxc 	 * If we are called from a boot script such as net-physical,
4120ba2cbe9Sxc 	 * it's quite likely that the root fs is still not writable.
4130ba2cbe9Sxc 	 * For this case, it's ok for the lock creation to fail since
4140ba2cbe9Sxc 	 * no one else could be accessing our configuration file.
4150ba2cbe9Sxc 	 */
4160ba2cbe9Sxc 	db_basename = strrchr(db_file, '/');
4170ba2cbe9Sxc 	if (db_basename == NULL || db_basename[1] == '\0')
4180ba2cbe9Sxc 		return (dladm_errno2status(EINVAL));
4190ba2cbe9Sxc 	db_basename++;
4200ba2cbe9Sxc 	(void) snprintf(lock, MAXPATHLEN, "/tmp/%s.lock", db_basename);
4210ba2cbe9Sxc 	if ((lock_fd = i_dladm_lock_db
4220ba2cbe9Sxc 	    (lock, (writeop ? F_WRLCK : F_RDLCK))) < 0 && errno != EROFS)
4230ba2cbe9Sxc 		return (dladm_errno2status(errno));
4240ba2cbe9Sxc 
4250ba2cbe9Sxc 	(void) snprintf(file, MAXPATHLEN, "%s/%s", dladm_rootdir, db_file);
4260ba2cbe9Sxc 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
4270ba2cbe9Sxc 		int	err = errno;
4280ba2cbe9Sxc 
4290ba2cbe9Sxc 		i_dladm_unlock_db(lock, lock_fd);
4300ba2cbe9Sxc 		if (err == ENOENT)
4310ba2cbe9Sxc 			return (DLADM_STATUS_DBNOTFOUND);
4320ba2cbe9Sxc 
4330ba2cbe9Sxc 		return (dladm_errno2status(err));
4340ba2cbe9Sxc 	}
4350ba2cbe9Sxc 
4360ba2cbe9Sxc 	if (writeop) {
4370ba2cbe9Sxc 		(void) snprintf(newfile, MAXPATHLEN, "%s/%s.new",
4380ba2cbe9Sxc 		    dladm_rootdir, db_file);
4390ba2cbe9Sxc 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
4400ba2cbe9Sxc 		    db_perms)) < 0) {
4410ba2cbe9Sxc 			(void) fclose(fp);
4420ba2cbe9Sxc 			i_dladm_unlock_db(lock, lock_fd);
4430ba2cbe9Sxc 			return (dladm_errno2status(errno));
4440ba2cbe9Sxc 		}
4450ba2cbe9Sxc 
4460ba2cbe9Sxc 		if ((nfp = fdopen(nfd, "w")) == NULL) {
4470ba2cbe9Sxc 			(void) close(nfd);
4480ba2cbe9Sxc 			(void) fclose(fp);
4490ba2cbe9Sxc 			(void) unlink(newfile);
4500ba2cbe9Sxc 			i_dladm_unlock_db(lock, lock_fd);
4510ba2cbe9Sxc 			return (dladm_errno2status(errno));
4520ba2cbe9Sxc 		}
4530ba2cbe9Sxc 	}
4540ba2cbe9Sxc 	status = (*process_db)(arg, fp, nfp);
4550ba2cbe9Sxc 	if (!writeop || status != DLADM_STATUS_OK)
4560ba2cbe9Sxc 		goto done;
4570ba2cbe9Sxc 
4580ba2cbe9Sxc 	/*
4590ba2cbe9Sxc 	 * Configuration files need to be owned by the 'dladm' user.
4600ba2cbe9Sxc 	 * If we are invoked by root, the file ownership needs to be fixed.
4610ba2cbe9Sxc 	 */
4620ba2cbe9Sxc 	if (getuid() == 0 || geteuid() == 0) {
4630ba2cbe9Sxc 		if (fchown(nfd, DLADM_DB_OWNER, DLADM_DB_GROUP) < 0) {
4640ba2cbe9Sxc 			status = dladm_errno2status(errno);
4650ba2cbe9Sxc 			goto done;
4660ba2cbe9Sxc 		}
4670ba2cbe9Sxc 	}
4680ba2cbe9Sxc 
4690ba2cbe9Sxc 	if (fflush(nfp) == EOF) {
4700ba2cbe9Sxc 		status = dladm_errno2status(errno);
4710ba2cbe9Sxc 		goto done;
4720ba2cbe9Sxc 	}
4730ba2cbe9Sxc 	(void) fclose(fp);
4740ba2cbe9Sxc 	(void) fclose(nfp);
4750ba2cbe9Sxc 
4760ba2cbe9Sxc 	if (rename(newfile, file) < 0) {
4770ba2cbe9Sxc 		(void) unlink(newfile);
4780ba2cbe9Sxc 		i_dladm_unlock_db(lock, lock_fd);
4790ba2cbe9Sxc 		return (dladm_errno2status(errno));
4800ba2cbe9Sxc 	}
4810ba2cbe9Sxc 
4820ba2cbe9Sxc 	i_dladm_unlock_db(lock, lock_fd);
4830ba2cbe9Sxc 	return (DLADM_STATUS_OK);
4840ba2cbe9Sxc 
4850ba2cbe9Sxc done:
4860ba2cbe9Sxc 	if (nfp != NULL) {
4870ba2cbe9Sxc 		(void) fclose(nfp);
4880ba2cbe9Sxc 		if (status != DLADM_STATUS_OK)
4890ba2cbe9Sxc 			(void) unlink(newfile);
4900ba2cbe9Sxc 	}
4910ba2cbe9Sxc 	(void) fclose(fp);
4920ba2cbe9Sxc 	i_dladm_unlock_db(lock, lock_fd);
4930ba2cbe9Sxc 	return (status);
4940ba2cbe9Sxc }
4950ba2cbe9Sxc 
4960ba2cbe9Sxc dladm_status_t
4970ba2cbe9Sxc dladm_set_rootdir(const char *rootdir)
4980ba2cbe9Sxc {
4990ba2cbe9Sxc 	DIR	*dp;
5000ba2cbe9Sxc 
5010ba2cbe9Sxc 	if (rootdir == NULL || *rootdir != '/' ||
5020ba2cbe9Sxc 	    (dp = opendir(rootdir)) == NULL)
5030ba2cbe9Sxc 		return (DLADM_STATUS_BADARG);
5040ba2cbe9Sxc 
5050ba2cbe9Sxc 	(void) strncpy(dladm_rootdir, rootdir, MAXPATHLEN);
5060ba2cbe9Sxc 	(void) closedir(dp);
5070ba2cbe9Sxc 	return (DLADM_STATUS_OK);
5080ba2cbe9Sxc }
509