xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_db.c (revision d62bc4ba)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include <assert.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <strings.h>
36 #include <syslog.h>
37 #include <sys/stat.h>
38 #include <pthread.h>
39 #include <unistd.h>
40 #include "dlmgmt_impl.h"
41 
42 typedef enum dlmgmt_db_op {
43 	DLMGMT_DB_OP_WRITE,
44 	DLMGMT_DB_OP_DELETE,
45 	DLMGMT_DB_OP_READ
46 } dlmgmt_db_op_t;
47 
48 typedef struct dlmgmt_db_req_s {
49 	struct dlmgmt_db_req_s	*ls_next;
50 	dlmgmt_db_op_t		ls_op;
51 	datalink_id_t		ls_linkid;
52 	uint32_t		ls_flags;	/* Either DLMGMT_ACTIVE or   */
53 						/* DLMGMT_PERSIST, not both. */
54 } dlmgmt_db_req_t;
55 
56 /*
57  * List of pending db updates (e.g., because of a read-only filesystem).
58  */
59 static dlmgmt_db_req_t	*dlmgmt_db_req_head = NULL;
60 static dlmgmt_db_req_t	*dlmgmt_db_req_tail = NULL;
61 
62 static int		dlmgmt_db_update(dlmgmt_db_op_t, datalink_id_t,
63 			    uint32_t);
64 static int		dlmgmt_process_db_req(dlmgmt_db_req_t *);
65 static int		dlmgmt_process_db_onereq(dlmgmt_db_req_t *, boolean_t);
66 static void		*dlmgmt_db_update_thread(void *);
67 static boolean_t	process_link_line(char *, dlmgmt_link_t **);
68 static int		process_db_write(dlmgmt_db_req_t *, FILE *, FILE *);
69 static int		process_db_read(dlmgmt_db_req_t *, FILE *, FILE *);
70 static void		generate_link_line(dlmgmt_link_t *, boolean_t, char *);
71 
72 #define	BUFLEN(lim, ptr)	(((lim) > (ptr)) ? ((lim) - (ptr)) : 0)
73 #define	MAXLINELEN		1024
74 
75 /*
76  * Translator functions to go from dladm_datatype_t to character strings.
77  * Each function takes a pointer to a buffer, the size of the buffer,
78  * the name of the attribute, and the value to be written.  The functions
79  * return the number of bytes written to the buffer.  If the buffer is not big
80  * enough to hold the string representing the value, then nothing is written
81  * and 0 is returned.
82  */
83 typedef size_t write_func_t(char *, size_t, char *, void *);
84 
85 /*
86  * Translator functions to read from a NULL terminated string buffer into
87  * something of the given DLADM_TYPE_*.  The functions each return the number
88  * of bytes read from the string buffer.  If there is an error reading data
89  * from the buffer, then 0 is returned.  It is the caller's responsibility
90  * to free the data allocated by these functions.
91  */
92 typedef size_t read_func_t(char *, void **);
93 
94 typedef struct translator_s {
95 	const char	*type_name;
96 	write_func_t	*write_func;
97 	read_func_t	*read_func;
98 } translator_t;
99 
100 /*
101  * Translator functions, defined later but declared here so that
102  * the translator table can be defined.
103  */
104 static write_func_t	write_str, write_boolean, write_uint64;
105 static read_func_t	read_str, read_boolean, read_int64;
106 
107 /*
108  * Translator table, indexed by dladm_datatype_t.
109  */
110 static translator_t translators[] = {
111 	{ "string",	write_str,	read_str	},
112 	{ "boolean",	write_boolean,	read_boolean	},
113 	{ "int",	write_uint64,	read_int64	}
114 };
115 
116 static size_t ntranslators = sizeof (translators) / sizeof (translator_t);
117 
118 #define	LINK_PROPERTY_DELIMINATOR	";"
119 #define	LINK_PROPERTY_TYPE_VALUE_SEP	","
120 #define	BASE_PROPERTY_LENGTH(t, n) (strlen(translators[(t)].type_name) +\
121 				    strlen(LINK_PROPERTY_TYPE_VALUE_SEP) +\
122 				    strlen(LINK_PROPERTY_DELIMINATOR) +\
123 				    strlen((n)))
124 #define	GENERATE_PROPERTY_STRING(buf, length, conv, name, type, val) \
125 	    (snprintf((buf), (length), "%s=%s%s" conv "%s", (name), \
126 	    translators[(type)].type_name, \
127 	    LINK_PROPERTY_TYPE_VALUE_SEP, (val), LINK_PROPERTY_DELIMINATOR))
128 
129 #define	DLMGMT_DB_OWNER	15
130 #define	DLMGMT_DB_GROUP	3
131 
132 /*
133  * Name of the cache file to keep the active <link name, linkid> mapping
134  */
135 static char	cachefile[MAXPATHLEN];
136 
137 #define	DLMGMT_TEMP_DB_DIR		"/etc/svc/volatile"
138 #define	DLMGMT_PERSISTENT_DB_PATH	"/etc/dladm/datalink.conf"
139 #define	DLMGMT_MAKE_FILE_DB_PATH(buffer, persistent)	\
140 	(void) snprintf((buffer), MAXPATHLEN, "%s", \
141 	(persistent) ? DLMGMT_PERSISTENT_DB_PATH : cachefile);
142 
143 static size_t
144 write_str(char *buffer, size_t buffer_length, char *name, void *value)
145 {
146 	char	*ptr = value;
147 	size_t	data_length = strnlen(ptr, buffer_length);
148 
149 	/*
150 	 * Strings are assumed to be NULL terminated.  In order to fit in
151 	 * the buffer, the string's length must be less then buffer_length.
152 	 * If the value is empty, there's no point in writing it, in fact,
153 	 * we shouldn't even see that case.
154 	 */
155 	if (data_length + BASE_PROPERTY_LENGTH(DLADM_TYPE_STR, name) ==
156 	    buffer_length || data_length == 0)
157 		return (0);
158 
159 	/*
160 	 * Since we know the string will fit in the buffer, snprintf will
161 	 * always return less than buffer_length, so we can just return
162 	 * whatever snprintf returns.
163 	 */
164 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%s",
165 	    name, DLADM_TYPE_STR, ptr));
166 }
167 
168 static size_t
169 write_boolean(char *buffer, size_t buffer_length, char *name, void *value)
170 {
171 	boolean_t	*ptr = value;
172 
173 	/*
174 	 * Booleans are either zero or one, so we only need room for two
175 	 * characters in the buffer.
176 	 */
177 	if (buffer_length <= 1 + BASE_PROPERTY_LENGTH(DLADM_TYPE_BOOLEAN, name))
178 		return (0);
179 
180 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%d",
181 	    name, DLADM_TYPE_BOOLEAN, *ptr));
182 }
183 
184 static size_t
185 write_uint64(char *buffer, size_t buffer_length, char *name, void *value)
186 {
187 	uint64_t	*ptr = value;
188 
189 	/*
190 	 * Limit checking for uint64_t is a little trickier.
191 	 */
192 	if (snprintf(NULL, 0, "%lld", *ptr)  +
193 	    BASE_PROPERTY_LENGTH(DLADM_TYPE_UINT64, name) >= buffer_length)
194 		return (0);
195 
196 	return (GENERATE_PROPERTY_STRING(buffer, buffer_length, "%lld",
197 	    name, DLADM_TYPE_UINT64, *ptr));
198 }
199 
200 static size_t
201 read_str(char *buffer, void **value)
202 {
203 	char		*ptr = calloc(MAXLINKATTRLEN, sizeof (char));
204 	ssize_t		len;
205 
206 	if (ptr == NULL || (len = snprintf(ptr, MAXLINKATTRLEN, "%s", buffer))
207 	    >= MAXLINKATTRLEN) {
208 		free(ptr);
209 		return (0);
210 	}
211 
212 	*(char **)value = ptr;
213 
214 	/* Account for NULL terminator */
215 	return (len + 1);
216 }
217 
218 static size_t
219 read_boolean(char *buffer, void **value)
220 {
221 	boolean_t	*ptr = calloc(1, sizeof (boolean_t));
222 
223 	if (ptr == NULL)
224 		return (0);
225 
226 	*ptr = atoi(buffer);
227 	*(boolean_t **)value = ptr;
228 
229 	return (sizeof (boolean_t));
230 }
231 
232 static size_t
233 read_int64(char *buffer, void **value)
234 {
235 	int64_t	*ptr = calloc(1, sizeof (int64_t));
236 
237 	if (ptr == NULL)
238 		return (0);
239 
240 	*ptr = (int64_t)atoll(buffer);
241 	*(int64_t **)value = ptr;
242 
243 	return (sizeof (int64_t));
244 }
245 
246 static int
247 dlmgmt_db_update(dlmgmt_db_op_t op, datalink_id_t linkid, uint32_t flags)
248 {
249 	dlmgmt_db_req_t	*req;
250 	int		err;
251 
252 	/*
253 	 * It is either a persistent request or an active request, not both.
254 	 */
255 	assert((flags == DLMGMT_PERSIST) || (flags == DLMGMT_ACTIVE));
256 
257 	if ((req = malloc(sizeof (dlmgmt_db_req_t))) == NULL)
258 		return (ENOMEM);
259 
260 	req->ls_next = NULL;
261 	req->ls_op = op;
262 	req->ls_linkid = linkid;
263 	req->ls_flags = flags;
264 
265 	/*
266 	 * If the return error is EINPROGRESS, this request is handled
267 	 * asynchronously; return success.
268 	 */
269 	err = dlmgmt_process_db_req(req);
270 	if (err != EINPROGRESS)
271 		free(req);
272 	else
273 		err = 0;
274 	return (err);
275 }
276 
277 #define	DLMGMT_DB_OP_STR(op)					\
278 	(((op) == DLMGMT_DB_OP_READ) ? "read" :			\
279 	(((op) == DLMGMT_DB_OP_WRITE) ? "write" : "delete"))
280 
281 #define	DLMGMT_DB_CONF_STR(flag)				\
282 	(((flag) == DLMGMT_ACTIVE) ? "active" :			\
283 	(((flag) == DLMGMT_PERSIST) ? "persistent" : ""))
284 
285 static int
286 dlmgmt_process_db_req(dlmgmt_db_req_t *req)
287 {
288 	pthread_t	tid;
289 	boolean_t	writeop;
290 	int		err;
291 
292 	/*
293 	 * If there are already pending "write" requests, queue this request in
294 	 * the pending list.  Note that this function is called while the
295 	 * dlmgmt_rw_lock is held, so it is safe to access the global variables.
296 	 */
297 	writeop = (req->ls_op != DLMGMT_DB_OP_READ);
298 	if (writeop && (req->ls_flags == DLMGMT_PERSIST) &&
299 	    (dlmgmt_db_req_head != NULL)) {
300 		dlmgmt_db_req_tail->ls_next = req;
301 		dlmgmt_db_req_tail = req;
302 		return (EINPROGRESS);
303 	}
304 
305 	err = dlmgmt_process_db_onereq(req, writeop);
306 	if (err != EINPROGRESS && err != 0 &&
307 	    (req->ls_flags != DLMGMT_ACTIVE || errno != ENOENT)) {
308 
309 		/*
310 		 * Log the error unless the request processing:
311 		 * - is successful;
312 		 * - is still in progress;
313 		 * - has failed with ENOENT because the active configuration
314 		 *   file is not created yet;
315 		 */
316 		dlmgmt_log(LOG_WARNING, "dlmgmt_process_db_onereq() %s "
317 		    "operation on %s configuration failed: %s",
318 		    DLMGMT_DB_OP_STR(req->ls_op),
319 		    DLMGMT_DB_CONF_STR(req->ls_flags), strerror(err));
320 	}
321 
322 	if (err == EINPROGRESS) {
323 		assert(req->ls_flags == DLMGMT_PERSIST);
324 		assert(writeop && dlmgmt_db_req_head == NULL);
325 		dlmgmt_db_req_tail = dlmgmt_db_req_head = req;
326 		err = pthread_create(&tid, NULL, dlmgmt_db_update_thread, NULL);
327 		if (err == 0)
328 			return (EINPROGRESS);
329 	}
330 	return (err);
331 }
332 
333 static int
334 dlmgmt_process_db_onereq(dlmgmt_db_req_t *req, boolean_t writeop)
335 {
336 	int	err = 0;
337 	FILE	*fp, *nfp = NULL;
338 	char	file[MAXPATHLEN];
339 	char	newfile[MAXPATHLEN];
340 	int	nfd;
341 
342 	DLMGMT_MAKE_FILE_DB_PATH(file, (req->ls_flags == DLMGMT_PERSIST));
343 	if ((fp = fopen(file, (writeop ? "r+" : "r"))) == NULL) {
344 		if (writeop && errno == EROFS) {
345 			/*
346 			 * This can happen at boot when the file system is
347 			 * read-only.  So add this request to the pending
348 			 * request list and start a retry thread.
349 			 */
350 			return (EINPROGRESS);
351 		} else if (req->ls_flags == DLMGMT_ACTIVE && errno == ENOENT) {
352 			/*
353 			 * It is fine if the file keeping active configuration
354 			 * does not exist. This happens during a new reboot.
355 			 */
356 			if (!writeop)
357 				return (ENOENT);
358 			/*
359 			 * If this is an update request for the active
360 			 * configuration, create the file.
361 			 */
362 			if ((fp = fopen(file, "w")) == NULL)
363 				return (errno == EROFS ? EINPROGRESS : errno);
364 		} else {
365 			return (errno);
366 		}
367 	}
368 
369 	if (writeop) {
370 		(void) snprintf(newfile, MAXPATHLEN, "%s.new", file);
371 		if ((nfd = open(newfile, O_WRONLY | O_CREAT | O_TRUNC,
372 		    S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) < 0) {
373 			(void) fclose(fp);
374 			return (errno);
375 		}
376 
377 		if ((nfp = fdopen(nfd, "w")) == NULL) {
378 			(void) close(nfd);
379 			(void) fclose(fp);
380 			(void) unlink(newfile);
381 			return (errno);
382 		}
383 	}
384 	if (writeop)
385 		err = process_db_write(req, fp, nfp);
386 	else
387 		err = process_db_read(req, fp, nfp);
388 	if (!writeop || err != 0)
389 		goto done;
390 
391 	/*
392 	 * Configuration files need to be owned by the 'dladm' user.
393 	 * If we are invoked by root, the file ownership needs to be fixed.
394 	 */
395 	if (getuid() == 0 || geteuid() == 0) {
396 		if (fchown(nfd, DLMGMT_DB_OWNER, DLMGMT_DB_GROUP) < 0) {
397 			err = errno;
398 			goto done;
399 		}
400 	}
401 
402 	if (fflush(nfp) == EOF) {
403 		err = errno;
404 		goto done;
405 	}
406 	(void) fclose(fp);
407 	(void) fclose(nfp);
408 
409 	if (rename(newfile, file) < 0) {
410 		(void) unlink(newfile);
411 		return (errno);
412 	}
413 
414 	return (0);
415 
416 done:
417 	if (nfp != NULL) {
418 		(void) fclose(nfp);
419 		if (err != 0)
420 			(void) unlink(newfile);
421 	}
422 	(void) fclose(fp);
423 	return (err);
424 }
425 
426 /*ARGSUSED*/
427 static void *
428 dlmgmt_db_update_thread(void *arg)
429 {
430 	dlmgmt_db_req_t	*req;
431 	int		err = 0;
432 
433 	dlmgmt_table_lock(B_TRUE);
434 
435 	assert(dlmgmt_db_req_head != NULL);
436 	while ((req = dlmgmt_db_req_head) != NULL) {
437 		assert(req->ls_flags == DLMGMT_PERSIST);
438 		err = dlmgmt_process_db_onereq(req, B_TRUE);
439 		if (err == EINPROGRESS) {
440 			/*
441 			 * The filesystem is still read only. Go to sleep and
442 			 * try again.
443 			 */
444 			dlmgmt_table_unlock();
445 			(void) sleep(5);
446 			dlmgmt_table_lock(B_TRUE);
447 			continue;
448 		}
449 
450 		/*
451 		 * The filesystem is no longer read only. Continue processing
452 		 * and remove the request from the pending list.
453 		 */
454 		dlmgmt_db_req_head = req->ls_next;
455 		if (dlmgmt_db_req_tail == req) {
456 			assert(dlmgmt_db_req_head == NULL);
457 			dlmgmt_db_req_tail = NULL;
458 		}
459 		free(req);
460 	}
461 
462 	dlmgmt_table_unlock();
463 	return (NULL);
464 }
465 
466 static int
467 parse_linkprops(char *buf, dlmgmt_link_t *linkp)
468 {
469 	boolean_t		found_type = B_FALSE;
470 	dladm_datatype_t	type = DLADM_TYPE_STR;
471 	int			i, len;
472 	int			err = 0;
473 	char			*curr;
474 	char			attr_name[MAXLINKATTRLEN];
475 	size_t			attr_buf_len = 0;
476 	void			*attr_buf = NULL;
477 
478 	curr = buf;
479 	len = strlen(buf);
480 	attr_name[0] = '\0';
481 	for (i = 0; i < len && err == 0; i++) {
482 		char		c = buf[i];
483 		boolean_t	match = (c == '=' ||
484 		    (c == ',' && !found_type) || c == ';');
485 
486 		/*
487 		 * Move to the next character if there is no match and
488 		 * if we have not reached the last character.
489 		 */
490 		if (!match && i != len - 1)
491 			continue;
492 
493 		if (match) {
494 			/*
495 			 * NUL-terminate the string pointed to by 'curr'.
496 			 */
497 			buf[i] = '\0';
498 			if (*curr == '\0')
499 				goto parse_fail;
500 		}
501 
502 		if (attr_name[0] != '\0' && found_type) {
503 			/*
504 			 * We get here after we have processed the "<prop>="
505 			 * pattern. The pattern we are now interested in is
506 			 * "<val>;".
507 			 */
508 			if (c == '=')
509 				goto parse_fail;
510 
511 			if (strcmp(attr_name, "name") == 0) {
512 				(void) read_str(curr, &attr_buf);
513 				(void) snprintf(linkp->ll_link,
514 				    MAXLINKNAMELEN, "%s", attr_buf);
515 			} else if (strcmp(attr_name, "class") == 0) {
516 				(void) read_int64(curr, &attr_buf);
517 				linkp->ll_class =
518 				    (datalink_class_t)*(int64_t *)attr_buf;
519 			} else if (strcmp(attr_name, "media") == 0) {
520 				(void) read_int64(curr, &attr_buf);
521 				linkp->ll_media =
522 				    (uint32_t)*(int64_t *)attr_buf;
523 			} else {
524 				attr_buf_len = translators[type].read_func(curr,
525 				    &attr_buf);
526 				err = linkattr_set(&(linkp->ll_head), attr_name,
527 				    attr_buf, attr_buf_len, type);
528 			}
529 
530 			free(attr_buf);
531 			attr_name[0] = '\0';
532 			found_type = B_FALSE;
533 		} else if (attr_name[0] != '\0') {
534 			/*
535 			 * Non-zero length attr_name and found_type of false
536 			 * indicates that we have not found the type for this
537 			 * attribute.  The pattern now is "<type>,<val>;", we
538 			 * want the <type> part of the pattern.
539 			 */
540 			for (type = 0; type < ntranslators; type++) {
541 				if (strcmp(curr,
542 				    translators[type].type_name) == 0) {
543 					found_type = B_TRUE;
544 					break;
545 				}
546 			}
547 
548 			if (!found_type)
549 				goto parse_fail;
550 		} else {
551 			/*
552 			 * A zero length attr_name indicates we are looking
553 			 * at the beginning of a link attribute.
554 			 */
555 			if (c != '=')
556 				goto parse_fail;
557 
558 			(void) snprintf(attr_name, MAXLINKATTRLEN, "%s", curr);
559 		}
560 		curr = buf + i + 1;
561 	}
562 
563 	return (err);
564 
565 parse_fail:
566 	return (-1);
567 }
568 
569 static boolean_t
570 process_link_line(char *buf, dlmgmt_link_t **linkpp)
571 {
572 	dlmgmt_link_t		*linkp;
573 	int			i, len, llen;
574 	char			*str, *lasts;
575 	char			tmpbuf[MAXLINELEN];
576 
577 	/*
578 	 * Use a copy of buf for parsing so that we can do whatever we want.
579 	 */
580 	(void) strlcpy(tmpbuf, buf, MAXLINELEN);
581 
582 	/*
583 	 * Skip leading spaces, blank lines, and comments.
584 	 */
585 	len = strlen(tmpbuf);
586 	for (i = 0; i < len; i++) {
587 		if (!isspace(tmpbuf[i]))
588 			break;
589 	}
590 	if (i == len || tmpbuf[i] == '#') {
591 		*linkpp = NULL;
592 		return (B_TRUE);
593 	}
594 
595 	linkp = calloc(1, sizeof (dlmgmt_link_t));
596 	if (linkp == NULL)
597 		goto fail;
598 
599 	str = tmpbuf + i;
600 	/*
601 	 * Find the link id and assign it to the link structure.
602 	 */
603 	if (strtok_r(str, " \n\t", &lasts) == NULL)
604 		goto fail;
605 
606 	llen = strlen(str);
607 	linkp->ll_linkid = atoi(str);
608 
609 	str += llen + 1;
610 	if (str >= tmpbuf + len)
611 		goto fail;
612 
613 	/*
614 	 * Now find the list of link properties.
615 	 */
616 	if ((str = strtok_r(str, " \n\t", &lasts)) == NULL)
617 		goto fail;
618 
619 	if (parse_linkprops(str, linkp) < 0)
620 		goto fail;
621 
622 	*linkpp = linkp;
623 	return (B_TRUE);
624 
625 fail:
626 	link_destroy(linkp);
627 
628 	/*
629 	 * Delete corrupted line.
630 	 */
631 	buf[0] = '\0';
632 	return (B_FALSE);
633 }
634 
635 static int
636 process_db_write(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
637 {
638 	boolean_t		done = B_FALSE;
639 	int			err = 0;
640 	dlmgmt_link_t		*linkp, *link_in_file, link;
641 	char			buf[MAXLINELEN];
642 
643 	if (req->ls_op == DLMGMT_DB_OP_WRITE) {
644 		/*
645 		 * find the link in the avl tree with the given linkid.
646 		 */
647 		link.ll_linkid = req->ls_linkid;
648 		linkp = avl_find(&dlmgmt_id_avl, &link, NULL);
649 		if (linkp == NULL || (linkp->ll_flags & req->ls_flags) == 0) {
650 			/*
651 			 * This link has already been changed. This could
652 			 * happen if the request is pending because of
653 			 * read-only file-system. If so, we are done.
654 			 */
655 			return (0);
656 		}
657 	}
658 
659 	while (err == 0 && fgets(buf, sizeof (buf), fp) != NULL &&
660 	    process_link_line(buf, &link_in_file)) {
661 		if (link_in_file == NULL || done) {
662 			/*
663 			 * this is a comment line, write it out.
664 			 */
665 			if (fputs(buf, nfp) == EOF)
666 				err = errno;
667 			continue;
668 		}
669 
670 		switch (req->ls_op) {
671 		case DLMGMT_DB_OP_WRITE:
672 			/*
673 			 * For write operations, if the linkid of the link
674 			 * read from the file does not match the id of what
675 			 * req->ll_linkid points to, write out the buffer.
676 			 * Otherwise, generate a new line. If we get to the
677 			 * end and have not seen what req->ll_linkid points
678 			 * to, write it out then.
679 			 */
680 			if (linkp == NULL ||
681 			    linkp->ll_linkid != link_in_file->ll_linkid) {
682 				if (fputs(buf, nfp) == EOF)
683 					err = errno;
684 			} else {
685 				generate_link_line(linkp,
686 				    req->ls_flags == DLMGMT_PERSIST, buf);
687 				if (fputs(buf, nfp) == EOF)
688 					err = errno;
689 				done = B_TRUE;
690 			}
691 			break;
692 		case DLMGMT_DB_OP_DELETE:
693 			/*
694 			 * Delete is simple.  If buf does not represent the
695 			 * link we're deleting, write it out.
696 			 */
697 			if (req->ls_linkid != link_in_file->ll_linkid) {
698 				if (fputs(buf, nfp) == EOF)
699 					err = errno;
700 			} else {
701 				done = B_TRUE;
702 			}
703 			break;
704 		case DLMGMT_DB_OP_READ:
705 		default:
706 			err = EINVAL;
707 			break;
708 		}
709 		link_destroy(link_in_file);
710 	}
711 
712 	/*
713 	 * If we get to the end of the file and have not seen what
714 	 * req->ll_linkid points to, write it out then.
715 	 */
716 	if (req->ls_op == DLMGMT_DB_OP_WRITE && !done) {
717 		generate_link_line(linkp, req->ls_flags == DLMGMT_PERSIST, buf);
718 		done = B_TRUE;
719 		if (fputs(buf, nfp) == EOF)
720 			err = errno;
721 	}
722 
723 	if (!done)
724 		err = ENOENT;
725 
726 	return (err);
727 }
728 
729 /* ARGSUSED1 */
730 static int
731 process_db_read(dlmgmt_db_req_t *req, FILE *fp, FILE *nfp)
732 {
733 	avl_index_t	name_where, id_where;
734 	dlmgmt_link_t	*link_in_file;
735 	dlmgmt_link_t	*linkp1, *linkp2;
736 	char		buf[MAXLINELEN];
737 	int		err = 0;
738 
739 	/*
740 	 * This loop processes each line of the configuration file.
741 	 */
742 	while (fgets(buf, MAXLINELEN, fp) != NULL) {
743 		if (!process_link_line(buf, &link_in_file)) {
744 			err = EINVAL;
745 			break;
746 		}
747 
748 		/*
749 		 * Skip the comment line.
750 		 */
751 		if (link_in_file == NULL)
752 			continue;
753 
754 		linkp1 = avl_find(&dlmgmt_name_avl, link_in_file, &name_where);
755 		linkp2 = avl_find(&dlmgmt_id_avl, link_in_file, &id_where);
756 		if ((linkp1 != NULL) || (linkp2 != NULL)) {
757 			/*
758 			 * If any of the following conditions are met, this is
759 			 * a duplicate entry:
760 			 *
761 			 * 1. link2 (with the given name) and link2 (with the
762 			 *    given id) are not the same link;
763 			 * 2. This is a persistent req and find the link with
764 			 *    the given name and id. Note that persistent db
765 			 *    is read before the active one.
766 			 * 3. Found the link with the given name and id but
767 			 *    the link is already active.
768 			 */
769 			if ((linkp1 != linkp2) ||
770 			    (req->ls_flags == DLMGMT_PERSIST) ||
771 			    ((linkp1->ll_flags & DLMGMT_ACTIVE) != 0)) {
772 				dlmgmt_log(LOG_WARNING, "Duplicate link "
773 				    "entries in repository:  link name %s "
774 				    "link id %i", link_in_file->ll_link,
775 				    link_in_file->ll_linkid);
776 			} else {
777 				linkp1->ll_flags |= DLMGMT_ACTIVE;
778 			}
779 			link_destroy(link_in_file);
780 		} else {
781 			avl_insert(&dlmgmt_name_avl, link_in_file, name_where);
782 			avl_insert(&dlmgmt_id_avl, link_in_file, id_where);
783 			dlmgmt_advance(link_in_file);
784 			link_in_file->ll_flags |= req->ls_flags;
785 		}
786 	}
787 
788 	return (err);
789 }
790 
791 /*
792  * Generate an entry in the link database.
793  * Each entry has this format:
794  * <link id>	<prop0>=<type>,<val>;...;<propn>=<type>,<val>;
795  */
796 static void
797 generate_link_line(dlmgmt_link_t *linkp, boolean_t persist, char *buf)
798 {
799 	char			tmpbuf[MAXLINELEN];
800 	char			*ptr;
801 	char			*lim = tmpbuf + MAXLINELEN;
802 	char			*name_to_write = NULL;
803 	datalink_id_t		id_to_write;
804 	dlmgmt_linkattr_t	*cur_p = NULL;
805 	uint64_t		u64;
806 
807 	ptr = tmpbuf;
808 	id_to_write = linkp->ll_linkid;
809 	ptr += snprintf(ptr, BUFLEN(lim, ptr), "%d\t", id_to_write);
810 	name_to_write = linkp->ll_link;
811 	ptr += write_str(ptr, BUFLEN(lim, ptr), "name", name_to_write);
812 	u64 = linkp->ll_class;
813 	ptr += write_uint64(ptr, BUFLEN(lim, ptr), "class", &u64);
814 	u64 = linkp->ll_media;
815 	ptr += write_uint64(ptr, BUFLEN(lim, ptr), "media", &u64);
816 
817 	/*
818 	 * The daemon does not keep any active link attribute. If this request
819 	 * is for active configuration, we are done.
820 	 */
821 	if (!persist)
822 		goto done;
823 
824 	for (cur_p = linkp->ll_head; cur_p != NULL; cur_p = cur_p->lp_next) {
825 		ptr += translators[cur_p->lp_type].write_func(ptr,
826 		    BUFLEN(lim, ptr), cur_p->lp_name, cur_p->lp_val);
827 	}
828 done:
829 	if (ptr > lim)
830 		return;
831 	(void) snprintf(buf, MAXLINELEN, "%s\n", tmpbuf);
832 }
833 
834 int
835 dlmgmt_delete_db_entry(datalink_id_t linkid, uint32_t flags)
836 {
837 	return (dlmgmt_db_update(DLMGMT_DB_OP_DELETE, linkid, flags));
838 }
839 
840 int
841 dlmgmt_write_db_entry(datalink_id_t linkid, uint32_t flags)
842 {
843 	int		err;
844 
845 	if (flags & DLMGMT_PERSIST) {
846 		if ((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE,
847 		    linkid, DLMGMT_PERSIST)) != 0) {
848 			return (err);
849 		}
850 	}
851 
852 	if (flags & DLMGMT_ACTIVE) {
853 		if (((err = dlmgmt_db_update(DLMGMT_DB_OP_WRITE,
854 		    linkid, DLMGMT_ACTIVE)) != 0) &&
855 		    (flags & DLMGMT_PERSIST)) {
856 			(void) dlmgmt_db_update(DLMGMT_DB_OP_DELETE,
857 			    linkid, DLMGMT_PERSIST);
858 			return (err);
859 		}
860 	}
861 
862 	return (0);
863 }
864 
865 /*
866  * Initialize the datalink <link name, linkid> mapping and the link's
867  * attributes list based on the configuration file /etc/dladm/datalink.conf
868  * and the active configuration cache file
869  * /etc/svc/volatile/datalink-management:default.cache.
870  *
871  * This function is called when the datalink-management service is started
872  * during reboot, and when the dlmgmtd daemon is restarted.
873  */
874 int
875 dlmgmt_db_init()
876 {
877 	char		filename[MAXPATHLEN];
878 	dlmgmt_db_req_t	req;
879 	int		err;
880 	dlmgmt_link_t	*linkp;
881 	char		*fmri, *c;
882 
883 	/*
884 	 * First derive the name of the cache file from the FMRI name. This
885 	 * cache name is used to keep active datalink configuration.
886 	 */
887 	if (debug) {
888 		(void) snprintf(cachefile, MAXPATHLEN, "%s/%s%s",
889 		    DLMGMT_TEMP_DB_DIR, progname, ".debug.cache");
890 	} else {
891 		if ((fmri = getenv("SMF_FMRI")) == NULL) {
892 			dlmgmt_log(LOG_WARNING, "dlmgmtd is an smf(5) managed "
893 			    "service and should not be run from the command "
894 			    "line.");
895 			return (EINVAL);
896 		}
897 
898 		/*
899 		 * The FMRI name is in the form of
900 		 * svc:/service/service:instance.  We need to remove the
901 		 * prefix "svc:/" and replace '/' with '-'.  The cache file
902 		 * name is in the form of "service:instance.cache".
903 		 */
904 		if ((c = strchr(fmri, '/')) != NULL)
905 			c++;
906 		else
907 			c = fmri;
908 		(void) snprintf(filename, MAXPATHLEN, "%s.cache", c);
909 		for (c = filename; *c != '\0'; c++) {
910 			if (*c == '/')
911 				*c = '-';
912 		}
913 
914 		(void) snprintf(cachefile, MAXPATHLEN, "%s/%s",
915 		    DLMGMT_TEMP_DB_DIR, filename);
916 	}
917 
918 	dlmgmt_table_lock(B_TRUE);
919 
920 	req.ls_next = NULL;
921 	req.ls_op = DLMGMT_DB_OP_READ;
922 	req.ls_linkid = DATALINK_INVALID_LINKID;
923 	req.ls_flags = DLMGMT_PERSIST;
924 
925 	if ((err = dlmgmt_process_db_req(&req)) != 0)
926 		goto done;
927 
928 	req.ls_flags = DLMGMT_ACTIVE;
929 	err = dlmgmt_process_db_req(&req);
930 	if (err == ENOENT) {
931 		/*
932 		 * The temporary datalink.conf does not exist. This is
933 		 * the first boot. Mark all the physical links active.
934 		 */
935 		for (linkp = avl_first(&dlmgmt_id_avl); linkp != NULL;
936 		    linkp = AVL_NEXT(&dlmgmt_id_avl, linkp)) {
937 			if (linkp->ll_class == DATALINK_CLASS_PHYS) {
938 				linkp->ll_flags |= DLMGMT_ACTIVE;
939 				(void) dlmgmt_write_db_entry(
940 				    linkp->ll_linkid, DLMGMT_ACTIVE);
941 			}
942 		}
943 		err = 0;
944 	}
945 
946 done:
947 	dlmgmt_table_unlock();
948 	return (err);
949 }
950