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