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