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