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