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