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