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) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
  24  * Copyright (c) 2013, Joyent, Inc. All rights reserved.
  25  */
  26 
  27 #include <libintl.h>
  28 #include <librestart.h>
  29 #include <librestart_priv.h>
  30 #include <libscf.h>
  31 #include <libscf_priv.h>
  32 
  33 #include <assert.h>
  34 #include <ctype.h>
  35 #include <dlfcn.h>
  36 #include <errno.h>
  37 #include <exec_attr.h>
  38 #include <grp.h>
  39 #include <libsysevent.h>
  40 #include <libuutil.h>
  41 #include <limits.h>
  42 #include <link.h>
  43 #include <malloc.h>
  44 #include <pool.h>
  45 #include <priv.h>
  46 #include <project.h>
  47 #include <pthread.h>
  48 #include <pwd.h>
  49 #include <secdb.h>
  50 #include <signal.h>
  51 #include <stdlib.h>
  52 #include <string.h>
  53 #include <syslog.h>
  54 #include <sys/corectl.h>
  55 #include <sys/machelf.h>
  56 #include <sys/secflags.h>
  57 #include <sys/task.h>
  58 #include <sys/types.h>
  59 #include <time.h>
  60 #include <unistd.h>
  61 #include <ucontext.h>
  62 
  63 #define min(a, b)               ((a) > (b) ? (b) : (a))
  64 
  65 #define MKW_TRUE        ":true"
  66 #define MKW_KILL        ":kill"
  67 #define MKW_KILL_PROC   ":kill_process"
  68 
  69 #define ALLOCFAIL       ((char *)"Allocation failure.")
  70 #define RCBROKEN        ((char *)"Repository connection broken.")
  71 
  72 #define MAX_COMMIT_RETRIES              10
  73 #define MAX_COMMIT_RETRY_INT            (5 * 1000000)   /* 5 seconds */
  74 #define INITIAL_COMMIT_RETRY_INT        (10000)         /* 1/100th second */
  75 
  76 /*
  77  * bad_fail() catches bugs in this and lower layers by reporting supposedly
  78  * impossible function failures.  The NDEBUG case keeps the strings out of the
  79  * library but still calls abort() so we can root-cause from the coredump.
  80  */
  81 #ifndef NDEBUG
  82 #define bad_fail(func, err)     {                                       \
  83         (void) fprintf(stderr,                                          \
  84             "At %s:%d, %s() failed with unexpected error %d.  Aborting.\n", \
  85             __FILE__, __LINE__, (func), (err));                         \
  86         abort();                                                        \
  87 }
  88 #else
  89 #define bad_fail(func, err)     abort()
  90 #endif
  91 
  92 struct restarter_event_handle {
  93         char                            *reh_restarter_name;
  94         char                            *reh_delegate_channel_name;
  95         evchan_t                        *reh_delegate_channel;
  96         char                            *reh_delegate_subscriber_id;
  97         char                            *reh_master_channel_name;
  98         evchan_t                        *reh_master_channel;
  99         char                            *reh_master_subscriber_id;
 100         int                             (*reh_handler)(restarter_event_t *);
 101 };
 102 
 103 struct restarter_event {
 104         sysevent_t                      *re_sysevent;
 105         restarter_event_type_t          re_type;
 106         char                            *re_instance_name;
 107         restarter_event_handle_t        *re_event_handle;
 108         restarter_instance_state_t      re_state;
 109         restarter_instance_state_t      re_next_state;
 110 };
 111 
 112 /*
 113  * Long reasons must all parse/read correctly in the following contexts:
 114  *
 115  * "A service instance transitioned state: %s."
 116  * "A service failed: %s."
 117  * "Reason: %s."
 118  * "The service transitioned state (%s) and ..."
 119  *
 120  * With the exception of restart_str_none they must also fit the following
 121  * moulds:
 122  *
 123  * "An instance transitioned because %s, and ..."
 124  * "An instance transitioned to <new-state> because %s, and ..."
 125  *
 126  * Note that whoever is rendering the long message must provide the
 127  * terminal punctuation - don't include it here.  Similarly, do not
 128  * provide an initial capital letter in reason-long.
 129  *
 130  * The long reason strings are Volatile - within the grammatical constraints
 131  * above we may improve them as need be.  The intention is that a consumer
 132  * may blindly render the string along the lines of the above examples,
 133  * but has no other guarantees as to the exact wording.  Long reasons
 134  * are localized.
 135  *
 136  * We define revisions of the set of short reason strings in use.  Within
 137  * a given revision, all short reasons are Committed.  Consumers must check
 138  * the revision in use before relying on the semantics of the short reason
 139  * codes - if the version exceeds that which they are familiar with they should
 140  * fail gracefully.  Having checked for version compatability, a consumer
 141  * is assured that
 142  *
 143  *      "short_reason_A iff semantic_A", provided:
 144  *
 145  *              . the restarter uses this short reason code at all,
 146  *              . the short reason is not "none" (which a restarter could
 147  *                specifiy for any transition semantics)
 148  *
 149  * To split/refine such a Committed semantic_A into further cases,
 150  * we are required to bump the revision number.  This should be an
 151  * infrequent occurence.  If you bump the revision number you may
 152  * need to make corresponding changes in any source that calls
 153  * restarter_str_version (e.g., FMA event generation).
 154  *
 155  * To add additional reasons to the set you must also bump the version
 156  * number.
 157  */
 158 
 159 /*
 160  * The following describes revision 0 of the set of transition reasons.
 161  * Read the preceding block comment before making any changes.
 162  */
 163 static const struct restarter_state_transition_reason restarter_str[] = {
 164         /*
 165          * Any transition for which the restarter has not provided a reason.
 166          */
 167         {
 168             restarter_str_none,
 169             "none",
 170             "the restarter gave no reason"
 171         },
 172 
 173         /*
 174          * A transition to maintenance state due to a
 175          * 'svcadm mark maintenance <fmri>'.  *Not* used if the libscf
 176          * interface smf_maintain_instance(3SCF) is used to request maintenance.
 177          */
 178         {
 179             restarter_str_administrative_request,
 180             "administrative_request",
 181             "maintenance was requested by an administrator"
 182         },
 183 
 184         /*
 185          * A transition to maintenance state if a repository inconsistency
 186          * exists when the service/instance state is first read by startd
 187          * into the graph engine (this can also happen during startd restart).
 188          */
 189         {
 190             restarter_str_bad_repo_state,
 191             "bad_repo_state",
 192             "an SMF repository inconsistecy exists"
 193         },
 194 
 195         /*
 196          * A transition 'maintenance -> uninitialized' resulting always
 197          * from 'svcadm clear <fmri>'.  *Not* used if the libscf interface
 198          * smf_restore_instance(3SCF) is used.
 199          */
 200         {
 201             restarter_str_clear_request,
 202             "clear_request",
 203             "maintenance clear was requested by an administrator"
 204         },
 205 
 206         /*
 207          * A transition 'online -> offline' due to a process core dump.
 208          */
 209         {
 210             restarter_str_ct_ev_core,
 211             "ct_ev_core",
 212             "a process dumped core"
 213         },
 214 
 215         /*
 216          * A transition 'online -> offline' due to an empty process contract,
 217          * i.e., the last process in a contract type service has exited.
 218          */
 219         {
 220             restarter_str_ct_ev_exit,
 221             "ct_ev_exit",
 222             "all processes in the service have exited"
 223         },
 224 
 225         /*
 226          * A transition 'online -> offline' due to a hardware error.
 227          */
 228         {
 229             restarter_str_ct_ev_hwerr,
 230             "ct_ev_hwerr",
 231             "a process was killed due to uncorrectable hardware error"
 232         },
 233 
 234         /*
 235          * A transition 'online -> offline' due to a process in the service
 236          * having received a fatal signal originating from outside the
 237          * service process contract.
 238          */
 239         {
 240             restarter_str_ct_ev_signal,
 241             "ct_ev_signal",
 242             "a process received a fatal signal from outside the service"
 243         },
 244 
 245         /*
 246          * A transition 'offline -> online' when all dependencies for the
 247          * service have been met.
 248          */
 249         {
 250             restarter_str_dependencies_satisfied,
 251             "dependencies_satisfied",
 252             "all dependencies have been satisfied"
 253         },
 254 
 255         /*
 256          * A transition 'online -> offline' because some dependency for the
 257          * service is no-longer met.
 258          */
 259         {
 260             restarter_str_dependency_activity,
 261             "dependency_activity",
 262             "a dependency activity required a stop"
 263         },
 264 
 265         /*
 266          * A transition to maintenance state due to a cycle in the
 267          * service dependencies.
 268          */
 269         {
 270             restarter_str_dependency_cycle,
 271             "dependency_cycle",
 272             "a dependency cycle exists"
 273         },
 274 
 275         /*
 276          * A transition 'online -> offline -> disabled' due to a
 277          * 'svcadm disable [-t] <fmri>' or smf_disable_instance(3SCF) call.
 278          */
 279         {
 280             restarter_str_disable_request,
 281             "disable_request",
 282             "a disable was requested"
 283         },
 284 
 285         /*
 286          * A transition 'disabled -> offline' due to a
 287          * 'svcadm enable [-t] <fmri>' or smf_enable_instance(3SCF) call.
 288          */
 289         {
 290             restarter_str_enable_request,
 291             "enable_request",
 292             "an enable was requested"
 293         },
 294 
 295         /*
 296          * A transition to maintenance state when a method fails
 297          * repeatedly for a retryable reason.
 298          */
 299         {
 300             restarter_str_fault_threshold_reached,
 301             "fault_threshold_reached",
 302             "a method is failing in a retryable manner but too often"
 303         },
 304 
 305         /*
 306          * A transition to uninitialized state when startd reads the service
 307          * configuration and inserts it into the graph engine.
 308          */
 309         {
 310             restarter_str_insert_in_graph,
 311             "insert_in_graph",
 312             "the instance was inserted in the graph"
 313         },
 314 
 315         /*
 316          * A transition to maintenance state due to an invalid dependency
 317          * declared for the service.
 318          */
 319         {
 320             restarter_str_invalid_dependency,
 321             "invalid_dependency",
 322             "a service has an invalid dependency"
 323         },
 324 
 325         /*
 326          * A transition to maintenance state because the service-declared
 327          * restarter is invalid.
 328          */
 329         {
 330             restarter_str_invalid_restarter,
 331             "invalid_restarter",
 332             "the service restarter is invalid"
 333         },
 334 
 335         /*
 336          * A transition to maintenance state because a restarter method
 337          * exited with one of SMF_EXIT_ERR_CONFIG, SMF_EXIT_ERR_NOSMF,
 338          * SMF_EXIT_ERR_PERM, or SMF_EXIT_ERR_FATAL.
 339          */
 340         {
 341             restarter_str_method_failed,
 342             "method_failed",
 343             "a start, stop or refresh method failed"
 344         },
 345 
 346         /*
 347          * A transition 'uninitialized -> {disabled|offline}' after
 348          * "insert_in_graph" to match the state configured in the
 349          * repository.
 350          */
 351         {
 352             restarter_str_per_configuration,
 353             "per_configuration",
 354             "the SMF repository configuration specifies this state"
 355         },
 356 
 357         /*
 358          * Refresh requested - no state change.
 359          */
 360         {
 361             restarter_str_refresh,
 362             NULL,
 363             "a refresh was requested (no change of state)"
 364         },
 365 
 366         /*
 367          * A transition 'online -> offline -> online' due to a
 368          * 'svcadm restart <fmri> or equivlaent libscf API call.
 369          * Both the 'online -> offline' and 'offline -> online' transtions
 370          * specify this reason.
 371          */
 372         {
 373             restarter_str_restart_request,
 374             "restart_request",
 375             "a restart was requested"
 376         },
 377 
 378         /*
 379          * A transition to maintenance state because the start method is
 380          * being executed successfully but too frequently.
 381          */
 382         {
 383             restarter_str_restarting_too_quickly,
 384             "restarting_too_quickly",
 385             "the instance is restarting too quickly"
 386         },
 387 
 388         /*
 389          * A transition to maintenance state due a service requesting
 390          * 'svcadm mark maintenance <fmri>' or equivalent libscf API call.
 391          * A command line 'svcadm mark maintenance <fmri>' does not produce
 392          * this reason - it produces administrative_request instead.
 393          */
 394         {
 395             restarter_str_service_request,
 396             "service_request",
 397             "maintenance was requested by another service"
 398         },
 399 
 400         /*
 401          * An instanced inserted into the graph at its existing state
 402          * during a startd restart - no state change.
 403          */
 404         {
 405             restarter_str_startd_restart,
 406             NULL,
 407             "the instance was inserted in the graph due to startd restart"
 408         }
 409 };
 410 
 411 uint32_t
 412 restarter_str_version(void)
 413 {
 414         return (RESTARTER_STRING_VERSION);
 415 }
 416 
 417 const char *
 418 restarter_get_str_short(restarter_str_t key)
 419 {
 420         int i;
 421         for (i = 0; i < sizeof (restarter_str) /
 422             sizeof (struct restarter_state_transition_reason); i++)
 423                 if (key == restarter_str[i].str_key)
 424                         return (restarter_str[i].str_short);
 425         return (NULL);
 426 }
 427 
 428 const char *
 429 restarter_get_str_long(restarter_str_t key)
 430 {
 431         int i;
 432         for (i = 0; i < sizeof (restarter_str) /
 433             sizeof (struct restarter_state_transition_reason); i++)
 434                 if (key == restarter_str[i].str_key)
 435                         return (dgettext(TEXT_DOMAIN,
 436                             restarter_str[i].str_long));
 437         return (NULL);
 438 }
 439 
 440 /*
 441  * A static no memory error message mc_error_t structure
 442  * to be used in cases when memory errors are to be returned
 443  * This avoids the need to attempt to allocate memory for the
 444  * message, therefore getting into a cycle of no memory failures.
 445  */
 446 mc_error_t mc_nomem_err = {
 447         0, ENOMEM, sizeof ("Out of memory") - 1, "Out of memory"
 448 };
 449 
 450 static const char * const allocfail = "Allocation failure.\n";
 451 static const char * const rcbroken = "Repository connection broken.\n";
 452 
 453 static int method_context_safety = 0;   /* Can safely call pools/projects. */
 454 
 455 int ndebug = 1;
 456 
 457 /* PRINTFLIKE3 */
 458 static mc_error_t *
 459 mc_error_create(mc_error_t *e, int type, const char *format, ...)
 460 {
 461         mc_error_t      *le;
 462         va_list         args;
 463         int             size;
 464 
 465         /*
 466          * If the type is ENOMEM and format is NULL, then
 467          * go ahead and return the default nomem error.
 468          * Otherwise, attempt to allocate the memory and if
 469          * that fails then there is no reason to continue.
 470          */
 471         if (type == ENOMEM && format == NULL)
 472                 return (&mc_nomem_err);
 473 
 474         if (e == NULL && (le = malloc(sizeof (mc_error_t))) == NULL)
 475                 return (&mc_nomem_err);
 476         else
 477                 le = e;
 478 
 479         le->type = type;
 480         le->destroy = 1;
 481         va_start(args, format);
 482         size = vsnprintf(NULL, 0, format, args) + 1;
 483         if (size >= RESTARTER_ERRMSGSZ) {
 484                 if ((le = realloc(e, sizeof (mc_error_t) +
 485                     (size - RESTARTER_ERRMSGSZ))) == NULL) {
 486                         size = RESTARTER_ERRMSGSZ - 1;
 487                         le = e;
 488                 }
 489         }
 490 
 491         le->size = size;
 492         (void) vsnprintf(le->msg, le->size, format, args);
 493         va_end(args);
 494 
 495         return (le);
 496 }
 497 
 498 void
 499 restarter_mc_error_destroy(mc_error_t *mc_err)
 500 {
 501         if (mc_err == NULL)
 502                 return;
 503 
 504         /*
 505          * If the error messages was allocated then free.
 506          */
 507         if (mc_err->destroy) {
 508                 free(mc_err);
 509         }
 510 }
 511 
 512 static void
 513 free_restarter_event_handle(struct restarter_event_handle *h)
 514 {
 515         if (h == NULL)
 516                 return;
 517 
 518         /*
 519          * Just free the memory -- don't unbind the sysevent handle,
 520          * as otherwise events may be lost if this is just a restarter
 521          * restart.
 522          */
 523 
 524         if (h->reh_restarter_name != NULL)
 525                 free(h->reh_restarter_name);
 526         if (h->reh_delegate_channel_name != NULL)
 527                 free(h->reh_delegate_channel_name);
 528         if (h->reh_delegate_subscriber_id != NULL)
 529                 free(h->reh_delegate_subscriber_id);
 530         if (h->reh_master_channel_name != NULL)
 531                 free(h->reh_master_channel_name);
 532         if (h->reh_master_subscriber_id != NULL)
 533                 free(h->reh_master_subscriber_id);
 534 
 535         free(h);
 536 }
 537 
 538 char *
 539 _restarter_get_channel_name(const char *fmri, int type)
 540 {
 541         char *name;
 542         char *chan_name = malloc(MAX_CHNAME_LEN);
 543         char prefix_name[3];
 544         int i;
 545 
 546         if (chan_name == NULL)
 547                 return (NULL);
 548 
 549         if (type == RESTARTER_CHANNEL_DELEGATE)
 550                 (void) strcpy(prefix_name, "d_");
 551         else if (type == RESTARTER_CHANNEL_MASTER)
 552                 (void) strcpy(prefix_name, "m_");
 553         else {
 554                 free(chan_name);
 555                 return (NULL);
 556         }
 557 
 558         /*
 559          * Create a unique name
 560          *
 561          * Use the entire name, using a replacement of the /
 562          * characters to get a better name.
 563          *
 564          * Remove the svc:/ from the beginning as this really
 565          * isn't going to provide any uniqueness...
 566          *
 567          * An fmri name greater than MAX_CHNAME_LEN is going
 568          * to be rejected as too long for the chan_name below
 569          * in the snprintf call.
 570          */
 571         if ((name = strdup(strchr(fmri, '/') + 1)) == NULL) {
 572                 free(chan_name);
 573                 return (NULL);
 574         }
 575         i = 0;
 576         while (name[i]) {
 577                 if (name[i] == '/') {
 578                         name[i] = '_';
 579                 }
 580 
 581                 i++;
 582         }
 583 
 584         /*
 585          * Should check for [a-z],[A-Z],[0-9],.,_,-,:
 586          */
 587 
 588         if (snprintf(chan_name, MAX_CHNAME_LEN, "com.sun:scf:%s%s",
 589             prefix_name, name) > MAX_CHNAME_LEN) {
 590                 free(chan_name);
 591                 chan_name = NULL;
 592         }
 593 
 594         free(name);
 595         return (chan_name);
 596 }
 597 
 598 int
 599 cb(sysevent_t *syse, void *cookie)
 600 {
 601         restarter_event_handle_t *h = (restarter_event_handle_t *)cookie;
 602         restarter_event_t *e;
 603         nvlist_t *attr_list = NULL;
 604         int ret = 0;
 605 
 606         e = uu_zalloc(sizeof (restarter_event_t));
 607         if (e == NULL)
 608                 uu_die(allocfail);
 609         e->re_event_handle = h;
 610         e->re_sysevent = syse;
 611 
 612         if (sysevent_get_attr_list(syse, &attr_list) != 0)
 613                 uu_die(allocfail);
 614 
 615         if ((nvlist_lookup_uint32(attr_list, RESTARTER_NAME_TYPE,
 616             &(e->re_type)) != 0) ||
 617             (nvlist_lookup_string(attr_list,
 618             RESTARTER_NAME_INSTANCE, &(e->re_instance_name)) != 0)) {
 619                 uu_warn("%s: Can't decode nvlist for event %p\n",
 620                     h->reh_restarter_name, (void *)syse);
 621 
 622                 ret = 0;
 623         } else {
 624                 ret = h->reh_handler(e);
 625         }
 626 
 627         uu_free(e);
 628         nvlist_free(attr_list);
 629         return (ret);
 630 }
 631 
 632 /*
 633  * restarter_bind_handle(uint32_t, char *, int (*)(restarter_event_t *), int,
 634  *     restarter_event_handle_t **)
 635  *
 636  * Bind to a delegated restarter event channel.
 637  * Each delegated restarter gets its own channel for resource management.
 638  *
 639  * Returns 0 on success or
 640  *   ENOTSUP    version mismatch
 641  *   EINVAL     restarter_name or event_handle is NULL
 642  *   ENOMEM     out of memory, too many channels, or too many subscriptions
 643  *   EBUSY      sysevent_evc_bind() could not establish binding
 644  *   EFAULT     internal sysevent_evc_bind()/sysevent_evc_subscribe() error
 645  *   EMFILE     out of file descriptors
 646  *   EPERM      insufficient privilege for sysevent_evc_bind()
 647  *   EEXIST     already subscribed
 648  */
 649 int
 650 restarter_bind_handle(uint32_t version, const char *restarter_name,
 651     int (*event_handler)(restarter_event_t *), int flags,
 652     restarter_event_handle_t **rehp)
 653 {
 654         restarter_event_handle_t *h;
 655         size_t sz;
 656         int err;
 657 
 658         if (version != RESTARTER_EVENT_VERSION)
 659                 return (ENOTSUP);
 660 
 661         if (restarter_name == NULL || event_handler == NULL)
 662                 return (EINVAL);
 663 
 664         if (flags & RESTARTER_FLAG_DEBUG)
 665                 ndebug++;
 666 
 667         if ((h = uu_zalloc(sizeof (restarter_event_handle_t))) == NULL)
 668                 return (ENOMEM);
 669 
 670         h->reh_delegate_subscriber_id = malloc(MAX_SUBID_LEN);
 671         h->reh_master_subscriber_id = malloc(MAX_SUBID_LEN);
 672         h->reh_restarter_name = strdup(restarter_name);
 673         if (h->reh_delegate_subscriber_id == NULL ||
 674             h->reh_master_subscriber_id == NULL ||
 675             h->reh_restarter_name == NULL) {
 676                 free_restarter_event_handle(h);
 677                 return (ENOMEM);
 678         }
 679 
 680         sz = strlcpy(h->reh_delegate_subscriber_id, "del", MAX_SUBID_LEN);
 681         assert(sz < MAX_SUBID_LEN);
 682         sz = strlcpy(h->reh_master_subscriber_id, "master", MAX_SUBID_LEN);
 683         assert(sz < MAX_SUBID_LEN);
 684 
 685         h->reh_delegate_channel_name =
 686             _restarter_get_channel_name(restarter_name,
 687             RESTARTER_CHANNEL_DELEGATE);
 688         h->reh_master_channel_name =
 689             _restarter_get_channel_name(restarter_name,
 690             RESTARTER_CHANNEL_MASTER);
 691 
 692         if (h->reh_delegate_channel_name == NULL ||
 693             h->reh_master_channel_name == NULL) {
 694                 free_restarter_event_handle(h);
 695                 return (ENOMEM);
 696         }
 697 
 698         if (sysevent_evc_bind(h->reh_delegate_channel_name,
 699             &h->reh_delegate_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
 700                 err = errno;
 701                 assert(err != EINVAL);
 702                 assert(err != ENOENT);
 703                 free_restarter_event_handle(h);
 704                 return (err);
 705         }
 706 
 707         if (sysevent_evc_bind(h->reh_master_channel_name,
 708             &h->reh_master_channel, EVCH_CREAT|EVCH_HOLD_PEND) != 0) {
 709                 err = errno;
 710                 assert(err != EINVAL);
 711                 assert(err != ENOENT);
 712                 free_restarter_event_handle(h);
 713                 return (err);
 714         }
 715 
 716         h->reh_handler = event_handler;
 717 
 718         assert(strlen(restarter_name) <= MAX_CLASS_LEN - 1);
 719         assert(strlen(h->reh_delegate_subscriber_id) <= MAX_SUBID_LEN - 1);
 720         assert(strlen(h->reh_master_subscriber_id) <= MAX_SUBID_LEN - 1);
 721 
 722         if (sysevent_evc_subscribe(h->reh_delegate_channel,
 723             h->reh_delegate_subscriber_id, EC_ALL, cb, h, EVCH_SUB_KEEP) != 0) {
 724                 err = errno;
 725                 assert(err != EINVAL);
 726                 free_restarter_event_handle(h);
 727                 return (err);
 728         }
 729 
 730         *rehp = h;
 731         return (0);
 732 }
 733 
 734 restarter_event_handle_t *
 735 restarter_event_get_handle(restarter_event_t *e)
 736 {
 737         assert(e != NULL && e->re_event_handle != NULL);
 738         return (e->re_event_handle);
 739 }
 740 
 741 restarter_event_type_t
 742 restarter_event_get_type(restarter_event_t *e)
 743 {
 744         assert(e != NULL);
 745         return (e->re_type);
 746 }
 747 
 748 ssize_t
 749 restarter_event_get_instance(restarter_event_t *e, char *inst, size_t sz)
 750 {
 751         assert(e != NULL && inst != NULL);
 752         return ((ssize_t)strlcpy(inst, e->re_instance_name, sz));
 753 }
 754 
 755 int
 756 restarter_event_get_current_states(restarter_event_t *e,
 757     restarter_instance_state_t *state, restarter_instance_state_t *next_state)
 758 {
 759         if (e == NULL)
 760                 return (-1);
 761         *state = e->re_state;
 762         *next_state = e->re_next_state;
 763         return (0);
 764 }
 765 
 766 /*
 767  * restarter_event_publish_retry() is a wrapper around sysevent_evc_publish().
 768  * In case, the event cannot be sent at the first attempt (sysevent_evc_publish
 769  * returned EAGAIN - sysevent queue full), this function retries a few time
 770  * and return ENOSPC if it reaches the retry limit.
 771  *
 772  * The arguments to this function map the arguments of sysevent_evc_publish().
 773  *
 774  * On success, return 0. On error, return
 775  *
 776  *   EFAULT - internal sysevent_evc_publish() error
 777  *   ENOMEM - internal sysevent_evc_publish() error
 778  *   EBADF - scp is invalid (sysevent_evc_publish() returned EINVAL)
 779  *   ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
 780  */
 781 int
 782 restarter_event_publish_retry(evchan_t *scp, const char *class,
 783     const char *subclass, const char *vendor, const char *pub_name,
 784     nvlist_t *attr_list, uint32_t flags)
 785 {
 786         int retries, ret;
 787         useconds_t retry_int = INITIAL_COMMIT_RETRY_INT;
 788 
 789         for (retries = 0; retries < MAX_COMMIT_RETRIES; retries++) {
 790                 ret = sysevent_evc_publish(scp, class, subclass, vendor,
 791                     pub_name, attr_list, flags);
 792                 if (ret == 0)
 793                         break;
 794 
 795                 switch (ret) {
 796                 case EAGAIN:
 797                         /* Queue is full */
 798                         (void) usleep(retry_int);
 799 
 800                         retry_int = min(retry_int * 2, MAX_COMMIT_RETRY_INT);
 801                         break;
 802 
 803                 case EINVAL:
 804                         ret = EBADF;
 805                         /* FALLTHROUGH */
 806 
 807                 case EFAULT:
 808                 case ENOMEM:
 809                         return (ret);
 810 
 811                 case EOVERFLOW:
 812                 default:
 813                         /* internal error - abort */
 814                         bad_fail("sysevent_evc_publish", ret);
 815                 }
 816         }
 817 
 818         if (retries == MAX_COMMIT_RETRIES)
 819                 ret = ENOSPC;
 820 
 821         return (ret);
 822 }
 823 
 824 /*
 825  * Commit the state, next state, and auxiliary state into the repository.
 826  * Let the graph engine know about the state change and error.  On success,
 827  * return 0. On error, return
 828  *   EPROTO - librestart compiled against different libscf
 829  *   ENOMEM - out of memory
 830  *          - repository server out of resources
 831  *   ENOTACTIVE - repository server not running
 832  *   ECONNABORTED - repository connection established, but then broken
 833  *                - unknown libscf error
 834  *   ENOENT - inst does not exist in the repository
 835  *   EPERM - insufficient permissions
 836  *   EACCESS - backend access denied
 837  *   EROFS - backend is readonly
 838  *   EFAULT - internal sysevent_evc_publish() error
 839  *   EBADF - h is invalid (sysevent_evc_publish() returned EINVAL)
 840  *   ENOSPC - sysevent queue full (sysevent_evc_publish() returned EAGAIN)
 841  */
 842 int
 843 restarter_set_states(restarter_event_handle_t *h, const char *inst,
 844     restarter_instance_state_t cur_state,
 845     restarter_instance_state_t new_cur_state,
 846     restarter_instance_state_t next_state,
 847     restarter_instance_state_t new_next_state, restarter_error_t e,
 848     restarter_str_t aux)
 849 {
 850         nvlist_t *attr;
 851         scf_handle_t *scf_h;
 852         instance_data_t id;
 853         int ret = 0;
 854         const char *p = restarter_get_str_short(aux);
 855 
 856         assert(h->reh_master_channel != NULL);
 857         assert(h->reh_master_channel_name != NULL);
 858         assert(h->reh_master_subscriber_id != NULL);
 859 
 860         if ((scf_h = scf_handle_create(SCF_VERSION)) == NULL) {
 861                 switch (scf_error()) {
 862                 case SCF_ERROR_VERSION_MISMATCH:
 863                         return (EPROTO);
 864 
 865                 case SCF_ERROR_NO_MEMORY:
 866                         return (ENOMEM);
 867 
 868                 default:
 869                         bad_fail("scf_handle_create", scf_error());
 870                 }
 871         }
 872 
 873         if (scf_handle_bind(scf_h) == -1) {
 874                 scf_handle_destroy(scf_h);
 875                 switch (scf_error()) {
 876                 case SCF_ERROR_NO_SERVER:
 877                         return (ENOTACTIVE);
 878 
 879                 case SCF_ERROR_NO_RESOURCES:
 880                         return (ENOMEM);
 881 
 882                 case SCF_ERROR_INVALID_ARGUMENT:
 883                 case SCF_ERROR_IN_USE:
 884                 default:
 885                         bad_fail("scf_handle_bind", scf_error());
 886                 }
 887         }
 888 
 889         if (nvlist_alloc(&attr, NV_UNIQUE_NAME, 0) != 0 ||
 890             nvlist_add_int32(attr, RESTARTER_NAME_STATE, new_cur_state) != 0 ||
 891             nvlist_add_int32(attr, RESTARTER_NAME_NEXT_STATE, new_next_state)
 892             != 0 ||
 893             nvlist_add_int32(attr, RESTARTER_NAME_ERROR, e) != 0 ||
 894             nvlist_add_string(attr, RESTARTER_NAME_INSTANCE, inst) != 0 ||
 895             nvlist_add_int32(attr, RESTARTER_NAME_REASON, aux) != 0) {
 896                 ret = ENOMEM;
 897         } else {
 898                 id.i_fmri = inst;
 899                 id.i_state = cur_state;
 900                 id.i_next_state = next_state;
 901 
 902                 ret = _restarter_commit_states(scf_h, &id, new_cur_state,
 903                     new_next_state, p);
 904 
 905                 if (ret == 0) {
 906                         ret = restarter_event_publish_retry(
 907                             h->reh_master_channel, "master", "state_change",
 908                             "com.sun", "librestart", attr, EVCH_NOSLEEP);
 909                 }
 910         }
 911 
 912         nvlist_free(attr);
 913         (void) scf_handle_unbind(scf_h);
 914         scf_handle_destroy(scf_h);
 915 
 916         return (ret);
 917 }
 918 
 919 restarter_instance_state_t
 920 restarter_string_to_state(char *string)
 921 {
 922         assert(string != NULL);
 923 
 924         if (strcmp(string, SCF_STATE_STRING_NONE) == 0)
 925                 return (RESTARTER_STATE_NONE);
 926         else if (strcmp(string, SCF_STATE_STRING_UNINIT) == 0)
 927                 return (RESTARTER_STATE_UNINIT);
 928         else if (strcmp(string, SCF_STATE_STRING_MAINT) == 0)
 929                 return (RESTARTER_STATE_MAINT);
 930         else if (strcmp(string, SCF_STATE_STRING_OFFLINE) == 0)
 931                 return (RESTARTER_STATE_OFFLINE);
 932         else if (strcmp(string, SCF_STATE_STRING_DISABLED) == 0)
 933                 return (RESTARTER_STATE_DISABLED);
 934         else if (strcmp(string, SCF_STATE_STRING_ONLINE) == 0)
 935                 return (RESTARTER_STATE_ONLINE);
 936         else if (strcmp(string, SCF_STATE_STRING_DEGRADED) == 0)
 937                 return (RESTARTER_STATE_DEGRADED);
 938         else {
 939                 return (RESTARTER_STATE_NONE);
 940         }
 941 }
 942 
 943 ssize_t
 944 restarter_state_to_string(restarter_instance_state_t state, char *string,
 945     size_t len)
 946 {
 947         assert(string != NULL);
 948 
 949         if (state == RESTARTER_STATE_NONE)
 950                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_NONE, len));
 951         else if (state == RESTARTER_STATE_UNINIT)
 952                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_UNINIT, len));
 953         else if (state == RESTARTER_STATE_MAINT)
 954                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_MAINT, len));
 955         else if (state == RESTARTER_STATE_OFFLINE)
 956                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_OFFLINE,
 957                     len));
 958         else if (state == RESTARTER_STATE_DISABLED)
 959                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DISABLED,
 960                     len));
 961         else if (state == RESTARTER_STATE_ONLINE)
 962                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_ONLINE, len));
 963         else if (state == RESTARTER_STATE_DEGRADED)
 964                 return ((ssize_t)strlcpy(string, SCF_STATE_STRING_DEGRADED,
 965                     len));
 966         else
 967                 return ((ssize_t)strlcpy(string, "unknown", len));
 968 }
 969 
 970 /*
 971  * Sets pg to the name property group of s_inst.  If it doesn't exist, it is
 972  * added.
 973  *
 974  * Fails with
 975  *   ECONNABORTED - repository disconnection or unknown libscf error
 976  *   EBADF - inst is not set
 977  *   ECANCELED - inst is deleted
 978  *   EPERM - permission is denied
 979  *   EACCES - backend denied access
 980  *   EROFS - backend readonly
 981  */
 982 static int
 983 instance_get_or_add_pg(scf_instance_t *inst, const char *name,
 984     const char *type, uint32_t flags, scf_propertygroup_t *pg)
 985 {
 986 again:
 987         if (scf_instance_get_pg(inst, name, pg) == 0)
 988                 return (0);
 989 
 990         switch (scf_error()) {
 991         case SCF_ERROR_CONNECTION_BROKEN:
 992         default:
 993                 return (ECONNABORTED);
 994 
 995         case SCF_ERROR_NOT_SET:
 996                 return (EBADF);
 997 
 998         case SCF_ERROR_DELETED:
 999                 return (ECANCELED);
1000 
1001         case SCF_ERROR_NOT_FOUND:
1002                 break;
1003 
1004         case SCF_ERROR_HANDLE_MISMATCH:
1005         case SCF_ERROR_INVALID_ARGUMENT:
1006                 bad_fail("scf_instance_get_pg", scf_error());
1007         }
1008 
1009         if (scf_instance_add_pg(inst, name, type, flags, pg) == 0)
1010                 return (0);
1011 
1012         switch (scf_error()) {
1013         case SCF_ERROR_CONNECTION_BROKEN:
1014         default:
1015                 return (ECONNABORTED);
1016 
1017         case SCF_ERROR_DELETED:
1018                 return (ECANCELED);
1019 
1020         case SCF_ERROR_EXISTS:
1021                 goto again;
1022 
1023         case SCF_ERROR_PERMISSION_DENIED:
1024                 return (EPERM);
1025 
1026         case SCF_ERROR_BACKEND_ACCESS:
1027                 return (EACCES);
1028 
1029         case SCF_ERROR_BACKEND_READONLY:
1030                 return (EROFS);
1031 
1032         case SCF_ERROR_HANDLE_MISMATCH:
1033         case SCF_ERROR_INVALID_ARGUMENT:
1034         case SCF_ERROR_NOT_SET:                 /* should be caught above */
1035                 bad_fail("scf_instance_add_pg", scf_error());
1036         }
1037 
1038         return (0);
1039 }
1040 
1041 /*
1042  * Fails with
1043  *   ECONNABORTED
1044  *   ECANCELED - pg was deleted
1045  */
1046 static int
1047 tx_set_value(scf_transaction_t *tx, scf_transaction_entry_t *ent,
1048     const char *pname, scf_type_t ty, scf_value_t *val)
1049 {
1050         int r;
1051 
1052         for (;;) {
1053                 if (scf_transaction_property_change_type(tx, ent, pname,
1054                     ty) == 0)
1055                         break;
1056 
1057                 switch (scf_error()) {
1058                 case SCF_ERROR_CONNECTION_BROKEN:
1059                 default:
1060                         return (ECONNABORTED);
1061 
1062                 case SCF_ERROR_DELETED:
1063                         return (ECANCELED);
1064 
1065                 case SCF_ERROR_NOT_FOUND:
1066                         break;
1067 
1068                 case SCF_ERROR_HANDLE_MISMATCH:
1069                 case SCF_ERROR_INVALID_ARGUMENT:
1070                 case SCF_ERROR_IN_USE:
1071                 case SCF_ERROR_NOT_SET:
1072                         bad_fail("scf_transaction_property_change_type",
1073                             scf_error());
1074                 }
1075 
1076                 if (scf_transaction_property_new(tx, ent, pname, ty) == 0)
1077                         break;
1078 
1079                 switch (scf_error()) {
1080                 case SCF_ERROR_CONNECTION_BROKEN:
1081                 default:
1082                         return (ECONNABORTED);
1083 
1084                 case SCF_ERROR_DELETED:
1085                         return (ECANCELED);
1086 
1087                 case SCF_ERROR_EXISTS:
1088                         break;
1089 
1090                 case SCF_ERROR_HANDLE_MISMATCH:
1091                 case SCF_ERROR_INVALID_ARGUMENT:
1092                 case SCF_ERROR_IN_USE:
1093                 case SCF_ERROR_NOT_SET:
1094                         bad_fail("scf_transaction_property_new", scf_error());
1095                 }
1096         }
1097 
1098         r = scf_entry_add_value(ent, val);
1099         assert(r == 0);
1100 
1101         return (0);
1102 }
1103 
1104 /*
1105  * Commit new_state, new_next_state, and aux to the repository for id.  If
1106  * successful, also set id's state and next-state as given, and return 0.
1107  * Fails with
1108  *   ENOMEM - out of memory
1109  *   ECONNABORTED - repository connection broken
1110  *                - unknown libscf error
1111  *   EINVAL - id->i_fmri is invalid or not an instance FMRI
1112  *   ENOENT - id->i_fmri does not exist
1113  *   EPERM - insufficient permissions
1114  *   EACCES - backend access denied
1115  *   EROFS - backend is readonly
1116  */
1117 int
1118 _restarter_commit_states(scf_handle_t *h, instance_data_t *id,
1119     restarter_instance_state_t new_state,
1120     restarter_instance_state_t new_state_next, const char *aux)
1121 {
1122         char str_state[MAX_SCF_STATE_STRING_SZ];
1123         char str_new_state[MAX_SCF_STATE_STRING_SZ];
1124         char str_state_next[MAX_SCF_STATE_STRING_SZ];
1125         char str_new_state_next[MAX_SCF_STATE_STRING_SZ];
1126         int ret = 0, r;
1127         struct timeval now;
1128         ssize_t sz;
1129 
1130         scf_transaction_t *t = NULL;
1131         scf_transaction_entry_t *t_state = NULL, *t_state_next = NULL;
1132         scf_transaction_entry_t *t_stime = NULL, *t_aux = NULL;
1133         scf_value_t *v_state = NULL, *v_state_next = NULL, *v_stime = NULL;
1134         scf_value_t *v_aux = NULL;
1135         scf_instance_t *s_inst = NULL;
1136         scf_propertygroup_t *pg = NULL;
1137 
1138         assert(new_state != RESTARTER_STATE_NONE);
1139 
1140         if ((s_inst = scf_instance_create(h)) == NULL ||
1141             (pg = scf_pg_create(h)) == NULL ||
1142             (t = scf_transaction_create(h)) == NULL ||
1143             (t_state = scf_entry_create(h)) == NULL ||
1144             (t_state_next = scf_entry_create(h)) == NULL ||
1145             (t_stime = scf_entry_create(h)) == NULL ||
1146             (t_aux = scf_entry_create(h)) == NULL ||
1147             (v_state = scf_value_create(h)) == NULL ||
1148             (v_state_next = scf_value_create(h)) == NULL ||
1149             (v_stime = scf_value_create(h)) == NULL ||
1150             (v_aux = scf_value_create(h)) == NULL) {
1151                 ret = ENOMEM;
1152                 goto out;
1153         }
1154 
1155         sz = restarter_state_to_string(new_state, str_new_state,
1156             sizeof (str_new_state));
1157         assert(sz < sizeof (str_new_state));
1158         sz = restarter_state_to_string(new_state_next, str_new_state_next,
1159             sizeof (str_new_state_next));
1160         assert(sz < sizeof (str_new_state_next));
1161         sz = restarter_state_to_string(id->i_state, str_state,
1162             sizeof (str_state));
1163         assert(sz < sizeof (str_state));
1164         sz = restarter_state_to_string(id->i_next_state, str_state_next,
1165             sizeof (str_state_next));
1166         assert(sz < sizeof (str_state_next));
1167 
1168         ret = gettimeofday(&now, NULL);
1169         assert(ret != -1);
1170 
1171         if (scf_handle_decode_fmri(h, id->i_fmri, NULL, NULL, s_inst,
1172             NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) {
1173                 switch (scf_error()) {
1174                 case SCF_ERROR_CONNECTION_BROKEN:
1175                 default:
1176                         ret = ECONNABORTED;
1177                         break;
1178 
1179                 case SCF_ERROR_INVALID_ARGUMENT:
1180                 case SCF_ERROR_CONSTRAINT_VIOLATED:
1181                         ret = EINVAL;
1182                         break;
1183 
1184                 case SCF_ERROR_NOT_FOUND:
1185                         ret = ENOENT;
1186                         break;
1187 
1188                 case SCF_ERROR_HANDLE_MISMATCH:
1189                         bad_fail("scf_handle_decode_fmri", scf_error());
1190                 }
1191                 goto out;
1192         }
1193 
1194 
1195         if (scf_value_set_astring(v_state, str_new_state) != 0 ||
1196             scf_value_set_astring(v_state_next, str_new_state_next) != 0)
1197                 bad_fail("scf_value_set_astring", scf_error());
1198 
1199         if (aux) {
1200                 if (scf_value_set_astring(v_aux, aux) != 0)
1201                         bad_fail("scf_value_set_astring", scf_error());
1202         }
1203 
1204         if (scf_value_set_time(v_stime, now.tv_sec, now.tv_usec * 1000) != 0)
1205                 bad_fail("scf_value_set_time", scf_error());
1206 
1207 add_pg:
1208         switch (r = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1209             SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg)) {
1210         case 0:
1211                 break;
1212 
1213         case ECONNABORTED:
1214         case EPERM:
1215         case EACCES:
1216         case EROFS:
1217                 ret = r;
1218                 goto out;
1219 
1220         case ECANCELED:
1221                 ret = ENOENT;
1222                 goto out;
1223 
1224         case EBADF:
1225         default:
1226                 bad_fail("instance_get_or_add_pg", r);
1227         }
1228 
1229         for (;;) {
1230                 if (scf_transaction_start(t, pg) != 0) {
1231                         switch (scf_error()) {
1232                         case SCF_ERROR_CONNECTION_BROKEN:
1233                         default:
1234                                 ret = ECONNABORTED;
1235                                 goto out;
1236 
1237                         case SCF_ERROR_NOT_SET:
1238                                 goto add_pg;
1239 
1240                         case SCF_ERROR_PERMISSION_DENIED:
1241                                 ret = EPERM;
1242                                 goto out;
1243 
1244                         case SCF_ERROR_BACKEND_ACCESS:
1245                                 ret = EACCES;
1246                                 goto out;
1247 
1248                         case SCF_ERROR_BACKEND_READONLY:
1249                                 ret = EROFS;
1250                                 goto out;
1251 
1252                         case SCF_ERROR_HANDLE_MISMATCH:
1253                         case SCF_ERROR_IN_USE:
1254                                 bad_fail("scf_transaction_start", scf_error());
1255                         }
1256                 }
1257 
1258                 if ((r = tx_set_value(t, t_state, SCF_PROPERTY_STATE,
1259                     SCF_TYPE_ASTRING, v_state)) != 0 ||
1260                     (r = tx_set_value(t, t_state_next, SCF_PROPERTY_NEXT_STATE,
1261                     SCF_TYPE_ASTRING, v_state_next)) != 0 ||
1262                     (r = tx_set_value(t, t_stime, SCF_PROPERTY_STATE_TIMESTAMP,
1263                     SCF_TYPE_TIME, v_stime)) != 0) {
1264                         switch (r) {
1265                         case ECONNABORTED:
1266                                 ret = ECONNABORTED;
1267                                 goto out;
1268 
1269                         case ECANCELED:
1270                                 scf_transaction_reset(t);
1271                                 goto add_pg;
1272 
1273                         default:
1274                                 bad_fail("tx_set_value", r);
1275                         }
1276                 }
1277 
1278                 if (aux) {
1279                         if ((r = tx_set_value(t, t_aux, SCF_PROPERTY_AUX_STATE,
1280                             SCF_TYPE_ASTRING, v_aux)) != 0) {
1281                                 switch (r) {
1282                                 case ECONNABORTED:
1283                                         ret = ECONNABORTED;
1284                                         goto out;
1285 
1286                                 case ECANCELED:
1287                                         scf_transaction_reset(t);
1288                                         goto add_pg;
1289 
1290                                 default:
1291                                         bad_fail("tx_set_value", r);
1292                                 }
1293                         }
1294                 }
1295 
1296                 ret = scf_transaction_commit(t);
1297                 if (ret == 1)
1298                         break;
1299                 if (ret == -1) {
1300                         switch (scf_error()) {
1301                         case SCF_ERROR_CONNECTION_BROKEN:
1302                         default:
1303                                 ret = ECONNABORTED;
1304                                 goto out;
1305 
1306                         case SCF_ERROR_PERMISSION_DENIED:
1307                                 ret = EPERM;
1308                                 goto out;
1309 
1310                         case SCF_ERROR_BACKEND_ACCESS:
1311                                 ret = EACCES;
1312                                 goto out;
1313 
1314                         case SCF_ERROR_BACKEND_READONLY:
1315                                 ret = EROFS;
1316                                 goto out;
1317 
1318                         case SCF_ERROR_NOT_SET:
1319                                 bad_fail("scf_transaction_commit", scf_error());
1320                         }
1321                 }
1322 
1323                 scf_transaction_reset(t);
1324                 if (scf_pg_update(pg) == -1) {
1325                         switch (scf_error()) {
1326                         case SCF_ERROR_CONNECTION_BROKEN:
1327                         default:
1328                                 ret = ECONNABORTED;
1329                                 goto out;
1330 
1331                         case SCF_ERROR_NOT_SET:
1332                                 goto add_pg;
1333                         }
1334                 }
1335         }
1336 
1337         id->i_state = new_state;
1338         id->i_next_state = new_state_next;
1339         ret = 0;
1340 
1341 out:
1342         scf_transaction_destroy(t);
1343         scf_entry_destroy(t_state);
1344         scf_entry_destroy(t_state_next);
1345         scf_entry_destroy(t_stime);
1346         scf_entry_destroy(t_aux);
1347         scf_value_destroy(v_state);
1348         scf_value_destroy(v_state_next);
1349         scf_value_destroy(v_stime);
1350         scf_value_destroy(v_aux);
1351         scf_pg_destroy(pg);
1352         scf_instance_destroy(s_inst);
1353 
1354         return (ret);
1355 }
1356 
1357 /*
1358  * Fails with
1359  *   EINVAL - type is invalid
1360  *   ENOMEM
1361  *   ECONNABORTED - repository connection broken
1362  *   EBADF - s_inst is not set
1363  *   ECANCELED - s_inst is deleted
1364  *   EPERM - permission denied
1365  *   EACCES - backend access denied
1366  *   EROFS - backend readonly
1367  */
1368 int
1369 restarter_remove_contract(scf_instance_t *s_inst, ctid_t contract_id,
1370     restarter_contract_type_t type)
1371 {
1372         scf_handle_t *h;
1373         scf_transaction_t *t = NULL;
1374         scf_transaction_entry_t *t_cid = NULL;
1375         scf_propertygroup_t *pg = NULL;
1376         scf_property_t *prop = NULL;
1377         scf_value_t *val;
1378         scf_iter_t *iter = NULL;
1379         const char *pname;
1380         int ret = 0, primary;
1381         uint64_t c;
1382 
1383         switch (type) {
1384         case RESTARTER_CONTRACT_PRIMARY:
1385                 primary = 1;
1386                 break;
1387         case RESTARTER_CONTRACT_TRANSIENT:
1388                 primary = 0;
1389                 break;
1390         default:
1391                 return (EINVAL);
1392         }
1393 
1394         h = scf_instance_handle(s_inst);
1395 
1396         pg = scf_pg_create(h);
1397         prop = scf_property_create(h);
1398         iter = scf_iter_create(h);
1399         t = scf_transaction_create(h);
1400 
1401         if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1402                 ret = ENOMEM;
1403                 goto remove_contract_cleanup;
1404         }
1405 
1406 add:
1407         scf_transaction_destroy_children(t);
1408         ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1409             SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1410         if (ret != 0)
1411                 goto remove_contract_cleanup;
1412 
1413         pname = primary? SCF_PROPERTY_CONTRACT :
1414             SCF_PROPERTY_TRANSIENT_CONTRACT;
1415 
1416         for (;;) {
1417                 if (scf_transaction_start(t, pg) != 0) {
1418                         switch (scf_error()) {
1419                         case SCF_ERROR_CONNECTION_BROKEN:
1420                         default:
1421                                 ret = ECONNABORTED;
1422                                 goto remove_contract_cleanup;
1423 
1424                         case SCF_ERROR_DELETED:
1425                                 goto add;
1426 
1427                         case SCF_ERROR_PERMISSION_DENIED:
1428                                 ret = EPERM;
1429                                 goto remove_contract_cleanup;
1430 
1431                         case SCF_ERROR_BACKEND_ACCESS:
1432                                 ret = EACCES;
1433                                 goto remove_contract_cleanup;
1434 
1435                         case SCF_ERROR_BACKEND_READONLY:
1436                                 ret = EROFS;
1437                                 goto remove_contract_cleanup;
1438 
1439                         case SCF_ERROR_HANDLE_MISMATCH:
1440                         case SCF_ERROR_IN_USE:
1441                         case SCF_ERROR_NOT_SET:
1442                                 bad_fail("scf_transaction_start", scf_error());
1443                         }
1444                 }
1445 
1446                 t_cid = scf_entry_create(h);
1447 
1448                 if (scf_pg_get_property(pg, pname, prop) == 0) {
1449 replace:
1450                         if (scf_transaction_property_change_type(t, t_cid,
1451                             pname, SCF_TYPE_COUNT) != 0) {
1452                                 switch (scf_error()) {
1453                                 case SCF_ERROR_CONNECTION_BROKEN:
1454                                 default:
1455                                         ret = ECONNABORTED;
1456                                         goto remove_contract_cleanup;
1457 
1458                                 case SCF_ERROR_DELETED:
1459                                         scf_entry_destroy(t_cid);
1460                                         goto add;
1461 
1462                                 case SCF_ERROR_NOT_FOUND:
1463                                         goto new;
1464 
1465                                 case SCF_ERROR_HANDLE_MISMATCH:
1466                                 case SCF_ERROR_INVALID_ARGUMENT:
1467                                 case SCF_ERROR_IN_USE:
1468                                 case SCF_ERROR_NOT_SET:
1469                                         bad_fail(
1470                                         "scf_transaction_property_changetype",
1471                                             scf_error());
1472                                 }
1473                         }
1474 
1475                         if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1476                                 if (scf_iter_property_values(iter, prop) != 0) {
1477                                         switch (scf_error()) {
1478                                         case SCF_ERROR_CONNECTION_BROKEN:
1479                                         default:
1480                                                 ret = ECONNABORTED;
1481                                                 goto remove_contract_cleanup;
1482 
1483                                         case SCF_ERROR_NOT_SET:
1484                                         case SCF_ERROR_HANDLE_MISMATCH:
1485                                                 bad_fail(
1486                                                     "scf_iter_property_values",
1487                                                     scf_error());
1488                                         }
1489                                 }
1490 
1491 next_val:
1492                                 val = scf_value_create(h);
1493                                 if (val == NULL) {
1494                                         assert(scf_error() ==
1495                                             SCF_ERROR_NO_MEMORY);
1496                                         ret = ENOMEM;
1497                                         goto remove_contract_cleanup;
1498                                 }
1499 
1500                                 ret = scf_iter_next_value(iter, val);
1501                                 if (ret == -1) {
1502                                         switch (scf_error()) {
1503                                         case SCF_ERROR_CONNECTION_BROKEN:
1504                                                 ret = ECONNABORTED;
1505                                                 goto remove_contract_cleanup;
1506 
1507                                         case SCF_ERROR_DELETED:
1508                                                 scf_value_destroy(val);
1509                                                 goto add;
1510 
1511                                         case SCF_ERROR_HANDLE_MISMATCH:
1512                                         case SCF_ERROR_INVALID_ARGUMENT:
1513                                         case SCF_ERROR_PERMISSION_DENIED:
1514                                         default:
1515                                                 bad_fail("scf_iter_next_value",
1516                                                     scf_error());
1517                                         }
1518                                 }
1519 
1520                                 if (ret == 1) {
1521                                         ret = scf_value_get_count(val, &c);
1522                                         assert(ret == 0);
1523 
1524                                         if (c != contract_id) {
1525                                                 ret = scf_entry_add_value(t_cid,
1526                                                     val);
1527                                                 assert(ret == 0);
1528                                         } else {
1529                                                 scf_value_destroy(val);
1530                                         }
1531 
1532                                         goto next_val;
1533                                 }
1534 
1535                                 scf_value_destroy(val);
1536                         } else {
1537                                 switch (scf_error()) {
1538                                 case SCF_ERROR_CONNECTION_BROKEN:
1539                                 default:
1540                                         ret = ECONNABORTED;
1541                                         goto remove_contract_cleanup;
1542 
1543                                 case SCF_ERROR_TYPE_MISMATCH:
1544                                         break;
1545 
1546                                 case SCF_ERROR_INVALID_ARGUMENT:
1547                                 case SCF_ERROR_NOT_SET:
1548                                         bad_fail("scf_property_is_type",
1549                                             scf_error());
1550                                 }
1551                         }
1552                 } else {
1553                         switch (scf_error()) {
1554                         case SCF_ERROR_CONNECTION_BROKEN:
1555                         default:
1556                                 ret = ECONNABORTED;
1557                                 goto remove_contract_cleanup;
1558 
1559                         case SCF_ERROR_DELETED:
1560                                 scf_entry_destroy(t_cid);
1561                                 goto add;
1562 
1563                         case SCF_ERROR_NOT_FOUND:
1564                                 break;
1565 
1566                         case SCF_ERROR_HANDLE_MISMATCH:
1567                         case SCF_ERROR_INVALID_ARGUMENT:
1568                         case SCF_ERROR_NOT_SET:
1569                                 bad_fail("scf_pg_get_property", scf_error());
1570                         }
1571 
1572 new:
1573                         if (scf_transaction_property_new(t, t_cid, pname,
1574                             SCF_TYPE_COUNT) != 0) {
1575                                 switch (scf_error()) {
1576                                 case SCF_ERROR_CONNECTION_BROKEN:
1577                                 default:
1578                                         ret = ECONNABORTED;
1579                                         goto remove_contract_cleanup;
1580 
1581                                 case SCF_ERROR_DELETED:
1582                                         scf_entry_destroy(t_cid);
1583                                         goto add;
1584 
1585                                 case SCF_ERROR_EXISTS:
1586                                         goto replace;
1587 
1588                                 case SCF_ERROR_HANDLE_MISMATCH:
1589                                 case SCF_ERROR_INVALID_ARGUMENT:
1590                                 case SCF_ERROR_NOT_SET:
1591                                         bad_fail("scf_transaction_property_new",
1592                                             scf_error());
1593                                 }
1594                         }
1595                 }
1596 
1597                 ret = scf_transaction_commit(t);
1598                 if (ret == -1) {
1599                         switch (scf_error()) {
1600                         case SCF_ERROR_CONNECTION_BROKEN:
1601                         default:
1602                                 ret = ECONNABORTED;
1603                                 goto remove_contract_cleanup;
1604 
1605                         case SCF_ERROR_DELETED:
1606                                 goto add;
1607 
1608                         case SCF_ERROR_PERMISSION_DENIED:
1609                                 ret = EPERM;
1610                                 goto remove_contract_cleanup;
1611 
1612                         case SCF_ERROR_BACKEND_ACCESS:
1613                                 ret = EACCES;
1614                                 goto remove_contract_cleanup;
1615 
1616                         case SCF_ERROR_BACKEND_READONLY:
1617                                 ret = EROFS;
1618                                 goto remove_contract_cleanup;
1619 
1620                         case SCF_ERROR_NOT_SET:
1621                                 bad_fail("scf_transaction_commit", scf_error());
1622                         }
1623                 }
1624                 if (ret == 1) {
1625                         ret = 0;
1626                         break;
1627                 }
1628 
1629                 scf_transaction_destroy_children(t);
1630                 if (scf_pg_update(pg) == -1) {
1631                         switch (scf_error()) {
1632                         case SCF_ERROR_CONNECTION_BROKEN:
1633                         default:
1634                                 ret = ECONNABORTED;
1635                                 goto remove_contract_cleanup;
1636 
1637                         case SCF_ERROR_DELETED:
1638                                 goto add;
1639 
1640                         case SCF_ERROR_NOT_SET:
1641                                 bad_fail("scf_pg_update", scf_error());
1642                         }
1643                 }
1644         }
1645 
1646 remove_contract_cleanup:
1647         scf_transaction_destroy_children(t);
1648         scf_transaction_destroy(t);
1649         scf_iter_destroy(iter);
1650         scf_property_destroy(prop);
1651         scf_pg_destroy(pg);
1652 
1653         return (ret);
1654 }
1655 
1656 /*
1657  * Fails with
1658  *   EINVAL - type is invalid
1659  *   ENOMEM
1660  *   ECONNABORTED - repository disconnection
1661  *   EBADF - s_inst is not set
1662  *   ECANCELED - s_inst is deleted
1663  *   EPERM
1664  *   EACCES
1665  *   EROFS
1666  */
1667 int
1668 restarter_store_contract(scf_instance_t *s_inst, ctid_t contract_id,
1669     restarter_contract_type_t type)
1670 {
1671         scf_handle_t *h;
1672         scf_transaction_t *t = NULL;
1673         scf_transaction_entry_t *t_cid = NULL;
1674         scf_value_t *val;
1675         scf_propertygroup_t *pg = NULL;
1676         scf_property_t *prop = NULL;
1677         scf_iter_t *iter = NULL;
1678         const char *pname;
1679         int ret = 0, primary;
1680 
1681         if (type == RESTARTER_CONTRACT_PRIMARY)
1682                 primary = 1;
1683         else if (type == RESTARTER_CONTRACT_TRANSIENT)
1684                 primary = 0;
1685         else
1686                 return (EINVAL);
1687 
1688         h = scf_instance_handle(s_inst);
1689 
1690         pg = scf_pg_create(h);
1691         prop = scf_property_create(h);
1692         iter = scf_iter_create(h);
1693         t = scf_transaction_create(h);
1694 
1695         if (pg == NULL || prop == NULL || iter == NULL || t == NULL) {
1696                 ret = ENOMEM;
1697                 goto out;
1698         }
1699 
1700 add:
1701         scf_transaction_destroy_children(t);
1702         ret = instance_get_or_add_pg(s_inst, SCF_PG_RESTARTER,
1703             SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg);
1704         if (ret != 0)
1705                 goto out;
1706 
1707         pname = primary ? SCF_PROPERTY_CONTRACT :
1708             SCF_PROPERTY_TRANSIENT_CONTRACT;
1709 
1710         for (;;) {
1711                 if (scf_transaction_start(t, pg) != 0) {
1712                         switch (scf_error()) {
1713                         case SCF_ERROR_CONNECTION_BROKEN:
1714                         default:
1715                                 ret = ECONNABORTED;
1716                                 goto out;
1717 
1718                         case SCF_ERROR_DELETED:
1719                                 goto add;
1720 
1721                         case SCF_ERROR_PERMISSION_DENIED:
1722                                 ret = EPERM;
1723                                 goto out;
1724 
1725                         case SCF_ERROR_BACKEND_ACCESS:
1726                                 ret = EACCES;
1727                                 goto out;
1728 
1729                         case SCF_ERROR_BACKEND_READONLY:
1730                                 ret = EROFS;
1731                                 goto out;
1732 
1733                         case SCF_ERROR_HANDLE_MISMATCH:
1734                         case SCF_ERROR_IN_USE:
1735                         case SCF_ERROR_NOT_SET:
1736                                 bad_fail("scf_transaction_start", scf_error());
1737                         }
1738                 }
1739 
1740                 t_cid = scf_entry_create(h);
1741                 if (t_cid == NULL) {
1742                         ret = ENOMEM;
1743                         goto out;
1744                 }
1745 
1746                 if (scf_pg_get_property(pg, pname, prop) == 0) {
1747 replace:
1748                         if (scf_transaction_property_change_type(t, t_cid,
1749                             pname, SCF_TYPE_COUNT) != 0) {
1750                                 switch (scf_error()) {
1751                                 case SCF_ERROR_CONNECTION_BROKEN:
1752                                 default:
1753                                         ret = ECONNABORTED;
1754                                         goto out;
1755 
1756                                 case SCF_ERROR_DELETED:
1757                                         scf_entry_destroy(t_cid);
1758                                         goto add;
1759 
1760                                 case SCF_ERROR_NOT_FOUND:
1761                                         goto new;
1762 
1763                                 case SCF_ERROR_HANDLE_MISMATCH:
1764                                 case SCF_ERROR_INVALID_ARGUMENT:
1765                                 case SCF_ERROR_IN_USE:
1766                                 case SCF_ERROR_NOT_SET:
1767                                         bad_fail(
1768                                         "scf_transaction_propert_change_type",
1769                                             scf_error());
1770                                 }
1771                         }
1772 
1773                         if (scf_property_is_type(prop, SCF_TYPE_COUNT) == 0) {
1774                                 if (scf_iter_property_values(iter, prop) != 0) {
1775                                         switch (scf_error()) {
1776                                         case SCF_ERROR_CONNECTION_BROKEN:
1777                                         default:
1778                                                 ret = ECONNABORTED;
1779                                                 goto out;
1780 
1781                                         case SCF_ERROR_NOT_SET:
1782                                         case SCF_ERROR_HANDLE_MISMATCH:
1783                                                 bad_fail(
1784                                                     "scf_iter_property_values",
1785                                                     scf_error());
1786                                         }
1787                                 }
1788 
1789 next_val:
1790                                 val = scf_value_create(h);
1791                                 if (val == NULL) {
1792                                         assert(scf_error() ==
1793                                             SCF_ERROR_NO_MEMORY);
1794                                         ret = ENOMEM;
1795                                         goto out;
1796                                 }
1797 
1798                                 ret = scf_iter_next_value(iter, val);
1799                                 if (ret == -1) {
1800                                         switch (scf_error()) {
1801                                         case SCF_ERROR_CONNECTION_BROKEN:
1802                                         default:
1803                                                 ret = ECONNABORTED;
1804                                                 goto out;
1805 
1806                                         case SCF_ERROR_DELETED:
1807                                                 scf_value_destroy(val);
1808                                                 goto add;
1809 
1810                                         case SCF_ERROR_HANDLE_MISMATCH:
1811                                         case SCF_ERROR_INVALID_ARGUMENT:
1812                                         case SCF_ERROR_PERMISSION_DENIED:
1813                                                 bad_fail(
1814                                                     "scf_iter_next_value",
1815                                                     scf_error());
1816                                         }
1817                                 }
1818 
1819                                 if (ret == 1) {
1820                                         ret = scf_entry_add_value(t_cid, val);
1821                                         assert(ret == 0);
1822 
1823                                         goto next_val;
1824                                 }
1825 
1826                                 scf_value_destroy(val);
1827                         } else {
1828                                 switch (scf_error()) {
1829                                 case SCF_ERROR_CONNECTION_BROKEN:
1830                                 default:
1831                                         ret = ECONNABORTED;
1832                                         goto out;
1833 
1834                                 case SCF_ERROR_TYPE_MISMATCH:
1835                                         break;
1836 
1837                                 case SCF_ERROR_INVALID_ARGUMENT:
1838                                 case SCF_ERROR_NOT_SET:
1839                                         bad_fail("scf_property_is_type",
1840                                             scf_error());
1841                                 }
1842                         }
1843                 } else {
1844                         switch (scf_error()) {
1845                         case SCF_ERROR_CONNECTION_BROKEN:
1846                         default:
1847                                 ret = ECONNABORTED;
1848                                 goto out;
1849 
1850                         case SCF_ERROR_DELETED:
1851                                 scf_entry_destroy(t_cid);
1852                                 goto add;
1853 
1854                         case SCF_ERROR_NOT_FOUND:
1855                                 break;
1856 
1857                         case SCF_ERROR_HANDLE_MISMATCH:
1858                         case SCF_ERROR_INVALID_ARGUMENT:
1859                         case SCF_ERROR_NOT_SET:
1860                                 bad_fail("scf_pg_get_property", scf_error());
1861                         }
1862 
1863 new:
1864                         if (scf_transaction_property_new(t, t_cid, pname,
1865                             SCF_TYPE_COUNT) != 0) {
1866                                 switch (scf_error()) {
1867                                 case SCF_ERROR_CONNECTION_BROKEN:
1868                                 default:
1869                                         ret = ECONNABORTED;
1870                                         goto out;
1871 
1872                                 case SCF_ERROR_DELETED:
1873                                         scf_entry_destroy(t_cid);
1874                                         goto add;
1875 
1876                                 case SCF_ERROR_EXISTS:
1877                                         goto replace;
1878 
1879                                 case SCF_ERROR_HANDLE_MISMATCH:
1880                                 case SCF_ERROR_INVALID_ARGUMENT:
1881                                 case SCF_ERROR_NOT_SET:
1882                                         bad_fail("scf_transaction_property_new",
1883                                             scf_error());
1884                                 }
1885                         }
1886                 }
1887 
1888                 val = scf_value_create(h);
1889                 if (val == NULL) {
1890                         assert(scf_error() == SCF_ERROR_NO_MEMORY);
1891                         ret = ENOMEM;
1892                         goto out;
1893                 }
1894 
1895                 scf_value_set_count(val, contract_id);
1896                 ret = scf_entry_add_value(t_cid, val);
1897                 assert(ret == 0);
1898 
1899                 ret = scf_transaction_commit(t);
1900                 if (ret == -1) {
1901                         switch (scf_error()) {
1902                         case SCF_ERROR_CONNECTION_BROKEN:
1903                         default:
1904                                 ret = ECONNABORTED;
1905                                 goto out;
1906 
1907                         case SCF_ERROR_DELETED:
1908                                 goto add;
1909 
1910                         case SCF_ERROR_PERMISSION_DENIED:
1911                                 ret = EPERM;
1912                                 goto out;
1913 
1914                         case SCF_ERROR_BACKEND_ACCESS:
1915                                 ret = EACCES;
1916                                 goto out;
1917 
1918                         case SCF_ERROR_BACKEND_READONLY:
1919                                 ret = EROFS;
1920                                 goto out;
1921 
1922                         case SCF_ERROR_NOT_SET:
1923                                 bad_fail("scf_transaction_commit", scf_error());
1924                         }
1925                 }
1926                 if (ret == 1) {
1927                         ret = 0;
1928                         break;
1929                 }
1930 
1931                 scf_transaction_destroy_children(t);
1932                 if (scf_pg_update(pg) == -1) {
1933                         switch (scf_error()) {
1934                         case SCF_ERROR_CONNECTION_BROKEN:
1935                         default:
1936                                 ret = ECONNABORTED;
1937                                 goto out;
1938 
1939                         case SCF_ERROR_DELETED:
1940                                 goto add;
1941 
1942                         case SCF_ERROR_NOT_SET:
1943                                 bad_fail("scf_pg_update", scf_error());
1944                         }
1945                 }
1946         }
1947 
1948 out:
1949         scf_transaction_destroy_children(t);
1950         scf_transaction_destroy(t);
1951         scf_iter_destroy(iter);
1952         scf_property_destroy(prop);
1953         scf_pg_destroy(pg);
1954 
1955         return (ret);
1956 }
1957 
1958 int
1959 restarter_rm_libs_loadable()
1960 {
1961         void *libhndl;
1962 
1963         if (method_context_safety)
1964                 return (1);
1965 
1966         if ((libhndl = dlopen("libpool.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1967                 return (0);
1968 
1969         (void) dlclose(libhndl);
1970 
1971         if ((libhndl = dlopen("libproject.so", RTLD_LAZY | RTLD_LOCAL)) == NULL)
1972                 return (0);
1973 
1974         (void) dlclose(libhndl);
1975 
1976         method_context_safety = 1;
1977 
1978         return (1);
1979 }
1980 
1981 static int
1982 get_astring_val(scf_propertygroup_t *pg, const char *name, char *buf,
1983     size_t bufsz, scf_property_t *prop, scf_value_t *val)
1984 {
1985         ssize_t szret;
1986 
1987         if (pg == NULL)
1988                 return (-1);
1989 
1990         if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
1991                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1992                         uu_die(rcbroken);
1993                 return (-1);
1994         }
1995 
1996         if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
1997                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
1998                         uu_die(rcbroken);
1999                 return (-1);
2000         }
2001 
2002         szret = scf_value_get_astring(val, buf, bufsz);
2003 
2004         return (szret >= 0 ? 0 : -1);
2005 }
2006 
2007 static int
2008 get_boolean_val(scf_propertygroup_t *pg, const char *name, uint8_t *b,
2009     scf_property_t *prop, scf_value_t *val)
2010 {
2011         if (scf_pg_get_property(pg, name, prop) != SCF_SUCCESS) {
2012                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2013                         uu_die(rcbroken);
2014                 return (-1);
2015         }
2016 
2017         if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2018                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
2019                         uu_die(rcbroken);
2020                 return (-1);
2021         }
2022 
2023         if (scf_value_get_boolean(val, b))
2024                 return (-1);
2025 
2026         return (0);
2027 }
2028 
2029 /*
2030  * Try to load mcp->pwd, if it isn't already.
2031  * Fails with
2032  *   ENOMEM - malloc() failed
2033  *   ENOENT - no entry found
2034  *   EIO - I/O error
2035  *   EMFILE - process out of file descriptors
2036  *   ENFILE - system out of file handles
2037  */
2038 static int
2039 lookup_pwd(struct method_context *mcp)
2040 {
2041         struct passwd *pwdp;
2042 
2043         if (mcp->pwbuf != NULL && mcp->pwd.pw_uid == mcp->uid)
2044                 return (0);
2045 
2046         if (mcp->pwbuf == NULL) {
2047                 mcp->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2048                 assert(mcp->pwbufsz >= 0);
2049                 mcp->pwbuf = malloc(mcp->pwbufsz);
2050                 if (mcp->pwbuf == NULL)
2051                         return (ENOMEM);
2052         }
2053 
2054         do {
2055                 errno = 0;
2056                 pwdp = getpwuid_r(mcp->uid, &mcp->pwd, mcp->pwbuf,
2057                     mcp->pwbufsz);
2058         } while (pwdp == NULL && errno == EINTR);
2059         if (pwdp != NULL)
2060                 return (0);
2061 
2062         free(mcp->pwbuf);
2063         mcp->pwbuf = NULL;
2064 
2065         switch (errno) {
2066         case 0:
2067         default:
2068                 /*
2069                  * Until bug 5065780 is fixed, getpwuid_r() can fail with
2070                  * ENOENT, particularly on the miniroot.  Since the
2071                  * documentation is inaccurate, we'll return ENOENT for unknown
2072                  * errors.
2073                  */
2074                 return (ENOENT);
2075 
2076         case EIO:
2077         case EMFILE:
2078         case ENFILE:
2079                 return (errno);
2080 
2081         case ERANGE:
2082                 bad_fail("getpwuid_r", errno);
2083                 /* NOTREACHED */
2084         }
2085 }
2086 
2087 /*
2088  * Get the user id for str.  Returns 0 on success or
2089  *   ERANGE     the uid is too big
2090  *   EINVAL     the string starts with a digit, but is not a valid uid
2091  *   ENOMEM     out of memory
2092  *   ENOENT     no passwd entry for str
2093  *   EIO        an I/O error has occurred
2094  *   EMFILE/ENFILE  out of file descriptors
2095  */
2096 int
2097 get_uid(const char *str, struct method_context *ci, uid_t *uidp)
2098 {
2099         if (isdigit(str[0])) {
2100                 uid_t uid;
2101                 char *cp;
2102 
2103                 errno = 0;
2104                 uid = strtol(str, &cp, 10);
2105 
2106                 if (uid == 0 && errno != 0) {
2107                         assert(errno != EINVAL);
2108                         return (errno);
2109                 }
2110 
2111                 for (; *cp != '\0'; ++cp)
2112                         if (*cp != ' ' || *cp != '\t')
2113                                 return (EINVAL);
2114 
2115                 if (uid > UID_MAX)
2116                         return (EINVAL);
2117 
2118                 *uidp = uid;
2119                 return (0);
2120         } else {
2121                 struct passwd *pwdp;
2122 
2123                 if (ci->pwbuf == NULL) {
2124                         ci->pwbufsz = sysconf(_SC_GETPW_R_SIZE_MAX);
2125                         ci->pwbuf = malloc(ci->pwbufsz);
2126                         if (ci->pwbuf == NULL)
2127                                 return (ENOMEM);
2128                 }
2129 
2130                 do {
2131                         errno = 0;
2132                         pwdp =
2133                             getpwnam_r(str, &ci->pwd, ci->pwbuf, ci->pwbufsz);
2134                 } while (pwdp == NULL && errno == EINTR);
2135 
2136                 if (pwdp != NULL) {
2137                         *uidp = ci->pwd.pw_uid;
2138                         return (0);
2139                 } else {
2140                         free(ci->pwbuf);
2141                         ci->pwbuf = NULL;
2142                         switch (errno) {
2143                         case 0:
2144                                 return (ENOENT);
2145 
2146                         case ENOENT:
2147                         case EIO:
2148                         case EMFILE:
2149                         case ENFILE:
2150                                 return (errno);
2151 
2152                         case ERANGE:
2153                         default:
2154                                 bad_fail("getpwnam_r", errno);
2155                                 /* NOTREACHED */
2156                         }
2157                 }
2158         }
2159 }
2160 
2161 gid_t
2162 get_gid(const char *str)
2163 {
2164         if (isdigit(str[0])) {
2165                 gid_t gid;
2166                 char *cp;
2167 
2168                 errno = 0;
2169                 gid = strtol(str, &cp, 10);
2170 
2171                 if (gid == 0 && errno != 0)
2172                         return ((gid_t)-1);
2173 
2174                 for (; *cp != '\0'; ++cp)
2175                         if (*cp != ' ' || *cp != '\t')
2176                                 return ((gid_t)-1);
2177 
2178                 return (gid);
2179         } else {
2180                 struct group grp, *ret;
2181                 char *buffer;
2182                 size_t buflen;
2183 
2184                 buflen = sysconf(_SC_GETGR_R_SIZE_MAX);
2185                 buffer = malloc(buflen);
2186                 if (buffer == NULL)
2187                         uu_die(allocfail);
2188 
2189                 errno = 0;
2190                 ret = getgrnam_r(str, &grp, buffer, buflen);
2191                 free(buffer);
2192 
2193                 return (ret == NULL ? (gid_t)-1 : grp.gr_gid);
2194         }
2195 }
2196 
2197 /*
2198  * Fails with
2199  *   ENOMEM - out of memory
2200  *   ENOENT - no passwd entry
2201  *            no project entry
2202  *   EIO - an I/O error occurred
2203  *   EMFILE - the process is out of file descriptors
2204  *   ENFILE - the system is out of file handles
2205  *   ERANGE - the project id is out of range
2206  *   EINVAL - str is invalid
2207  *   E2BIG - the project entry was too big
2208  *   -1 - the name service switch is misconfigured
2209  */
2210 int
2211 get_projid(const char *str, struct method_context *cip)
2212 {
2213         int ret;
2214         void *buf;
2215         const size_t bufsz = PROJECT_BUFSZ;
2216         struct project proj, *pp;
2217 
2218         if (strcmp(str, ":default") == 0) {
2219                 if (cip->uid == 0) {
2220                         /* Don't change project for root services */
2221                         cip->project = NULL;
2222                         return (0);
2223                 }
2224 
2225                 switch (ret = lookup_pwd(cip)) {
2226                 case 0:
2227                         break;
2228 
2229                 case ENOMEM:
2230                 case ENOENT:
2231                 case EIO:
2232                 case EMFILE:
2233                 case ENFILE:
2234                         return (ret);
2235 
2236                 default:
2237                         bad_fail("lookup_pwd", ret);
2238                 }
2239 
2240                 buf = malloc(bufsz);
2241                 if (buf == NULL)
2242                         return (ENOMEM);
2243 
2244                 do {
2245                         errno = 0;
2246                         pp = getdefaultproj(cip->pwd.pw_name, &proj, buf,
2247                             bufsz);
2248                 } while (pp == NULL && errno == EINTR);
2249 
2250                 /* to be continued ... */
2251         } else {
2252                 projid_t projid;
2253                 char *cp;
2254 
2255                 if (!isdigit(str[0])) {
2256                         cip->project = strdup(str);
2257                         return (cip->project != NULL ? 0 : ENOMEM);
2258                 }
2259 
2260                 errno = 0;
2261                 projid = strtol(str, &cp, 10);
2262 
2263                 if (projid == 0 && errno != 0) {
2264                         assert(errno == ERANGE);
2265                         return (errno);
2266                 }
2267 
2268                 for (; *cp != '\0'; ++cp)
2269                         if (*cp != ' ' || *cp != '\t')
2270                                 return (EINVAL);
2271 
2272                 if (projid > MAXPROJID)
2273                         return (ERANGE);
2274 
2275                 buf = malloc(bufsz);
2276                 if (buf == NULL)
2277                         return (ENOMEM);
2278 
2279                 do {
2280                         errno = 0;
2281                         pp = getprojbyid(projid, &proj, buf, bufsz);
2282                 } while (pp == NULL && errno == EINTR);
2283         }
2284 
2285         if (pp) {
2286                 cip->project = strdup(pp->pj_name);
2287                 free(buf);
2288                 return (cip->project != NULL ? 0 : ENOMEM);
2289         }
2290 
2291         free(buf);
2292 
2293         switch (errno) {
2294         case 0:
2295                 return (ENOENT);
2296 
2297         case EIO:
2298         case EMFILE:
2299         case ENFILE:
2300                 return (errno);
2301 
2302         case ERANGE:
2303                 return (E2BIG);
2304 
2305         default:
2306                 return (-1);
2307         }
2308 }
2309 
2310 /*
2311  * Parse the supp_groups property value and populate ci->groups.  Returns
2312  * EINVAL (get_gid() failed for one of the components), E2BIG (the property has
2313  * more than NGROUPS_MAX-1 groups), or 0 on success.
2314  */
2315 int
2316 get_groups(char *str, struct method_context *ci)
2317 {
2318         char *cp, *end, *next;
2319         uint_t i;
2320 
2321         const char * const whitespace = " \t";
2322         const char * const illegal = ", \t";
2323 
2324         if (str[0] == '\0') {
2325                 ci->ngroups = 0;
2326                 return (0);
2327         }
2328 
2329         for (cp = str, i = 0; *cp != '\0'; ) {
2330                 /* skip whitespace */
2331                 cp += strspn(cp, whitespace);
2332 
2333                 /* find the end */
2334                 end = cp + strcspn(cp, illegal);
2335 
2336                 /* skip whitespace after end */
2337                 next = end + strspn(end, whitespace);
2338 
2339                 /* if there's a comma, it separates the fields */
2340                 if (*next == ',')
2341                         ++next;
2342 
2343                 *end = '\0';
2344 
2345                 if ((ci->groups[i] = get_gid(cp)) == (gid_t)-1) {
2346                         ci->ngroups = 0;
2347                         return (EINVAL);
2348                 }
2349 
2350                 ++i;
2351                 if (i > NGROUPS_MAX - 1) {
2352                         ci->ngroups = 0;
2353                         return (E2BIG);
2354                 }
2355 
2356                 cp = next;
2357         }
2358 
2359         ci->ngroups = i;
2360         return (0);
2361 }
2362 
2363 
2364 /*
2365  * Return an error message structure containing the error message
2366  * with context, and the error so the caller can make a decision
2367  * on what to do next.
2368  *
2369  * Because get_ids uses the mc_error_create() function which can
2370  * reallocate the merr, this function must return the merr pointer
2371  * in case it was reallocated.
2372  */
2373 static mc_error_t *
2374 get_profile(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2375     scf_property_t *prop, scf_value_t *val, const char *cmdline,
2376     struct method_context *ci, mc_error_t *merr)
2377 {
2378         char *buf = ci->vbuf;
2379         ssize_t buf_sz = ci->vbuf_sz;
2380         char cmd[PATH_MAX];
2381         char *cp, *value;
2382         const char *cmdp;
2383         execattr_t *eap;
2384         mc_error_t *err = merr;
2385         int r;
2386 
2387         if (!(get_astring_val(methpg, SCF_PROPERTY_PROFILE, buf, buf_sz, prop,
2388             val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PROFILE, buf,
2389             buf_sz, prop, val) == 0))
2390                 return (mc_error_create(merr, scf_error(),
2391                     "Method context requires a profile, but the  \"%s\" "
2392                     "property could not be read. scf_error is %s",
2393                     SCF_PROPERTY_PROFILE, scf_strerror(scf_error())));
2394 
2395         /* Extract the command from the command line. */
2396         cp = strpbrk(cmdline, " \t");
2397 
2398         if (cp == NULL) {
2399                 cmdp = cmdline;
2400         } else {
2401                 (void) strncpy(cmd, cmdline, cp - cmdline);
2402                 cmd[cp - cmdline] = '\0';
2403                 cmdp = cmd;
2404         }
2405 
2406         /* Require that cmdp[0] == '/'? */
2407 
2408         eap = getexecprof(buf, KV_COMMAND, cmdp, GET_ONE);
2409         if (eap == NULL)
2410                 return (mc_error_create(merr, ENOENT,
2411                     "Could not find the execution profile \"%s\", "
2412                     "command %s.", buf, cmdp));
2413 
2414         /* Based on pfexec.c */
2415 
2416         /* Get the euid first so we don't override ci->pwd for the uid. */
2417         if ((value = kva_match(eap->attr, EXECATTR_EUID_KW)) != NULL) {
2418                 if ((r = get_uid(value, ci, &ci->euid)) != 0) {
2419                         ci->euid = (uid_t)-1;
2420                         err = mc_error_create(merr, r,
2421                             "Could not interpret profile euid value \"%s\", "
2422                             "from the execution profile \"%s\", error %d.",
2423                             value, buf, r);
2424                         goto out;
2425                 }
2426         }
2427 
2428         if ((value = kva_match(eap->attr, EXECATTR_UID_KW)) != NULL) {
2429                 if ((r = get_uid(value, ci, &ci->uid)) != 0) {
2430                         ci->euid = ci->uid = (uid_t)-1;
2431                         err = mc_error_create(merr, r,
2432                             "Could not interpret profile uid value \"%s\", "
2433                             "from the execution profile \"%s\", error %d.",
2434                             value, buf, r);
2435                         goto out;
2436                 }
2437                 ci->euid = ci->uid;
2438         }
2439 
2440         if ((value = kva_match(eap->attr, EXECATTR_GID_KW)) != NULL) {
2441                 ci->egid = ci->gid = get_gid(value);
2442                 if (ci->gid == (gid_t)-1) {
2443                         err = mc_error_create(merr, EINVAL,
2444                             "Could not interpret profile gid value \"%s\", "
2445                             "from the execution profile \"%s\".", value, buf);
2446                         goto out;
2447                 }
2448         }
2449 
2450         if ((value = kva_match(eap->attr, EXECATTR_EGID_KW)) != NULL) {
2451                 ci->egid = get_gid(value);
2452                 if (ci->egid == (gid_t)-1) {
2453                         err = mc_error_create(merr, EINVAL,
2454                             "Could not interpret profile egid value \"%s\", "
2455                             "from the execution profile \"%s\".", value, buf);
2456                         goto out;
2457                 }
2458         }
2459 
2460         if ((value = kva_match(eap->attr, EXECATTR_LPRIV_KW)) != NULL) {
2461                 ci->lpriv_set = priv_str_to_set(value, ",", NULL);
2462                 if (ci->lpriv_set == NULL) {
2463                         if (errno != EINVAL)
2464                                 err = mc_error_create(merr, ENOMEM,
2465                                     ALLOCFAIL);
2466                         else
2467                                 err = mc_error_create(merr, EINVAL,
2468                                     "Could not interpret profile "
2469                                     "limitprivs value \"%s\", from "
2470                                     "the execution profile \"%s\".",
2471                                     value, buf);
2472                         goto out;
2473                 }
2474         }
2475 
2476         if ((value = kva_match(eap->attr, EXECATTR_IPRIV_KW)) != NULL) {
2477                 ci->priv_set = priv_str_to_set(value, ",", NULL);
2478                 if (ci->priv_set == NULL) {
2479                         if (errno != EINVAL)
2480                                 err = mc_error_create(merr, ENOMEM,
2481                                     ALLOCFAIL);
2482                         else
2483                                 err = mc_error_create(merr, EINVAL,
2484                                     "Could not interpret profile privs value "
2485                                     "\"%s\", from the execution profile "
2486                                     "\"%s\".", value, buf);
2487                         goto out;
2488                 }
2489         }
2490 
2491 out:
2492         free_execattr(eap);
2493 
2494         return (err);
2495 }
2496 
2497 /*
2498  * Return an error message structure containing the error message
2499  * with context, and the error so the caller can make a decision
2500  * on what to do next.
2501  *
2502  * Because get_ids uses the mc_error_create() function which can
2503  * reallocate the merr, this function must return the merr pointer
2504  * in case it was reallocated.
2505  */
2506 static mc_error_t *
2507 get_ids(scf_propertygroup_t *methpg, scf_propertygroup_t *instpg,
2508     scf_property_t *prop, scf_value_t *val, struct method_context *ci,
2509     mc_error_t *merr)
2510 {
2511         char *vbuf = ci->vbuf;
2512         ssize_t vbuf_sz = ci->vbuf_sz;
2513         int r;
2514 
2515         /*
2516          * This should never happen because the caller should fall through
2517          * another path of just setting the ids to defaults, instead of
2518          * attempting to get the ids here.
2519          */
2520         if (methpg == NULL && instpg == NULL)
2521                 return (mc_error_create(merr, ENOENT,
2522                     "No property groups to get ids from."));
2523 
2524         if (!(get_astring_val(methpg, SCF_PROPERTY_USER,
2525             vbuf, vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2526             SCF_PROPERTY_USER, vbuf, vbuf_sz, prop,
2527             val) == 0))
2528                 return (mc_error_create(merr, ENOENT,
2529                     "Could not get \"%s\" property.", SCF_PROPERTY_USER));
2530 
2531         if ((r = get_uid(vbuf, ci, &ci->uid)) != 0) {
2532                 ci->uid = (uid_t)-1;
2533                 return (mc_error_create(merr, r,
2534                     "Could not interpret \"%s\" property value \"%s\", "
2535                     "error %d.", SCF_PROPERTY_USER, vbuf, r));
2536         }
2537 
2538         if (!(get_astring_val(methpg, SCF_PROPERTY_GROUP, vbuf, vbuf_sz, prop,
2539             val) == 0 || get_astring_val(instpg, SCF_PROPERTY_GROUP, vbuf,
2540             vbuf_sz, prop, val) == 0)) {
2541                 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2542                         (void) strcpy(vbuf, ":default");
2543                 } else {
2544                         return (mc_error_create(merr, ENOENT,
2545                             "Could not get \"%s\" property.",
2546                             SCF_PROPERTY_GROUP));
2547                 }
2548         }
2549 
2550         if (strcmp(vbuf, ":default") != 0) {
2551                 ci->gid = get_gid(vbuf);
2552                 if (ci->gid == (gid_t)-1) {
2553                         return (mc_error_create(merr, ENOENT,
2554                             "Could not interpret \"%s\" property value \"%s\".",
2555                             SCF_PROPERTY_GROUP, vbuf));
2556                 }
2557         } else {
2558                 switch (r = lookup_pwd(ci)) {
2559                 case 0:
2560                         ci->gid = ci->pwd.pw_gid;
2561                         break;
2562 
2563                 case ENOENT:
2564                         ci->gid = (gid_t)-1;
2565                         return (mc_error_create(merr, ENOENT,
2566                             "No passwd entry for uid \"%d\".", ci->uid));
2567 
2568                 case ENOMEM:
2569                         return (mc_error_create(merr, ENOMEM,
2570                             "Out of memory."));
2571 
2572                 case EIO:
2573                 case EMFILE:
2574                 case ENFILE:
2575                         return (mc_error_create(merr, ENFILE,
2576                             "getpwuid_r() failed, error %d.", r));
2577 
2578                 default:
2579                         bad_fail("lookup_pwd", r);
2580                 }
2581         }
2582 
2583         if (!(get_astring_val(methpg, SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz,
2584             prop, val) == 0 || get_astring_val(instpg,
2585             SCF_PROPERTY_SUPP_GROUPS, vbuf, vbuf_sz, prop, val) == 0)) {
2586                 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2587                         (void) strcpy(vbuf, ":default");
2588                 } else {
2589                         return (mc_error_create(merr, ENOENT,
2590                             "Could not get supplemental groups (\"%s\") "
2591                             "property.", SCF_PROPERTY_SUPP_GROUPS));
2592                 }
2593         }
2594 
2595         if (strcmp(vbuf, ":default") != 0) {
2596                 switch (r = get_groups(vbuf, ci)) {
2597                 case 0:
2598                         break;
2599 
2600                 case EINVAL:
2601                         return (mc_error_create(merr, EINVAL,
2602                             "Could not interpret supplemental groups (\"%s\") "
2603                             "property value \"%s\".", SCF_PROPERTY_SUPP_GROUPS,
2604                             vbuf));
2605 
2606                 case E2BIG:
2607                         return (mc_error_create(merr, E2BIG,
2608                             "Too many supplemental groups values in \"%s\".",
2609                             vbuf));
2610 
2611                 default:
2612                         bad_fail("get_groups", r);
2613                 }
2614         } else {
2615                 ci->ngroups = -1;
2616         }
2617 
2618         if (!(get_astring_val(methpg, SCF_PROPERTY_PRIVILEGES, vbuf, vbuf_sz,
2619             prop, val) == 0 || get_astring_val(instpg, SCF_PROPERTY_PRIVILEGES,
2620             vbuf, vbuf_sz, prop, val) == 0)) {
2621                 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2622                         (void) strcpy(vbuf, ":default");
2623                 } else {
2624                         return (mc_error_create(merr, ENOENT,
2625                             "Could not get \"%s\" property.",
2626                             SCF_PROPERTY_PRIVILEGES));
2627                 }
2628         }
2629 
2630         /*
2631          * For default privs, we need to keep priv_set == NULL, as
2632          * we use this test elsewhere.
2633          */
2634         if (strcmp(vbuf, ":default") != 0) {
2635                 ci->priv_set = priv_str_to_set(vbuf, ",", NULL);
2636                 if (ci->priv_set == NULL) {
2637                         if (errno != EINVAL) {
2638                                 return (mc_error_create(merr, ENOMEM,
2639                                     ALLOCFAIL));
2640                         } else {
2641                                 return (mc_error_create(merr, EINVAL,
2642                                     "Could not interpret \"%s\" "
2643                                     "property value \"%s\".",
2644                                     SCF_PROPERTY_PRIVILEGES, vbuf));
2645                         }
2646                 }
2647         }
2648 
2649         if (!(get_astring_val(methpg, SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf,
2650             vbuf_sz, prop, val) == 0 || get_astring_val(instpg,
2651             SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf, vbuf_sz, prop, val) == 0)) {
2652                 if (scf_error() == SCF_ERROR_NOT_FOUND) {
2653                         (void) strcpy(vbuf, ":default");
2654                 } else {
2655                         return (mc_error_create(merr, ENOENT,
2656                             "Could not get \"%s\" property.",
2657                             SCF_PROPERTY_LIMIT_PRIVILEGES));
2658                 }
2659         }
2660 
2661         if (strcmp(vbuf, ":default") == 0)
2662                 /*
2663                  * L must default to all privileges so root NPA services see
2664                  * iE = all.  "zone" is all privileges available in the current
2665                  * zone, equivalent to "all" in the global zone.
2666                  */
2667                 (void) strcpy(vbuf, "zone");
2668 
2669         ci->lpriv_set = priv_str_to_set(vbuf, ",", NULL);
2670         if (ci->lpriv_set == NULL) {
2671                 if (errno != EINVAL) {
2672                         return (mc_error_create(merr, ENOMEM, ALLOCFAIL));
2673                 } else {
2674                         return (mc_error_create(merr, EINVAL,
2675                             "Could not interpret \"%s\" property value \"%s\".",
2676                             SCF_PROPERTY_LIMIT_PRIVILEGES, vbuf));
2677                 }
2678         }
2679 
2680         return (merr);
2681 }
2682 
2683 static int
2684 get_environment(scf_handle_t *h, scf_propertygroup_t *pg,
2685     struct method_context *mcp, scf_property_t *prop, scf_value_t *val)
2686 {
2687         scf_iter_t *iter;
2688         scf_type_t type;
2689         size_t i = 0;
2690         int ret;
2691 
2692         if (scf_pg_get_property(pg, SCF_PROPERTY_ENVIRONMENT, prop) != 0) {
2693                 if (scf_error() == SCF_ERROR_NOT_FOUND)
2694                         return (ENOENT);
2695                 return (scf_error());
2696         }
2697         if (scf_property_type(prop, &type) != 0)
2698                 return (scf_error());
2699         if (type != SCF_TYPE_ASTRING)
2700                 return (EINVAL);
2701         if ((iter = scf_iter_create(h)) == NULL)
2702                 return (scf_error());
2703 
2704         if (scf_iter_property_values(iter, prop) != 0) {
2705                 ret = scf_error();
2706                 scf_iter_destroy(iter);
2707                 return (ret);
2708         }
2709 
2710         mcp->env_sz = 10;
2711 
2712         if ((mcp->env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz)) == NULL) {
2713                 ret = ENOMEM;
2714                 goto out;
2715         }
2716 
2717         while ((ret = scf_iter_next_value(iter, val)) == 1) {
2718                 ret = scf_value_get_as_string(val, mcp->vbuf, mcp->vbuf_sz);
2719                 if (ret == -1) {
2720                         ret = scf_error();
2721                         goto out;
2722                 }
2723 
2724                 if ((mcp->env[i] = strdup(mcp->vbuf)) == NULL) {
2725                         ret = ENOMEM;
2726                         goto out;
2727                 }
2728 
2729                 if (++i == mcp->env_sz) {
2730                         char **env;
2731                         mcp->env_sz *= 2;
2732                         env = uu_zalloc(sizeof (*mcp->env) * mcp->env_sz);
2733                         if (env == NULL) {
2734                                 ret = ENOMEM;
2735                                 goto out;
2736                         }
2737                         (void) memcpy(env, mcp->env,
2738                             sizeof (*mcp->env) * (mcp->env_sz / 2));
2739                         free(mcp->env);
2740                         mcp->env = env;
2741                 }
2742         }
2743 
2744         if (ret == -1)
2745                 ret = scf_error();
2746 
2747 out:
2748         scf_iter_destroy(iter);
2749         return (ret);
2750 }
2751 
2752 /*
2753  * Fetch method context information from the repository, allocate and fill
2754  * a method_context structure, return it in *mcpp, and return NULL.
2755  *
2756  * If no method_context is defined, original init context is provided, where
2757  * the working directory is '/', and uid/gid are 0/0.  But if a method_context
2758  * is defined at any level the smf_method(5) method_context defaults are used.
2759  *
2760  * Return an error message structure containing the error message
2761  * with context, and the error so the caller can make a decision
2762  * on what to do next.
2763  *
2764  * Error Types :
2765  *      E2BIG           Too many values or entry is too big
2766  *      EINVAL          Invalid value
2767  *      EIO             an I/O error has occured
2768  *      ENOENT          no entry for value
2769  *      ENOMEM          out of memory
2770  *      ENOTSUP         Version mismatch
2771  *      ERANGE          value is out of range
2772  *      EMFILE/ENFILE   out of file descriptors
2773  *
2774  *      SCF_ERROR_BACKEND_ACCESS
2775  *      SCF_ERROR_CONNECTION_BROKEN
2776  *      SCF_ERROR_DELETED
2777  *      SCF_ERROR_CONSTRAINT_VIOLATED
2778  *      SCF_ERROR_HANDLE_DESTROYED
2779  *      SCF_ERROR_INTERNAL
2780  *      SCF_ERROR_INVALID_ARGUMENT
2781  *      SCF_ERROR_NO_MEMORY
2782  *      SCF_ERROR_NO_RESOURCES
2783  *      SCF_ERROR_NOT_BOUND
2784  *      SCF_ERROR_NOT_FOUND
2785  *      SCF_ERROR_NOT_SET
2786  *      SCF_ERROR_TYPE_MISMATCH
2787  *
2788  */
2789 mc_error_t *
2790 restarter_get_method_context(uint_t version, scf_instance_t *inst,
2791     scf_snapshot_t *snap, const char *mname, const char *cmdline,
2792     struct method_context **mcpp)
2793 {
2794         scf_handle_t *h;
2795         scf_propertygroup_t *methpg = NULL;
2796         scf_propertygroup_t *instpg = NULL;
2797         scf_propertygroup_t *pg = NULL;
2798         scf_property_t *prop = NULL;
2799         scf_value_t *val = NULL;
2800         scf_type_t ty;
2801         uint8_t use_profile;
2802         int ret = 0;
2803         int mc_used = 0;
2804         mc_error_t *err = NULL;
2805         struct method_context *cip;
2806 
2807         if ((err = malloc(sizeof (mc_error_t))) == NULL)
2808                 return (mc_error_create(NULL, ENOMEM, NULL));
2809 
2810         /* Set the type to zero to track if an error occured. */
2811         err->type = 0;
2812 
2813         if (version != RESTARTER_METHOD_CONTEXT_VERSION)
2814                 return (mc_error_create(err, ENOTSUP,
2815                     "Invalid client version %d. (Expected %d)",
2816                     version, RESTARTER_METHOD_CONTEXT_VERSION));
2817 
2818         /* Get the handle before we allocate anything. */
2819         h = scf_instance_handle(inst);
2820         if (h == NULL)
2821                 return (mc_error_create(err, scf_error(),
2822                     scf_strerror(scf_error())));
2823 
2824         cip = malloc(sizeof (*cip));
2825         if (cip == NULL)
2826                 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2827 
2828         (void) memset(cip, 0, sizeof (*cip));
2829         cip->uid = (uid_t)-1;
2830         cip->euid = (uid_t)-1;
2831         cip->gid = (gid_t)-1;
2832         cip->egid = (gid_t)-1;
2833 
2834         cip->vbuf_sz = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
2835         assert(cip->vbuf_sz >= 0);
2836         cip->vbuf = malloc(cip->vbuf_sz);
2837         if (cip->vbuf == NULL) {
2838                 free(cip);
2839                 return (mc_error_create(err, ENOMEM, ALLOCFAIL));
2840         }
2841 
2842         if ((instpg = scf_pg_create(h)) == NULL ||
2843             (methpg = scf_pg_create(h)) == NULL ||
2844             (prop = scf_property_create(h)) == NULL ||
2845             (val = scf_value_create(h)) == NULL) {
2846                 err = mc_error_create(err, scf_error(),
2847                     "Failed to create repository object: %s",
2848                     scf_strerror(scf_error()));
2849                 goto out;
2850         }
2851 
2852         /*
2853          * The method environment, and the credentials/profile data,
2854          * may be found either in the pg for the method (methpg),
2855          * or in the instance/service SCF_PG_METHOD_CONTEXT pg (named
2856          * instpg below).
2857          */
2858 
2859         if (scf_instance_get_pg_composed(inst, snap, mname, methpg) !=
2860             SCF_SUCCESS) {
2861                 err = mc_error_create(err, scf_error(), "Unable to get the "
2862                     "\"%s\" method, %s", mname, scf_strerror(scf_error()));
2863                 goto out;
2864         }
2865 
2866         if (scf_instance_get_pg_composed(inst, snap, SCF_PG_METHOD_CONTEXT,
2867             instpg) != SCF_SUCCESS) {
2868                 if (scf_error() != SCF_ERROR_NOT_FOUND) {
2869                         err = mc_error_create(err, scf_error(),
2870                             "Unable to retrieve the \"%s\" property group, %s",
2871                             SCF_PG_METHOD_CONTEXT, scf_strerror(scf_error()));
2872                         goto out;
2873                 }
2874                 scf_pg_destroy(instpg);
2875                 instpg = NULL;
2876         } else {
2877                 mc_used++;
2878         }
2879 
2880         ret = get_environment(h, methpg, cip, prop, val);
2881         if (ret == ENOENT && instpg != NULL) {
2882                 ret = get_environment(h, instpg, cip, prop, val);
2883         }
2884 
2885         switch (ret) {
2886         case 0:
2887                 mc_used++;
2888                 break;
2889         case ENOENT:
2890                 break;
2891         case ENOMEM:
2892                 err = mc_error_create(err, ret, "Out of memory.");
2893                 goto out;
2894         case EINVAL:
2895                 err = mc_error_create(err, ret, "Invalid method environment.");
2896                 goto out;
2897         default:
2898                 err = mc_error_create(err, ret,
2899                     "Get method environment failed: %s", scf_strerror(ret));
2900                 goto out;
2901         }
2902 
2903         pg = methpg;
2904 
2905         ret = scf_pg_get_property(pg, SCF_PROPERTY_USE_PROFILE, prop);
2906         if (ret && scf_error() == SCF_ERROR_NOT_FOUND && instpg != NULL) {
2907                 pg = NULL;
2908                 ret = scf_pg_get_property(instpg, SCF_PROPERTY_USE_PROFILE,
2909                     prop);
2910         }
2911 
2912         if (ret) {
2913                 switch (scf_error()) {
2914                 case SCF_ERROR_NOT_FOUND:
2915                         /* No profile context: use default credentials */
2916                         cip->uid = 0;
2917                         cip->gid = 0;
2918                         break;
2919 
2920                 case SCF_ERROR_CONNECTION_BROKEN:
2921                         err = mc_error_create(err, SCF_ERROR_CONNECTION_BROKEN,
2922                             RCBROKEN);
2923                         goto out;
2924 
2925                 case SCF_ERROR_DELETED:
2926                         err = mc_error_create(err, SCF_ERROR_NOT_FOUND,
2927                             "Could not find property group \"%s\"",
2928                             pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2929                         goto out;
2930 
2931                 case SCF_ERROR_HANDLE_MISMATCH:
2932                 case SCF_ERROR_INVALID_ARGUMENT:
2933                 case SCF_ERROR_NOT_SET:
2934                 default:
2935                         bad_fail("scf_pg_get_property", scf_error());
2936                 }
2937         } else {
2938                 if (scf_property_type(prop, &ty) != SCF_SUCCESS) {
2939                         ret = scf_error();
2940                         switch (ret) {
2941                         case SCF_ERROR_CONNECTION_BROKEN:
2942                                 err = mc_error_create(err,
2943                                     SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2944                                 break;
2945 
2946                         case SCF_ERROR_DELETED:
2947                                 err = mc_error_create(err,
2948                                     SCF_ERROR_NOT_FOUND,
2949                                     "Could not find property group \"%s\"",
2950                                     pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2951                                 break;
2952 
2953                         case SCF_ERROR_NOT_SET:
2954                         default:
2955                                 bad_fail("scf_property_type", ret);
2956                         }
2957 
2958                         goto out;
2959                 }
2960 
2961                 if (ty != SCF_TYPE_BOOLEAN) {
2962                         err = mc_error_create(err,
2963                             SCF_ERROR_TYPE_MISMATCH,
2964                             "\"%s\" property is not boolean in property group "
2965                             "\"%s\".", SCF_PROPERTY_USE_PROFILE,
2966                             pg == NULL ? SCF_PG_METHOD_CONTEXT : mname);
2967                         goto out;
2968                 }
2969 
2970                 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
2971                         ret = scf_error();
2972                         switch (ret) {
2973                         case SCF_ERROR_CONNECTION_BROKEN:
2974                                 err = mc_error_create(err,
2975                                     SCF_ERROR_CONNECTION_BROKEN, RCBROKEN);
2976                                 break;
2977 
2978                         case SCF_ERROR_CONSTRAINT_VIOLATED:
2979                                 err = mc_error_create(err,
2980                                     SCF_ERROR_CONSTRAINT_VIOLATED,
2981                                     "\"%s\" property has multiple values.",
2982                                     SCF_PROPERTY_USE_PROFILE);
2983                                 break;
2984 
2985                         case SCF_ERROR_NOT_FOUND:
2986                                 err = mc_error_create(err,
2987                                     SCF_ERROR_NOT_FOUND,
2988                                     "\"%s\" property has no values.",
2989                                     SCF_PROPERTY_USE_PROFILE);
2990                                 break;
2991                         default:
2992                                 bad_fail("scf_property_get_value", ret);
2993                         }
2994 
2995                         goto out;
2996                 }
2997 
2998                 mc_used++;
2999                 ret = scf_value_get_boolean(val, &use_profile);
3000                 assert(ret == SCF_SUCCESS);
3001 
3002                 /* get ids & privileges */
3003                 if (use_profile)
3004                         err = get_profile(pg, instpg, prop, val, cmdline,
3005                             cip, err);
3006                 else
3007                         err = get_ids(pg, instpg, prop, val, cip, err);
3008 
3009                 if (err->type != 0)
3010                         goto out;
3011         }
3012 
3013         /* get working directory */
3014         if ((methpg != NULL && scf_pg_get_property(methpg,
3015             SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS) ||
3016             (instpg != NULL && scf_pg_get_property(instpg,
3017             SCF_PROPERTY_WORKING_DIRECTORY, prop) == SCF_SUCCESS)) {
3018                 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3019                         ret = scf_error();
3020                         switch (ret) {
3021                         case SCF_ERROR_CONNECTION_BROKEN:
3022                                 err = mc_error_create(err, ret, RCBROKEN);
3023                                 break;
3024 
3025                         case SCF_ERROR_CONSTRAINT_VIOLATED:
3026                                 err = mc_error_create(err, ret,
3027                                     "\"%s\" property has multiple values.",
3028                                     SCF_PROPERTY_WORKING_DIRECTORY);
3029                                 break;
3030 
3031                         case SCF_ERROR_NOT_FOUND:
3032                                 err = mc_error_create(err, ret,
3033                                     "\"%s\" property has no values.",
3034                                     SCF_PROPERTY_WORKING_DIRECTORY);
3035                                 break;
3036 
3037                         default:
3038                                 bad_fail("scf_property_get_value", ret);
3039                         }
3040 
3041                         goto out;
3042                 }
3043 
3044                 mc_used++;
3045                 ret = scf_value_get_astring(val, cip->vbuf, cip->vbuf_sz);
3046                 assert(ret != -1);
3047         } else {
3048                 ret = scf_error();
3049                 switch (ret) {
3050                 case SCF_ERROR_NOT_FOUND:
3051                         /* okay if missing. */
3052                         (void) strcpy(cip->vbuf, ":default");
3053                         break;
3054 
3055                 case SCF_ERROR_CONNECTION_BROKEN:
3056                         err = mc_error_create(err, ret, RCBROKEN);
3057                         goto out;
3058 
3059                 case SCF_ERROR_DELETED:
3060                         err = mc_error_create(err, ret,
3061                             "Property group could not be found");
3062                         goto out;
3063 
3064                 case SCF_ERROR_HANDLE_MISMATCH:
3065                 case SCF_ERROR_INVALID_ARGUMENT:
3066                 case SCF_ERROR_NOT_SET:
3067                 default:
3068                         bad_fail("scf_pg_get_property", ret);
3069                 }
3070         }
3071 
3072         if (strcmp(cip->vbuf, ":default") == 0 ||
3073             strcmp(cip->vbuf, ":home") == 0) {
3074                 switch (ret = lookup_pwd(cip)) {
3075                 case 0:
3076                         break;
3077 
3078                 case ENOMEM:
3079                         err = mc_error_create(err, ret, "Out of memory.");
3080                         goto out;
3081 
3082                 case ENOENT:
3083                 case EIO:
3084                 case EMFILE:
3085                 case ENFILE:
3086                         err = mc_error_create(err, ret,
3087                             "Could not get passwd entry.");
3088                         goto out;
3089 
3090                 default:
3091                         bad_fail("lookup_pwd", ret);
3092                 }
3093 
3094                 cip->working_dir = strdup(cip->pwd.pw_dir);
3095                 if (cip->working_dir == NULL) {
3096                         err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3097                         goto out;
3098                 }
3099         } else {
3100                 cip->working_dir = strdup(cip->vbuf);
3101                 if (cip->working_dir == NULL) {
3102                         err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3103                         goto out;
3104                 }
3105         }
3106 
3107         /* get security flags */
3108         if ((methpg != NULL && scf_pg_get_property(methpg,
3109             SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS) ||
3110             (instpg != NULL && scf_pg_get_property(instpg,
3111             SCF_PROPERTY_SECFLAGS, prop) == SCF_SUCCESS)) {
3112                 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3113                         ret = scf_error();
3114                         switch (ret) {
3115                         case SCF_ERROR_CONNECTION_BROKEN:
3116                                 err = mc_error_create(err, ret, RCBROKEN);
3117                                 break;
3118 
3119                         case SCF_ERROR_CONSTRAINT_VIOLATED:
3120                                 err = mc_error_create(err, ret,
3121                                     "\"%s\" property has multiple values.",
3122                                     SCF_PROPERTY_SECFLAGS);
3123                                 break;
3124 
3125                         case SCF_ERROR_NOT_FOUND:
3126                                 err = mc_error_create(err, ret,
3127                                     "\"%s\" property has no values.",
3128                                     SCF_PROPERTY_SECFLAGS);
3129                                 break;
3130 
3131                         default:
3132                                 bad_fail("scf_property_get_value", ret);
3133                         }
3134 
3135                         (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3136                 } else {
3137                         ret = scf_value_get_astring(val, cip->vbuf,
3138                             cip->vbuf_sz);
3139                         assert(ret != -1);
3140                 }
3141                 mc_used++;
3142         } else {
3143                 ret = scf_error();
3144                 switch (ret) {
3145                 case SCF_ERROR_NOT_FOUND:
3146                         /* okay if missing. */
3147                         (void) strlcpy(cip->vbuf, ":default", cip->vbuf_sz);
3148                         break;
3149 
3150                 case SCF_ERROR_CONNECTION_BROKEN:
3151                         err = mc_error_create(err, ret, RCBROKEN);
3152                         goto out;
3153 
3154                 case SCF_ERROR_DELETED:
3155                         err = mc_error_create(err, ret,
3156                             "Property group could not be found");
3157                         goto out;
3158 
3159                 case SCF_ERROR_HANDLE_MISMATCH:
3160                 case SCF_ERROR_INVALID_ARGUMENT:
3161                 case SCF_ERROR_NOT_SET:
3162                 default:
3163                         bad_fail("scf_pg_get_property", ret);
3164                 }
3165         }
3166 
3167 
3168         if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3169                 err = mc_error_create(err, EINVAL, "couldn't fetch "
3170                     "default security-flags");
3171                 goto out;
3172         }
3173 
3174         if (strcmp(cip->vbuf, ":default") == 0) {
3175                 if (secflags_parse(&cip->def_secflags.psf_inherit, "default",
3176                     &cip->secflag_delta) != 0) {
3177                         err = mc_error_create(err, EINVAL, "couldn't parse "
3178                             "security flags: %s", cip->vbuf);
3179                         goto out;
3180                 }
3181         } else {
3182                 if (secflags_parse(&cip->def_secflags.psf_inherit, cip->vbuf,
3183                     &cip->secflag_delta) != 0) {
3184                         err = mc_error_create(err, EINVAL, "couldn't parse "
3185                             "security flags: %s", cip->vbuf);
3186                         goto out;
3187                 }
3188         }
3189 
3190         /* get (optional) corefile pattern */
3191         if ((methpg != NULL && scf_pg_get_property(methpg,
3192             SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS) ||
3193             (instpg != NULL && scf_pg_get_property(instpg,
3194             SCF_PROPERTY_COREFILE_PATTERN, prop) == SCF_SUCCESS)) {
3195                 if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3196                         ret = scf_error();
3197                         switch (ret) {
3198                         case SCF_ERROR_CONNECTION_BROKEN:
3199                                 err = mc_error_create(err, ret, RCBROKEN);
3200                                 break;
3201 
3202                         case SCF_ERROR_CONSTRAINT_VIOLATED:
3203                                 err = mc_error_create(err, ret,
3204                                     "\"%s\" property has multiple values.",
3205                                     SCF_PROPERTY_COREFILE_PATTERN);
3206                                 break;
3207 
3208                         case SCF_ERROR_NOT_FOUND:
3209                                 err = mc_error_create(err, ret,
3210                                     "\"%s\" property has no values.",
3211                                     SCF_PROPERTY_COREFILE_PATTERN);
3212                                 break;
3213 
3214                         default:
3215                                 bad_fail("scf_property_get_value", ret);
3216                         }
3217 
3218                 } else {
3219 
3220                         ret = scf_value_get_astring(val, cip->vbuf,
3221                             cip->vbuf_sz);
3222                         assert(ret != -1);
3223 
3224                         cip->corefile_pattern = strdup(cip->vbuf);
3225                         if (cip->corefile_pattern == NULL) {
3226                                 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3227                                 goto out;
3228                         }
3229                 }
3230 
3231                 mc_used++;
3232         } else {
3233                 ret = scf_error();
3234                 switch (ret) {
3235                 case SCF_ERROR_NOT_FOUND:
3236                         /* okay if missing. */
3237                         break;
3238 
3239                 case SCF_ERROR_CONNECTION_BROKEN:
3240                         err = mc_error_create(err, ret, RCBROKEN);
3241                         goto out;
3242 
3243                 case SCF_ERROR_DELETED:
3244                         err = mc_error_create(err, ret,
3245                             "Property group could not be found");
3246                         goto out;
3247 
3248                 case SCF_ERROR_HANDLE_MISMATCH:
3249                 case SCF_ERROR_INVALID_ARGUMENT:
3250                 case SCF_ERROR_NOT_SET:
3251                 default:
3252                         bad_fail("scf_pg_get_property", ret);
3253                 }
3254         }
3255 
3256         if (restarter_rm_libs_loadable()) {
3257                 /* get project */
3258                 if ((methpg != NULL && scf_pg_get_property(methpg,
3259                     SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS) ||
3260                     (instpg != NULL && scf_pg_get_property(instpg,
3261                     SCF_PROPERTY_PROJECT, prop) == SCF_SUCCESS)) {
3262                         if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3263                                 ret = scf_error();
3264                                 switch (ret) {
3265                                 case SCF_ERROR_CONNECTION_BROKEN:
3266                                         err = mc_error_create(err, ret,
3267                                             RCBROKEN);
3268                                         break;
3269 
3270                                 case SCF_ERROR_CONSTRAINT_VIOLATED:
3271                                         err = mc_error_create(err, ret,
3272                                             "\"%s\" property has multiple "
3273                                             "values.", SCF_PROPERTY_PROJECT);
3274                                         break;
3275 
3276                                 case SCF_ERROR_NOT_FOUND:
3277                                         err = mc_error_create(err, ret,
3278                                             "\"%s\" property has no values.",
3279                                             SCF_PROPERTY_PROJECT);
3280                                         break;
3281 
3282                                 default:
3283                                         bad_fail("scf_property_get_value", ret);
3284                                 }
3285 
3286                                 (void) strcpy(cip->vbuf, ":default");
3287                         } else {
3288                                 ret = scf_value_get_astring(val, cip->vbuf,
3289                                     cip->vbuf_sz);
3290                                 assert(ret != -1);
3291                         }
3292 
3293                         mc_used++;
3294                 } else {
3295                         (void) strcpy(cip->vbuf, ":default");
3296                 }
3297 
3298                 switch (ret = get_projid(cip->vbuf, cip)) {
3299                 case 0:
3300                         break;
3301 
3302                 case ENOMEM:
3303                         err = mc_error_create(err, ret, "Out of memory.");
3304                         goto out;
3305 
3306                 case ENOENT:
3307                         err = mc_error_create(err, ret,
3308                             "Missing passwd or project entry for \"%s\".",
3309                             cip->vbuf);
3310                         goto out;
3311 
3312                 case EIO:
3313                         err = mc_error_create(err, ret, "I/O error.");
3314                         goto out;
3315 
3316                 case EMFILE:
3317                 case ENFILE:
3318                         err = mc_error_create(err, ret,
3319                             "Out of file descriptors.");
3320                         goto out;
3321 
3322                 case -1:
3323                         err = mc_error_create(err, ret,
3324                             "Name service switch is misconfigured.");
3325                         goto out;
3326 
3327                 case ERANGE:
3328                 case E2BIG:
3329                         err = mc_error_create(err, ret,
3330                             "Project ID \"%s\" too big.", cip->vbuf);
3331                         goto out;
3332 
3333                 case EINVAL:
3334                         err = mc_error_create(err, ret,
3335                             "Project ID \"%s\" is invalid.", cip->vbuf);
3336                         goto out;
3337 
3338                 default:
3339                         bad_fail("get_projid", ret);
3340                 }
3341 
3342                 /* get resource pool */
3343                 if ((methpg != NULL && scf_pg_get_property(methpg,
3344                     SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS) ||
3345                     (instpg != NULL && scf_pg_get_property(instpg,
3346                     SCF_PROPERTY_RESOURCE_POOL, prop) == SCF_SUCCESS)) {
3347                         if (scf_property_get_value(prop, val) != SCF_SUCCESS) {
3348                                 ret = scf_error();
3349                                 switch (ret) {
3350                                 case SCF_ERROR_CONNECTION_BROKEN:
3351                                         err = mc_error_create(err, ret,
3352                                             RCBROKEN);
3353                                         break;
3354 
3355                                 case SCF_ERROR_CONSTRAINT_VIOLATED:
3356                                         err = mc_error_create(err, ret,
3357                                             "\"%s\" property has multiple "
3358                                             "values.",
3359                                             SCF_PROPERTY_RESOURCE_POOL);
3360                                         break;
3361 
3362                                 case SCF_ERROR_NOT_FOUND:
3363                                         err = mc_error_create(err, ret,
3364                                             "\"%s\" property has no "
3365                                             "values.",
3366                                             SCF_PROPERTY_RESOURCE_POOL);
3367                                         break;
3368 
3369                                 default:
3370                                         bad_fail("scf_property_get_value", ret);
3371                                 }
3372 
3373                                 (void) strcpy(cip->vbuf, ":default");
3374                         } else {
3375                                 ret = scf_value_get_astring(val, cip->vbuf,
3376                                     cip->vbuf_sz);
3377                                 assert(ret != -1);
3378                         }
3379 
3380                         mc_used++;
3381                 } else {
3382                         ret = scf_error();
3383                         switch (ret) {
3384                         case SCF_ERROR_NOT_FOUND:
3385                                 /* okay if missing. */
3386                                 (void) strcpy(cip->vbuf, ":default");
3387                                 break;
3388 
3389                         case SCF_ERROR_CONNECTION_BROKEN:
3390                                 err = mc_error_create(err, ret, RCBROKEN);
3391                                 goto out;
3392 
3393                         case SCF_ERROR_DELETED:
3394                                 err = mc_error_create(err, ret,
3395                                     "property group could not be found.");
3396                                 goto out;
3397 
3398                         case SCF_ERROR_HANDLE_MISMATCH:
3399                         case SCF_ERROR_INVALID_ARGUMENT:
3400                         case SCF_ERROR_NOT_SET:
3401                         default:
3402                                 bad_fail("scf_pg_get_property", ret);
3403                         }
3404                 }
3405 
3406                 if (strcmp(cip->vbuf, ":default") != 0) {
3407                         cip->resource_pool = strdup(cip->vbuf);
3408                         if (cip->resource_pool == NULL) {
3409                                 err = mc_error_create(err, ENOMEM, ALLOCFAIL);
3410                                 goto out;
3411                         }
3412                 }
3413         }
3414 
3415         /*
3416          * A method_context was not used for any configurable
3417          * elements or attributes, so reset and use the simple
3418          * defaults that provide historic init behavior.
3419          */
3420         if (mc_used == 0) {
3421                 free(cip->pwbuf);
3422                 free(cip->vbuf);
3423                 free(cip->working_dir);
3424 
3425                 (void) memset(cip, 0, sizeof (*cip));
3426                 cip->uid = 0;
3427                 cip->gid = 0;
3428                 cip->euid = (uid_t)-1;
3429                 cip->egid = (gid_t)-1;
3430 
3431                 if (scf_default_secflags(h, &cip->def_secflags) != 0) {
3432                         err = mc_error_create(err, EINVAL, "couldn't fetch "
3433                             "default security-flags");
3434                         goto out;
3435                 }
3436 
3437                 if (secflags_parse(&cip->def_secflags.psf_inherit, "default",
3438                     &cip->secflag_delta) != 0) {
3439                         err = mc_error_create(err, EINVAL, "couldn't parse "
3440                             "security flags: %s", cip->vbuf);
3441                         goto out;
3442                 }
3443         }
3444 
3445         *mcpp = cip;
3446 
3447 out:
3448         (void) scf_value_destroy(val);
3449         scf_property_destroy(prop);
3450         scf_pg_destroy(instpg);
3451         scf_pg_destroy(methpg);
3452 
3453         if (cip->pwbuf != NULL) {
3454                 free(cip->pwbuf);
3455                 cip->pwbuf = NULL;
3456         }
3457 
3458         free(cip->vbuf);
3459 
3460         if (err->type != 0) {
3461                 restarter_free_method_context(cip);
3462         } else {
3463                 restarter_mc_error_destroy(err);
3464                 err = NULL;
3465         }
3466 
3467         return (err);
3468 }
3469 
3470 /*
3471  * Modify the current process per the given method_context.  On success, returns
3472  * 0.  Note that the environment is not modified by this function to include the
3473  * environment variables in cip->env.
3474  *
3475  * On failure, sets *fp to NULL or the name of the function which failed,
3476  * and returns one of the following error codes.  The words in parentheses are
3477  * the values to which *fp may be set for the error case.
3478  *   ENOMEM - malloc() failed
3479  *   EIO - an I/O error occurred (getpwuid_r, chdir)
3480  *   EMFILE - process is out of file descriptors (getpwuid_r)
3481  *   ENFILE - system is out of file handles (getpwuid_r)
3482  *   EINVAL - gid or egid is out of range (setregid)
3483  *            ngroups is too big (setgroups)
3484  *            project's project id is bad (setproject)
3485  *            uid or euid is out of range (setreuid)
3486  *            poolname is invalid (pool_set_binding)
3487  *   EPERM - insufficient privilege (setregid, initgroups, setgroups, setppriv,
3488  *               setproject, setreuid, settaskid)
3489  *   ENOENT - uid has a passwd entry but no shadow entry
3490  *            working_dir does not exist (chdir)
3491  *            uid has no passwd entry
3492  *            the pool could not be found (pool_set_binding)
3493  *   EFAULT - lpriv_set or priv_set has a bad address (setppriv)
3494  *            working_dir has a bad address (chdir)
3495  *   EACCES - could not access working_dir (chdir)
3496  *            in a TASK_FINAL task (setproject, settaskid)
3497  *            no resource pool accepting default binding exists (setproject)
3498  *   ELOOP - too many symbolic links in working_dir (chdir)
3499  *   ENAMETOOLONG - working_dir is too long (chdir)
3500  *   ENOLINK - working_dir is on an inaccessible remote machine (chdir)
3501  *   ENOTDIR - working_dir is not a directory (chdir)
3502  *   ESRCH - uid is not a user of project (setproject)
3503  *           project is invalid (setproject)
3504  *           the resource pool specified for project is unknown (setproject)
3505  *   EBADF - the configuration for the pool is invalid (pool_set_binding)
3506  *   -1 - core_set_process_path() failed (core_set_process_path)
3507  *        a resource control assignment failed (setproject)
3508  *        a system error occurred during pool_set_binding (pool_set_binding)
3509  */
3510 int
3511 restarter_set_method_context(struct method_context *cip, const char **fp)
3512 {
3513         pid_t mypid = -1;
3514         int r, ret;
3515         secflagdelta_t delta = {0};
3516 
3517         cip->pwbuf = NULL;
3518         *fp = NULL;
3519 
3520         if (cip->gid != (gid_t)-1) {
3521                 if (setregid(cip->gid,
3522                     cip->egid != (gid_t)-1 ? cip->egid : cip->gid) != 0) {
3523                         *fp = "setregid";
3524 
3525                         ret = errno;
3526                         assert(ret == EINVAL || ret == EPERM);
3527                         goto out;
3528                 }
3529         } else {
3530                 if (cip->pwbuf == NULL) {
3531                         switch (ret = lookup_pwd(cip)) {
3532                         case 0:
3533                                 break;
3534 
3535                         case ENOMEM:
3536                         case ENOENT:
3537                                 *fp = NULL;
3538                                 goto out;
3539 
3540                         case EIO:
3541                         case EMFILE:
3542                         case ENFILE:
3543                                 *fp = "getpwuid_r";
3544                                 goto out;
3545 
3546                         default:
3547                                 bad_fail("lookup_pwd", ret);
3548                         }
3549                 }
3550 
3551                 if (setregid(cip->pwd.pw_gid,
3552                     cip->egid != (gid_t)-1 ?
3553                     cip->egid : cip->pwd.pw_gid) != 0) {
3554                         *fp = "setregid";
3555 
3556                         ret = errno;
3557                         assert(ret == EINVAL || ret == EPERM);
3558                         goto out;
3559                 }
3560         }
3561 
3562         if (cip->ngroups == -1) {
3563                 if (cip->pwbuf == NULL) {
3564                         switch (ret = lookup_pwd(cip)) {
3565                         case 0:
3566                                 break;
3567 
3568                         case ENOMEM:
3569                         case ENOENT:
3570                                 *fp = NULL;
3571                                 goto out;
3572 
3573                         case EIO:
3574                         case EMFILE:
3575                         case ENFILE:
3576                                 *fp = "getpwuid_r";
3577                                 goto out;
3578 
3579                         default:
3580                                 bad_fail("lookup_pwd", ret);
3581                         }
3582                 }
3583 
3584                 /* Ok if cip->gid == -1 */
3585                 if (initgroups(cip->pwd.pw_name, cip->gid) != 0) {
3586                         *fp = "initgroups";
3587                         ret = errno;
3588                         assert(ret == EPERM);
3589                         goto out;
3590                 }
3591         } else if (cip->ngroups > 0 &&
3592             setgroups(cip->ngroups, cip->groups) != 0) {
3593                 *fp = "setgroups";
3594 
3595                 ret = errno;
3596                 assert(ret == EINVAL || ret == EPERM);
3597                 goto out;
3598         }
3599 
3600         if (cip->corefile_pattern != NULL) {
3601                 mypid = getpid();
3602 
3603                 if (core_set_process_path(cip->corefile_pattern,
3604                     strlen(cip->corefile_pattern) + 1, mypid) != 0) {
3605                         *fp = "core_set_process_path";
3606                         ret = -1;
3607                         goto out;
3608                 }
3609         }
3610 
3611 
3612         delta.psd_ass_active = B_TRUE;
3613         secflags_copy(&delta.psd_assign, &cip->def_secflags.psf_inherit);
3614         if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3615             &delta) != 0) {
3616                 *fp = "psecflags (inherit defaults)";
3617                 ret = errno;
3618                 goto out;
3619         }
3620 
3621         if (psecflags(P_PID, P_MYID, PSF_INHERIT,
3622             &cip->secflag_delta) != 0) {
3623                 *fp = "psecflags (inherit)";
3624                 ret = errno;
3625                 goto out;
3626         }
3627 
3628         secflags_copy(&delta.psd_assign, &cip->def_secflags.psf_lower);
3629         if (psecflags(P_PID, P_MYID, PSF_LOWER,
3630             &delta) != 0) {
3631                 *fp = "psecflags (lower)";
3632                 ret = errno;
3633                 goto out;
3634         }
3635 
3636         secflags_copy(&delta.psd_assign, &cip->def_secflags.psf_upper);
3637         if (psecflags(P_PID, P_MYID, PSF_UPPER,
3638             &delta) != 0) {
3639                 *fp = "psecflags (upper)";
3640                 ret = errno;
3641                 goto out;
3642         }
3643 
3644         if (restarter_rm_libs_loadable()) {
3645                 if (cip->project == NULL) {
3646                         if (settaskid(getprojid(), TASK_NORMAL) == -1) {
3647                                 switch (errno) {
3648                                 case EACCES:
3649                                 case EPERM:
3650                                         *fp = "settaskid";
3651                                         ret = errno;
3652                                         goto out;
3653 
3654                                 case EINVAL:
3655                                 default:
3656                                         bad_fail("settaskid", errno);
3657                                 }
3658                         }
3659                 } else {
3660                         switch (ret = lookup_pwd(cip)) {
3661                         case 0:
3662                                 break;
3663 
3664                         case ENOMEM:
3665                         case ENOENT:
3666                                 *fp = NULL;
3667                                 goto out;
3668 
3669                         case EIO:
3670                         case EMFILE:
3671                         case ENFILE:
3672                                 *fp = "getpwuid_r";
3673                                 goto out;
3674 
3675                         default:
3676                                 bad_fail("lookup_pwd", ret);
3677                         }
3678 
3679                         *fp = "setproject";
3680 
3681                         switch (setproject(cip->project, cip->pwd.pw_name,
3682                             TASK_NORMAL)) {
3683                         case 0:
3684                                 break;
3685 
3686                         case SETPROJ_ERR_TASK:
3687                         case SETPROJ_ERR_POOL:
3688                                 ret = errno;
3689                                 goto out;
3690 
3691                         default:
3692                                 ret = -1;
3693                                 goto out;
3694                         }
3695                 }
3696 
3697                 if (cip->resource_pool != NULL) {
3698                         if (mypid == -1)
3699                                 mypid = getpid();
3700 
3701                         *fp = "pool_set_binding";
3702 
3703                         if (pool_set_binding(cip->resource_pool, P_PID,
3704                             mypid) != PO_SUCCESS) {
3705                                 switch (pool_error()) {
3706                                 case POE_INVALID_SEARCH:
3707                                         ret = ENOENT;
3708                                         break;
3709 
3710                                 case POE_BADPARAM:
3711                                         ret = EINVAL;
3712                                         break;
3713 
3714                                 case POE_INVALID_CONF:
3715                                         ret = EBADF;
3716                                         break;
3717 
3718                                 case POE_SYSTEM:
3719                                         ret = -1;
3720                                         break;
3721 
3722                                 default:
3723                                         bad_fail("pool_set_binding",
3724                                             pool_error());
3725                                 }
3726 
3727                                 goto out;
3728                         }
3729                 }
3730         }
3731 
3732         /*
3733          * Now, we have to assume our ID. If the UID is 0, we want it to be
3734          * privilege-aware, otherwise the limit set gets used instead of E/P.
3735          * We can do this by setting P as well, which keeps
3736          * PA status (see priv_can_clear_PA()).
3737          */
3738 
3739         *fp = "setppriv";
3740 
3741         if (cip->lpriv_set != NULL) {
3742                 if (setppriv(PRIV_SET, PRIV_LIMIT, cip->lpriv_set) != 0) {
3743                         ret = errno;
3744                         assert(ret == EFAULT || ret == EPERM);
3745                         goto out;
3746                 }
3747         }
3748         if (cip->priv_set != NULL) {
3749                 if (setppriv(PRIV_SET, PRIV_INHERITABLE, cip->priv_set) != 0) {
3750                         ret = errno;
3751                         assert(ret == EFAULT || ret == EPERM);
3752                         goto out;
3753                 }
3754         }
3755 
3756         /*
3757          * If the limit privset is already set, then must be privilege
3758          * aware.  Otherwise, don't assume anything, and force privilege
3759          * aware status.
3760          */
3761 
3762         if (cip->lpriv_set == NULL && cip->priv_set != NULL) {
3763                 ret = setpflags(PRIV_AWARE, 1);
3764                 assert(ret == 0);
3765         }
3766 
3767         *fp = "setreuid";
3768         if (setreuid(cip->uid,
3769             cip->euid != (uid_t)-1 ? cip->euid : cip->uid) != 0) {
3770                 ret = errno;
3771                 assert(ret == EINVAL || ret == EPERM);
3772                 goto out;
3773         }
3774 
3775         *fp = "setppriv";
3776         if (cip->priv_set != NULL) {
3777                 if (setppriv(PRIV_SET, PRIV_PERMITTED, cip->priv_set) != 0) {
3778                         ret = errno;
3779                         assert(ret == EFAULT || ret == EPERM);
3780                         goto out;
3781                 }
3782         }
3783 
3784         /*
3785          * The last thing to do is chdir to the specified working directory.
3786          * This should come after the uid switching as only the user might
3787          * have access to the specified directory.
3788          */
3789         if (cip->working_dir != NULL) {
3790                 do {
3791                         r = chdir(cip->working_dir);
3792                 } while (r != 0 && errno == EINTR);
3793                 if (r != 0) {
3794                         *fp = "chdir";
3795                         ret = errno;
3796                         goto out;
3797                 }
3798         }
3799 
3800         ret = 0;
3801 out:
3802         free(cip->pwbuf);
3803         cip->pwbuf = NULL;
3804         return (ret);
3805 }
3806 
3807 void
3808 restarter_free_method_context(struct method_context *mcp)
3809 {
3810         size_t i;
3811 
3812         if (mcp->lpriv_set != NULL)
3813                 priv_freeset(mcp->lpriv_set);
3814         if (mcp->priv_set != NULL)
3815                 priv_freeset(mcp->priv_set);
3816 
3817         if (mcp->env != NULL) {
3818                 for (i = 0; i < mcp->env_sz; i++)
3819                         free(mcp->env[i]);
3820                 free(mcp->env);
3821         }
3822 
3823         free(mcp->working_dir);
3824         free(mcp->corefile_pattern);
3825         free(mcp->project);
3826         free(mcp->resource_pool);
3827         free(mcp);
3828 }
3829 
3830 /*
3831  * Method keyword functions
3832  */
3833 
3834 int
3835 restarter_is_null_method(const char *meth)
3836 {
3837         return (strcmp(meth, MKW_TRUE) == 0);
3838 }
3839 
3840 static int
3841 is_kill_method(const char *method, const char *kill_str,
3842     size_t kill_str_len)
3843 {
3844         const char *cp;
3845         int sig;
3846 
3847         if (strncmp(method, kill_str, kill_str_len) != 0 ||
3848             (method[kill_str_len] != '\0' &&
3849             !isspace(method[kill_str_len])))
3850                 return (-1);
3851 
3852         cp = method + kill_str_len;
3853         while (*cp != '\0' && isspace(*cp))
3854                 ++cp;
3855 
3856         if (*cp == '\0')
3857                 return (SIGTERM);
3858 
3859         if (*cp != '-')
3860                 return (-1);
3861 
3862         return (str2sig(cp + 1, &sig) == 0 ? sig : -1);
3863 }
3864 
3865 int
3866 restarter_is_kill_proc_method(const char *method)
3867 {
3868         return (is_kill_method(method, MKW_KILL_PROC,
3869             sizeof (MKW_KILL_PROC) - 1));
3870 }
3871 
3872 int
3873 restarter_is_kill_method(const char *method)
3874 {
3875         return (is_kill_method(method, MKW_KILL, sizeof (MKW_KILL) - 1));
3876 }
3877 
3878 /*
3879  * Stubs for now.
3880  */
3881 
3882 /* ARGSUSED */
3883 int
3884 restarter_event_get_enabled(restarter_event_t *e)
3885 {
3886         return (-1);
3887 }
3888 
3889 /* ARGSUSED */
3890 uint64_t
3891 restarter_event_get_seq(restarter_event_t *e)
3892 {
3893         return (-1);
3894 }
3895 
3896 /* ARGSUSED */
3897 void
3898 restarter_event_get_time(restarter_event_t *e, hrtime_t *time)
3899 {
3900 }
3901 
3902 /*
3903  * Check for and validate fmri specified in restarter_actions/auxiliary_fmri
3904  * 0 - Success
3905  * 1 - Failure
3906  */
3907 int
3908 restarter_inst_validate_ractions_aux_fmri(scf_instance_t *inst)
3909 {
3910         scf_handle_t *h;
3911         scf_propertygroup_t *pg;
3912         scf_property_t *prop;
3913         scf_value_t *val;
3914         char *aux_fmri;
3915         size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
3916         int ret = 1;
3917 
3918         if ((aux_fmri = malloc(size)) == NULL)
3919                 return (1);
3920 
3921         h = scf_instance_handle(inst);
3922 
3923         pg = scf_pg_create(h);
3924         prop = scf_property_create(h);
3925         val = scf_value_create(h);
3926         if (pg == NULL || prop == NULL || val == NULL)
3927                 goto out;
3928 
3929         if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3930             SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3931             pg) != SCF_SUCCESS)
3932                 goto out;
3933 
3934         if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
3935             prop, val) != SCF_SUCCESS)
3936                 goto out;
3937 
3938         if (scf_parse_fmri(aux_fmri, NULL, NULL, NULL, NULL, NULL,
3939             NULL) != SCF_SUCCESS)
3940                 goto out;
3941 
3942         ret = 0;
3943 
3944 out:
3945         free(aux_fmri);
3946         scf_value_destroy(val);
3947         scf_property_destroy(prop);
3948         scf_pg_destroy(pg);
3949         return (ret);
3950 }
3951 
3952 /*
3953  * Get instance's boolean value in restarter_actions/auxiliary_tty
3954  * Return -1 on failure
3955  */
3956 int
3957 restarter_inst_ractions_from_tty(scf_instance_t *inst)
3958 {
3959         scf_handle_t *h;
3960         scf_propertygroup_t *pg;
3961         scf_property_t *prop;
3962         scf_value_t *val;
3963         uint8_t has_tty;
3964         int ret = -1;
3965 
3966         h = scf_instance_handle(inst);
3967         pg = scf_pg_create(h);
3968         prop = scf_property_create(h);
3969         val = scf_value_create(h);
3970         if (pg == NULL || prop == NULL || val == NULL)
3971                 goto out;
3972 
3973         if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
3974             SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
3975             pg) != SCF_SUCCESS)
3976                 goto out;
3977 
3978         if (get_boolean_val(pg, SCF_PROPERTY_AUX_TTY, &has_tty, prop,
3979             val) != SCF_SUCCESS)
3980                 goto out;
3981 
3982         ret = has_tty;
3983 
3984 out:
3985         scf_value_destroy(val);
3986         scf_property_destroy(prop);
3987         scf_pg_destroy(pg);
3988         return (ret);
3989 }
3990 
3991 /*
3992  * If the instance's dump-on-restart property exists, remove it and return true,
3993  * otherwise return false.
3994  */
3995 int
3996 restarter_inst_dump(scf_instance_t *inst)
3997 {
3998         scf_handle_t *h;
3999         scf_propertygroup_t *pg;
4000         scf_property_t *prop;
4001         scf_value_t *val;
4002         int ret = 0;
4003 
4004         h = scf_instance_handle(inst);
4005         pg = scf_pg_create(h);
4006         prop = scf_property_create(h);
4007         val = scf_value_create(h);
4008         if (pg == NULL || prop == NULL || val == NULL)
4009                 goto out;
4010 
4011         if (scf_instance_get_pg(inst, SCF_PG_RESTARTER_ACTIONS, pg) !=
4012             SCF_SUCCESS) {
4013                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4014                         uu_die(rcbroken);
4015                 goto out;
4016         }
4017 
4018         if (scf_pg_get_property(pg, SCF_PROPERTY_DODUMP, prop) != SCF_SUCCESS) {
4019                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4020                         uu_die(rcbroken);
4021                 goto out;
4022         }
4023 
4024         ret = 1;
4025 
4026         if (scf_instance_delete_prop(inst, SCF_PG_RESTARTER_ACTIONS,
4027             SCF_PROPERTY_DODUMP) != SCF_SUCCESS) {
4028                 if (scf_error() == SCF_ERROR_CONNECTION_BROKEN)
4029                         uu_die(rcbroken);
4030                 goto out;
4031         }
4032 
4033 out:
4034         scf_value_destroy(val);
4035         scf_property_destroy(prop);
4036         scf_pg_destroy(pg);
4037         return (ret);
4038 }
4039 
4040 static int
4041 restarter_inst_set_astring_prop(scf_instance_t *inst, const char *pgname,
4042     const char *pgtype, uint32_t pgflags, const char *pname, const char *str)
4043 {
4044         scf_handle_t *h;
4045         scf_propertygroup_t *pg;
4046         scf_transaction_t *t;
4047         scf_transaction_entry_t *e;
4048         scf_value_t *v;
4049         int ret = 1, r;
4050 
4051         h = scf_instance_handle(inst);
4052 
4053         pg = scf_pg_create(h);
4054         t = scf_transaction_create(h);
4055         e = scf_entry_create(h);
4056         v = scf_value_create(h);
4057         if (pg == NULL || t == NULL || e == NULL || v == NULL)
4058                 goto out;
4059 
4060         if (instance_get_or_add_pg(inst, pgname, pgtype, pgflags, pg))
4061                 goto out;
4062 
4063         if (scf_value_set_astring(v, str) != SCF_SUCCESS)
4064                 goto out;
4065 
4066         for (;;) {
4067                 if (scf_transaction_start(t, pg) != 0)
4068                         goto out;
4069 
4070                 if (tx_set_value(t, e, pname, SCF_TYPE_ASTRING, v) != 0)
4071                         goto out;
4072 
4073                 if ((r = scf_transaction_commit(t)) == 1)
4074                         break;
4075 
4076                 if (r == -1)
4077                         goto out;
4078 
4079                 scf_transaction_reset(t);
4080                 if (scf_pg_update(pg) == -1)
4081                         goto out;
4082         }
4083         ret = 0;
4084 
4085 out:
4086         scf_transaction_destroy(t);
4087         scf_entry_destroy(e);
4088         scf_value_destroy(v);
4089         scf_pg_destroy(pg);
4090 
4091         return (ret);
4092 }
4093 
4094 int
4095 restarter_inst_set_aux_fmri(scf_instance_t *inst)
4096 {
4097         scf_handle_t *h;
4098         scf_propertygroup_t *pg;
4099         scf_property_t *prop;
4100         scf_value_t *val;
4101         char *aux_fmri;
4102         size_t size = scf_limit(SCF_LIMIT_MAX_VALUE_LENGTH);
4103         int ret = 1;
4104 
4105         if ((aux_fmri = malloc(size)) == NULL)
4106                 return (1);
4107 
4108         h = scf_instance_handle(inst);
4109 
4110         pg = scf_pg_create(h);
4111         prop = scf_property_create(h);
4112         val = scf_value_create(h);
4113         if (pg == NULL || prop == NULL || val == NULL)
4114                 goto out;
4115 
4116         /*
4117          * Get auxiliary_fmri value from restarter_actions pg
4118          */
4119         if (instance_get_or_add_pg(inst, SCF_PG_RESTARTER_ACTIONS,
4120             SCF_PG_RESTARTER_ACTIONS_TYPE, SCF_PG_RESTARTER_ACTIONS_FLAGS,
4121             pg) != SCF_SUCCESS)
4122                 goto out;
4123 
4124         if (get_astring_val(pg, SCF_PROPERTY_AUX_FMRI, aux_fmri, size,
4125             prop, val) != SCF_SUCCESS)
4126                 goto out;
4127 
4128         /*
4129          * Populate restarter/auxiliary_fmri with the obtained fmri.
4130          */
4131         ret = restarter_inst_set_astring_prop(inst, SCF_PG_RESTARTER,
4132             SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS,
4133             SCF_PROPERTY_AUX_FMRI, aux_fmri);
4134 
4135 out:
4136         free(aux_fmri);
4137         scf_value_destroy(val);
4138         scf_property_destroy(prop);
4139         scf_pg_destroy(pg);
4140         return (ret);
4141 }
4142 
4143 int
4144 restarter_inst_reset_aux_fmri(scf_instance_t *inst)
4145 {
4146         return (scf_instance_delete_prop(inst,
4147             SCF_PG_RESTARTER, SCF_PROPERTY_AUX_FMRI));
4148 }
4149 
4150 int
4151 restarter_inst_reset_ractions_aux_fmri(scf_instance_t *inst)
4152 {
4153         return (scf_instance_delete_prop(inst,
4154             SCF_PG_RESTARTER_ACTIONS, SCF_PROPERTY_AUX_FMRI));
4155 }