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