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
nwamd_escalate(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
nwamd_deescalate(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
nwamd_start_childv(const char * command,char const * const * argv)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
nwamd_link_belongs_to_this_zone(const char * linkname)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
release_scf_resources(scf_resources_t * res)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
create_scf_resources(const char * fmri,scf_resources_t * res)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
get_property_value(const char * fmri,const char * pg,const char * prop,boolean_t running,scf_resources_t * res)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
nwamd_lookup_boolean_property(const char * lfmri,const char * lpg,const char * lprop,boolean_t * answer)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
nwamd_lookup_string_property(const char * lfmri,const char * lpg,const char * lprop,char * buf,size_t bufsz)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
nwamd_lookup_count_property(const char * lfmri,const char * lpg,const char * lprop,uint64_t * answer)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
set_property_value(scf_resources_t * res,const char * propname,scf_type_t proptype)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
nwamd_set_count_property(const char * fmri,const char * pg,const char * prop,uint64_t count)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
nwamd_set_string_property(const char * fmri,const char * pg,const char * prop,const char * str)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
nwamd_delete_scf_property(const char * fmri,const char * pg,const char * prop)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