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