xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_db.c (revision 2b24ab6b)
1d62bc4baSyz /*
2d62bc4baSyz  * CDDL HEADER START
3d62bc4baSyz  *
4d62bc4baSyz  * The contents of this file are subject to the terms of the
5d62bc4baSyz  * Common Development and Distribution License (the "License").
6d62bc4baSyz  * You may not use this file except in compliance with the License.
7d62bc4baSyz  *
8d62bc4baSyz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9d62bc4baSyz  * or http://www.opensolaris.org/os/licensing.
10d62bc4baSyz  * See the License for the specific language governing permissions
11d62bc4baSyz  * and limitations under the License.
12d62bc4baSyz  *
13d62bc4baSyz  * When distributing Covered Code, include this CDDL HEADER in each
14d62bc4baSyz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15d62bc4baSyz  * If applicable, add the following below this CDDL HEADER, with the
16d62bc4baSyz  * fields enclosed by brackets "[]" replaced with your own identifying
17d62bc4baSyz  * information: Portions Copyright [yyyy] [name of copyright owner]
18d62bc4baSyz  *
19d62bc4baSyz  * CDDL HEADER END
20d62bc4baSyz  */
21d62bc4baSyz 
22d62bc4baSyz /*
23*2b24ab6bSSebastien Roy  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24d62bc4baSyz  * Use is subject to license terms.
25d62bc4baSyz  */
26d62bc4baSyz 
27d62bc4baSyz #include <assert.h>
28d62bc4baSyz #include <ctype.h>
29d62bc4baSyz #include <errno.h>
30d62bc4baSyz #include <fcntl.h>
31d62bc4baSyz #include <stdio.h>
32d62bc4baSyz #include <stdlib.h>
33*2b24ab6bSSebastien Roy #include <string.h>
34d62bc4baSyz #include <strings.h>
35d62bc4baSyz #include <syslog.h>
36*2b24ab6bSSebastien Roy #include <zone.h>
37*2b24ab6bSSebastien Roy #include <sys/types.h>
38d62bc4baSyz #include <sys/stat.h>
39*2b24ab6bSSebastien Roy #include <stropts.h>
40*2b24ab6bSSebastien Roy #include <sys/conf.h>
41d62bc4baSyz #include <pthread.h>
42d62bc4baSyz #include <unistd.h>
43*2b24ab6bSSebastien Roy #include <wait.h>
44*2b24ab6bSSebastien Roy #include <libcontract.h>
45*2b24ab6bSSebastien Roy #include <sys/contract/process.h>
46d62bc4baSyz #include "dlmgmt_impl.h"
47d62bc4baSyz 
48d62bc4baSyz typedef enum dlmgmt_db_op {
49d62bc4baSyz 	DLMGMT_DB_OP_WRITE,
50d62bc4baSyz 	DLMGMT_DB_OP_DELETE,
51d62bc4baSyz 	DLMGMT_DB_OP_READ
52d62bc4baSyz } dlmgmt_db_op_t;
53d62bc4baSyz 
54d62bc4baSyz typedef struct dlmgmt_db_req_s {
55d62bc4baSyz 	struct dlmgmt_db_req_s	*ls_next;
56d62bc4baSyz 	dlmgmt_db_op_t		ls_op;
57*2b24ab6bSSebastien Roy 	char			ls_link[MAXLINKNAMELEN];
58d62bc4baSyz 	datalink_id_t		ls_linkid;
59*2b24ab6bSSebastien Roy 	zoneid_t		ls_zoneid;
60d62bc4baSyz 	uint32_t		ls_flags;	/* Either DLMGMT_ACTIVE or   */
61d62bc4baSyz 						/* DLMGMT_PERSIST, not both. */
62d62bc4baSyz } dlmgmt_db_req_t;
63d62bc4baSyz 
64d62bc4baSyz /*
65d62bc4baSyz  * List of pending db updates (e.g., because of a read-only filesystem).
66d62bc4baSyz  */
67d62bc4baSyz static dlmgmt_db_req_t	*dlmgmt_db_req_head = NULL;
68d62bc4baSyz static dlmgmt_db_req_t	*dlmgmt_db_req_tail = NULL;
69d62bc4baSyz 
70*2b24ab6bSSebastien Roy /*
71*2b24ab6bSSebastien Roy  * rewrite_needed is set to B_TRUE by process_link_line() if it encounters a
72*2b24ab6bSSebastien Roy  * line with an old format.  This will cause the file being read to be
73*2b24ab6bSSebastien Roy  * re-written with the current format.
74*2b24ab6bSSebastien Roy  */
75*2b24ab6bSSebastien Roy static boolean_t	rewrite_needed;
76*2b24ab6bSSebastien Roy 
77*2b24ab6bSSebastien Roy static int		dlmgmt_db_update(dlmgmt_db_op_t, const char *,
78*2b24ab6bSSebastien Roy 			    dlmgmt_link_t *, uint32_t);
79d62bc4baSyz static int		dlmgmt_process_db_req(dlmgmt_db_req_t *);
80d62bc4baSyz static int		dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
81d62bc4baSyz static void		*dlmgmt_db_update_thread(void *);
82*2b24ab6bSSebastien Roy static boolean_t	process_link_line(char *, dlmgmt_link_t *);
83d62bc4baSyz static int		process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
84*2b24ab6bSSebastien Roy static int		process_db_read(dlmgmt_db_req_t *, FILE *);
85d62bc4baSyz static void		generate_link_line(dlmgmt_link_t *, boolean_t, char *);
86d62bc4baSyz 
87d62bc4baSyz #define	BUFLEN(lim, ptr)	(((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
88d62bc4baSyz #define	MAXLINELEN		1024
89d62bc4baSyz 
90*2b24ab6bSSebastien Roy typedef void db_walk_func_t(dlmgmt_link_t *);
91*2b24ab6bSSebastien Roy 
92d62bc4baSyz /*
93d62bc4baSyz  * Translator functions to go from dladm_datatype_t to character strings.
94d62bc4baSyz  * Each function takes a pointer to a buffer, the size of the buffer,
95d62bc4baSyz  * the name of the attribute, and the value to be written.  The functions
96d62bc4baSyz  * return the number of bytes written to the buffer.  If the buffer is not big
97d62bc4baSyz  * enough to hold the string representing the value, then nothing is written
98d62bc4baSyz  * and 0 is returned.
99d62bc4baSyz  */
100d62bc4baSyz typedef size_t write_func_t(char *, size_t, char *, void *);
101d62bc4baSyz 
102d62bc4baSyz /*
103d62bc4baSyz  * Translator functions to read from a NULL terminated string buffer into
104d62bc4baSyz  * something of the given DLADM_TYPE_*.  The functions each return the number
105d62bc4baSyz  * of bytes read from the string buffer.  If there is an error reading data
106d62bc4baSyz  * from the buffer, then 0 is returned.  It is the caller's responsibility
107d62bc4baSyz  * to free the data allocated by these functions.
108d62bc4baSyz  */
109d62bc4baSyz typedef size_t read_func_t(char *, void **);
110d62bc4baSyz 
111d62bc4baSyz typedef struct translator_s {
112d62bc4baSyz 	const char	*type_name;
113d62bc4baSyz 	write_func_t	*write_func;
114d62bc4baSyz 	read_func_t	*read_func;
115d62bc4baSyz } translator_t;
116d62bc4baSyz 
117d62bc4baSyz /*
118d62bc4baSyz  * Translator functions, defined later but declared here so that
119d62bc4baSyz  * the translator table can be defined.
120d62bc4baSyz  */
121d62bc4baSyz static write_func_t	write_str, write_boolean, write_uint64;
122d62bc4baSyz static read_func_t	read_str, read_boolean, read_int64;
123d62bc4baSyz 
124d62bc4baSyz /*
125d62bc4baSyz  * Translator table, indexed by dladm_datatype_t.
126d62bc4baSyz  */
127d62bc4baSyz static translator_t translators[] = {
128d62bc4baSyz 	{ "string",	write_str,	read_str	},
129d62bc4baSyz 	{ "boolean",	write_boolean,	read_boolean	},
130d62bc4baSyz 	{ "int",	write_uint64,	read_int64	}
131d62bc4baSyz };
132d62bc4baSyz 
133d62bc4baSyz static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
134d62bc4baSyz 
135d62bc4baSyz #define	LINK_PROPERTY_DELIMINATOR	";"
136d62bc4baSyz #define	LINK_PROPERTY_TYPE_VALUE_SEP	","
137d62bc4baSyz #define	BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
138d62bc4baSyz 				    strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
139d62bc4baSyz 				    strlen(LINK_PROPERTY_DELIMINATOR) +\
140d62bc4baSyz 				    strlen((n)))
141d62bc4baSyz #define	GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
142d62bc4baSyz 	    (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
143d62bc4baSyz 	    translators[(type)].type_name, \
144d62bc4baSyz 	    LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
145d62bc4baSyz 
146d62bc4baSyz /*
147d62bc4baSyz  * Name of the cache file to keep the active <link name, linkid> mapping
148d62bc4baSyz  */
149*2b24ab6bSSebastien Roy char	cachefile[MAXPATHLEN];
150d62bc4baSyz 
151d62bc4baSyz #define	DLMGMT_PERSISTENT_DB_PATH	"/etc/dladm/datalink.conf"
152d62bc4baSyz #define	DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent)	\
153d62bc4baSyz 	(void) snprintf((buffer), MAXPATHLEN, "%s", \
154d62bc4baSyz 	(persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
155d62bc4baSyz 
156*2b24ab6bSSebastien Roy typedef struct zopen_arg {
157*2b24ab6bSSebastien Roy 	const char	*zopen_modestr;
158*2b24ab6bSSebastien Roy 	int		*zopen_pipe;
159*2b24ab6bSSebastien Roy 	int		zopen_fd;
160*2b24ab6bSSebastien Roy } zopen_arg_t;
161*2b24ab6bSSebastien Roy 
162*2b24ab6bSSebastien Roy typedef struct zrename_arg {
163*2b24ab6bSSebastien Roy 	const char	*zrename_newname;
164*2b24ab6bSSebastien Roy } zrename_arg_t;
165*2b24ab6bSSebastien Roy 
166*2b24ab6bSSebastien Roy typedef union zfoparg {
167*2b24ab6bSSebastien Roy 	zopen_arg_t	zfop_openarg;
168*2b24ab6bSSebastien Roy 	zrename_arg_t	zfop_renamearg;
169*2b24ab6bSSebastien Roy } zfoparg_t;
170*2b24ab6bSSebastien Roy 
171*2b24ab6bSSebastien Roy typedef struct zfcbarg {
172*2b24ab6bSSebastien Roy 	boolean_t	zfarg_inglobalzone; /* is callback in global zone? */
173*2b24ab6bSSebastien Roy 	zoneid_t	zfarg_finglobalzone; /* is file in global zone? */
174*2b24ab6bSSebastien Roy 	const char	*zfarg_filename;
175*2b24ab6bSSebastien Roy 	zfoparg_t	*zfarg_oparg;
176*2b24ab6bSSebastien Roy } zfarg_t;
177*2b24ab6bSSebastien Roy #define	zfarg_openarg	zfarg_oparg->zfop_openarg
178*2b24ab6bSSebastien Roy #define	zfarg_renamearg	zfarg_oparg->zfop_renamearg
179*2b24ab6bSSebastien Roy 
180*2b24ab6bSSebastien Roy /* zone file callback */
181*2b24ab6bSSebastien Roy typedef int zfcb_t(zfarg_t *);
182*2b24ab6bSSebastien Roy 
183*2b24ab6bSSebastien Roy /*
184*2b24ab6bSSebastien Roy  * Execute an operation on filename relative to zoneid's zone root.  If the
185*2b24ab6bSSebastien Roy  * file is in the global zone, then the zfcb() callback will simply be called
186*2b24ab6bSSebastien Roy  * directly.  If the file is in a non-global zone, then zfcb() will be called
187*2b24ab6bSSebastien Roy  * both from the global zone's context, and from the non-global zone's context
188*2b24ab6bSSebastien Roy  * (from a fork()'ed child that has entered the non-global zone).  This is
189*2b24ab6bSSebastien Roy  * done to allow the callback to communicate with itself if needed (e.g. to
190*2b24ab6bSSebastien Roy  * pass back the file descriptor of an opened file).
191*2b24ab6bSSebastien Roy  */
192*2b24ab6bSSebastien Roy static int
193*2b24ab6bSSebastien Roy dlmgmt_zfop(const char *filename, zoneid_t zoneid, zfcb_t *zfcb,
194*2b24ab6bSSebastien Roy     zfoparg_t *zfoparg)
195*2b24ab6bSSebastien Roy {
196*2b24ab6bSSebastien Roy 	int		ctfd;
197*2b24ab6bSSebastien Roy 	int		err;
198*2b24ab6bSSebastien Roy 	pid_t		childpid;
199*2b24ab6bSSebastien Roy 	siginfo_t	info;
200*2b24ab6bSSebastien Roy 	zfarg_t		zfarg;
201*2b24ab6bSSebastien Roy 
202*2b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
203*2b24ab6bSSebastien Roy 		/*
204*2b24ab6bSSebastien Roy 		 * We need to access a file that isn't in the global zone.
205*2b24ab6bSSebastien Roy 		 * Accessing non-global zone files from the global zone is
206*2b24ab6bSSebastien Roy 		 * unsafe (due to symlink attacks), we'll need to fork a child
207*2b24ab6bSSebastien Roy 		 * that enters the zone in question and executes the callback
208*2b24ab6bSSebastien Roy 		 * that will operate on the file.
209*2b24ab6bSSebastien Roy 		 *
210*2b24ab6bSSebastien Roy 		 * Before we proceed with this zone tango, we need to create a
211*2b24ab6bSSebastien Roy 		 * new process contract for the child, as required by
212*2b24ab6bSSebastien Roy 		 * zone_enter().
213*2b24ab6bSSebastien Roy 		 */
214*2b24ab6bSSebastien Roy 		errno = 0;
215*2b24ab6bSSebastien Roy 		ctfd = open64("/system/contract/process/template", O_RDWR);
216*2b24ab6bSSebastien Roy 		if (ctfd == -1)
217*2b24ab6bSSebastien Roy 			return (errno);
218*2b24ab6bSSebastien Roy 		if ((err = ct_tmpl_set_critical(ctfd, 0)) != 0 ||
219*2b24ab6bSSebastien Roy 		    (err = ct_tmpl_set_informative(ctfd, 0)) != 0 ||
220*2b24ab6bSSebastien Roy 		    (err = ct_pr_tmpl_set_fatal(ctfd, CT_PR_EV_HWERR)) != 0 ||
221*2b24ab6bSSebastien Roy 		    (err = ct_pr_tmpl_set_param(ctfd, CT_PR_PGRPONLY)) != 0 ||
222*2b24ab6bSSebastien Roy 		    (err = ct_tmpl_activate(ctfd)) != 0) {
223*2b24ab6bSSebastien Roy 			return (err);
224*2b24ab6bSSebastien Roy 		}
225*2b24ab6bSSebastien Roy 		childpid = fork();
226*2b24ab6bSSebastien Roy 		(void) ct_tmpl_clear(ctfd);
227*2b24ab6bSSebastien Roy 		(void) close(ctfd);
228*2b24ab6bSSebastien Roy 		switch (childpid) {
229*2b24ab6bSSebastien Roy 		case -1:
230*2b24ab6bSSebastien Roy 			return (err);
231*2b24ab6bSSebastien Roy 		case 0:
232*2b24ab6bSSebastien Roy 			/*
233*2b24ab6bSSebastien Roy 			 * Elevate our privileges as zone_enter() requires all
234*2b24ab6bSSebastien Roy 			 * privileges.
235*2b24ab6bSSebastien Roy 			 */
236*2b24ab6bSSebastien Roy 			if ((err = dlmgmt_elevate_privileges()) != 0)
237*2b24ab6bSSebastien Roy 				_exit(err);
238*2b24ab6bSSebastien Roy 			if (zone_enter(zoneid) == -1)
239*2b24ab6bSSebastien Roy 				_exit(errno);
240*2b24ab6bSSebastien Roy 			if ((err = dlmgmt_drop_privileges()) != 0)
241*2b24ab6bSSebastien Roy 				_exit(err);
242*2b24ab6bSSebastien Roy 			break;
243*2b24ab6bSSebastien Roy 		default:
244*2b24ab6bSSebastien Roy 			if (waitid(P_PID, childpid, &info, WEXITED) == -1)
245*2b24ab6bSSebastien Roy 				return (errno);
246*2b24ab6bSSebastien Roy 			if (info.si_status != 0)
247*2b24ab6bSSebastien Roy 				return (info.si_status);
248*2b24ab6bSSebastien Roy 		}
249*2b24ab6bSSebastien Roy 	}
250*2b24ab6bSSebastien Roy 
251*2b24ab6bSSebastien Roy 	zfarg.zfarg_inglobalzone = (zoneid == GLOBAL_ZONEID || childpid != 0);
252*2b24ab6bSSebastien Roy 	zfarg.zfarg_finglobalzone = (zoneid == GLOBAL_ZONEID);
253*2b24ab6bSSebastien Roy 	zfarg.zfarg_filename = filename;
254*2b24ab6bSSebastien Roy 	zfarg.zfarg_oparg = zfoparg;
255*2b24ab6bSSebastien Roy 	err = zfcb(&zfarg);
256*2b24ab6bSSebastien Roy 	if (!zfarg.zfarg_inglobalzone)
257*2b24ab6bSSebastien Roy 		_exit(err);
258*2b24ab6bSSebastien Roy 	return (err);
259*2b24ab6bSSebastien Roy }
260*2b24ab6bSSebastien Roy 
261*2b24ab6bSSebastien Roy static int
262*2b24ab6bSSebastien Roy dlmgmt_zopen_cb(zfarg_t *zfarg)
263*2b24ab6bSSebastien Roy {
264*2b24ab6bSSebastien Roy 	struct strrecvfd recvfd;
265*2b24ab6bSSebastien Roy 	boolean_t	newfile = B_FALSE;
266*2b24ab6bSSebastien Roy 	boolean_t	inglobalzone = zfarg->zfarg_inglobalzone;
267*2b24ab6bSSebastien Roy 	zoneid_t	finglobalzone = zfarg->zfarg_finglobalzone;
268*2b24ab6bSSebastien Roy 	const char	*filename = zfarg->zfarg_filename;
269*2b24ab6bSSebastien Roy 	const char	*modestr = zfarg->zfarg_openarg.zopen_modestr;
270*2b24ab6bSSebastien Roy 	int		*p = zfarg->zfarg_openarg.zopen_pipe;
271*2b24ab6bSSebastien Roy 	struct stat	statbuf;
272*2b24ab6bSSebastien Roy 	int		oflags;
273*2b24ab6bSSebastien Roy 	mode_t		mode;
274*2b24ab6bSSebastien Roy 	int		fd = -1;
275*2b24ab6bSSebastien Roy 	int		err;
276*2b24ab6bSSebastien Roy 
277*2b24ab6bSSebastien Roy 	/* We only ever open a file for reading or writing, not both. */
278*2b24ab6bSSebastien Roy 	oflags = (modestr[0] == 'r') ? O_RDONLY : O_WRONLY | O_CREAT | O_TRUNC;
279*2b24ab6bSSebastien Roy 	mode = (modestr[0] == 'r') ? 0 : S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
280*2b24ab6bSSebastien Roy 
281*2b24ab6bSSebastien Roy 	/* Open the file if we're in the same zone as the file. */
282*2b24ab6bSSebastien Roy 	if (inglobalzone == finglobalzone) {
283*2b24ab6bSSebastien Roy 		/*
284*2b24ab6bSSebastien Roy 		 * First determine if we will be creating the file as part of
285*2b24ab6bSSebastien Roy 		 * opening it.  If so, then we'll need to ensure that it has
286*2b24ab6bSSebastien Roy 		 * the proper ownership after having opened it.
287*2b24ab6bSSebastien Roy 		 */
288*2b24ab6bSSebastien Roy 		if (oflags & O_CREAT) {
289*2b24ab6bSSebastien Roy 			if (stat(filename, &statbuf) == -1) {
290*2b24ab6bSSebastien Roy 				if (errno == ENOENT)
291*2b24ab6bSSebastien Roy 					newfile = B_TRUE;
292*2b24ab6bSSebastien Roy 				else
293*2b24ab6bSSebastien Roy 					return (errno);
294*2b24ab6bSSebastien Roy 			}
295*2b24ab6bSSebastien Roy 		}
296*2b24ab6bSSebastien Roy 		if ((fd = open(filename, oflags, mode)) == -1)
297*2b24ab6bSSebastien Roy 			return (errno);
298*2b24ab6bSSebastien Roy 		if (newfile) {
299*2b24ab6bSSebastien Roy 			if (chown(filename, UID_DLADM, GID_SYS) == -1) {
300*2b24ab6bSSebastien Roy 				err = errno;
301*2b24ab6bSSebastien Roy 				(void) close(fd);
302*2b24ab6bSSebastien Roy 				return (err);
303*2b24ab6bSSebastien Roy 			}
304*2b24ab6bSSebastien Roy 		}
305*2b24ab6bSSebastien Roy 	}
306*2b24ab6bSSebastien Roy 
307*2b24ab6bSSebastien Roy 	/*
308*2b24ab6bSSebastien Roy 	 * If we're not in the global zone, send the file-descriptor back to
309*2b24ab6bSSebastien Roy 	 * our parent in the global zone.
310*2b24ab6bSSebastien Roy 	 */
311*2b24ab6bSSebastien Roy 	if (!inglobalzone) {
312*2b24ab6bSSebastien Roy 		assert(!finglobalzone);
313*2b24ab6bSSebastien Roy 		assert(fd != -1);
314*2b24ab6bSSebastien Roy 		return (ioctl(p[1], I_SENDFD, fd) == -1 ? errno : 0);
315*2b24ab6bSSebastien Roy 	}
316*2b24ab6bSSebastien Roy 
317*2b24ab6bSSebastien Roy 	/*
318*2b24ab6bSSebastien Roy 	 * At this point, we know we're in the global zone.  If the file was
319*2b24ab6bSSebastien Roy 	 * in a non-global zone, receive the file-descriptor from our child in
320*2b24ab6bSSebastien Roy 	 * the non-global zone.
321*2b24ab6bSSebastien Roy 	 */
322*2b24ab6bSSebastien Roy 	if (!finglobalzone) {
323*2b24ab6bSSebastien Roy 		if (ioctl(p[0], I_RECVFD, &recvfd) == -1)
324*2b24ab6bSSebastien Roy 			return (errno);
325*2b24ab6bSSebastien Roy 		fd = recvfd.fd;
326*2b24ab6bSSebastien Roy 	}
327*2b24ab6bSSebastien Roy 
328*2b24ab6bSSebastien Roy 	zfarg->zfarg_openarg.zopen_fd = fd;
329*2b24ab6bSSebastien Roy 	return (0);
330*2b24ab6bSSebastien Roy }
331*2b24ab6bSSebastien Roy 
332*2b24ab6bSSebastien Roy static int
333*2b24ab6bSSebastien Roy dlmgmt_zunlink_cb(zfarg_t *zfarg)
334*2b24ab6bSSebastien Roy {
335*2b24ab6bSSebastien Roy 	if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
336*2b24ab6bSSebastien Roy 		return (0);
337*2b24ab6bSSebastien Roy 	return (unlink(zfarg->zfarg_filename) == 0 ? 0 : errno);
338*2b24ab6bSSebastien Roy }
339*2b24ab6bSSebastien Roy 
340*2b24ab6bSSebastien Roy static int
341*2b24ab6bSSebastien Roy dlmgmt_zrename_cb(zfarg_t *zfarg)
342*2b24ab6bSSebastien Roy {
343*2b24ab6bSSebastien Roy 	if (zfarg->zfarg_inglobalzone != zfarg->zfarg_finglobalzone)
344*2b24ab6bSSebastien Roy 		return (0);
345*2b24ab6bSSebastien Roy 	return (rename(zfarg->zfarg_filename,
346*2b24ab6bSSebastien Roy 	    zfarg->zfarg_renamearg.zrename_newname) == 0 ? 0 : errno);
347*2b24ab6bSSebastien Roy }
348*2b24ab6bSSebastien Roy 
349*2b24ab6bSSebastien Roy /*
350*2b24ab6bSSebastien Roy  * Same as fopen(3C), except that it opens the file relative to zoneid's zone
351*2b24ab6bSSebastien Roy  * root.
352*2b24ab6bSSebastien Roy  */
353*2b24ab6bSSebastien Roy static FILE *
354*2b24ab6bSSebastien Roy dlmgmt_zfopen(const char *filename, const char *modestr, zoneid_t zoneid,
355*2b24ab6bSSebastien Roy     int *err)
356*2b24ab6bSSebastien Roy {
357*2b24ab6bSSebastien Roy 	int		p[2];
358*2b24ab6bSSebastien Roy 	zfoparg_t	zfoparg;
359*2b24ab6bSSebastien Roy 	FILE		*fp = NULL;
360*2b24ab6bSSebastien Roy 
361*2b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID && pipe(p) == -1) {
362*2b24ab6bSSebastien Roy 		*err = errno;
363*2b24ab6bSSebastien Roy 		return (NULL);
364*2b24ab6bSSebastien Roy 	}
365*2b24ab6bSSebastien Roy 
366*2b24ab6bSSebastien Roy 	zfoparg.zfop_openarg.zopen_modestr = modestr;
367*2b24ab6bSSebastien Roy 	zfoparg.zfop_openarg.zopen_pipe = p;
368*2b24ab6bSSebastien Roy 	*err = dlmgmt_zfop(filename, zoneid, dlmgmt_zopen_cb, &zfoparg);
369*2b24ab6bSSebastien Roy 	if (zoneid != GLOBAL_ZONEID) {
370*2b24ab6bSSebastien Roy 		(void) close(p[0]);
371*2b24ab6bSSebastien Roy 		(void) close(p[1]);
372*2b24ab6bSSebastien Roy 	}
373*2b24ab6bSSebastien Roy 	if (*err == 0) {
374*2b24ab6bSSebastien Roy 		fp = fdopen(zfoparg.zfop_openarg.zopen_fd, modestr);
375*2b24ab6bSSebastien Roy 		if (fp == NULL) {
376*2b24ab6bSSebastien Roy 			*err = errno;
377*2b24ab6bSSebastien Roy 			(void) close(zfoparg.zfop_openarg.zopen_fd);
378*2b24ab6bSSebastien Roy 		}
379*2b24ab6bSSebastien Roy 	}
380*2b24ab6bSSebastien Roy 	return (fp);
381*2b24ab6bSSebastien Roy }
382*2b24ab6bSSebastien Roy 
383*2b24ab6bSSebastien Roy /*
384*2b24ab6bSSebastien Roy  * Same as rename(2), except that old and new are relative to zoneid's zone
385*2b24ab6bSSebastien Roy  * root.
386*2b24ab6bSSebastien Roy  */
387*2b24ab6bSSebastien Roy static int
388*2b24ab6bSSebastien Roy dlmgmt_zrename(const char *old, const char *new, zoneid_t zoneid)
389*2b24ab6bSSebastien Roy {
390*2b24ab6bSSebastien Roy 	zfoparg_t zfoparg;
391*2b24ab6bSSebastien Roy 
392*2b24ab6bSSebastien Roy 	zfoparg.zfop_renamearg.zrename_newname = new;
393*2b24ab6bSSebastien Roy 	return (dlmgmt_zfop(old, zoneid, dlmgmt_zrename_cb, &zfoparg));
394*2b24ab6bSSebastien Roy }
395*2b24ab6bSSebastien Roy 
396*2b24ab6bSSebastien Roy /*
397*2b24ab6bSSebastien Roy  * Same as unlink(2), except that filename is relative to zoneid's zone root.
398*2b24ab6bSSebastien Roy  */
399*2b24ab6bSSebastien Roy static int
400*2b24ab6bSSebastien Roy dlmgmt_zunlink(const char *filename, zoneid_t zoneid)
401*2b24ab6bSSebastien Roy {
402*2b24ab6bSSebastien Roy 	return (dlmgmt_zfop(filename, zoneid, dlmgmt_zunlink_cb, NULL));
403*2b24ab6bSSebastien Roy }
404*2b24ab6bSSebastien Roy 
405d62bc4baSyz static size_t
406d62bc4baSyz write_str(char *buffer, size_t buffer_length, char *name, void *value)
407d62bc4baSyz {
408d62bc4baSyz 	char	*ptr = value;
409d62bc4baSyz 	size_t	data_length = strnlen(ptr, buffer_length);
410d62bc4baSyz 
411d62bc4baSyz 	/*
412d62bc4baSyz 	 * Strings are assumed to be NULL terminated.  In order to fit in
413d62bc4baSyz 	 * the buffer, the string's length must be less then buffer_length.
414d62bc4baSyz 	 * If the value is empty, there's no point in writing it, in fact,
415d62bc4baSyz 	 * we shouldn't even see that case.
416d62bc4baSyz 	 */
417d62bc4baSyz 	if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
418d62bc4baSyz 	    buffer_length || data_length == 0)
419d62bc4baSyz 		return (0);
420d62bc4baSyz 
421d62bc4baSyz 	/*
422d62bc4baSyz 	 * Since we know the string will fit in the buffer, snprintf will
423d62bc4baSyz 	 * always return less than buffer_length, so we can just return
424d62bc4baSyz 	 * whatever snprintf returns.
425d62bc4baSyz 	 */
426d62bc4baSyz 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
427d62bc4baSyz 	    name, DLADM_TYPE_STR, ptr));
428d62bc4baSyz }
429d62bc4baSyz 
430d62bc4baSyz static size_t
431d62bc4baSyz write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
432d62bc4baSyz {
433d62bc4baSyz 	boolean_t	*ptr = value;
434d62bc4baSyz 
435d62bc4baSyz 	/*
436d62bc4baSyz 	 * Booleans are either zero or one, so we only need room for two
437d62bc4baSyz 	 * characters in the buffer.
438d62bc4baSyz 	 */
439d62bc4baSyz 	if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
440d62bc4baSyz 		return (0);
441d62bc4baSyz 
442d62bc4baSyz 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
443d62bc4baSyz 	    name, DLADM_TYPE_BOOLEAN, *ptr));
444d62bc4baSyz }
445d62bc4baSyz 
446d62bc4baSyz static size_t
447d62bc4baSyz write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
448d62bc4baSyz {
449d62bc4baSyz 	uint64_t	*ptr = value;
450d62bc4baSyz 
451d62bc4baSyz 	/*
452d62bc4baSyz 	 * Limit checking for uint64_t is a little trickier.
453d62bc4baSyz 	 */
454d62bc4baSyz 	if (snprintf(NULL, 0, "%lld", *ptr)  +
455d62bc4baSyz 	    BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
456d62bc4baSyz 		return (0);
457d62bc4baSyz 
458d62bc4baSyz 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
459d62bc4baSyz 	    name, DLADM_TYPE_UINT64, *ptr));
460d62bc4baSyz }
461d62bc4baSyz 
462d62bc4baSyz static size_t
463d62bc4baSyz read_str(char *buffer, void **value)
464d62bc4baSyz {
465024b0a25Sseb 	char		*ptr = calloc(MAXLINKATTRVALLEN, sizeof (char));
466d62bc4baSyz 	ssize_t		len;
467d62bc4baSyz 
468024b0a25Sseb 	if (ptr == NULL || (len = strlcpy(ptr, buffer, MAXLINKATTRVALLEN))
469024b0a25Sseb 	    >= MAXLINKATTRVALLEN) {
470d62bc4baSyz 		free(ptr);
471d62bc4baSyz 		return (0);
472d62bc4baSyz 	}
473d62bc4baSyz 
474d62bc4baSyz 	*(char **)value = ptr;
475d62bc4baSyz 
476d62bc4baSyz 	/* Account for NULL terminator */
477d62bc4baSyz 	return (len + 1);
478d62bc4baSyz }
479d62bc4baSyz 
480d62bc4baSyz static size_t
481d62bc4baSyz read_boolean(char *buffer, void **value)
482d62bc4baSyz {
483d62bc4baSyz 	boolean_t	*ptr = calloc(1, sizeof (boolean_t));
484d62bc4baSyz 
485d62bc4baSyz 	if (ptr == NULL)
486d62bc4baSyz 		return (0);
487d62bc4baSyz 
488d62bc4baSyz 	*ptr = atoi(buffer);
489d62bc4baSyz 	*(boolean_t **)value = ptr;
490d62bc4baSyz 
491d62bc4baSyz 	return (sizeof (boolean_t));
492d62bc4baSyz }
493d62bc4baSyz 
494d62bc4baSyz static size_t
495d62bc4baSyz read_int64(char *buffer, void **value)
496d62bc4baSyz {
497d62bc4baSyz 	int64_t	*ptr = calloc(1, sizeof (int64_t));
498d62bc4baSyz 
499d62bc4baSyz 	if (ptr == NULL)
500d62bc4baSyz 		return (0);
501d62bc4baSyz 
502d62bc4baSyz 	*ptr = (int64_t)atoll(buffer);
503d62bc4baSyz 	*(int64_t **)value = ptr;
504d62bc4baSyz 
505d62bc4baSyz 	return (sizeof (int64_t));
506d62bc4baSyz }
507d62bc4baSyz 
508*2b24ab6bSSebastien Roy static dlmgmt_db_req_t *
509*2b24ab6bSSebastien Roy dlmgmt_db_req_alloc(dlmgmt_db_op_t op, const char *linkname,
510*2b24ab6bSSebastien Roy     datalink_id_t linkid, zoneid_t zoneid, uint32_t flags, int *err)
511*2b24ab6bSSebastien Roy {
512*2b24ab6bSSebastien Roy 	dlmgmt_db_req_t *req;
513*2b24ab6bSSebastien Roy 
514*2b24ab6bSSebastien Roy 	if ((req = calloc(1, sizeof (dlmgmt_db_req_t))) == NULL) {
515*2b24ab6bSSebastien Roy 		*err = errno;
516*2b24ab6bSSebastien Roy 	} else {
517*2b24ab6bSSebastien Roy 		req->ls_op = op;
518*2b24ab6bSSebastien Roy 		if (linkname != NULL)
519*2b24ab6bSSebastien Roy 			(void) strlcpy(req->ls_link, linkname, MAXLINKNAMELEN);
520*2b24ab6bSSebastien Roy 		req->ls_linkid = linkid;
521*2b24ab6bSSebastien Roy 		req->ls_zoneid = zoneid;
522*2b24ab6bSSebastien Roy 		req->ls_flags = flags;
523*2b24ab6bSSebastien Roy 	}
524*2b24ab6bSSebastien Roy 	return (req);
525*2b24ab6bSSebastien Roy }
526*2b24ab6bSSebastien Roy 
527*2b24ab6bSSebastien Roy /*
528*2b24ab6bSSebastien Roy  * Update the db entry with name "entryname" using information from "linkp".
529*2b24ab6bSSebastien Roy  */
530d62bc4baSyz static int
531*2b24ab6bSSebastien Roy dlmgmt_db_update(dlmgmt_db_op_t op, const char *entryname, dlmgmt_link_t *linkp,
532*2b24ab6bSSebastien Roy     uint32_t flags)
533d62bc4baSyz {
534d62bc4baSyz 	dlmgmt_db_req_t	*req;
535d62bc4baSyz 	int		err;
536d62bc4baSyz 
537*2b24ab6bSSebastien Roy 	/* It is either a persistent request or an active request, not both. */
538d62bc4baSyz 	assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
539d62bc4baSyz 
540*2b24ab6bSSebastien Roy 	if ((req = dlmgmt_db_req_alloc(op, entryname, linkp->ll_linkid,
541*2b24ab6bSSebastien Roy 	    linkp->ll_zoneid, flags, &err)) == NULL)
542*2b24ab6bSSebastien Roy 		return (err);
543d62bc4baSyz 
544d62bc4baSyz 	/*
545d62bc4baSyz 	 * If the return error is EINPROGRESS, this request is handled
546d62bc4baSyz 	 * asynchronously; return success.
547d62bc4baSyz 	 */
548d62bc4baSyz 	err = dlmgmt_process_db_req(req);
549d62bc4baSyz 	if (err != EINPROGRESS)
550d62bc4baSyz 		free(req);
551d62bc4baSyz 	else
552d62bc4baSyz 		err = 0;
553d62bc4baSyz 	return (err);
554d62bc4baSyz }
555d62bc4baSyz 
556d62bc4baSyz #define	DLMGMT_DB_OP_STR(op)					\
557d62bc4baSyz 	(((op) == DLMGMT_DB_OP_READ) ? "read" :			\
558d62bc4baSyz 	(((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
559d62bc4baSyz 
560d62bc4baSyz #define	DLMGMT_DB_CONF_STR(flag)				\
561d62bc4baSyz 	(((flag) == DLMGMT_ACTIVE) ? "active" :			\
562d62bc4baSyz 	(((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
563d62bc4baSyz 
564d62bc4baSyz static int
565d62bc4baSyz dlmgmt_process_db_req(dlmgmt_db_req_t *req)
566d62bc4baSyz {
567d62bc4baSyz 	pthread_t	tid;
568d62bc4baSyz 	boolean_t	writeop;
569d62bc4baSyz 	int		err;
570d62bc4baSyz 
571d62bc4baSyz 	/*
572d62bc4baSyz 	 * If there are already pending "write" requests, queue this request in
573d62bc4baSyz 	 * the pending list.  Note that this function is called while the
574d62bc4baSyz 	 * dlmgmt_rw_lock is held, so it is safe to access the global variables.
575d62bc4baSyz 	 */
576d62bc4baSyz 	writeop = (req->ls_op != DLMGMT_DB_OP_READ);
577d62bc4baSyz 	if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
578d62bc4baSyz 	    (dlmgmt_db_req_head != NULL)) {
579d62bc4baSyz 		dlmgmt_db_req_tail->ls_next = req;
580d62bc4baSyz 		dlmgmt_db_req_tail = req;
581d62bc4baSyz 		return (EINPROGRESS);
582d62bc4baSyz 	}
583d62bc4baSyz 
584d62bc4baSyz 	err = dlmgmt_process_db_onereq(req, writeop);
585*2b24ab6bSSebastien Roy 	if (err != EINPROGRESS && err != 0 && err != ENOENT) {
586d62bc4baSyz 		/*
587*2b24ab6bSSebastien Roy 		 * Log the error unless the request processing is still in
588*2b24ab6bSSebastien Roy 		 * progress or if the configuration file hasn't been created
589*2b24ab6bSSebastien Roy 		 * yet (ENOENT).
590d62bc4baSyz 		 */
591d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
592d62bc4baSyz 		    "operation on %s configuration failed: %s",
593d62bc4baSyz 		    DLMGMT_DB_OP_STR(req->ls_op),
594d62bc4baSyz 		    DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
595d62bc4baSyz 	}
596d62bc4baSyz 
597d62bc4baSyz 	if (err == EINPROGRESS) {
598d62bc4baSyz 		assert(req->ls_flags == DLMGMT_PERSIST);
599d62bc4baSyz 		assert(writeop && dlmgmt_db_req_head == NULL);
600d62bc4baSyz 		dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
601d62bc4baSyz 		err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
602d62bc4baSyz 		if (err == 0)
603d62bc4baSyz 			return (EINPROGRESS);
604d62bc4baSyz 	}
605d62bc4baSyz 	return (err);
606d62bc4baSyz }
607d62bc4baSyz 
608d62bc4baSyz static int
609d62bc4baSyz dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
610d62bc4baSyz {
611d62bc4baSyz 	int	err = 0;
612d62bc4baSyz 	FILE	*fp, *nfp = NULL;
613d62bc4baSyz 	char	file[MAXPATHLEN];
614d62bc4baSyz 	char	newfile[MAXPATHLEN];
615d62bc4baSyz 
616d62bc4baSyz 	DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
617*2b24ab6bSSebastien Roy 	fp = dlmgmt_zfopen(file, "r", req->ls_zoneid, &err);
618*2b24ab6bSSebastien Roy 	/*
619*2b24ab6bSSebastien Roy 	 * Note that it is not an error if the file doesn't exist.  If we're
620*2b24ab6bSSebastien Roy 	 * reading, we treat this case the same way as an empty file.  If
621*2b24ab6bSSebastien Roy 	 * we're writing, the file will be created when we open the file for
622*2b24ab6bSSebastien Roy 	 * writing below.
623*2b24ab6bSSebastien Roy 	 */
624*2b24ab6bSSebastien Roy 	if (fp == NULL && !writeop)
625*2b24ab6bSSebastien Roy 		return (err);
626d62bc4baSyz 
627d62bc4baSyz 	if (writeop) {
628d62bc4baSyz 		(void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
629*2b24ab6bSSebastien Roy 		nfp = dlmgmt_zfopen(newfile, "w", req->ls_zoneid, &err);
630*2b24ab6bSSebastien Roy 		if (nfp == NULL) {
631*2b24ab6bSSebastien Roy 			/*
632*2b24ab6bSSebastien Roy 			 * EROFS can happen at boot when the file system is
633*2b24ab6bSSebastien Roy 			 * read-only.  Return EINPROGRESS so that the caller
634*2b24ab6bSSebastien Roy 			 * can add this request to the pending request list
635*2b24ab6bSSebastien Roy 			 * and start a retry thread.
636*2b24ab6bSSebastien Roy 			 */
637*2b24ab6bSSebastien Roy 			err = (errno == EROFS ? EINPROGRESS : errno);
638*2b24ab6bSSebastien Roy 			goto done;
639d62bc4baSyz 		}
640d62bc4baSyz 	}
641*2b24ab6bSSebastien Roy 	if (writeop) {
642*2b24ab6bSSebastien Roy 		if ((err = process_db_write(req, fp, nfp)) == 0)
643*2b24ab6bSSebastien Roy 			err = dlmgmt_zrename(newfile, file, req->ls_zoneid);
644*2b24ab6bSSebastien Roy 	} else {
645*2b24ab6bSSebastien Roy 		err = process_db_read(req, fp);
646d62bc4baSyz 	}
647d62bc4baSyz 
648d62bc4baSyz done:
649d62bc4baSyz 	if (nfp != NULL) {
650d62bc4baSyz 		(void) fclose(nfp);
651d62bc4baSyz 		if (err != 0)
652*2b24ab6bSSebastien Roy 			(void) dlmgmt_zunlink(newfile, req->ls_zoneid);
653d62bc4baSyz 	}
654d62bc4baSyz 	(void) fclose(fp);
655d62bc4baSyz 	return (err);
656d62bc4baSyz }
657d62bc4baSyz 
658d62bc4baSyz /*ARGSUSED*/
659d62bc4baSyz static void *
660d62bc4baSyz dlmgmt_db_update_thread(void *arg)
661d62bc4baSyz {
662d62bc4baSyz 	dlmgmt_db_req_t	*req;
663d62bc4baSyz 
664d62bc4baSyz 	dlmgmt_table_lock(B_TRUE);
665d62bc4baSyz 
666d62bc4baSyz 	assert(dlmgmt_db_req_head != NULL);
667d62bc4baSyz 	while ((req = dlmgmt_db_req_head) != NULL) {
668d62bc4baSyz 		assert(req->ls_flags == DLMGMT_PERSIST);
669*2b24ab6bSSebastien Roy 		if (dlmgmt_process_db_onereq(req, B_TRUE) == EINPROGRESS) {
670d62bc4baSyz 			/*
671d62bc4baSyz 			 * The filesystem is still read only. Go to sleep and
672d62bc4baSyz 			 * try again.
673d62bc4baSyz 			 */
674d62bc4baSyz 			dlmgmt_table_unlock();
675d62bc4baSyz 			(void) sleep(5);
676d62bc4baSyz 			dlmgmt_table_lock(B_TRUE);
677d62bc4baSyz 			continue;
678d62bc4baSyz 		}
679d62bc4baSyz 
680d62bc4baSyz 		/*
681d62bc4baSyz 		 * The filesystem is no longer read only. Continue processing
682d62bc4baSyz 		 * and remove the request from the pending list.
683d62bc4baSyz 		 */
684d62bc4baSyz 		dlmgmt_db_req_head = req->ls_next;
685d62bc4baSyz 		if (dlmgmt_db_req_tail == req) {
686d62bc4baSyz 			assert(dlmgmt_db_req_head == NULL);
687d62bc4baSyz 			dlmgmt_db_req_tail = NULL;
688d62bc4baSyz 		}
689d62bc4baSyz 		free(req);
690d62bc4baSyz 	}
691d62bc4baSyz 
692d62bc4baSyz 	dlmgmt_table_unlock();
693d62bc4baSyz 	return (NULL);
694d62bc4baSyz }
695d62bc4baSyz 
696d62bc4baSyz static int
697d62bc4baSyz parse_linkprops(char *buf, dlmgmt_link_t *linkp)
698d62bc4baSyz {
699d62bc4baSyz 	boolean_t		found_type = B_FALSE;
700d62bc4baSyz 	dladm_datatype_t	type = DLADM_TYPE_STR;
701d62bc4baSyz 	int			i, len;
702d62bc4baSyz 	int			err = 0;
703d62bc4baSyz 	char			*curr;
704d62bc4baSyz 	char			attr_name[MAXLINKATTRLEN];
705d62bc4baSyz 	size_t			attr_buf_len = 0;
706d62bc4baSyz 	void			*attr_buf = NULL;
707d62bc4baSyz 
708d62bc4baSyz 	curr = buf;
709d62bc4baSyz 	len = strlen(buf);
710d62bc4baSyz 	attr_name[0] = '\0';
711d62bc4baSyz 	for (i = 0; i < len && err == 0; i++) {
712d62bc4baSyz 		char		c = buf[i];
713d62bc4baSyz 		boolean_t	match = (c == '=' ||
714d62bc4baSyz 		    (c == ',' && !found_type) || c == ';');
715d62bc4baSyz 
716d62bc4baSyz 		/*
717d62bc4baSyz 		 * Move to the next character if there is no match and
718d62bc4baSyz 		 * if we have not reached the last character.
719d62bc4baSyz 		 */
720d62bc4baSyz 		if (!match && i != len - 1)
721d62bc4baSyz 			continue;
722d62bc4baSyz 
723d62bc4baSyz 		if (match) {
724d62bc4baSyz 			/*
725d62bc4baSyz 			 * NUL-terminate the string pointed to by 'curr'.
726d62bc4baSyz 			 */
727d62bc4baSyz 			buf[i] = '\0';
728d62bc4baSyz 			if (*curr == '\0')
729d62bc4baSyz 				goto parse_fail;
730d62bc4baSyz 		}
731d62bc4baSyz 
732d62bc4baSyz 		if (attr_name[0] != '\0' && found_type) {
733d62bc4baSyz 			/*
734d62bc4baSyz 			 * We get here after we have processed the "<prop>="
735d62bc4baSyz 			 * pattern. The pattern we are now interested in is
736d62bc4baSyz 			 * "<val>;".
737d62bc4baSyz 			 */
738d62bc4baSyz 			if (c == '=')
739d62bc4baSyz 				goto parse_fail;
740d62bc4baSyz 
741*2b24ab6bSSebastien Roy 			if (strcmp(attr_name, "linkid") == 0) {
742*2b24ab6bSSebastien Roy 				(void) read_int64(curr, &attr_buf);
743*2b24ab6bSSebastien Roy 				linkp->ll_linkid =
744*2b24ab6bSSebastien Roy 				    (datalink_class_t)*(int64_t *)attr_buf;
745*2b24ab6bSSebastien Roy 			} else if (strcmp(attr_name, "name") == 0) {
746d62bc4baSyz 				(void) read_str(curr, &attr_buf);
747d62bc4baSyz 				(void) snprintf(linkp->ll_link,
748d62bc4baSyz 				    MAXLINKNAMELEN, "%s", attr_buf);
749d62bc4baSyz 			} else if (strcmp(attr_name, "class") == 0) {
750d62bc4baSyz 				(void) read_int64(curr, &attr_buf);
751d62bc4baSyz 				linkp->ll_class =
752d62bc4baSyz 				    (datalink_class_t)*(int64_t *)attr_buf;
753d62bc4baSyz 			} else if (strcmp(attr_name, "media") == 0) {
754d62bc4baSyz 				(void) read_int64(curr, &attr_buf);
755d62bc4baSyz 				linkp->ll_media =
756d62bc4baSyz 				    (uint32_t)*(int64_t *)attr_buf;
757d62bc4baSyz 			} else {
758d62bc4baSyz 				attr_buf_len = translators[type].read_func(curr,
759d62bc4baSyz 				    &attr_buf);
760d62bc4baSyz 				err = linkattr_set(&(linkp->ll_head), attr_name,
761d62bc4baSyz 				    attr_buf, attr_buf_len, type);
762d62bc4baSyz 			}
763d62bc4baSyz 
764d62bc4baSyz 			free(attr_buf);
765d62bc4baSyz 			attr_name[0] = '\0';
766d62bc4baSyz 			found_type = B_FALSE;
767d62bc4baSyz 		} else if (attr_name[0] != '\0') {
768d62bc4baSyz 			/*
769d62bc4baSyz 			 * Non-zero length attr_name and found_type of false
770d62bc4baSyz 			 * indicates that we have not found the type for this
771d62bc4baSyz 			 * attribute.  The pattern now is "<type>,<val>;", we
772d62bc4baSyz 			 * want the <type> part of the pattern.
773d62bc4baSyz 			 */
774d62bc4baSyz 			for (type = 0; type < ntranslators; type++) {
775d62bc4baSyz 				if (strcmp(curr,
776d62bc4baSyz 				    translators[type].type_name) == 0) {
777d62bc4baSyz 					found_type = B_TRUE;
778d62bc4baSyz 					break;
779d62bc4baSyz 				}
780d62bc4baSyz 			}
781d62bc4baSyz 
782d62bc4baSyz 			if (!found_type)
783d62bc4baSyz 				goto parse_fail;
784d62bc4baSyz 		} else {
785d62bc4baSyz 			/*
786d62bc4baSyz 			 * A zero length attr_name indicates we are looking
787d62bc4baSyz 			 * at the beginning of a link attribute.
788d62bc4baSyz 			 */
789d62bc4baSyz 			if (c != '=')
790d62bc4baSyz 				goto parse_fail;
791d62bc4baSyz 
792d62bc4baSyz 			(void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
793d62bc4baSyz 		}
794d62bc4baSyz 		curr = buf + i + 1;
795d62bc4baSyz 	}
796d62bc4baSyz 
797d62bc4baSyz 	return (err);
798d62bc4baSyz 
799d62bc4baSyz parse_fail:
800d62bc4baSyz 	return (-1);
801d62bc4baSyz }
802d62bc4baSyz 
803d62bc4baSyz static boolean_t
804*2b24ab6bSSebastien Roy process_link_line(char *buf, dlmgmt_link_t *linkp)
805d62bc4baSyz {
806*2b24ab6bSSebastien Roy 	int	i, len, llen;
807*2b24ab6bSSebastien Roy 	char	*str, *lasts;
808*2b24ab6bSSebastien Roy 	char	tmpbuf[MAXLINELEN];
809*2b24ab6bSSebastien Roy 
810*2b24ab6bSSebastien Roy 	bzero(linkp, sizeof (*linkp));
811*2b24ab6bSSebastien Roy 	linkp->ll_linkid = DATALINK_INVALID_LINKID;
812d62bc4baSyz 
813d62bc4baSyz 	/*
814d62bc4baSyz 	 * Use a copy of buf for parsing so that we can do whatever we want.
815d62bc4baSyz 	 */
816d62bc4baSyz 	(void) strlcpy(tmpbuf, buf, MAXLINELEN);
817d62bc4baSyz 
818d62bc4baSyz 	/*
819d62bc4baSyz 	 * Skip leading spaces, blank lines, and comments.
820d62bc4baSyz 	 */
821d62bc4baSyz 	len = strlen(tmpbuf);
822d62bc4baSyz 	for (i = 0; i < len; i++) {
823d62bc4baSyz 		if (!isspace(tmpbuf[i]))
824d62bc4baSyz 			break;
825d62bc4baSyz 	}
826*2b24ab6bSSebastien Roy 	if (i == len || tmpbuf[i] == '#')
827d62bc4baSyz 		return (B_TRUE);
828d62bc4baSyz 
829d62bc4baSyz 	str = tmpbuf + i;
830d62bc4baSyz 	/*
831*2b24ab6bSSebastien Roy 	 * Find the link name and assign it to the link structure.
832d62bc4baSyz 	 */
833d62bc4baSyz 	if (strtok_r(str, " \n\t", &lasts) == NULL)
834d62bc4baSyz 		goto fail;
835d62bc4baSyz 
836d62bc4baSyz 	llen = strlen(str);
837*2b24ab6bSSebastien Roy 	/*
838*2b24ab6bSSebastien Roy 	 * Note that a previous version of the persistent datalink.conf file
839*2b24ab6bSSebastien Roy 	 * stored the linkid as the first field.  In that case, the name will
840*2b24ab6bSSebastien Roy 	 * be obtained through parse_linkprops from a property with the format
841*2b24ab6bSSebastien Roy 	 * "name=<linkname>".  If we encounter such a format, we set
842*2b24ab6bSSebastien Roy 	 * rewrite_needed so that dlmgmt_db_init() can rewrite the file with
843*2b24ab6bSSebastien Roy 	 * the new format after it's done reading in the data.
844*2b24ab6bSSebastien Roy 	 */
845*2b24ab6bSSebastien Roy 	if (isdigit(str[0])) {
846*2b24ab6bSSebastien Roy 		linkp->ll_linkid = atoi(str);
847*2b24ab6bSSebastien Roy 		rewrite_needed = B_TRUE;
848*2b24ab6bSSebastien Roy 	} else {
849*2b24ab6bSSebastien Roy 		if (strlcpy(linkp->ll_link, str, sizeof (linkp->ll_link)) >=
850*2b24ab6bSSebastien Roy 		    sizeof (linkp->ll_link))
851*2b24ab6bSSebastien Roy 			goto fail;
852*2b24ab6bSSebastien Roy 	}
853d62bc4baSyz 
854d62bc4baSyz 	str += llen + 1;
855d62bc4baSyz 	if (str >= tmpbuf + len)
856d62bc4baSyz 		goto fail;
857d62bc4baSyz 
858d62bc4baSyz 	/*
859d62bc4baSyz 	 * Now find the list of link properties.
860d62bc4baSyz 	 */
861d62bc4baSyz 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
862d62bc4baSyz 		goto fail;
863d62bc4baSyz 
864d62bc4baSyz 	if (parse_linkprops(str, linkp) < 0)
865d62bc4baSyz 		goto fail;
866d62bc4baSyz 
867d62bc4baSyz 	return (B_TRUE);
868d62bc4baSyz 
869d62bc4baSyz fail:
870d62bc4baSyz 	/*
871d62bc4baSyz 	 * Delete corrupted line.
872d62bc4baSyz 	 */
873d62bc4baSyz 	buf[0] = '\0';
874d62bc4baSyz 	return (B_FALSE);
875d62bc4baSyz }
876d62bc4baSyz 
877*2b24ab6bSSebastien Roy /*
878*2b24ab6bSSebastien Roy  * Find any properties in linkp that refer to "old", and rename to "new".
879*2b24ab6bSSebastien Roy  * Return B_TRUE if any renaming occurred.
880*2b24ab6bSSebastien Roy  */
881*2b24ab6bSSebastien Roy static int
882*2b24ab6bSSebastien Roy dlmgmt_attr_rename(dlmgmt_link_t *linkp, const char *old, const char *new,
883*2b24ab6bSSebastien Roy     boolean_t *renamed)
884*2b24ab6bSSebastien Roy {
885*2b24ab6bSSebastien Roy 	dlmgmt_linkattr_t	*attrp;
886*2b24ab6bSSebastien Roy 	char			*newval = NULL, *pname;
887*2b24ab6bSSebastien Roy 	char			valcp[MAXLINKATTRVALLEN];
888*2b24ab6bSSebastien Roy 	size_t			newsize;
889*2b24ab6bSSebastien Roy 
890*2b24ab6bSSebastien Roy 	*renamed = B_FALSE;
891*2b24ab6bSSebastien Roy 
892*2b24ab6bSSebastien Roy 	if ((attrp = linkattr_find(linkp->ll_head, "linkover")) != NULL ||
893*2b24ab6bSSebastien Roy 	    (attrp = linkattr_find(linkp->ll_head, "simnetpeer")) != NULL) {
894*2b24ab6bSSebastien Roy 		if (strcmp(old, (char *)attrp->lp_val) == 0) {
895*2b24ab6bSSebastien Roy 			newsize = strlen(new) + 1;
896*2b24ab6bSSebastien Roy 			if ((newval = malloc(newsize)) == NULL)
897*2b24ab6bSSebastien Roy 				return (errno);
898*2b24ab6bSSebastien Roy 			(void) strcpy(newval, new);
899*2b24ab6bSSebastien Roy 			free(attrp->lp_val);
900*2b24ab6bSSebastien Roy 			attrp->lp_val = newval;
901*2b24ab6bSSebastien Roy 			attrp->lp_sz = newsize;
902*2b24ab6bSSebastien Roy 			*renamed = B_TRUE;
903*2b24ab6bSSebastien Roy 		}
904*2b24ab6bSSebastien Roy 		return (0);
905*2b24ab6bSSebastien Roy 	}
906*2b24ab6bSSebastien Roy 
907*2b24ab6bSSebastien Roy 	if ((attrp = linkattr_find(linkp->ll_head, "portnames")) == NULL)
908*2b24ab6bSSebastien Roy 		return (0);
909*2b24ab6bSSebastien Roy 
910*2b24ab6bSSebastien Roy 	/* <linkname>:[<linkname>:]... */
911*2b24ab6bSSebastien Roy 	if ((newval = calloc(MAXLINKATTRVALLEN, sizeof (char))) == NULL)
912*2b24ab6bSSebastien Roy 		return (errno);
913*2b24ab6bSSebastien Roy 
914*2b24ab6bSSebastien Roy 	bcopy(attrp->lp_val, valcp, sizeof (valcp));
915*2b24ab6bSSebastien Roy 	pname = strtok(valcp, ":");
916*2b24ab6bSSebastien Roy 	while (pname != NULL) {
917*2b24ab6bSSebastien Roy 		if (strcmp(pname, old) == 0) {
918*2b24ab6bSSebastien Roy 			(void) strcat(newval, new);
919*2b24ab6bSSebastien Roy 			*renamed = B_TRUE;
920*2b24ab6bSSebastien Roy 		} else {
921*2b24ab6bSSebastien Roy 			(void) strcat(newval, pname);
922*2b24ab6bSSebastien Roy 		}
923*2b24ab6bSSebastien Roy 		(void) strcat(newval, ":");
924*2b24ab6bSSebastien Roy 		pname = strtok(NULL, ":");
925*2b24ab6bSSebastien Roy 	}
926*2b24ab6bSSebastien Roy 	if (*renamed) {
927*2b24ab6bSSebastien Roy 		free(attrp->lp_val);
928*2b24ab6bSSebastien Roy 		attrp->lp_val = newval;
929*2b24ab6bSSebastien Roy 		attrp->lp_sz = strlen(newval) + 1;
930*2b24ab6bSSebastien Roy 	} else {
931*2b24ab6bSSebastien Roy 		free(newval);
932*2b24ab6bSSebastien Roy 	}
933*2b24ab6bSSebastien Roy 	return (0);
934*2b24ab6bSSebastien Roy }
935*2b24ab6bSSebastien Roy 
936d62bc4baSyz static int
937d62bc4baSyz process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
938d62bc4baSyz {
939d62bc4baSyz 	boolean_t		done = B_FALSE;
940d62bc4baSyz 	int			err = 0;
941*2b24ab6bSSebastien Roy 	dlmgmt_link_t		link_in_file, *linkp = NULL, *dblinkp;
942*2b24ab6bSSebastien Roy 	boolean_t		persist = (req->ls_flags == DLMGMT_PERSIST);
943*2b24ab6bSSebastien Roy 	boolean_t		writeall, rename, attr_renamed;
944d62bc4baSyz 	char			buf[MAXLINELEN];
945d62bc4baSyz 
946*2b24ab6bSSebastien Roy 	writeall = (req->ls_linkid == DATALINK_ALL_LINKID);
947*2b24ab6bSSebastien Roy 
948*2b24ab6bSSebastien Roy 	if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall) {
949d62bc4baSyz 		/*
950d62bc4baSyz 		 * find the link in the avl tree with the given linkid.
951d62bc4baSyz 		 */
952*2b24ab6bSSebastien Roy 		linkp = link_by_id(req->ls_linkid, req->ls_zoneid);
953d62bc4baSyz 		if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
954d62bc4baSyz 			/*
955d62bc4baSyz 			 * This link has already been changed. This could
956d62bc4baSyz 			 * happen if the request is pending because of
957d62bc4baSyz 			 * read-only file-system. If so, we are done.
958d62bc4baSyz 			 */
959d62bc4baSyz 			return (0);
960d62bc4baSyz 		}
961*2b24ab6bSSebastien Roy 		/*
962*2b24ab6bSSebastien Roy 		 * In the case of a rename, linkp's name has been updated to
963*2b24ab6bSSebastien Roy 		 * the new name, and req->ls_link is the old link name.
964*2b24ab6bSSebastien Roy 		 */
965*2b24ab6bSSebastien Roy 		rename = (strcmp(req->ls_link, linkp->ll_link) != 0);
966d62bc4baSyz 	}
967d62bc4baSyz 
968*2b24ab6bSSebastien Roy 	/*
969*2b24ab6bSSebastien Roy 	 * fp can be NULL if the file didn't initially exist and we're
970*2b24ab6bSSebastien Roy 	 * creating it as part of this write operation.
971*2b24ab6bSSebastien Roy 	 */
972*2b24ab6bSSebastien Roy 	if (fp == NULL)
973*2b24ab6bSSebastien Roy 		goto write;
974*2b24ab6bSSebastien Roy 
975d62bc4baSyz 	while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
976d62bc4baSyz 	    process_link_line(buf, &link_in_file)) {
977*2b24ab6bSSebastien Roy 		if (link_in_file.ll_link[0] == '\0' || done) {
978d62bc4baSyz 			/*
9790dc974a9SCathy Zhou 			 * this is a comment line or we are done updating the
980*2b24ab6bSSebastien Roy 			 * line for the specified link, write the rest of
981*2b24ab6bSSebastien Roy 			 * lines out.
982d62bc4baSyz 			 */
983d62bc4baSyz 			if (fputs(buf, nfp) == EOF)
984d62bc4baSyz 				err = errno;
985d62bc4baSyz 			continue;
986d62bc4baSyz 		}
987d62bc4baSyz 
988d62bc4baSyz 		switch (req->ls_op) {
989d62bc4baSyz 		case DLMGMT_DB_OP_WRITE:
990d62bc4baSyz 			/*
991*2b24ab6bSSebastien Roy 			 * For write operations, we generate a new output line
992*2b24ab6bSSebastien Roy 			 * if we're either writing all links (writeall) or if
993*2b24ab6bSSebastien Roy 			 * the name of the link in the file matches the one
994*2b24ab6bSSebastien Roy 			 * we're looking for.  Otherwise, we write out the
995*2b24ab6bSSebastien Roy 			 * buffer as-is.
996*2b24ab6bSSebastien Roy 			 *
997*2b24ab6bSSebastien Roy 			 * If we're doing a rename operation, ensure that any
998*2b24ab6bSSebastien Roy 			 * references to the link being renamed in link
999*2b24ab6bSSebastien Roy 			 * properties are also updated before we write
1000*2b24ab6bSSebastien Roy 			 * anything.
1001d62bc4baSyz 			 */
1002*2b24ab6bSSebastien Roy 			if (writeall) {
1003*2b24ab6bSSebastien Roy 				linkp = link_by_name(link_in_file.ll_link,
1004*2b24ab6bSSebastien Roy 				    req->ls_zoneid);
1005d62bc4baSyz 			}
1006*2b24ab6bSSebastien Roy 			if (writeall || strcmp(req->ls_link,
1007*2b24ab6bSSebastien Roy 			    link_in_file.ll_link) == 0) {
1008*2b24ab6bSSebastien Roy 				generate_link_line(linkp, persist, buf);
1009*2b24ab6bSSebastien Roy 				if (!writeall && !rename)
1010*2b24ab6bSSebastien Roy 					done = B_TRUE;
1011*2b24ab6bSSebastien Roy 			} else if (rename && persist) {
1012*2b24ab6bSSebastien Roy 				dblinkp = link_by_name(link_in_file.ll_link,
1013*2b24ab6bSSebastien Roy 				    req->ls_zoneid);
1014*2b24ab6bSSebastien Roy 				err = dlmgmt_attr_rename(dblinkp, req->ls_link,
1015*2b24ab6bSSebastien Roy 				    linkp->ll_link, &attr_renamed);
1016*2b24ab6bSSebastien Roy 				if (err != 0)
1017*2b24ab6bSSebastien Roy 					break;
1018*2b24ab6bSSebastien Roy 				if (attr_renamed) {
1019*2b24ab6bSSebastien Roy 					generate_link_line(dblinkp, persist,
1020*2b24ab6bSSebastien Roy 					    buf);
1021*2b24ab6bSSebastien Roy 				}
1022*2b24ab6bSSebastien Roy 			}
1023*2b24ab6bSSebastien Roy 			if (fputs(buf, nfp) == EOF)
1024*2b24ab6bSSebastien Roy 				err = errno;
1025d62bc4baSyz 			break;
1026d62bc4baSyz 		case DLMGMT_DB_OP_DELETE:
1027d62bc4baSyz 			/*
1028d62bc4baSyz 			 * Delete is simple.  If buf does not represent the
1029d62bc4baSyz 			 * link we're deleting, write it out.
1030d62bc4baSyz 			 */
1031*2b24ab6bSSebastien Roy 			if (strcmp(req->ls_link, link_in_file.ll_link) != 0) {
1032d62bc4baSyz 				if (fputs(buf, nfp) == EOF)
1033d62bc4baSyz 					err = errno;
1034d62bc4baSyz 			} else {
1035d62bc4baSyz 				done = B_TRUE;
1036d62bc4baSyz 			}
1037d62bc4baSyz 			break;
1038d62bc4baSyz 		case DLMGMT_DB_OP_READ:
1039d62bc4baSyz 		default:
1040d62bc4baSyz 			err = EINVAL;
1041d62bc4baSyz 			break;
1042d62bc4baSyz 		}
1043d62bc4baSyz 	}
1044d62bc4baSyz 
1045*2b24ab6bSSebastien Roy write:
1046d62bc4baSyz 	/*
1047*2b24ab6bSSebastien Roy 	 * If we get to the end of the file and have not seen what linkid
1048*2b24ab6bSSebastien Roy 	 * points to, write it out then.
1049d62bc4baSyz 	 */
1050*2b24ab6bSSebastien Roy 	if (req->ls_op == DLMGMT_DB_OP_WRITE && !writeall && !rename && !done) {
1051*2b24ab6bSSebastien Roy 		generate_link_line(linkp, persist, buf);
1052d62bc4baSyz 		done = B_TRUE;
1053d62bc4baSyz 		if (fputs(buf, nfp) == EOF)
1054d62bc4baSyz 			err = errno;
1055d62bc4baSyz 	}
1056d62bc4baSyz 
1057d62bc4baSyz 	return (err);
1058d62bc4baSyz }
1059d62bc4baSyz 
1060d62bc4baSyz static int
1061*2b24ab6bSSebastien Roy process_db_read(dlmgmt_db_req_t *req, FILE *fp)
1062d62bc4baSyz {
1063d62bc4baSyz 	avl_index_t	name_where, id_where;
1064*2b24ab6bSSebastien Roy 	dlmgmt_link_t	link_in_file, *newlink, *link_in_db;
1065d62bc4baSyz 	char		buf[MAXLINELEN];
1066d62bc4baSyz 	int		err = 0;
1067d62bc4baSyz 
1068d62bc4baSyz 	/*
1069d62bc4baSyz 	 * This loop processes each line of the configuration file.
1070d62bc4baSyz 	 */
1071d62bc4baSyz 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
1072d62bc4baSyz 		if (!process_link_line(buf, &link_in_file)) {
1073d62bc4baSyz 			err = EINVAL;
1074d62bc4baSyz 			break;
1075d62bc4baSyz 		}
1076d62bc4baSyz 
1077d62bc4baSyz 		/*
1078d62bc4baSyz 		 * Skip the comment line.
1079d62bc4baSyz 		 */
1080*2b24ab6bSSebastien Roy 		if (link_in_file.ll_link[0] == '\0')
1081*2b24ab6bSSebastien Roy 			continue;
1082*2b24ab6bSSebastien Roy 
1083*2b24ab6bSSebastien Roy 		if ((req->ls_flags & DLMGMT_ACTIVE) &&
1084*2b24ab6bSSebastien Roy 		    link_in_file.ll_linkid == DATALINK_INVALID_LINKID)
1085d62bc4baSyz 			continue;
1086d62bc4baSyz 
1087*2b24ab6bSSebastien Roy 		link_in_file.ll_zoneid = req->ls_zoneid;
1088*2b24ab6bSSebastien Roy 		link_in_db = avl_find(&dlmgmt_name_avl, &link_in_file,
1089*2b24ab6bSSebastien Roy 		    &name_where);
1090*2b24ab6bSSebastien Roy 		if (link_in_db != NULL) {
1091d62bc4baSyz 			/*
1092*2b24ab6bSSebastien Roy 			 * If the link in the database already has the flag
1093*2b24ab6bSSebastien Roy 			 * for this request set, then the entry is a
1094*2b24ab6bSSebastien Roy 			 * duplicate.  If it's not a duplicate, then simply
1095*2b24ab6bSSebastien Roy 			 * turn on the appropriate flag on the existing link.
1096d62bc4baSyz 			 */
1097*2b24ab6bSSebastien Roy 			if (link_in_db->ll_flags & req->ls_flags) {
1098*2b24ab6bSSebastien Roy 				dlmgmt_log(LOG_WARNING, "Duplicate links "
1099*2b24ab6bSSebastien Roy 				    "in the repository: %s",
1100*2b24ab6bSSebastien Roy 				    link_in_file.ll_link);
1101d62bc4baSyz 			} else {
1102*2b24ab6bSSebastien Roy 				if (req->ls_flags & DLMGMT_PERSIST) {
1103*2b24ab6bSSebastien Roy 					/*
1104*2b24ab6bSSebastien Roy 					 * Save the newly read properties into
1105*2b24ab6bSSebastien Roy 					 * the existing link.
1106*2b24ab6bSSebastien Roy 					 */
1107*2b24ab6bSSebastien Roy 					assert(link_in_db->ll_head == NULL);
1108*2b24ab6bSSebastien Roy 					link_in_db->ll_head =
1109*2b24ab6bSSebastien Roy 					    link_in_file.ll_head;
1110*2b24ab6bSSebastien Roy 				}
1111*2b24ab6bSSebastien Roy 				link_in_db->ll_flags |= req->ls_flags;
1112d62bc4baSyz 			}
1113d62bc4baSyz 		} else {
1114*2b24ab6bSSebastien Roy 			/*
1115*2b24ab6bSSebastien Roy 			 * This is a new link.  Allocate a new dlmgmt_link_t
1116*2b24ab6bSSebastien Roy 			 * and add it to the trees.
1117*2b24ab6bSSebastien Roy 			 */
1118*2b24ab6bSSebastien Roy 			newlink = calloc(1, sizeof (*newlink));
1119*2b24ab6bSSebastien Roy 			if (newlink == NULL) {
1120*2b24ab6bSSebastien Roy 				dlmgmt_log(LOG_WARNING, "Unable to allocate "
1121*2b24ab6bSSebastien Roy 				    "memory to create new link %s",
1122*2b24ab6bSSebastien Roy 				    link_in_file.ll_link);
1123*2b24ab6bSSebastien Roy 				continue;
1124*2b24ab6bSSebastien Roy 			}
1125*2b24ab6bSSebastien Roy 			bcopy(&link_in_file, newlink, sizeof (*newlink));
1126*2b24ab6bSSebastien Roy 
1127*2b24ab6bSSebastien Roy 			if (newlink->ll_linkid == DATALINK_INVALID_LINKID)
1128*2b24ab6bSSebastien Roy 				newlink->ll_linkid = dlmgmt_nextlinkid;
1129*2b24ab6bSSebastien Roy 			if (avl_find(&dlmgmt_id_avl, newlink, &id_where) !=
1130*2b24ab6bSSebastien Roy 			    NULL) {
1131*2b24ab6bSSebastien Roy 				link_destroy(newlink);
1132*2b24ab6bSSebastien Roy 				continue;
1133*2b24ab6bSSebastien Roy 			}
1134*2b24ab6bSSebastien Roy 			if ((req->ls_flags & DLMGMT_ACTIVE) &&
1135*2b24ab6bSSebastien Roy 			    link_activate(newlink) != 0) {
1136*2b24ab6bSSebastien Roy 				dlmgmt_log(LOG_WARNING, "Unable to activate %s",
1137*2b24ab6bSSebastien Roy 				    newlink->ll_link);
1138*2b24ab6bSSebastien Roy 				link_destroy(newlink);
1139*2b24ab6bSSebastien Roy 				continue;
1140*2b24ab6bSSebastien Roy 			}
1141*2b24ab6bSSebastien Roy 			avl_insert(&dlmgmt_name_avl, newlink, name_where);
1142*2b24ab6bSSebastien Roy 			avl_insert(&dlmgmt_id_avl, newlink, id_where);
1143*2b24ab6bSSebastien Roy 			dlmgmt_advance(newlink);
1144*2b24ab6bSSebastien Roy 			newlink->ll_flags |= req->ls_flags;
1145d62bc4baSyz 		}
1146d62bc4baSyz 	}
1147d62bc4baSyz 
1148d62bc4baSyz 	return (err);
1149d62bc4baSyz }
1150d62bc4baSyz 
1151d62bc4baSyz /*
1152d62bc4baSyz  * Generate an entry in the link database.
1153d62bc4baSyz  * Each entry has this format:
1154*2b24ab6bSSebastien Roy  * <link name>	<prop0>=<type>,<val>;...;<propn>=<type>,<val>;
1155d62bc4baSyz  */
1156d62bc4baSyz static void
1157d62bc4baSyz generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
1158d62bc4baSyz {
1159d62bc4baSyz 	char			tmpbuf[MAXLINELEN];
1160*2b24ab6bSSebastien Roy 	char			*ptr = tmpbuf;
1161d62bc4baSyz 	char			*lim = tmpbuf + MAXLINELEN;
1162d62bc4baSyz 	dlmgmt_linkattr_t	*cur_p = NULL;
1163d62bc4baSyz 	uint64_t		u64;
1164d62bc4baSyz 
1165*2b24ab6bSSebastien Roy 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%s\t", linkp->ll_link);
1166*2b24ab6bSSebastien Roy 	if (!persist) {
1167*2b24ab6bSSebastien Roy 		/*
1168*2b24ab6bSSebastien Roy 		 * We store the linkid in the active database so that dlmgmtd
1169*2b24ab6bSSebastien Roy 		 * can recover in the event that it is restarted.
1170*2b24ab6bSSebastien Roy 		 */
1171*2b24ab6bSSebastien Roy 		u64 = linkp->ll_linkid;
1172*2b24ab6bSSebastien Roy 		ptr += write_uint64(ptr, BUFLEN(lim, ptr), "linkid", &u64);
1173*2b24ab6bSSebastien Roy 	}
1174d62bc4baSyz 	u64 = linkp->ll_class;
1175d62bc4baSyz 	ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
1176d62bc4baSyz 	u64 = linkp->ll_media;
1177d62bc4baSyz 	ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
1178d62bc4baSyz 
1179d62bc4baSyz 	/*
1180*2b24ab6bSSebastien Roy 	 * The daemon does not keep any active link attribute. Only store the
1181*2b24ab6bSSebastien Roy 	 * attributes if this request is for persistent configuration,
1182d62bc4baSyz 	 */
1183*2b24ab6bSSebastien Roy 	if (persist) {
1184*2b24ab6bSSebastien Roy 		for (cur_p = linkp->ll_head; cur_p != NULL;
1185*2b24ab6bSSebastien Roy 		    cur_p = cur_p->lp_next) {
1186*2b24ab6bSSebastien Roy 			ptr += translators[cur_p->lp_type].write_func(ptr,
1187*2b24ab6bSSebastien Roy 			    BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
1188*2b24ab6bSSebastien Roy 		}
1189d62bc4baSyz 	}
1190*2b24ab6bSSebastien Roy 
1191*2b24ab6bSSebastien Roy 	if (ptr <= lim)
1192*2b24ab6bSSebastien Roy 		(void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
1193d62bc4baSyz }
1194d62bc4baSyz 
1195d62bc4baSyz int
1196*2b24ab6bSSebastien Roy dlmgmt_delete_db_entry(dlmgmt_link_t *linkp, uint32_t flags)
1197d62bc4baSyz {
1198*2b24ab6bSSebastien Roy 	return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkp->ll_link, linkp,
1199*2b24ab6bSSebastien Roy 	    flags));
1200d62bc4baSyz }
1201d62bc4baSyz 
1202d62bc4baSyz int
1203*2b24ab6bSSebastien Roy dlmgmt_write_db_entry(const char *entryname, dlmgmt_link_t *linkp,
1204*2b24ab6bSSebastien Roy     uint32_t flags)
1205d62bc4baSyz {
1206*2b24ab6bSSebastien Roy 	int err;
1207d62bc4baSyz 
1208d62bc4baSyz 	if (flags & DLMGMT_PERSIST) {
1209*2b24ab6bSSebastien Roy 		if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1210*2b24ab6bSSebastien Roy 		    linkp, DLMGMT_PERSIST)) != 0) {
1211d62bc4baSyz 			return (err);
1212d62bc4baSyz 		}
1213d62bc4baSyz 	}
1214d62bc4baSyz 
1215d62bc4baSyz 	if (flags & DLMGMT_ACTIVE) {
1216*2b24ab6bSSebastien Roy 		if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE, entryname,
1217*2b24ab6bSSebastien Roy 		    linkp, DLMGMT_ACTIVE)) != 0) && (flags & DLMGMT_PERSIST)) {
1218*2b24ab6bSSebastien Roy 			(void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE, entryname,
1219*2b24ab6bSSebastien Roy 			    linkp, DLMGMT_PERSIST);
1220d62bc4baSyz 			return (err);
1221d62bc4baSyz 		}
1222d62bc4baSyz 	}
1223d62bc4baSyz 
1224d62bc4baSyz 	return (0);
1225d62bc4baSyz }
1226d62bc4baSyz 
1227*2b24ab6bSSebastien Roy /*
1228*2b24ab6bSSebastien Roy  * Upgrade properties that have link IDs as values to link names.  Because '.'
1229*2b24ab6bSSebastien Roy  * is a valid linkname character, the port separater for link aggregations
1230*2b24ab6bSSebastien Roy  * must be changed to ':'.4
1231*2b24ab6bSSebastien Roy  */
1232*2b24ab6bSSebastien Roy static void
1233*2b24ab6bSSebastien Roy linkattr_upgrade(dlmgmt_linkattr_t *attrp)
1234*2b24ab6bSSebastien Roy {
1235*2b24ab6bSSebastien Roy 	datalink_id_t	linkid;
1236*2b24ab6bSSebastien Roy 	char		*portidstr;
1237*2b24ab6bSSebastien Roy 	char		portname[MAXLINKNAMELEN + 1];
1238*2b24ab6bSSebastien Roy 	dlmgmt_link_t	*linkp;
1239*2b24ab6bSSebastien Roy 	char		*new_attr_val;
1240*2b24ab6bSSebastien Roy 	size_t		new_attr_sz;
1241*2b24ab6bSSebastien Roy 	boolean_t	upgraded = B_FALSE;
1242*2b24ab6bSSebastien Roy 
1243*2b24ab6bSSebastien Roy 	if (strcmp(attrp->lp_name, "linkover") == 0 ||
1244*2b24ab6bSSebastien Roy 	    strcmp(attrp->lp_name, "simnetpeer") == 0) {
1245*2b24ab6bSSebastien Roy 		if (attrp->lp_type == DLADM_TYPE_UINT64) {
1246*2b24ab6bSSebastien Roy 			linkid = *(datalink_id_t *)attrp->lp_val;
1247*2b24ab6bSSebastien Roy 			if ((linkp = link_by_id(linkid, GLOBAL_ZONEID)) == NULL)
1248*2b24ab6bSSebastien Roy 				return;
1249*2b24ab6bSSebastien Roy 			new_attr_sz = strlen(linkp->ll_link) + 1;
1250*2b24ab6bSSebastien Roy 			if ((new_attr_val = malloc(new_attr_sz)) == NULL)
1251*2b24ab6bSSebastien Roy 				return;
1252*2b24ab6bSSebastien Roy 			(void) strcpy(new_attr_val, linkp->ll_link);
1253*2b24ab6bSSebastien Roy 			upgraded = B_TRUE;
1254*2b24ab6bSSebastien Roy 		}
1255*2b24ab6bSSebastien Roy 	} else if (strcmp(attrp->lp_name, "portnames") == 0) {
1256*2b24ab6bSSebastien Roy 		/*
1257*2b24ab6bSSebastien Roy 		 * The old format for "portnames" was
1258*2b24ab6bSSebastien Roy 		 * "<linkid>.[<linkid>.]...".  The new format is
1259*2b24ab6bSSebastien Roy 		 * "<linkname>:[<linkname>:]...".
1260*2b24ab6bSSebastien Roy 		 */
1261*2b24ab6bSSebastien Roy 		if (!isdigit(((char *)attrp->lp_val)[0]))
1262*2b24ab6bSSebastien Roy 			return;
1263*2b24ab6bSSebastien Roy 		new_attr_val = calloc(MAXLINKATTRVALLEN, sizeof (char));
1264*2b24ab6bSSebastien Roy 		if (new_attr_val == NULL)
1265*2b24ab6bSSebastien Roy 			return;
1266*2b24ab6bSSebastien Roy 		portidstr = (char *)attrp->lp_val;
1267*2b24ab6bSSebastien Roy 		while (*portidstr != '\0') {
1268*2b24ab6bSSebastien Roy 			errno = 0;
1269*2b24ab6bSSebastien Roy 			linkid = strtol(portidstr, &portidstr, 10);
1270*2b24ab6bSSebastien Roy 			if (linkid == 0 || *portidstr != '.' ||
1271*2b24ab6bSSebastien Roy 			    (linkp = link_by_id(linkid, GLOBAL_ZONEID)) ==
1272*2b24ab6bSSebastien Roy 			    NULL) {
1273*2b24ab6bSSebastien Roy 				free(new_attr_val);
1274*2b24ab6bSSebastien Roy 				return;
1275*2b24ab6bSSebastien Roy 			}
1276*2b24ab6bSSebastien Roy 			(void) snprintf(portname, sizeof (portname), "%s:",
1277*2b24ab6bSSebastien Roy 			    linkp->ll_link);
1278*2b24ab6bSSebastien Roy 			if (strlcat(new_attr_val, portname,
1279*2b24ab6bSSebastien Roy 			    MAXLINKATTRVALLEN) >= MAXLINKATTRVALLEN) {
1280*2b24ab6bSSebastien Roy 				free(new_attr_val);
1281*2b24ab6bSSebastien Roy 				return;
1282*2b24ab6bSSebastien Roy 			}
1283*2b24ab6bSSebastien Roy 			/* skip the '.' delimiter */
1284*2b24ab6bSSebastien Roy 			portidstr++;
1285*2b24ab6bSSebastien Roy 		}
1286*2b24ab6bSSebastien Roy 		new_attr_sz = strlen(new_attr_val) + 1;
1287*2b24ab6bSSebastien Roy 		upgraded = B_TRUE;
1288*2b24ab6bSSebastien Roy 	}
1289*2b24ab6bSSebastien Roy 
1290*2b24ab6bSSebastien Roy 	if (upgraded) {
1291*2b24ab6bSSebastien Roy 		attrp->lp_type = DLADM_TYPE_STR;
1292*2b24ab6bSSebastien Roy 		attrp->lp_sz = new_attr_sz;
1293*2b24ab6bSSebastien Roy 		free(attrp->lp_val);
1294*2b24ab6bSSebastien Roy 		attrp->lp_val = new_attr_val;
1295*2b24ab6bSSebastien Roy 	}
1296*2b24ab6bSSebastien Roy }
1297*2b24ab6bSSebastien Roy 
1298*2b24ab6bSSebastien Roy static void
1299*2b24ab6bSSebastien Roy dlmgmt_db_upgrade(dlmgmt_link_t *linkp)
1300*2b24ab6bSSebastien Roy {
1301*2b24ab6bSSebastien Roy 	dlmgmt_linkattr_t *attrp;
1302*2b24ab6bSSebastien Roy 
1303*2b24ab6bSSebastien Roy 	for (attrp = linkp->ll_head; attrp != NULL; attrp = attrp->lp_next)
1304*2b24ab6bSSebastien Roy 		linkattr_upgrade(attrp);
1305*2b24ab6bSSebastien Roy }
1306*2b24ab6bSSebastien Roy 
1307*2b24ab6bSSebastien Roy static void
1308*2b24ab6bSSebastien Roy dlmgmt_db_phys_activate(dlmgmt_link_t *linkp)
1309*2b24ab6bSSebastien Roy {
1310*2b24ab6bSSebastien Roy 	linkp->ll_flags |= DLMGMT_ACTIVE;
1311*2b24ab6bSSebastien Roy 	(void) dlmgmt_write_db_entry(linkp->ll_link, linkp, DLMGMT_ACTIVE);
1312*2b24ab6bSSebastien Roy }
1313*2b24ab6bSSebastien Roy 
1314*2b24ab6bSSebastien Roy static void
1315*2b24ab6bSSebastien Roy dlmgmt_db_walk(zoneid_t zoneid, datalink_class_t class, db_walk_func_t *func)
1316*2b24ab6bSSebastien Roy {
1317*2b24ab6bSSebastien Roy 	dlmgmt_link_t *linkp;
1318*2b24ab6bSSebastien Roy 
1319*2b24ab6bSSebastien Roy 	for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
1320*2b24ab6bSSebastien Roy 	    linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
1321*2b24ab6bSSebastien Roy 		if (linkp->ll_zoneid == zoneid && (linkp->ll_class & class))
1322*2b24ab6bSSebastien Roy 			func(linkp);
1323*2b24ab6bSSebastien Roy 	}
1324*2b24ab6bSSebastien Roy }
1325*2b24ab6bSSebastien Roy 
1326d62bc4baSyz /*
1327d62bc4baSyz  * Initialize the datalink <link name, linkid> mapping and the link's
1328d62bc4baSyz  * attributes list based on the configuration file /etc/dladm/datalink.conf
1329d62bc4baSyz  * and the active configuration cache file
1330b9e076dcSyz  * /etc/svc/volatile/dladm/datalink-management:default.cache.
1331d62bc4baSyz  */
1332d62bc4baSyz int
1333*2b24ab6bSSebastien Roy dlmgmt_db_init(zoneid_t zoneid)
1334d62bc4baSyz {
1335*2b24ab6bSSebastien Roy 	dlmgmt_db_req_t	*req;
1336d62bc4baSyz 	int		err;
1337*2b24ab6bSSebastien Roy 	boolean_t	boot = B_FALSE;
1338d62bc4baSyz 
1339*2b24ab6bSSebastien Roy 	if ((req = dlmgmt_db_req_alloc(DLMGMT_DB_OP_READ, NULL,
1340*2b24ab6bSSebastien Roy 	    DATALINK_INVALID_LINKID, zoneid, DLMGMT_ACTIVE, &err)) == NULL)
1341*2b24ab6bSSebastien Roy 		return (err);
1342d62bc4baSyz 
1343*2b24ab6bSSebastien Roy 	if ((err = dlmgmt_process_db_req(req)) != 0) {
1344d62bc4baSyz 		/*
1345*2b24ab6bSSebastien Roy 		 * If we get back ENOENT, that means that the active
1346*2b24ab6bSSebastien Roy 		 * configuration file doesn't exist yet, and is not an error.
1347*2b24ab6bSSebastien Roy 		 * We'll create it down below after we've loaded the
1348*2b24ab6bSSebastien Roy 		 * persistent configuration.
1349d62bc4baSyz 		 */
1350*2b24ab6bSSebastien Roy 		if (err != ENOENT)
1351*2b24ab6bSSebastien Roy 			goto done;
1352*2b24ab6bSSebastien Roy 		boot = B_TRUE;
1353d62bc4baSyz 	}
1354d62bc4baSyz 
1355*2b24ab6bSSebastien Roy 	req->ls_flags = DLMGMT_PERSIST;
1356*2b24ab6bSSebastien Roy 	err = dlmgmt_process_db_req(req);
1357*2b24ab6bSSebastien Roy 	if (err != 0 && err != ENOENT)
1358d62bc4baSyz 		goto done;
1359*2b24ab6bSSebastien Roy 	err = 0;
1360*2b24ab6bSSebastien Roy 	if (rewrite_needed) {
1361d62bc4baSyz 		/*
1362*2b24ab6bSSebastien Roy 		 * First update links in memory, then dump the entire db to
1363*2b24ab6bSSebastien Roy 		 * disk.
1364d62bc4baSyz 		 */
1365*2b24ab6bSSebastien Roy 		dlmgmt_db_walk(zoneid, DATALINK_CLASS_ALL, dlmgmt_db_upgrade);
1366*2b24ab6bSSebastien Roy 		req->ls_op = DLMGMT_DB_OP_WRITE;
1367*2b24ab6bSSebastien Roy 		req->ls_linkid = DATALINK_ALL_LINKID;
1368*2b24ab6bSSebastien Roy 		if ((err = dlmgmt_process_db_req(req)) != 0 &&
1369*2b24ab6bSSebastien Roy 		    err != EINPROGRESS)
1370*2b24ab6bSSebastien Roy 			goto done;
1371*2b24ab6bSSebastien Roy 	}
1372*2b24ab6bSSebastien Roy 	if (boot) {
1373*2b24ab6bSSebastien Roy 		dlmgmt_db_walk(zoneid, DATALINK_CLASS_PHYS,
1374*2b24ab6bSSebastien Roy 		    dlmgmt_db_phys_activate);
1375d62bc4baSyz 	}
1376d62bc4baSyz 
1377d62bc4baSyz done:
1378*2b24ab6bSSebastien Roy 	if (err == EINPROGRESS)
1379*2b24ab6bSSebastien Roy 		err = 0;
1380*2b24ab6bSSebastien Roy 	else
1381*2b24ab6bSSebastien Roy 		free(req);
1382d62bc4baSyz 	return (err);
1383d62bc4baSyz }
1384*2b24ab6bSSebastien Roy 
1385*2b24ab6bSSebastien Roy /*
1386*2b24ab6bSSebastien Roy  * Remove all links in the given zoneid.
1387*2b24ab6bSSebastien Roy  */
1388*2b24ab6bSSebastien Roy void
1389*2b24ab6bSSebastien Roy dlmgmt_db_fini(zoneid_t zoneid)
1390*2b24ab6bSSebastien Roy {
1391*2b24ab6bSSebastien Roy 	dlmgmt_link_t *linkp = avl_first(&dlmgmt_name_avl), *next_linkp;
1392*2b24ab6bSSebastien Roy 
1393*2b24ab6bSSebastien Roy 	while (linkp != NULL) {
1394*2b24ab6bSSebastien Roy 		next_linkp = AVL_NEXT(&dlmgmt_name_avl, linkp);
1395*2b24ab6bSSebastien Roy 		if (linkp->ll_zoneid == zoneid) {
1396*2b24ab6bSSebastien Roy 			(void) dlmgmt_destroy_common(linkp,
1397*2b24ab6bSSebastien Roy 			    DLMGMT_ACTIVE | DLMGMT_PERSIST);
1398*2b24ab6bSSebastien Roy 		}
1399*2b24ab6bSSebastien Roy 		linkp = next_linkp;
1400*2b24ab6bSSebastien Roy 	}
1401*2b24ab6bSSebastien Roy }
1402