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