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