xref: /titanic_52/usr/src/cmd/cmd-inet/lib/nwamd/util.c (revision 8fd04b8338ed5093ec2d1e668fa620b7de44c177)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 /*
28  * util.c contains a set of miscellaneous utility functions which,
29  * among other things:
30  * - start a child process
31  * - look up the zone name
32  * - look up/set SMF properties
33  * - drop/escalate privs
34  */
35 
36 #include <assert.h>
37 #include <errno.h>
38 #include <inetcfg.h>
39 #include <libdllink.h>
40 #include <limits.h>
41 #include <libscf.h>
42 #include <net/if.h>
43 #include <pthread.h>
44 #include <pwd.h>
45 #include <spawn.h>
46 #include <stdarg.h>
47 #include <stdio.h>
48 #include <stdlib.h>
49 #include <string.h>
50 #include <strings.h>
51 #include <stropts.h>
52 #include <sys/socket.h>
53 #include <sys/sockio.h>
54 #include <sys/types.h>
55 #include <unistd.h>
56 #include <wait.h>
57 #include <zone.h>
58 
59 #include "util.h"
60 #include "llp.h"
61 
62 extern char **environ;
63 extern sigset_t original_sigmask;
64 
65 /*
66  * A holder for all the resources needed to get a property value
67  * using libscf.
68  */
69 typedef struct scf_resources {
70 	scf_handle_t *sr_handle;
71 	scf_instance_t *sr_inst;
72 	scf_snapshot_t *sr_snap;
73 	scf_propertygroup_t *sr_pg;
74 	scf_property_t *sr_prop;
75 	scf_value_t *sr_val;
76 	scf_transaction_t *sr_tx;
77 	scf_transaction_entry_t *sr_ent;
78 } scf_resources_t;
79 
80 static pthread_mutex_t uid_mutex = PTHREAD_MUTEX_INITIALIZER;
81 static uid_t uid;
82 static int uid_cnt;
83 
84 void
85 nwamd_to_root(void) {
86 	(void) pthread_mutex_lock(&uid_mutex);
87 	if (uid == 0)
88 		uid = getuid();
89 	if (uid_cnt++ == 0) {
90 		nwamd_escalate_privs();
91 		if (setuid(0) == -1)
92 			nlog(LOG_ERR, "setuid(0) failed %s", strerror(errno));
93 	}
94 	(void) pthread_mutex_unlock(&uid_mutex);
95 }
96 
97 void
98 nwamd_from_root(void) {
99 	(void) pthread_mutex_lock(&uid_mutex);
100 	assert(uid_cnt > 0);
101 	if (--uid_cnt == 0) {
102 		if (setuid(uid) == -1)
103 			nlog(LOG_ERR, "setuid(%d) failed %s", uid,
104 			    strerror(errno));
105 		nwamd_drop_unneeded_privs();
106 	}
107 	(void) pthread_mutex_unlock(&uid_mutex);
108 }
109 
110 /*
111  *
112  * This starts a child process determined by command.  If command contains a
113  * slash then it is assumed to be a full path; otherwise the path is searched
114  * for an executable file with the name command.  Command is also used as
115  * argv[0] of the new process.  The rest of the arguments of the function
116  * up to the first NULL make up pointers to arguments of the new process.
117  *
118  * This function returns child exit status on success and -1 on failure.
119  *
120  * NOTE: original_sigmask must be set before this function is called.
121  */
122 int
123 nwamd_start_childv(const char *command, char const * const *argv)
124 {
125 	posix_spawnattr_t attr;
126 	sigset_t fullset;
127 	int i, rc, status, n;
128 	pid_t pid;
129 	char vbuf[1024];
130 
131 	vbuf[0] = 0;
132 	n = sizeof (vbuf);
133 	for (i = 1; argv[i] != NULL && n > 2; i++) {
134 		n -= strlcat(vbuf, " ", n);
135 		n -= strlcat(vbuf, argv[i], n);
136 	}
137 	if (argv[i] != NULL || n < 0)
138 		nlog(LOG_ERR, "nwamd_start_childv can't log full arg vector");
139 
140 	if ((rc = posix_spawnattr_init(&attr)) != 0) {
141 		nlog(LOG_DEBUG, "posix_spawnattr_init %d %s\n",
142 		    rc, strerror(rc));
143 		return (-1);
144 	}
145 	(void) sigfillset(&fullset);
146 	if ((rc = posix_spawnattr_setsigdefault(&attr, &fullset)) != 0) {
147 		nlog(LOG_DEBUG, "setsigdefault %d %s\n", rc, strerror(rc));
148 		return (-1);
149 	}
150 	if ((rc = posix_spawnattr_setsigmask(&attr, &original_sigmask)) != 0) {
151 		nlog(LOG_DEBUG, "setsigmask %d %s\n", rc, strerror(rc));
152 		return (-1);
153 	}
154 	if ((rc = posix_spawnattr_setflags(&attr,
155 	    POSIX_SPAWN_SETSIGDEF|POSIX_SPAWN_SETSIGMASK)) != 0) {
156 		nlog(LOG_DEBUG, "setflags %d %s\n", rc, strerror(rc));
157 		return (-1);
158 	}
159 
160 	if ((rc = posix_spawnp(&pid, command, NULL, &attr, (char * const *)argv,
161 	    environ)) > 0) {
162 		nlog(LOG_DEBUG, "posix_spawnp failed errno %d", rc);
163 		return (-1);
164 	}
165 
166 	if ((rc = posix_spawnattr_destroy(&attr)) != 0) {
167 		nlog(LOG_DEBUG, "posix_spawn_attr_destroy %d %s\n",
168 		    rc, strerror(rc));
169 		return (-1);
170 	}
171 
172 	(void) waitpid(pid, &status, 0);
173 	if (WIFSIGNALED(status) || WIFSTOPPED(status)) {
174 		i = WIFSIGNALED(status) ? WTERMSIG(status) : WSTOPSIG(status);
175 		nlog(LOG_ERR, "'%s%s' %s with signal %d (%s)", command, vbuf,
176 		    (WIFSIGNALED(status) ? "terminated" : "stopped"), i,
177 		    strsignal(i));
178 		return (-2);
179 	} else {
180 		nlog(LOG_INFO, "'%s%s' completed normally: %d", command, vbuf,
181 		    WEXITSTATUS(status));
182 		return (WEXITSTATUS(status));
183 	}
184 }
185 
186 /*
187  * For global zone, check if the link is used by a non-global
188  * zone, note that the non-global zones doesn't need this check,
189  * because zoneadm has taken care of this when the zone boots.
190  * In the global zone, we ignore events for local-zone-owned links
191  * since these are taken care of by the local zone's network
192  * configuration services.
193  */
194 boolean_t
195 nwamd_link_belongs_to_this_zone(const char *linkname)
196 {
197 	zoneid_t zoneid;
198 	char zonename[ZONENAME_MAX];
199 	int ret;
200 
201 	zoneid = getzoneid();
202 	if (zoneid == GLOBAL_ZONEID) {
203 		datalink_id_t linkid;
204 		dladm_status_t status;
205 		char errstr[DLADM_STRSIZE];
206 
207 		if ((status = dladm_name2info(dld_handle, linkname, &linkid,
208 		    NULL, NULL, NULL)) != DLADM_STATUS_OK) {
209 			nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
210 			    "could not get linkid for %s: %s",
211 			    linkname, dladm_status2str(status, errstr));
212 			return (B_FALSE);
213 		}
214 		zoneid = ALL_ZONES;
215 		ret = zone_check_datalink(&zoneid, linkid);
216 		if (ret == 0) {
217 			(void) getzonenamebyid(zoneid, zonename, ZONENAME_MAX);
218 			nlog(LOG_DEBUG, "nwamd_link_belongs_to_this_zone: "
219 			    "%s is used by non-global zone: %s",
220 			    linkname, zonename);
221 			return (B_FALSE);
222 		}
223 	}
224 	return (B_TRUE);
225 }
226 
227 void
228 nwamd_drop_unneeded_privs(void)
229 {
230 	priv_set_t *priv_set, *allpriv_set;
231 
232 	/* build up our minimal set of privs; start with the basic set */
233 	priv_set = priv_str_to_set("basic", ",", NULL);
234 	allpriv_set = priv_str_to_set("zone", ",", NULL);
235 	if (priv_set == NULL || allpriv_set == NULL)
236 		pfail("converting privilege sets: %s", strerror(errno));
237 
238 	(void) priv_addset(priv_set, PRIV_FILE_CHOWN_SELF);
239 	(void) priv_addset(priv_set, PRIV_FILE_DAC_READ);
240 	(void) priv_addset(priv_set, PRIV_FILE_DAC_WRITE);
241 	(void) priv_addset(priv_set, PRIV_NET_RAWACCESS);
242 	(void) priv_addset(priv_set, PRIV_NET_PRIVADDR);
243 	(void) priv_addset(priv_set, PRIV_PROC_AUDIT);
244 	(void) priv_addset(priv_set, PRIV_PROC_OWNER);
245 	(void) priv_addset(priv_set, PRIV_PROC_SETID);
246 	(void) priv_addset(priv_set, PRIV_SYS_CONFIG);
247 	(void) priv_addset(priv_set, PRIV_SYS_IP_CONFIG);
248 	(void) priv_addset(priv_set, PRIV_SYS_IPC_CONFIG);
249 	(void) priv_addset(priv_set, PRIV_SYS_MOUNT);
250 	(void) priv_addset(priv_set, PRIV_SYS_NET_CONFIG);
251 	(void) priv_addset(priv_set, PRIV_SYS_RES_CONFIG);
252 	(void) priv_addset(priv_set, PRIV_SYS_RESOURCE);
253 
254 	/*
255 	 * Since our zone might not have all these privs,
256 	 * just ask for those that are available.
257 	 */
258 	priv_intersect(allpriv_set, priv_set);
259 
260 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
261 		priv_freeset(allpriv_set);
262 		priv_freeset(priv_set);
263 		pfail("setppriv inheritable: %s", strerror(errno));
264 	}
265 	/*
266 	 * Need to ensure permitted set contains all privs so we can escalate
267 	 * later.
268 	 */
269 	if (setppriv(PRIV_SET, PRIV_PERMITTED, allpriv_set) == -1) {
270 		priv_freeset(allpriv_set);
271 		priv_freeset(priv_set);
272 		pfail("setppriv permitted: %s", strerror(errno));
273 	}
274 	/*
275 	 * We need to find a smaller set of privs that are important to us.
276 	 * Otherwise we really are not gaining much by doing this.
277 	 */
278 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
279 		priv_freeset(allpriv_set);
280 		priv_freeset(priv_set);
281 		pfail("setppriv effective: %s", strerror(errno));
282 	}
283 
284 	priv_freeset(priv_set);
285 	priv_freeset(allpriv_set);
286 }
287 
288 void
289 nwamd_escalate_privs(void)
290 {
291 	priv_set_t *priv_set;
292 	priv_set = priv_str_to_set("zone", ",", NULL);
293 	if (priv_set == NULL)
294 		pfail("creating privilege set: %s", strerror(errno));
295 
296 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
297 		priv_freeset(priv_set);
298 		pfail("setppriv effective: %s", strerror(errno));
299 	}
300 	priv_freeset(priv_set);
301 }
302 
303 /*
304  * Inputs:
305  *   res is a pointer to the scf_resources_t to be released.
306  */
307 static void
308 release_scf_resources(scf_resources_t *res)
309 {
310 	scf_entry_destroy(res->sr_ent);
311 	scf_transaction_destroy(res->sr_tx);
312 	scf_value_destroy(res->sr_val);
313 	scf_property_destroy(res->sr_prop);
314 	scf_pg_destroy(res->sr_pg);
315 	scf_snapshot_destroy(res->sr_snap);
316 	scf_instance_destroy(res->sr_inst);
317 	(void) scf_handle_unbind(res->sr_handle);
318 	scf_handle_destroy(res->sr_handle);
319 }
320 
321 /*
322  * Inputs:
323  *   fmri is the instance to look up
324  * Outputs:
325  *   res is a pointer to an scf_resources_t.  This is an internal
326  *   structure that holds all the handles needed to get a specific
327  *   property from the running snapshot; on a successful return it
328  *   contains the scf_value_t that should be passed to the desired
329  *   scf_value_get_foo() function, and must be freed after use by
330  *   calling release_scf_resources().  On a failure return, any
331  *   resources that may have been assigned to res are released, so
332  *   the caller does not need to do any cleanup in the failure case.
333  * Returns:
334  *    0 on success
335  *   -1 on failure
336  */
337 
338 static int
339 create_scf_resources(const char *fmri, scf_resources_t *res)
340 {
341 	res->sr_tx = NULL;
342 	res->sr_ent = NULL;
343 	res->sr_inst = NULL;
344 	res->sr_snap = NULL;
345 	res->sr_pg = NULL;
346 	res->sr_prop = NULL;
347 	res->sr_val = NULL;
348 
349 	if ((res->sr_handle = scf_handle_create(SCF_VERSION)) == NULL) {
350 		return (-1);
351 	}
352 
353 	if (scf_handle_bind(res->sr_handle) != 0) {
354 		scf_handle_destroy(res->sr_handle);
355 		return (-1);
356 	}
357 	if ((res->sr_inst = scf_instance_create(res->sr_handle)) == NULL) {
358 		goto failure;
359 	}
360 	if (scf_handle_decode_fmri(res->sr_handle, fmri, NULL, NULL,
361 	    res->sr_inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) {
362 		goto failure;
363 	}
364 	if ((res->sr_snap = scf_snapshot_create(res->sr_handle)) == NULL) {
365 		goto failure;
366 	}
367 	if (scf_instance_get_snapshot(res->sr_inst, "running",
368 	    res->sr_snap) != 0) {
369 		goto failure;
370 	}
371 	if ((res->sr_pg = scf_pg_create(res->sr_handle)) == NULL) {
372 		goto failure;
373 	}
374 	if ((res->sr_prop = scf_property_create(res->sr_handle)) == NULL) {
375 		goto failure;
376 	}
377 	if ((res->sr_val = scf_value_create(res->sr_handle)) == NULL) {
378 		goto failure;
379 	}
380 	if ((res->sr_tx = scf_transaction_create(res->sr_handle)) == NULL) {
381 		goto failure;
382 	}
383 	if ((res->sr_ent = scf_entry_create(res->sr_handle)) == NULL) {
384 		goto failure;
385 	}
386 	return (0);
387 
388 failure:
389 	nlog(LOG_ERR, "create_scf_resources failed: %s",
390 	    scf_strerror(scf_error()));
391 	release_scf_resources(res);
392 	return (-1);
393 }
394 
395 /*
396  * Inputs:
397  *   fmri is the instance to look up
398  *   pg is the property group to look up
399  *   prop is the property within that group to look up
400  *   running specifies if running snapshot is to be used
401  * Outputs:
402  *   res is a pointer to an scf_resources_t.  This is an internal
403  *   structure that holds all the handles needed to get a specific
404  *   property from the running snapshot; on a successful return it
405  *   contains the scf_value_t that should be passed to the desired
406  *   scf_value_get_foo() function, and must be freed after use by
407  *   calling release_scf_resources().  On a failure return, any
408  *   resources that may have been assigned to res are released, so
409  *   the caller does not need to do any cleanup in the failure case.
410  * Returns:
411  *    0 on success
412  *   -1 on failure
413  */
414 static int
415 get_property_value(const char *fmri, const char *pg, const char *prop,
416     boolean_t running, scf_resources_t *res)
417 {
418 	if (create_scf_resources(fmri, res) != 0)
419 		return (-1);
420 
421 	if (scf_instance_get_pg_composed(res->sr_inst,
422 	    running ? res->sr_snap : NULL, pg, res->sr_pg) != 0) {
423 		goto failure;
424 	}
425 	if (scf_pg_get_property(res->sr_pg, prop, res->sr_prop) != 0) {
426 		goto failure;
427 	}
428 	if (scf_property_get_value(res->sr_prop, res->sr_val) != 0) {
429 		goto failure;
430 	}
431 	return (0);
432 
433 failure:
434 	release_scf_resources(res);
435 	return (-1);
436 }
437 
438 /*
439  * Inputs:
440  *   lfmri is the instance fmri to look up
441  *   lpg is the property group to look up
442  *   lprop is the property within that group to look up
443  * Outputs:
444  *   answer is a pointer to the property value
445  * Returns:
446  *    0 on success
447  *   -1 on failure
448  * If successful, the property value is retured in *answer.
449  * Otherwise, *answer is undefined, and it is up to the caller to decide
450  * how to handle that case.
451  */
452 int
453 nwamd_lookup_boolean_property(const char *lfmri, const char *lpg,
454     const char *lprop, boolean_t *answer)
455 {
456 	int result = -1;
457 	scf_resources_t res;
458 	uint8_t prop_val;
459 
460 	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
461 
462 		/*
463 		 * an error was already logged by get_property_value,
464 		 * and it released any resources assigned to res before
465 		 * returning.
466 		 */
467 		return (result);
468 	}
469 	if (scf_value_get_boolean(res.sr_val, &prop_val) != 0) {
470 		goto cleanup;
471 	}
472 	*answer = (boolean_t)prop_val;
473 	result = 0;
474 cleanup:
475 	release_scf_resources(&res);
476 	return (result);
477 }
478 
479 /*
480  * Inputs:
481  *   lfmri is the instance fmri to look up
482  *   lpg is the property group to look up
483  *   lprop is the property within that group to look up
484  *   buf is the place to put the answer
485  *   bufsz is the size of buf
486  * Outputs:
487  *
488  * Returns:
489  *    0 on success
490  *   -1 on failure
491  * If successful, the property value is retured in buf.
492  * Otherwise, buf is undefined, and it is up to the caller to decide
493  * how to handle that case.
494  */
495 int
496 nwamd_lookup_string_property(const char *lfmri, const char *lpg,
497     const char *lprop, char *buf, size_t bufsz)
498 {
499 	int result = -1;
500 	scf_resources_t res;
501 
502 	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
503 		/*
504 		 * The above function fails when trying to get a
505 		 * non-persistent property group from the running snapshot.
506 		 * Try going for the non-running snapshot.
507 		 */
508 		if (get_property_value(lfmri, lpg, lprop, B_FALSE, &res) != 0) {
509 			/*
510 			 * an error was already logged by get_property_value,
511 			 * and it released any resources assigned to res before
512 			 * returning.
513 			 */
514 			return (result);
515 		}
516 	}
517 	if (scf_value_get_astring(res.sr_val, buf, bufsz) == 0)
518 		goto cleanup;
519 
520 	result = 0;
521 cleanup:
522 	release_scf_resources(&res);
523 	return (result);
524 }
525 
526 /*
527  * Inputs:
528  *   lfmri is the instance fmri to look up
529  *   lpg is the property group to look up
530  *   lprop is the property within that group to look up
531  * Outputs:
532  *   answer is a pointer to the property value
533  * Returns:
534  *    0 on success
535  *   -1 on failure
536  * If successful, the property value is retured in *answer.
537  * Otherwise, *answer is undefined, and it is up to the caller to decide
538  * how to handle that case.
539  */
540 int
541 nwamd_lookup_count_property(const char *lfmri, const char *lpg,
542     const char *lprop, uint64_t *answer)
543 {
544 	int result = -1;
545 	scf_resources_t res;
546 
547 	if (get_property_value(lfmri, lpg, lprop, B_TRUE, &res) != 0) {
548 
549 		/*
550 		 * an error was already logged by get_property_value,
551 		 * and it released any resources assigned to res before
552 		 * returning.
553 		 */
554 		return (result);
555 	}
556 	if (scf_value_get_count(res.sr_val, answer) != 0) {
557 		goto cleanup;
558 	}
559 	result = 0;
560 cleanup:
561 	release_scf_resources(&res);
562 	return (result);
563 }
564 
565 static int
566 set_property_value(scf_resources_t *res, const char *propname,
567     scf_type_t proptype)
568 {
569 	int result = -1;
570 	boolean_t new;
571 
572 retry:
573 	new = (scf_pg_get_property(res->sr_pg, propname, res->sr_prop) != 0);
574 
575 	if (scf_transaction_start(res->sr_tx, res->sr_pg) == -1) {
576 		goto failure;
577 	}
578 	if (new) {
579 		if (scf_transaction_property_new(res->sr_tx, res->sr_ent,
580 		    propname, proptype) == -1) {
581 			goto failure;
582 		}
583 	} else {
584 		if (scf_transaction_property_change(res->sr_tx, res->sr_ent,
585 		    propname, proptype) == -1) {
586 			goto failure;
587 		}
588 	}
589 
590 	if (scf_entry_add_value(res->sr_ent, res->sr_val) != 0) {
591 		goto failure;
592 	}
593 
594 	result = scf_transaction_commit(res->sr_tx);
595 	if (result == 0) {
596 		scf_transaction_reset(res->sr_tx);
597 		if (scf_pg_update(res->sr_pg) == -1) {
598 			goto failure;
599 		}
600 		nlog(LOG_INFO, "set_property_value: transaction commit failed "
601 		    "for %s; retrying", propname);
602 		goto retry;
603 	}
604 	if (result == -1)
605 		goto failure;
606 	return (0);
607 
608 failure:
609 	return (-1);
610 }
611 
612 int
613 nwamd_set_count_property(const char *fmri, const char *pg, const char *prop,
614     uint64_t count)
615 {
616 	scf_resources_t res;
617 
618 	if (create_scf_resources(fmri, &res) != 0)
619 		return (-1);
620 
621 	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
622 	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
623 		if (scf_error() != SCF_ERROR_EXISTS)
624 			goto failure;
625 		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
626 		    res.sr_pg) != 0)
627 			goto failure;
628 	}
629 
630 	scf_value_set_count(res.sr_val, (uint64_t)count);
631 
632 	if (set_property_value(&res, prop, SCF_TYPE_COUNT) != 0)
633 		goto failure;
634 
635 	release_scf_resources(&res);
636 	return (0);
637 
638 failure:
639 	nlog(LOG_INFO, "nwamd_set_count_property: scf failure %s while "
640 	    "setting %s", scf_strerror(scf_error()), prop);
641 	release_scf_resources(&res);
642 	return (-1);
643 }
644 
645 int
646 nwamd_set_string_property(const char *fmri, const char *pg, const char *prop,
647     const char *str)
648 {
649 	scf_resources_t res;
650 
651 	if (create_scf_resources(fmri, &res) != 0)
652 		return (-1);
653 
654 	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
655 	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
656 		if (scf_error() != SCF_ERROR_EXISTS)
657 			goto failure;
658 		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
659 		    res.sr_pg) != 0)
660 			goto failure;
661 	}
662 
663 	if (scf_value_set_astring(res.sr_val, str) != 0)
664 		goto failure;
665 
666 	if (set_property_value(&res, prop, SCF_TYPE_ASTRING) != 0)
667 		goto failure;
668 
669 	release_scf_resources(&res);
670 	return (0);
671 
672 failure:
673 	nlog(LOG_INFO, "nwamd_set_string_property: scf failure %s while "
674 	    "setting %s", scf_strerror(scf_error()), prop);
675 	release_scf_resources(&res);
676 	return (-1);
677 }
678 
679 /*
680  * Deletes property prop from property group pg in SMF instance fmri.
681  * Returns 0 on success, -1 on failure.
682  */
683 int
684 nwamd_delete_scf_property(const char *fmri, const char *pg, const char *prop)
685 {
686 	scf_resources_t res;
687 	int result = -1;
688 
689 	if (create_scf_resources(fmri, &res) != 0)
690 		return (-1);
691 
692 	if (scf_instance_add_pg(res.sr_inst, pg, SCF_GROUP_APPLICATION,
693 	    SCF_PG_FLAG_NONPERSISTENT, res.sr_pg) != 0) {
694 		if (scf_error() != SCF_ERROR_EXISTS)
695 			goto failure;
696 		if (scf_instance_get_pg_composed(res.sr_inst, NULL, pg,
697 		    res.sr_pg) != 0)
698 			goto failure;
699 	}
700 
701 	if (scf_pg_get_property(res.sr_pg, prop, res.sr_prop) != 0)
702 		goto failure;
703 retry:
704 	if (scf_transaction_start(res.sr_tx, res.sr_pg) == -1)
705 		goto failure;
706 
707 	if (scf_transaction_property_delete(res.sr_tx, res.sr_ent, prop) == -1)
708 		goto failure;
709 
710 	result = scf_transaction_commit(res.sr_tx);
711 	if (result == 0) {
712 		scf_transaction_reset(res.sr_tx);
713 		if (scf_pg_update(res.sr_pg) == -1)
714 			goto failure;
715 		goto retry;
716 	}
717 	if (result == -1)
718 		goto failure;
719 
720 	release_scf_resources(&res);
721 	return (0);
722 failure:
723 	release_scf_resources(&res);
724 	return (-1);
725 }
726