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