xref: /titanic_44/usr/src/cmd/cmd-inet/lib/ipmgmtd/ipmgmt_persist.c (revision 327151705b7439cb7ab35c370f682cac7ef9523a)
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) 2010, Oracle and/or its affiliates. All rights reserved.
24  */
25 
26 /*
27  * Contains DB walker functions, which are of type `db_wfunc_t';
28  *
29  * typedef boolean_t db_wfunc_t(void *cbarg, nvlist_t *db_nvl, char *buf,
30  *				size_t bufsize, int *errp);
31  *
32  * ipadm_rw_db() walks through the data store, one line at a time and calls
33  * these call back functions with:
34  *	`cbarg'  - callback argument
35  *	`db_nvl' - representing a line from DB in nvlist_t form
36  *	`buf'	 - character buffer to hold modified line
37  *	`bufsize'- size of the buffer
38  *	`errp' - captures any error inside the walker function.
39  *
40  * All the 'write' callback functions modify `db_nvl' based on `cbarg' and
41  * copy string representation of `db_nvl' (using ipadm_nvlist2str()) into `buf'.
42  * To delete a line from the DB, buf[0] is set to `\0'. Inside ipadm_rw_db(),
43  * the modified `buf' is written back into DB.
44  *
45  * All the 'read' callback functions, retrieve the information from the DB, by
46  * reading `db_nvl' and then populate the `cbarg'.
47  */
48 
49 #include <stdlib.h>
50 #include <strings.h>
51 #include <errno.h>
52 #include <assert.h>
53 #include <sys/types.h>
54 #include <sys/socket.h>
55 #include <netinet/in.h>
56 #include <arpa/inet.h>
57 #include <unistd.h>
58 #include "ipmgmt_impl.h"
59 #include <libscf.h>
60 
61 #define	ATYPE	"_atype"		/* name of the address type nvpair */
62 #define	FLAGS	"_flags"		/* name of the flags nvpair */
63 
64 /*
65  * flag used by ipmgmt_persist_aobjmap() to indicate address type is
66  * IPADM_ADDR_IPV6_ADDRCONF.
67  */
68 #define	IPMGMT_ATYPE_V6ACONF	0x1
69 
70 extern pthread_rwlock_t ipmgmt_dbconf_lock;
71 
72 /*
73  * Checks if the database nvl, `db_nvl' contains and matches ALL of the passed
74  * in private nvpairs `proto', `ifname' & `aobjname'.
75  */
76 static boolean_t
77 ipmgmt_nvlist_match(nvlist_t *db_nvl, const char *proto, const char *ifname,
78     const char *aobjname)
79 {
80 	char		*db_proto = NULL, *db_ifname = NULL;
81 	char		*db_aobjname = NULL;
82 	nvpair_t	*nvp;
83 	char		*name;
84 
85 	/* walk through db_nvl and retrieve all its private nvpairs */
86 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
87 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
88 		name = nvpair_name(nvp);
89 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
90 			(void) nvpair_value_string(nvp, &db_proto);
91 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
92 			(void) nvpair_value_string(nvp, &db_ifname);
93 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
94 			(void) nvpair_value_string(nvp, &db_aobjname);
95 	}
96 
97 	if (proto != NULL && proto[0] == '\0')
98 		proto = NULL;
99 	if (ifname != NULL && ifname[0] == '\0')
100 		ifname = NULL;
101 	if (aobjname != NULL && aobjname[0] == '\0')
102 		aobjname = NULL;
103 
104 	if ((proto == NULL && db_proto != NULL) ||
105 	    (proto != NULL && db_proto == NULL) ||
106 	    strcmp(proto, db_proto) != 0) {
107 		/* no intersection - different protocols. */
108 		return (B_FALSE);
109 	}
110 	if ((ifname == NULL && db_ifname != NULL) ||
111 	    (ifname != NULL && db_ifname == NULL) ||
112 	    strcmp(ifname, db_ifname) != 0) {
113 		/* no intersection - different interfaces. */
114 		return (B_FALSE);
115 	}
116 	if ((aobjname == NULL && db_aobjname != NULL) ||
117 	    (aobjname != NULL && db_aobjname == NULL) ||
118 	    strcmp(aobjname, db_aobjname) != 0) {
119 		/* no intersection - different address objects */
120 		return (B_FALSE);
121 	}
122 
123 	return (B_TRUE);
124 }
125 
126 /*
127  * Checks if the database nvl, `db_nvl' and the input nvl, `in_nvl' intersects.
128  */
129 static boolean_t
130 ipmgmt_nvlist_intersects(nvlist_t *db_nvl, nvlist_t *in_nvl)
131 {
132 	nvpair_t	*nvp;
133 	char		*name;
134 	char		*proto = NULL, *ifname = NULL, *aobjname = NULL;
135 
136 	/* walk through in_nvl and retrieve all its private nvpairs */
137 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
138 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
139 		name = nvpair_name(nvp);
140 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
141 			(void) nvpair_value_string(nvp, &proto);
142 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
143 			(void) nvpair_value_string(nvp, &ifname);
144 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
145 			(void) nvpair_value_string(nvp, &aobjname);
146 	}
147 
148 	return (ipmgmt_nvlist_match(db_nvl, proto, ifname, aobjname));
149 }
150 
151 /*
152  * Checks if the database nvl, `db_nvl', contains and matches ANY of the passed
153  * in private nvpairs `proto', `ifname' & `aobjname'.
154  */
155 static boolean_t
156 ipmgmt_nvlist_contains(nvlist_t *db_nvl, const char *proto,
157     const char *ifname, char *aobjname)
158 {
159 	char		*db_ifname = NULL, *db_proto = NULL;
160 	char		*db_aobjname = NULL;
161 	nvpair_t	*nvp;
162 	char		*name;
163 
164 	/* walk through db_nvl and retrieve all private nvpairs */
165 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
166 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
167 		name = nvpair_name(nvp);
168 		if (strcmp(IPADM_NVP_PROTONAME, name) == 0)
169 			(void) nvpair_value_string(nvp, &db_proto);
170 		else if (strcmp(IPADM_NVP_IFNAME, name) == 0)
171 			(void) nvpair_value_string(nvp, &db_ifname);
172 		else if (strcmp(IPADM_NVP_AOBJNAME, name) == 0)
173 			(void) nvpair_value_string(nvp, &db_aobjname);
174 	}
175 
176 	if (proto != NULL && proto[0] != '\0') {
177 		if ((db_proto == NULL || strcmp(proto, db_proto) != 0))
178 			return (B_FALSE);
179 	}
180 	if (ifname != NULL && ifname[0] != '\0') {
181 		if ((db_ifname == NULL || strcmp(ifname, db_ifname) != 0))
182 			return (B_FALSE);
183 	}
184 	if (aobjname != NULL && aobjname[0] != '\0') {
185 		if ((db_aobjname == NULL || strcmp(aobjname, db_aobjname) != 0))
186 			return (B_FALSE);
187 	}
188 
189 	return (B_TRUE);
190 }
191 
192 /*
193  * Retrieves the property value from the DB. The property whose value is to be
194  * retrieved is in `pargp->ia_pname'.
195  */
196 /* ARGSUSED */
197 boolean_t
198 ipmgmt_db_getprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
199     int *errp)
200 {
201 	ipmgmt_prop_arg_t	*pargp = arg;
202 	boolean_t		cont = B_TRUE;
203 	char			*pval;
204 	int			err = 0;
205 
206 	*errp = 0;
207 
208 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
209 	    pargp->ia_ifname, pargp->ia_aobjname))
210 		return (B_TRUE);
211 
212 	if ((err = nvlist_lookup_string(db_nvl, pargp->ia_pname,
213 	    &pval)) == 0) {
214 		(void) strlcpy(pargp->ia_pval, pval, sizeof (pargp->ia_pval));
215 		/*
216 		 * We have retrieved what we are looking for.
217 		 * Stop the walker.
218 		 */
219 		cont = B_FALSE;
220 	} else {
221 		if (err == ENOENT)
222 			err = 0;
223 		*errp = err;
224 	}
225 
226 	return (cont);
227 }
228 
229 /*
230  * Removes the property value from the DB. The property whose value is to be
231  * removed is in `pargp->ia_pname'.
232  */
233 /* ARGSUSED */
234 boolean_t
235 ipmgmt_db_resetprop(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
236     int *errp)
237 {
238 	ipmgmt_prop_arg_t	*pargp = arg;
239 
240 	*errp = 0;
241 	if (!ipmgmt_nvlist_match(db_nvl, pargp->ia_module,
242 	    pargp->ia_ifname, pargp->ia_aobjname))
243 		return (B_TRUE);
244 
245 	if (!nvlist_exists(db_nvl, pargp->ia_pname))
246 		return (B_TRUE);
247 
248 	/*
249 	 * We found the property in the DB. If IPMGMT_REMOVE is not set then
250 	 * delete the entry from the db. If it is set, then the property is a
251 	 * multi-valued property so just remove the specified values from DB.
252 	 */
253 	if (pargp->ia_flags & IPMGMT_REMOVE) {
254 		char	*dbpval = NULL;
255 		char	*inpval = pargp->ia_pval;
256 		char	pval[MAXPROPVALLEN];
257 		char	*val, *lasts;
258 
259 		*errp = nvlist_lookup_string(db_nvl, pargp->ia_pname, &dbpval);
260 		if (*errp != 0)
261 			return (B_FALSE);
262 
263 		/*
264 		 * multi-valued properties are represented as comma separated
265 		 * values. Use string tokenizer functions to split them and
266 		 * search for the value to be removed.
267 		 */
268 		bzero(pval, sizeof (pval));
269 		if ((val = strtok_r(dbpval, ",", &lasts)) != NULL) {
270 			if (strcmp(val, inpval) != 0)
271 				(void) strlcat(pval, val, MAXPROPVALLEN);
272 			while ((val = strtok_r(NULL, ",", &lasts)) != NULL) {
273 				if (strcmp(val, inpval) != 0) {
274 					if (pval[0] != '\0')
275 						(void) strlcat(pval, ",",
276 						    MAXPROPVALLEN);
277 					(void) strlcat(pval, val,
278 					    MAXPROPVALLEN);
279 				}
280 			}
281 		} else {
282 			if (strcmp(dbpval, inpval) != 0)
283 				*errp = ENOENT;
284 			else
285 				buf[0] =  '\0';
286 			return (B_FALSE);
287 		}
288 		*errp = nvlist_add_string(db_nvl, pargp->ia_pname, pval);
289 		if (*errp != 0)
290 			return (B_FALSE);
291 
292 		(void) memset(buf, 0, buflen);
293 		if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
294 			/* buffer overflow */
295 			*errp = ENOBUFS;
296 		}
297 	} else {
298 		buf[0] = '\0';
299 	}
300 
301 	/* stop the search */
302 	return (B_FALSE);
303 }
304 
305 /*
306  * Input arguments can have IPADM_NVP_AOBJNAME or IPADM_NVP_IFNAME. A match is
307  * found, when one of the following occurs first.
308  * - the input aobjname matches the db aobjname. Return the db address.
309  * - the input interface matches the db interface. Return all the
310  *   matching db lines with addresses.
311  */
312 /* ARGSUSED */
313 boolean_t
314 ipmgmt_db_getaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
315     int *errp)
316 {
317 	ipmgmt_getaddr_cbarg_t	*cbarg = arg;
318 	char		*db_aobjname = NULL;
319 	char		*db_ifname = NULL;
320 	nvlist_t	*db_addr = NULL;
321 	char		name[IPMGMT_STRSIZE];
322 	nvpair_t	*nvp;
323 	boolean_t	add_nvl = B_FALSE;
324 
325 	/* Parse db nvlist */
326 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
327 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
328 		if (nvpair_type(nvp) == DATA_TYPE_NVLIST)
329 			(void) nvpair_value_nvlist(nvp, &db_addr);
330 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_IFNAME) == 0)
331 			(void) nvpair_value_string(nvp, &db_ifname);
332 		else if (strcmp(nvpair_name(nvp), IPADM_NVP_AOBJNAME) == 0)
333 			(void) nvpair_value_string(nvp, &db_aobjname);
334 	}
335 
336 	if (db_aobjname == NULL) /* Not an address */
337 		return (B_TRUE);
338 
339 	/* Check for a match between the aobjnames or the interface name */
340 	if (cbarg->cb_aobjname[0] != '\0') {
341 		if (strcmp(cbarg->cb_aobjname, db_aobjname) == 0)
342 			add_nvl = B_TRUE;
343 	} else if (cbarg->cb_ifname[0] != '\0') {
344 		if (strcmp(cbarg->cb_ifname, db_ifname) == 0)
345 			add_nvl = B_TRUE;
346 	} else {
347 		add_nvl = B_TRUE;
348 	}
349 
350 	if (add_nvl) {
351 		(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
352 		    cbarg->cb_ocnt);
353 		*errp = nvlist_add_nvlist(cbarg->cb_onvl, name, db_nvl);
354 		if (*errp == 0)
355 			cbarg->cb_ocnt++;
356 	}
357 	return (B_TRUE);
358 }
359 
360 /*
361  * This function takes the appropriate lock, read or write, based on the
362  * `db_op' and then calls DB walker ipadm_rw_db().
363  */
364 extern int
365 ipmgmt_db_walk(db_wfunc_t *db_walk_func, void *db_warg, ipadm_db_op_t db_op)
366 {
367 	int		err;
368 	boolean_t	writeop;
369 	mode_t		mode;
370 
371 	writeop = (db_op != IPADM_DB_READ);
372 
373 	if (writeop) {
374 		(void) pthread_rwlock_wrlock(&ipmgmt_dbconf_lock);
375 		mode = IPADM_FILE_MODE;
376 	} else {
377 		(void) pthread_rwlock_rdlock(&ipmgmt_dbconf_lock);
378 		mode = 0;
379 	}
380 
381 	err = ipadm_rw_db(db_walk_func, db_warg, IPADM_DB_FILE, mode, db_op);
382 	(void) pthread_rwlock_unlock(&ipmgmt_dbconf_lock);
383 	return (err);
384 }
385 
386 /*
387  * Used to add an entry towards the end of DB. It just returns B_TRUE for
388  * every line of the DB. When we reach the end, ipadm_rw_db() adds the
389  * line at the end.
390  */
391 /* ARGSUSED */
392 boolean_t
393 ipmgmt_db_add(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen, int *errp)
394 {
395 	return (B_TRUE);
396 }
397 
398 /*
399  * This function is used to update or create an entry in DB. The nvlist_t,
400  * `in_nvl', represents the line we are looking for. Once we ensure the right
401  * line from DB, we update that entry.
402  */
403 boolean_t
404 ipmgmt_db_update(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
405     int *errp)
406 {
407 	ipadm_dbwrite_cbarg_t	*cb = arg;
408 	uint_t			flags = cb->dbw_flags;
409 	nvlist_t		*in_nvl = cb->dbw_nvl;
410 	nvpair_t		*nvp;
411 	char			*name, *instrval = NULL, *dbstrval = NULL;
412 	char			pval[MAXPROPVALLEN];
413 
414 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
415 		return (B_TRUE);
416 
417 	for (nvp = nvlist_next_nvpair(in_nvl, NULL); nvp != NULL;
418 	    nvp = nvlist_next_nvpair(in_nvl, nvp)) {
419 		name = nvpair_name(nvp);
420 		if (!IPADM_PRIV_NVP(name) && nvlist_exists(db_nvl, name))
421 			break;
422 	}
423 
424 	if (nvp == NULL)
425 		return (B_TRUE);
426 
427 	assert(nvpair_type(nvp) == DATA_TYPE_STRING);
428 
429 	if ((*errp = nvpair_value_string(nvp, &instrval)) != 0)
430 		return (B_FALSE);
431 
432 	/*
433 	 * If IPMGMT_APPEND is set then we are dealing with multi-valued
434 	 * properties. We append to the entry from the db, with the new value.
435 	 */
436 	if (flags & IPMGMT_APPEND) {
437 		if ((*errp = nvlist_lookup_string(db_nvl, name,
438 		    &dbstrval)) != 0)
439 			return (B_FALSE);
440 		(void) snprintf(pval, MAXPROPVALLEN, "%s,%s", dbstrval,
441 		    instrval);
442 		if ((*errp = nvlist_add_string(db_nvl, name, pval)) != 0)
443 			return (B_FALSE);
444 	} else {
445 		/* case	of in-line update of a db entry */
446 		if ((*errp = nvlist_add_string(db_nvl, name, instrval)) != 0)
447 			return (B_FALSE);
448 	}
449 
450 	(void) memset(buf, 0, buflen);
451 	if (ipadm_nvlist2str(db_nvl, buf, buflen) == 0) {
452 		/* buffer overflow */
453 		*errp = ENOBUFS;
454 	}
455 	*errp = 0;
456 
457 	/* we updated the DB entry, so do not continue */
458 	return (B_FALSE);
459 }
460 
461 /*
462  * For the given `cbarg->cb_ifname' interface, retrieves any persistent
463  * interface information (used in 'ipadm show-if')
464  */
465 /* ARGSUSED */
466 boolean_t
467 ipmgmt_db_getif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
468     int *errp)
469 {
470 	ipmgmt_getif_cbarg_t	*cbarg = arg;
471 	char			*ifname = cbarg->cb_ifname;
472 	char			*intf = NULL;
473 	ipadm_if_info_t		*ifp = NULL;
474 	sa_family_t		af;
475 	char			*afstr;
476 
477 	*errp = 0;
478 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) != 0 ||
479 	    nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &intf) != 0 ||
480 	    (ifname[0] != '\0' && strcmp(ifname, intf) != 0)) {
481 		return (B_TRUE);
482 	}
483 	af = atoi(afstr);
484 	for (ifp = cbarg->cb_ifinfo; ifp != NULL; ifp = ifp->ifi_next) {
485 		if (strcmp(ifp->ifi_name, intf) == 0)
486 			break;
487 	}
488 	if (ifp == NULL) {
489 		ipadm_if_info_t *new;
490 
491 		if ((new = calloc(1, sizeof (*new))) == NULL) {
492 			*errp = ENOMEM;
493 			return (B_FALSE); /* don't continue the walk */
494 		}
495 		new->ifi_next = cbarg->cb_ifinfo;
496 		cbarg->cb_ifinfo = new;
497 		ifp = new;
498 		(void) strlcpy(ifp->ifi_name, intf, sizeof (ifp->ifi_name));
499 	}
500 
501 	if (af == AF_INET) {
502 		ifp->ifi_pflags |= IFIF_IPV4;
503 	} else {
504 		assert(af == AF_INET6);
505 		ifp->ifi_pflags |= IFIF_IPV6;
506 	}
507 
508 	/* Terminate the walk if we found both v4 and v6 interfaces. */
509 	if (ifname[0] != '\0' && (ifp->ifi_pflags & IFIF_IPV4) &&
510 	    (ifp->ifi_pflags & IFIF_IPV6))
511 		return (B_FALSE);
512 
513 	return (B_TRUE);
514 }
515 
516 /*
517  * Deletes those entries from the database for which interface name
518  * matches with the given `cbarg->cb_ifname'
519  */
520 /* ARGSUSED */
521 boolean_t
522 ipmgmt_db_resetif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
523     int *errp)
524 {
525 	ipmgmt_if_cbarg_t *cbarg = arg;
526 	boolean_t	isv6 = (cbarg->cb_family == AF_INET6);
527 	char		*ifname = cbarg->cb_ifname;
528 	char		*modstr = NULL;
529 	char		*afstr;
530 	char		*aobjname;
531 	uint_t		proto;
532 	ipmgmt_aobjmap_t *head;
533 	boolean_t	aobjfound = B_FALSE;
534 
535 	*errp = 0;
536 
537 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, ifname, NULL))
538 		return (B_TRUE);
539 
540 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_FAMILY, &afstr) == 0) {
541 		if (atoi(afstr) == cbarg->cb_family)
542 			goto delete;
543 		return (B_TRUE);
544 	}
545 
546 	/* Reset all the interface configurations for 'ifname' */
547 	if (isv6 && (nvlist_exists(db_nvl, IPADM_NVP_IPV6ADDR) ||
548 	    nvlist_exists(db_nvl, IPADM_NVP_INTFID))) {
549 		goto delete;
550 	}
551 	if (!isv6 &&
552 	    (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
553 	    nvlist_exists(db_nvl, IPADM_NVP_DHCP))) {
554 		goto delete;
555 	}
556 
557 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_AOBJNAME, &aobjname) == 0) {
558 		/*
559 		 * This must be an address property. Delete this
560 		 * line if there is a match in the address family.
561 		 */
562 		head = aobjmap.aobjmap_head;
563 		while (head != NULL) {
564 			if (strcmp(head->am_aobjname, aobjname) == 0) {
565 				aobjfound = B_TRUE;
566 				if (head->am_family == cbarg->cb_family)
567 					goto delete;
568 			}
569 			head = head->am_next;
570 		}
571 		/*
572 		 * If aobjfound = B_FALSE, then this address is not
573 		 * available in active configuration. We should go ahead
574 		 * and delete it.
575 		 */
576 		if (!aobjfound)
577 			goto delete;
578 	}
579 
580 	/*
581 	 * If we are removing both v4 and v6 interface, then we get rid of
582 	 * all the properties for that interface. On the other hand, if we
583 	 * are deleting only v4 instance of an interface, then we delete v4
584 	 * properties only.
585 	 */
586 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME, &modstr) == 0) {
587 		proto = ipadm_str2proto(modstr);
588 		switch (proto) {
589 		case MOD_PROTO_IPV6:
590 			if (isv6)
591 				goto delete;
592 			break;
593 		case MOD_PROTO_IPV4:
594 			if (!isv6)
595 				goto delete;
596 			break;
597 		case MOD_PROTO_IP:
598 			/* this should never be the case, today */
599 			assert(0);
600 			break;
601 		}
602 	}
603 	/* Not found a match yet. Continue processing the db */
604 	return (B_TRUE);
605 delete:
606 	/* delete the line from the db */
607 	buf[0] = '\0';
608 	return (B_TRUE);
609 }
610 
611 /*
612  * Deletes those entries from the database for which address object name
613  * matches with the given `cbarg->cb_aobjname'
614  */
615 /* ARGSUSED */
616 boolean_t
617 ipmgmt_db_resetaddr(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
618     int *errp)
619 {
620 	ipmgmt_resetaddr_cbarg_t *cbarg = arg;
621 	char		*aobjname = cbarg->cb_aobjname;
622 
623 	*errp = 0;
624 	if (!ipmgmt_nvlist_contains(db_nvl, NULL, NULL, aobjname))
625 		return (B_TRUE);
626 
627 	/* delete the line from the db */
628 	buf[0] = '\0';
629 	return (B_TRUE);
630 }
631 
632 /*
633  * Retrieves all interface props, including addresses, for given interface(s).
634  * `invl' contains the list of interfaces, for which information need to be
635  * retrieved.
636  */
637 /* ARGSUSED */
638 boolean_t
639 ipmgmt_db_initif(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
640     int *errp)
641 {
642 	ipmgmt_initif_cbarg_t	*cbarg = arg;
643 	nvlist_t		*onvl = cbarg->cb_onvl;
644 	nvlist_t		*invl = cbarg->cb_invl;
645 	sa_family_t		in_af = cbarg->cb_family;
646 	char			*db_ifname;
647 
648 	*errp = 0;
649 	if (nvlist_lookup_string(db_nvl, IPADM_NVP_IFNAME, &db_ifname) == 0 &&
650 	    nvlist_exists(invl, db_ifname)) {
651 		char		name[IPMGMT_STRSIZE];
652 		sa_family_t	db_af = in_af;
653 		uint_t		proto;
654 		char		*pstr;
655 
656 		if (in_af != AF_UNSPEC) {
657 			if (nvlist_lookup_string(db_nvl, IPADM_NVP_PROTONAME,
658 			    &pstr) == 0) {
659 				proto = ipadm_str2proto(pstr);
660 				if (proto == MOD_PROTO_IPV4)
661 					db_af = AF_INET;
662 				else if (proto == MOD_PROTO_IPV6)
663 					db_af = AF_INET6;
664 				else
665 					db_af = in_af;
666 			} else {
667 				if (nvlist_exists(db_nvl, IPADM_NVP_IPV4ADDR) ||
668 				    nvlist_exists(db_nvl, IPADM_NVP_DHCP))
669 					db_af = AF_INET;
670 				else
671 					db_af = AF_INET6;
672 			}
673 		}
674 		if (in_af == db_af) {
675 			(void) snprintf(name, sizeof (name), "%s_%d", db_ifname,
676 			    cbarg->cb_ocnt);
677 			*errp = nvlist_add_nvlist(onvl, name, db_nvl);
678 			if (*errp == 0)
679 				cbarg->cb_ocnt++;
680 		}
681 	}
682 	return (B_TRUE);
683 }
684 
685 /*
686  * helper function for ipmgmt_aobjmap_op(). Adds the node pointed by `nodep'
687  * into `aobjmap' structure.
688  */
689 static int
690 i_ipmgmt_add_amnode(ipmgmt_aobjmap_t *nodep)
691 {
692 	ipmgmt_aobjmap_t	*new, *head;
693 
694 	head = aobjmap.aobjmap_head;
695 	if ((new = malloc(sizeof (ipmgmt_aobjmap_t))) == NULL)
696 		return (ENOMEM);
697 	*new = *nodep;
698 	new->am_next = NULL;
699 
700 	/* Add the node at the beginning of the list */
701 	if (head == NULL) {
702 		aobjmap.aobjmap_head = new;
703 	} else {
704 		new->am_next = aobjmap.aobjmap_head;
705 		aobjmap.aobjmap_head = new;
706 	}
707 	return (0);
708 }
709 
710 /*
711  * A recursive function to generate alphabetized number given a decimal number.
712  * Decimal 0 to 25 maps to 'a' to 'z' and then the counting continues with 'aa',
713  * 'ab', 'ac', et al.
714  */
715 static void
716 i_ipmgmt_num2priv_aobjname(uint32_t num, char **cp, char *endp)
717 {
718 	if (num >= 26)
719 		i_ipmgmt_num2priv_aobjname(num / 26 - 1, cp, endp);
720 	if (*cp != endp) {
721 		*cp[0] = 'a' + (num % 26);
722 		(*cp)++;
723 	}
724 }
725 
726 /*
727  * This function generates an `aobjname', when required, and then does
728  * lookup-add. If `nodep->am_aobjname' is not an empty string, then it walks
729  * through the `aobjmap' to check if an address object with the same
730  * `nodep->am_aobjname' exists. If it exists, EEXIST is returned as duplicate
731  * `aobjname's are not allowed.
732  *
733  * If `nodep->am_aobjname' is an empty string then the daemon generates an
734  * `aobjname' using the `am_nextnum', which contains the next number to be
735  * used to generate `aobjname'. `am_nextnum' is converted to base26 using
736  * `a-z' alphabets in i_ipmgmt_num2priv_aobjname().
737  *
738  * `am_nextnum' will be 0 to begin with. Every time an address object that
739  * needs `aobjname' is added it's incremented by 1. So for the first address
740  * object on net0 the `am_aobjname' will be net0/_a and `am_nextnum' will be 1.
741  * For the second address object on that interface `am_aobjname' will be net0/_b
742  * and  `am_nextnum' will incremented to 2.
743  */
744 static int
745 i_ipmgmt_lookupadd_amnode(ipmgmt_aobjmap_t *nodep)
746 {
747 	ipmgmt_aobjmap_t	*head;
748 	uint32_t		nextnum;
749 
750 	for (head = aobjmap.aobjmap_head; head != NULL; head = head->am_next)
751 		if (strcmp(head->am_ifname, nodep->am_ifname) == 0)
752 			break;
753 	nextnum = (head == NULL ? 0 : head->am_nextnum);
754 
755 	/*
756 	 * if `aobjname' is empty, then the daemon has to generate the
757 	 * next `aobjname' for the given interface and family.
758 	 */
759 	if (nodep->am_aobjname[0] == '\0') {
760 		char tmpstr[IPADM_AOBJ_USTRSIZ - 1];  /* 1 for leading  '_' */
761 		char *cp = tmpstr;
762 		char *endp = tmpstr + sizeof (tmpstr);
763 
764 		i_ipmgmt_num2priv_aobjname(nextnum, &cp, endp);
765 
766 		if (cp == endp)
767 			return (EINVAL);
768 		cp[0] = '\0';
769 
770 		if (snprintf(nodep->am_aobjname, IPADM_AOBJSIZ, "%s/_%s",
771 		    nodep->am_ifname, tmpstr) >= IPADM_AOBJSIZ) {
772 			return (EINVAL);
773 		}
774 		nodep->am_nextnum = ++nextnum;
775 	} else {
776 		for (head = aobjmap.aobjmap_head; head != NULL;
777 		    head = head->am_next) {
778 			if (strcmp(head->am_aobjname, nodep->am_aobjname) == 0)
779 				return (EEXIST);
780 		}
781 		nodep->am_nextnum = nextnum;
782 	}
783 	return (i_ipmgmt_add_amnode(nodep));
784 }
785 
786 /*
787  * Performs following operations on the global `aobjmap' linked list.
788  * (a) ADDROBJ_ADD: add or update address object in `aobjmap'
789  * (b) ADDROBJ_DELETE: delete address object from `aobjmap'
790  * (c) ADDROBJ_LOOKUPADD: place a stub address object in `aobjmap'
791  * (d) ADDROBJ_SETLIFNUM: Sets the lifnum for an address object in `aobjmap'
792  */
793 int
794 ipmgmt_aobjmap_op(ipmgmt_aobjmap_t *nodep, uint32_t op)
795 {
796 	ipmgmt_aobjmap_t	*head, *prev, *matched = NULL;
797 	boolean_t		update = B_TRUE;
798 	int			err = 0;
799 	ipadm_db_op_t		db_op;
800 
801 	(void) pthread_rwlock_wrlock(&aobjmap.aobjmap_rwlock);
802 
803 	head = aobjmap.aobjmap_head;
804 	switch (op) {
805 	case ADDROBJ_ADD:
806 		/*
807 		 * check for stub nodes (added by ADDROBJ_LOOKUPADD) and
808 		 * update, else add the new node.
809 		 */
810 		for (; head != NULL; head = head->am_next) {
811 			/*
812 			 * For IPv6, we need to distinguish between the
813 			 * linklocal and non-linklocal nodes
814 			 */
815 			if (strcmp(head->am_aobjname,
816 			    nodep->am_aobjname) == 0 &&
817 			    (head->am_atype != IPADM_ADDR_IPV6_ADDRCONF ||
818 			    head->am_linklocal == nodep->am_linklocal))
819 				break;
820 		}
821 
822 		if (head != NULL) {
823 			/* update the node */
824 			(void) strlcpy(head->am_ifname, nodep->am_ifname,
825 			    sizeof (head->am_ifname));
826 			head->am_lnum = nodep->am_lnum;
827 			head->am_family = nodep->am_family;
828 			head->am_flags = nodep->am_flags;
829 			head->am_atype = nodep->am_atype;
830 			if (head->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
831 				head->am_ifid = nodep->am_ifid;
832 				head->am_linklocal = nodep->am_linklocal;
833 			}
834 		} else {
835 			for (head = aobjmap.aobjmap_head; head != NULL;
836 			    head = head->am_next) {
837 				if (strcmp(head->am_ifname,
838 				    nodep->am_ifname) == 0)
839 					break;
840 			}
841 			nodep->am_nextnum = (head == NULL ? 0 :
842 			    head->am_nextnum);
843 			err = i_ipmgmt_add_amnode(nodep);
844 		}
845 		db_op = IPADM_DB_WRITE;
846 		break;
847 	case ADDROBJ_DELETE:
848 		prev = head;
849 		while (head != NULL) {
850 			if (strcmp(head->am_aobjname,
851 			    nodep->am_aobjname) == 0) {
852 				nodep->am_atype = head->am_atype;
853 				/*
854 				 * There could be multiple IPV6_ADDRCONF nodes,
855 				 * with same address object name, so check for
856 				 * logical number also.
857 				 */
858 				if (head->am_atype !=
859 				    IPADM_ADDR_IPV6_ADDRCONF ||
860 				    nodep->am_lnum == head->am_lnum)
861 					break;
862 			}
863 			prev = head;
864 			head = head->am_next;
865 		}
866 		if (head != NULL) {
867 			/*
868 			 * If the address object is in both active and
869 			 * persistent configuration and the user is deleting it
870 			 * only from active configuration then mark this node
871 			 * for deletion by reseting IPMGMT_ACTIVE bit.
872 			 * With this the same address object name cannot
873 			 * be reused until it is permanently removed.
874 			 */
875 			if (head->am_flags == (IPMGMT_ACTIVE|IPMGMT_PERSIST) &&
876 			    nodep->am_flags == IPMGMT_ACTIVE) {
877 				/* Update flags in the in-memory map. */
878 				head->am_flags &= ~IPMGMT_ACTIVE;
879 				head->am_lnum = -1;
880 
881 				/* Update info in file. */
882 				db_op = IPADM_DB_WRITE;
883 				*nodep = *head;
884 			} else {
885 				(void) strlcpy(nodep->am_ifname,
886 				    head->am_ifname,
887 				    sizeof (nodep->am_ifname));
888 				/* otherwise delete the node */
889 				if (head == aobjmap.aobjmap_head)
890 					aobjmap.aobjmap_head = head->am_next;
891 				else
892 					prev->am_next = head->am_next;
893 				free(head);
894 				db_op = IPADM_DB_DELETE;
895 			}
896 		} else {
897 			err = ENOENT;
898 		}
899 		break;
900 	case ADDROBJ_LOOKUPADD:
901 		err = i_ipmgmt_lookupadd_amnode(nodep);
902 		update = B_FALSE;
903 		break;
904 	case ADDROBJ_SETLIFNUM:
905 		update = B_FALSE;
906 		for (; head != NULL; head = head->am_next) {
907 			if (strcmp(head->am_ifname,
908 			    nodep->am_ifname) == 0 &&
909 			    head->am_family == nodep->am_family &&
910 			    head->am_lnum == nodep->am_lnum) {
911 				err = EEXIST;
912 				break;
913 			}
914 			if (strcmp(head->am_aobjname,
915 			    nodep->am_aobjname) == 0) {
916 				matched = head;
917 			}
918 		}
919 		if (err == EEXIST)
920 			break;
921 		if (matched != NULL) {
922 			/* update the lifnum */
923 			matched->am_lnum = nodep->am_lnum;
924 		} else {
925 			err = ENOENT;
926 		}
927 		break;
928 	default:
929 		assert(0);
930 	}
931 
932 	if (err == 0 && update)
933 		err = ipmgmt_persist_aobjmap(nodep, db_op);
934 
935 	(void) pthread_rwlock_unlock(&aobjmap.aobjmap_rwlock);
936 
937 	return (err);
938 }
939 
940 /*
941  * Given a node in `aobjmap', this function converts it into nvlist_t structure.
942  * The content to be written to DB must be represented as nvlist_t.
943  */
944 static int
945 i_ipmgmt_node2nvl(nvlist_t **nvl, ipmgmt_aobjmap_t *np)
946 {
947 	int	err;
948 	char	strval[IPMGMT_STRSIZE];
949 
950 	*nvl = NULL;
951 	if ((err = nvlist_alloc(nvl, NV_UNIQUE_NAME, 0)) != 0)
952 		goto fail;
953 
954 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_AOBJNAME,
955 	    np->am_aobjname)) != 0)
956 		goto fail;
957 
958 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_IFNAME,
959 	    np->am_ifname)) != 0)
960 		goto fail;
961 
962 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_lnum);
963 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_LIFNUM, strval)) != 0)
964 		goto fail;
965 
966 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_family);
967 	if ((err = nvlist_add_string(*nvl, IPADM_NVP_FAMILY, strval)) != 0)
968 		goto fail;
969 
970 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_flags);
971 	if ((err = nvlist_add_string(*nvl, FLAGS, strval)) != 0)
972 		goto fail;
973 
974 	(void) snprintf(strval, IPMGMT_STRSIZE, "%d", np->am_atype);
975 	if ((err = nvlist_add_string(*nvl, ATYPE, strval)) != 0)
976 		goto fail;
977 
978 	if (np->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
979 		struct sockaddr_in6	*in6;
980 
981 		in6 = (struct sockaddr_in6 *)&np->am_ifid;
982 		if (np->am_linklocal &&
983 		    IN6_IS_ADDR_UNSPECIFIED(&in6->sin6_addr)) {
984 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
985 			    "default")) != 0)
986 				goto fail;
987 		} else {
988 			if (inet_ntop(AF_INET6, &in6->sin6_addr, strval,
989 			    IPMGMT_STRSIZE) == NULL) {
990 				err = errno;
991 				goto fail;
992 			}
993 			if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
994 			    strval)) != 0)
995 				goto fail;
996 		}
997 	} else {
998 		if ((err = nvlist_add_string(*nvl, IPADM_NVP_IPNUMADDR,
999 		    "")) != 0)
1000 			goto fail;
1001 	}
1002 	return (err);
1003 fail:
1004 	nvlist_free(*nvl);
1005 	return (err);
1006 }
1007 
1008 /*
1009  * Read the aobjmap data store and build the in-memory representation
1010  * of the aobjmap. We don't need to hold any locks while building this as
1011  * we do this in very early stage of daemon coming up, even before the door
1012  * is opened.
1013  */
1014 /* ARGSUSED */
1015 extern boolean_t
1016 ipmgmt_aobjmap_init(void *arg, nvlist_t *db_nvl, char *buf, size_t buflen,
1017     int *errp)
1018 {
1019 	nvpair_t		*nvp = NULL;
1020 	char			*name, *strval = NULL;
1021 	ipmgmt_aobjmap_t 	node;
1022 	struct sockaddr_in6	*in6;
1023 
1024 	*errp = 0;
1025 	node.am_next = NULL;
1026 	for (nvp = nvlist_next_nvpair(db_nvl, NULL); nvp != NULL;
1027 	    nvp = nvlist_next_nvpair(db_nvl, nvp)) {
1028 		name = nvpair_name(nvp);
1029 
1030 		if ((*errp = nvpair_value_string(nvp, &strval)) != 0)
1031 			return (B_TRUE);
1032 		if (strcmp(IPADM_NVP_AOBJNAME, name) == 0) {
1033 			(void) strlcpy(node.am_aobjname, strval,
1034 			    sizeof (node.am_aobjname));
1035 		} else if (strcmp(IPADM_NVP_IFNAME, name) == 0) {
1036 			(void) strlcpy(node.am_ifname, strval,
1037 			    sizeof (node.am_ifname));
1038 		} else if (strcmp(IPADM_NVP_LIFNUM, name) == 0) {
1039 			node.am_lnum = atoi(strval);
1040 		} else if (strcmp(IPADM_NVP_FAMILY, name) == 0) {
1041 			node.am_family = (sa_family_t)atoi(strval);
1042 		} else if (strcmp(FLAGS, name) == 0) {
1043 			node.am_flags = atoi(strval);
1044 		} else if (strcmp(ATYPE, name) == 0) {
1045 			node.am_atype = (ipadm_addr_type_t)atoi(strval);
1046 		} else if (strcmp(IPADM_NVP_IPNUMADDR, name) == 0) {
1047 			if (node.am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1048 				in6 = (struct sockaddr_in6 *)&node.am_ifid;
1049 				if (strcmp(strval, "default") == 0) {
1050 					bzero(in6, sizeof (node.am_ifid));
1051 					node.am_linklocal = B_TRUE;
1052 				} else {
1053 					(void) inet_pton(AF_INET6, strval,
1054 					    &in6->sin6_addr);
1055 					if (IN6_IS_ADDR_UNSPECIFIED(
1056 					    &in6->sin6_addr))
1057 						node.am_linklocal = B_TRUE;
1058 				}
1059 			}
1060 		}
1061 	}
1062 
1063 	/* we have all the information we need, add the node */
1064 	*errp = i_ipmgmt_add_amnode(&node);
1065 
1066 	return (B_TRUE);
1067 }
1068 
1069 /*
1070  * Updates an entry from the temporary cache file, which matches the given
1071  * address object name.
1072  */
1073 /* ARGSUSED */
1074 static boolean_t
1075 ipmgmt_update_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1076     size_t buflen, int *errp)
1077 {
1078 	ipadm_dbwrite_cbarg_t	*cb = arg;
1079 	nvlist_t		*in_nvl = cb->dbw_nvl;
1080 	uint32_t		flags = cb->dbw_flags;
1081 	char			*db_lifnumstr = NULL, *in_lifnumstr = NULL;
1082 
1083 	*errp = 0;
1084 	if (!ipmgmt_nvlist_intersects(db_nvl, in_nvl))
1085 		return (B_TRUE);
1086 
1087 	if (flags & IPMGMT_ATYPE_V6ACONF) {
1088 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1089 		    &db_lifnumstr) != 0 ||
1090 		    nvlist_lookup_string(in_nvl, IPADM_NVP_LIFNUM,
1091 		    &in_lifnumstr) != 0 ||
1092 		    (atoi(db_lifnumstr) != -1 && atoi(in_lifnumstr) != -1 &&
1093 		    strcmp(db_lifnumstr, in_lifnumstr) != 0))
1094 			return (B_TRUE);
1095 	}
1096 
1097 	/* we found the match */
1098 	(void) memset(buf, 0, buflen);
1099 	if (ipadm_nvlist2str(in_nvl, buf, buflen) == 0) {
1100 		/* buffer overflow */
1101 		*errp = ENOBUFS;
1102 	}
1103 
1104 	/* stop the walker */
1105 	return (B_FALSE);
1106 }
1107 
1108 /*
1109  * Deletes an entry from the temporary cache file, which matches the given
1110  * address object name.
1111  */
1112 /* ARGSUSED */
1113 static boolean_t
1114 ipmgmt_delete_aobjmap(void *arg, nvlist_t *db_nvl, char *buf,
1115     size_t buflen, int *errp)
1116 {
1117 	ipmgmt_aobjmap_t	*nodep = arg;
1118 	char			*db_lifnumstr = NULL;
1119 
1120 	*errp = 0;
1121 	if (!ipmgmt_nvlist_match(db_nvl, NULL, nodep->am_ifname,
1122 	    nodep->am_aobjname))
1123 		return (B_TRUE);
1124 
1125 	if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF) {
1126 		if (nvlist_lookup_string(db_nvl, IPADM_NVP_LIFNUM,
1127 		    &db_lifnumstr) != 0 || atoi(db_lifnumstr) != nodep->am_lnum)
1128 			return (B_TRUE);
1129 	}
1130 
1131 	/* we found the match, delete the line from the db */
1132 	buf[0] = '\0';
1133 
1134 	/* stop the walker */
1135 	return (B_FALSE);
1136 }
1137 
1138 /*
1139  * Adds or deletes aobjmap node information into a temporary cache file.
1140  */
1141 extern int
1142 ipmgmt_persist_aobjmap(ipmgmt_aobjmap_t *nodep, ipadm_db_op_t op)
1143 {
1144 	int			err;
1145 	ipadm_dbwrite_cbarg_t	cb;
1146 	nvlist_t		*nvl = NULL;
1147 
1148 	if (op == IPADM_DB_WRITE) {
1149 		if ((err = i_ipmgmt_node2nvl(&nvl, nodep)) != 0)
1150 			return (err);
1151 		cb.dbw_nvl = nvl;
1152 		if (nodep->am_atype == IPADM_ADDR_IPV6_ADDRCONF)
1153 			cb.dbw_flags = IPMGMT_ATYPE_V6ACONF;
1154 		else
1155 			cb.dbw_flags = 0;
1156 
1157 		err = ipadm_rw_db(ipmgmt_update_aobjmap, &cb,
1158 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_WRITE);
1159 		nvlist_free(nvl);
1160 	} else {
1161 		assert(op == IPADM_DB_DELETE);
1162 
1163 		err = ipadm_rw_db(ipmgmt_delete_aobjmap, nodep,
1164 		    ADDROBJ_MAPPING_DB_FILE, IPADM_FILE_MODE, IPADM_DB_DELETE);
1165 	}
1166 	return (err);
1167 }
1168 
1169 typedef struct scf_resources {
1170 	scf_handle_t *sr_handle;
1171 	scf_instance_t *sr_inst;
1172 	scf_propertygroup_t *sr_pg;
1173 	scf_property_t *sr_prop;
1174 	scf_value_t *sr_val;
1175 	scf_transaction_t *sr_tx;
1176 	scf_transaction_entry_t *sr_ent;
1177 } scf_resources_t;
1178 
1179 /*
1180  * Inputs:
1181  *   res is a pointer to the scf_resources_t to be released.
1182  */
1183 static void
1184 ipmgmt_release_scf_resources(scf_resources_t *res)
1185 {
1186 	scf_entry_destroy(res->sr_ent);
1187 	scf_transaction_destroy(res->sr_tx);
1188 	scf_value_destroy(res->sr_val);
1189 	scf_property_destroy(res->sr_prop);
1190 	scf_pg_destroy(res->sr_pg);
1191 	scf_instance_destroy(res->sr_inst);
1192 	(void) scf_handle_unbind(res->sr_handle);
1193 	scf_handle_destroy(res->sr_handle);
1194 }
1195 
1196 /*
1197  * Inputs:
1198  *   fmri is the instance to look up
1199  * Outputs:
1200  *   res is a pointer to an scf_resources_t.  This is an internal
1201  *   structure that holds all the handles needed to get a specific
1202  *   property from the running snapshot; on a successful return it
1203  *   contains the scf_value_t that should be passed to the desired
1204  *   scf_value_get_foo() function, and must be freed after use by
1205  *   calling release_scf_resources().  On a failure return, any
1206  *   resources that may have been assigned to res are released, so
1207  *   the caller does not need to do any cleanup in the failure case.
1208  * Returns:
1209  *    0 on success
1210  *   -1 on failure
1211  */
1212 
1213 static int
1214 ipmgmt_create_scf_resources(const char *fmri, scf_resources_t *res)
1215 {
1216 	res->sr_tx = NULL;
1217 	res->sr_ent = NULL;
1218 	res->sr_inst = NULL;
1219 	res->sr_pg = NULL;
1220 	res->sr_prop = NULL;
1221 	res->sr_val = NULL;
1222 
1223 	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
1224 		return (-1);
1225 	}
1226 
1227 	if (scf_handle_bind(res->sr_handle) != 0) {
1228 		scf_handle_destroy(res->sr_handle);
1229 		return (-1);
1230 	}
1231 	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
1232 		goto failure;
1233 	}
1234 	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
1235 	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
1236 		goto failure;
1237 	}
1238 	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
1239 		goto failure;
1240 	}
1241 	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
1242 		goto failure;
1243 	}
1244 	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
1245 		goto failure;
1246 	}
1247 	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
1248 		goto failure;
1249 	}
1250 	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
1251 		goto failure;
1252 	}
1253 	return (0);
1254 
1255 failure:
1256 	ipmgmt_release_scf_resources(res);
1257 	return (-1);
1258 }
1259 
1260 static int
1261 ipmgmt_set_property_value(scf_resources_t *res, const char *propname,
1262     scf_type_t proptype)
1263 {
1264 	int result = -1;
1265 	boolean_t new;
1266 
1267 retry:
1268 	new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0);
1269 
1270 	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) {
1271 		goto failure;
1272 	}
1273 	if (new) {
1274 		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
1275 		    propname, proptype) == -1) {
1276 			goto failure;
1277 		}
1278 	} else {
1279 		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
1280 		    propname, proptype) == -1) {
1281 			goto failure;
1282 		}
1283 	}
1284 
1285 	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
1286 		goto failure;
1287 	}
1288 
1289 	result = scf_transaction_commit(res->sr_tx);
1290 	if (result == 0) {
1291 		scf_transaction_reset(res->sr_tx);
1292 		if (scf_pg_update(res->sr_pg) == -1) {
1293 			goto failure;
1294 		}
1295 		goto retry;
1296 	}
1297 	if (result == -1)
1298 		goto failure;
1299 	return (0);
1300 
1301 failure:
1302 	return (-1);
1303 }
1304 
1305 /*
1306  * Returns TRUE if this is the first boot, else return FALSE. The
1307  * "ipmgmtd/first_boot_done" property is persistently set up on
1308  * IPMGMTD_FMRI on the first boot. Note that the presence of
1309  * "first_boot_done" itself is sufficient to indicate that this is
1310  * not the first boot i.e., the value of the property is immaterial.
1311  */
1312 extern boolean_t
1313 ipmgmt_first_boot()
1314 {
1315 	scf_simple_prop_t *prop;
1316 	ssize_t	numvals;
1317 	scf_resources_t res;
1318 	scf_error_t err;
1319 
1320 	if (ipmgmt_create_scf_resources(IPMGMTD_FMRI, &res) != 0)
1321 		return (B_TRUE); /* err on the side of caution */
1322 	prop = scf_simple_prop_get(res.sr_handle,
1323 	    IPMGMTD_FMRI, "ipmgmtd", "first_boot_done");
1324 	numvals = scf_simple_prop_numvalues(prop);
1325 	if (numvals > 0) {
1326 		scf_simple_prop_free(prop);
1327 		ipmgmt_release_scf_resources(&res);
1328 		return (B_FALSE);
1329 	}
1330 
1331 	/*
1332 	 * mark the first boot by setting ipmgmtd/first_boot_done to true
1333 	 */
1334 	if (scf_instance_add_pg(res.sr_inst, "ipmgmtd", SCF_GROUP_APPLICATION,
1335 	    0, res.sr_pg) != 0) {
1336 		if ((err = scf_error()) != SCF_ERROR_EXISTS)
1337 			goto failure;
1338 		/*
1339 		 * err == SCF_ERROR_EXISTS is by itself sufficient to declare
1340 		 * that this is not the first boot, but we create a simple
1341 		 * property as a place-holder, so that we don't leave an
1342 		 * empty process group behind.
1343 		 */
1344 		if (scf_instance_get_pg_composed(res.sr_inst, NULL, "ipmgmtd",
1345 		    res.sr_pg) != 0) {
1346 			err = scf_error();
1347 			goto failure;
1348 		}
1349 	}
1350 
1351 	if (scf_value_set_astring(res.sr_val, "true") != 0) {
1352 		err = scf_error();
1353 		goto failure;
1354 	}
1355 
1356 	if (ipmgmt_set_property_value(&res, "first_boot_done",
1357 	    SCF_TYPE_ASTRING) != 0) {
1358 		ipmgmt_log(LOG_WARNING,
1359 		    "Could not set rval of first_boot_done");
1360 	}
1361 
1362 failure:
1363 	ipmgmt_log(LOG_WARNING, "ipmgmt_first_boot scf error %s",
1364 	    scf_strerror(err));
1365 	ipmgmt_release_scf_resources(&res);
1366 	return (B_TRUE);
1367 }
1368