/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. * Copyright 2012 Milan Jurik. All rights reserved. * Copyright 2020 Joyent, Inc. */ #include #include #include #include #include #include #include #include #include #include "startd.h" #define SMF_SNAPSHOT_RUNNING "running" #define INFO_EVENTS_ALL "info_events_all" char * inst_fmri_to_svc_fmri(const char *fmri) { char *buf, *sfmri; const char *scope, *svc; int r; boolean_t local; buf = startd_alloc(max_scf_fmri_size); sfmri = startd_alloc(max_scf_fmri_size); (void) strcpy(buf, fmri); r = scf_parse_svc_fmri(buf, &scope, &svc, NULL, NULL, NULL); assert(r == 0); local = strcmp(scope, SCF_SCOPE_LOCAL) == 0; (void) snprintf(sfmri, max_scf_fmri_size, "svc:%s%s/%s", local ? "" : "//", local ? "" : scope, svc); startd_free(buf, max_scf_fmri_size); return (sfmri); } /* * Wrapper for the scf_*_create() functions. On SCF_ERROR_NO_MEMORY and * SCF_ERROR_NO_RESOURCES, retries or dies. So this can only fail with * SCF_ERROR_INVALID_ARGUMENT, if h is NULL. */ void * libscf_object_create(void *f(scf_handle_t *), scf_handle_t *h) { void *o; uint_t try, msecs; scf_error_t err; o = f(h); if (o != NULL) return (o); err = scf_error(); if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES) return (NULL); msecs = ALLOC_DELAY; for (try = 0; try < ALLOC_RETRY; ++try) { (void) poll(NULL, 0, msecs); msecs *= ALLOC_DELAY_MULT; o = f(h); if (o != NULL) return (o); err = scf_error(); if (err != SCF_ERROR_NO_MEMORY && err != SCF_ERROR_NO_RESOURCES) return (NULL); } uu_die("Insufficient memory.\n"); /* NOTREACHED */ } scf_snapshot_t * libscf_get_running_snapshot(scf_instance_t *inst) { scf_handle_t *h; scf_snapshot_t *snap; h = scf_instance_handle(inst); if (h == NULL) return (NULL); snap = scf_snapshot_create(h); if (snap == NULL) return (NULL); if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) return (snap); scf_snapshot_destroy(snap); return (NULL); } /* * Make sure a service has a "running" snapshot. If it doesn't, make one from * the editing configuration. */ scf_snapshot_t * libscf_get_or_make_running_snapshot(scf_instance_t *inst, const char *fmri, boolean_t retake) { scf_handle_t *h; scf_snapshot_t *snap; h = scf_instance_handle(inst); snap = scf_snapshot_create(h); if (snap == NULL) goto err; if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) return (snap); switch (scf_error()) { case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_DELETED: scf_snapshot_destroy(snap); return (NULL); default: err: log_error(LOG_NOTICE, "Could not check for running snapshot of %s (%s).\n", fmri, scf_strerror(scf_error())); scf_snapshot_destroy(snap); return (NULL); } if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) { log_framework(LOG_DEBUG, "Took running snapshot for %s.\n", fmri); } else { if (retake && scf_error() == SCF_ERROR_BACKEND_READONLY) restarter_mark_pending_snapshot(fmri, RINST_RETAKE_RUNNING); else log_error(LOG_DEBUG, "Could not create running snapshot for %s " "(%s).\n", fmri, scf_strerror(scf_error())); scf_snapshot_destroy(snap); snap = NULL; } return (snap); } /* * When a service comes up, point the "start" snapshot at the "running" * snapshot. Returns 0 on success, ENOTSUP if fmri designates something other * than an instance, ECONNABORTED, ENOENT if the instance does not exist, or * EACCES. */ int libscf_snapshots_poststart(scf_handle_t *h, const char *fmri, boolean_t retake) { scf_instance_t *inst = NULL; scf_snapshot_t *running, *start = NULL; int ret = 0, r; r = libscf_fmri_get_instance(h, fmri, &inst); switch (r) { case 0: break; case ENOTSUP: case ECONNABORTED: case ENOENT: return (r); case EINVAL: default: assert(0); abort(); } start = safe_scf_snapshot_create(h); again: running = libscf_get_or_make_running_snapshot(inst, fmri, retake); if (running == NULL) { ret = 0; goto out; } lookup: if (scf_instance_get_snapshot(inst, "start", start) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_NOT_FOUND: if (_scf_snapshot_take_new(inst, "start", start) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ENOENT; goto out; case SCF_ERROR_EXISTS: goto lookup; case SCF_ERROR_NO_RESOURCES: uu_die("Repository server out of " "resources.\n"); /* NOTREACHED */ case SCF_ERROR_BACKEND_READONLY: goto readonly; case SCF_ERROR_PERMISSION_DENIED: uu_die("Insufficient privileges.\n"); /* NOTREACHED */ case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INTERNAL: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("_scf_snapshot_take_new", scf_error()); } } break; case SCF_ERROR_DELETED: ret = ENOENT; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: case SCF_ERROR_INVALID_ARGUMENT: bad_error("scf_instance_get_snapshot", scf_error()); } } if (_scf_snapshot_attach(running, start) == 0) { log_framework(LOG_DEBUG, "Updated \"start\" snapshot for %s.\n", fmri); } else { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: scf_snapshot_destroy(running); goto again; case SCF_ERROR_NO_RESOURCES: uu_die("Repository server out of resources.\n"); /* NOTREACHED */ case SCF_ERROR_PERMISSION_DENIED: uu_die("Insufficient privileges.\n"); /* NOTREACHED */ case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: readonly: if (retake) restarter_mark_pending_snapshot(fmri, RINST_RETAKE_START); break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("_scf_snapshot_attach", scf_error()); } } out: scf_snapshot_destroy(start); scf_snapshot_destroy(running); scf_instance_destroy(inst); return (ret); } /* * Before a refresh, update the "running" snapshot from the editing * configuration. * * Returns 0 on success and -1 on failure. */ int libscf_snapshots_refresh(scf_instance_t *inst, const char *fmri) { scf_handle_t *h; scf_snapshot_t *snap; boolean_t err = 1; h = scf_instance_handle(inst); if (h == NULL) goto out; snap = scf_snapshot_create(h); if (snap == NULL) goto out; if (scf_instance_get_snapshot(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) { if (_scf_snapshot_take_attach(inst, snap) == 0) err = 0; } else { switch (scf_error()) { case SCF_ERROR_DELETED: err = 0; goto out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_NOT_SET: assert(0); abort(); /* NOTREACHED */ default: goto out; } log_error(LOG_DEBUG, "Service %s has no %s snapshot; creating one.\n", fmri, SMF_SNAPSHOT_RUNNING); if (_scf_snapshot_take_new(inst, SMF_SNAPSHOT_RUNNING, snap) == 0) err = 0; } out: scf_snapshot_destroy(snap); if (!err) return (0); log_error(LOG_WARNING, "Could not update \"running\" snapshot for refresh of %s.\n", fmri); return (-1); } /* * int libscf_read_single_astring() * Reads a single astring value of the requested property into the * pre-allocated buffer (conventionally of size max_scf_value_size). * Multiple values constitute an error. * * Returns 0 on success or LIBSCF_PROPERTY_ABSENT or LIBSCF_PROPERTY_ERROR. */ static int libscf_read_single_astring(scf_handle_t *h, scf_property_t *prop, char **ret) { scf_value_t *val = safe_scf_value_create(h); int r = 0; if (scf_property_get_value(prop, val) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) r = LIBSCF_PROPERTY_ABSENT; else r = LIBSCF_PROPERTY_ERROR; goto read_single_astring_fail; } if (scf_value_get_astring(val, *ret, max_scf_value_size) <= 0) { r = LIBSCF_PROPERTY_ERROR; goto read_single_astring_fail; } read_single_astring_fail: scf_value_destroy(val); return (r); } /* * libscf_get_stn_tset */ int32_t libscf_get_stn_tset(scf_instance_t *inst) { scf_handle_t *h = scf_instance_handle(inst); scf_propertygroup_t *pg = scf_pg_create(h); char *pgname = NULL; int32_t t, f, tset; assert(inst != NULL); pgname = startd_alloc(max_scf_fmri_size); if (h == NULL || pg == NULL) { tset = -1; goto cleanup; } for (tset = 0, t = 1; t < SCF_STATE_ALL; t <<= 1) { f = t << 16; (void) strcpy(pgname, SCF_STN_PREFIX_TO); (void) strlcat(pgname, smf_state_to_string(t), max_scf_fmri_size); if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == SCF_SUCCESS) { tset |= t; } else if (scf_error() != SCF_ERROR_NOT_FOUND && scf_error() != SCF_ERROR_DELETED) { tset = -1; goto cleanup; } (void) strcpy(pgname, SCF_STN_PREFIX_FROM); (void) strlcat(pgname, smf_state_to_string(t), max_scf_fmri_size); if (scf_instance_get_pg_composed(inst, NULL, pgname, pg) == SCF_SUCCESS) { tset |= f; } else if (scf_error() != SCF_ERROR_NOT_FOUND && scf_error() != SCF_ERROR_DELETED) { tset = -1; goto cleanup; } } cleanup: scf_pg_destroy(pg); startd_free(pgname, max_scf_fmri_size); return (tset); } static int32_t libscf_get_global_stn_tset(scf_handle_t *h) { scf_instance_t *inst = scf_instance_create(h); int32_t tset = -1; if (inst == NULL) { goto cleanup; } if (scf_handle_decode_fmri(h, SCF_INSTANCE_GLOBAL, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { goto cleanup; } tset = libscf_get_stn_tset(inst); cleanup: scf_instance_destroy(inst); if (tset == -1) log_framework(LOG_WARNING, "Failed to get system wide notification parameters: %s\n", scf_strerror(scf_error())); return (tset); } static int libscf_read_state(const scf_propertygroup_t *pg, const char *prop_name, restarter_instance_state_t *state) { scf_handle_t *h; scf_property_t *prop; char *char_state = startd_alloc(max_scf_value_size); int ret = 0; h = scf_pg_handle(pg); prop = safe_scf_property_create(h); if (scf_pg_get_property(pg, prop_name, prop) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) ret = LIBSCF_PROPERTY_ABSENT; else ret = LIBSCF_PROPERTY_ERROR; } else { ret = libscf_read_single_astring(h, prop, &char_state); if (ret != 0) { if (ret != LIBSCF_PROPERTY_ABSENT) ret = LIBSCF_PROPERTY_ERROR; } else { *state = restarter_string_to_state(char_state); ret = 0; } } startd_free(char_state, max_scf_value_size); scf_property_destroy(prop); return (ret); } /* * int libscf_read_states(const scf_propertygroup_t *, * restarter_instance_state_t *, restarter_instance_state_t *) * * Set the current state and next_state values for the given service instance. * Returns 0 on success, or a libscf error code on failure. */ int libscf_read_states(const scf_propertygroup_t *pg, restarter_instance_state_t *state, restarter_instance_state_t *next_state) { int state_ret, next_state_ret, ret; state_ret = libscf_read_state(pg, SCF_PROPERTY_STATE, state); next_state_ret = libscf_read_state(pg, SCF_PROPERTY_NEXT_STATE, next_state); if (state_ret == LIBSCF_PROPERTY_ERROR || next_state_ret == LIBSCF_PROPERTY_ERROR) { ret = LIBSCF_PROPERTY_ERROR; } else if (state_ret == 0 && next_state_ret == 0) { ret = 0; } else if (state_ret == LIBSCF_PROPERTY_ABSENT && next_state_ret == LIBSCF_PROPERTY_ABSENT) { *state = RESTARTER_STATE_UNINIT; *next_state = RESTARTER_STATE_NONE; ret = 0; } else if (state_ret == LIBSCF_PROPERTY_ABSENT || next_state_ret == LIBSCF_PROPERTY_ABSENT) { log_framework(LOG_DEBUG, "Only one repository state exists, setting " "restarter states to MAINTENANCE and NONE\n"); *state = RESTARTER_STATE_MAINT; *next_state = RESTARTER_STATE_NONE; ret = 0; } else { ret = LIBSCF_PROPERTY_ERROR; } read_states_out: return (ret); } /* * depgroup_empty() * * Returns 0 if not empty. * Returns 1 if empty. * Returns -1 on error (check scf_error()). */ int depgroup_empty(scf_handle_t *h, scf_propertygroup_t *pg) { int empty = 1; scf_iter_t *iter; scf_property_t *prop; int ret; iter = safe_scf_iter_create(h); prop = safe_scf_property_create(h); if (scf_iter_pg_properties(iter, pg) != SCF_SUCCESS) { scf_property_destroy(prop); scf_iter_destroy(iter); return (-1); } ret = scf_iter_next_property(iter, prop); if (ret < 0) { scf_property_destroy(prop); scf_iter_destroy(iter); return (-1); } if (ret == 1) empty = 0; scf_property_destroy(prop); scf_iter_destroy(iter); return (empty); } gv_type_t depgroup_read_scheme(scf_handle_t *h, scf_propertygroup_t *pg) { scf_property_t *prop; char *scheme = startd_alloc(max_scf_value_size); gv_type_t ret; prop = safe_scf_property_create(h); if (scf_pg_get_property(pg, SCF_PROPERTY_TYPE, prop) == -1 || libscf_read_single_astring(h, prop, &scheme) != 0) { scf_property_destroy(prop); startd_free(scheme, max_scf_value_size); return (GVT_UNSUPPORTED); } if (strcmp(scheme, "service") == 0) ret = GVT_INST; else if (strcmp(scheme, "path") == 0) ret = GVT_FILE; else ret = GVT_UNSUPPORTED; startd_free(scheme, max_scf_value_size); scf_property_destroy(prop); return (ret); } depgroup_type_t depgroup_read_grouping(scf_handle_t *h, scf_propertygroup_t *pg) { char *grouping = startd_alloc(max_scf_value_size); depgroup_type_t ret; scf_property_t *prop = safe_scf_property_create(h); if (scf_pg_get_property(pg, SCF_PROPERTY_GROUPING, prop) == -1 || libscf_read_single_astring(h, prop, &grouping) != 0) { scf_property_destroy(prop); startd_free(grouping, max_scf_value_size); return (DEPGRP_UNSUPPORTED); } if (strcmp(grouping, SCF_DEP_REQUIRE_ANY) == 0) ret = DEPGRP_REQUIRE_ANY; else if (strcmp(grouping, SCF_DEP_REQUIRE_ALL) == 0) ret = DEPGRP_REQUIRE_ALL; else if (strcmp(grouping, SCF_DEP_OPTIONAL_ALL) == 0) ret = DEPGRP_OPTIONAL_ALL; else if (strcmp(grouping, SCF_DEP_EXCLUDE_ALL) == 0) ret = DEPGRP_EXCLUDE_ALL; else { ret = DEPGRP_UNSUPPORTED; } startd_free(grouping, max_scf_value_size); scf_property_destroy(prop); return (ret); } restarter_error_t depgroup_read_restart(scf_handle_t *h, scf_propertygroup_t *pg) { scf_property_t *prop = safe_scf_property_create(h); char *restart_on = startd_alloc(max_scf_value_size); restarter_error_t ret; if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1 || libscf_read_single_astring(h, prop, &restart_on) != 0) { startd_free(restart_on, max_scf_value_size); scf_property_destroy(prop); return (RERR_UNSUPPORTED); } if (strcmp(restart_on, SCF_DEP_RESET_ON_ERROR) == 0) ret = RERR_FAULT; else if (strcmp(restart_on, SCF_DEP_RESET_ON_RESTART) == 0) ret = RERR_RESTART; else if (strcmp(restart_on, SCF_DEP_RESET_ON_REFRESH) == 0) ret = RERR_REFRESH; else if (strcmp(restart_on, SCF_DEP_RESET_ON_NONE) == 0) ret = RERR_NONE; else ret = RERR_UNSUPPORTED; startd_free(restart_on, max_scf_value_size); scf_property_destroy(prop); return (ret); } /* * int get_boolean() * Fetches the value of a boolean property of the given property group. * Returns * 0 - success * ECONNABORTED - repository connection broken * ECANCELED - pg was deleted * ENOENT - the property doesn't exist or has no values * EINVAL - the property has the wrong type * the property is not single-valued * EACCES - the current user does not have permission to read the value */ static int get_boolean(scf_propertygroup_t *pg, const char *propname, uint8_t *valuep) { scf_handle_t *h; scf_property_t *prop; scf_value_t *val; int ret = 0, r; scf_type_t type; h = scf_pg_handle(pg); prop = safe_scf_property_create(h); val = safe_scf_value_create(h); if (scf_pg_get_property(pg, propname, prop) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } if (scf_property_type(prop, &type) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ENOENT; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_property_type", scf_error()); } } if (type != SCF_TYPE_BOOLEAN) { ret = EINVAL; goto out; } if (scf_property_get_value(prop, val) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto out; case SCF_ERROR_CONSTRAINT_VIOLATED: ret = EINVAL; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EACCES; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_property_get_value", scf_error()); } } r = scf_value_get_boolean(val, valuep); assert(r == 0); out: scf_value_destroy(val); scf_property_destroy(prop); return (ret); } /* * get info event property from restarter:default */ int libscf_get_info_events_all(scf_propertygroup_t *pg) { uint8_t v; int r = 0; if (get_boolean(pg, INFO_EVENTS_ALL, &v) == 0) { r = v; } else if (scf_error() != SCF_ERROR_NOT_FOUND) { uu_warn("Failed get_boolean %s/%s: %s\n", SCF_PG_OPTIONS, INFO_EVENTS_ALL, scf_strerror(scf_error())); } return (r); } /* * int get_count() * Fetches the value of a count property of the given property group. * Returns * 0 - success * ECONNABORTED - repository connection broken * unknown libscf error * ECANCELED - pg was deleted * ENOENT - the property doesn't exist or has no values * EINVAL - the property has the wrong type * the property is not single-valued * EACCES - the current user does not have permission to read the value */ static int get_count(scf_propertygroup_t *pg, const char *propname, uint64_t *valuep) { scf_handle_t *h; scf_property_t *prop; scf_value_t *val; int ret = 0, r; h = scf_pg_handle(pg); prop = safe_scf_property_create(h); val = safe_scf_value_create(h); if (scf_pg_get_property(pg, propname, prop) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } if (scf_property_is_type(prop, SCF_TYPE_COUNT) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_TYPE_MISMATCH: ret = EINVAL; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: bad_error("scf_property_is_type", scf_error()); } } if (scf_property_get_value(prop, val) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto out; case SCF_ERROR_CONSTRAINT_VIOLATED: ret = EINVAL; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EACCES; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_property_get_value", scf_error()); } } r = scf_value_get_count(val, valuep); assert(r == 0); out: scf_value_destroy(val); scf_property_destroy(prop); return (ret); } static void get_restarter(scf_handle_t *h, scf_propertygroup_t *pg, char **restarter) { scf_property_t *prop = safe_scf_property_create(h); if (scf_pg_get_property(pg, SCF_PROPERTY_RESTARTER, prop) == -1 || libscf_read_single_astring(h, prop, restarter) != 0) *restarter[0] = '\0'; scf_property_destroy(prop); } /* * int libscf_instance_get_fmri(scf_instance_t *, char **) * Give a valid SCF instance, return its FMRI. Returns 0 on success, * ECONNABORTED, or ECANCELED if inst is deleted. */ int libscf_instance_get_fmri(scf_instance_t *inst, char **retp) { char *inst_fmri = startd_alloc(max_scf_fmri_size); inst_fmri[0] = 0; if (scf_instance_to_fmri(inst, inst_fmri, max_scf_fmri_size) <= 0) { startd_free(inst_fmri, max_scf_fmri_size); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_SET: assert(0); abort(); } } *retp = inst_fmri; return (0); } /* * int libscf_fmri_get_instance(scf_handle_t *, const char *, * scf_instance_t **) * Given a valid SCF handle and an FMRI, return the SCF instance that matches * exactly. The instance must be released using scf_instance_destroy(). * Returns 0 on success, EINVAL if the FMRI is invalid, ENOTSUP if the FMRI * is valid but designates something other than an instance, ECONNABORTED if * the repository connection is broken, or ENOENT if the instance does not * exist. */ int libscf_fmri_get_instance(scf_handle_t *h, const char *fmri, scf_instance_t **instp) { scf_instance_t *inst; int r; inst = safe_scf_instance_create(h); r = libscf_lookup_instance(fmri, inst); if (r == 0) *instp = inst; else scf_instance_destroy(inst); return (r); } int libscf_lookup_instance(const char *fmri, scf_instance_t *inst) { if (scf_handle_decode_fmri(scf_instance_handle(inst), fmri, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) != SCF_SUCCESS) { switch (scf_error()) { case SCF_ERROR_INVALID_ARGUMENT: return (EINVAL); case SCF_ERROR_CONSTRAINT_VIOLATED: return (ENOTSUP); case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_NOT_FOUND: return (ENOENT); case SCF_ERROR_HANDLE_MISMATCH: default: bad_error("scf_handle_decode_fmri", scf_error()); } } return (0); } /* * int libscf_get_deathrow() * Read deathrow for inst. Returns 0, ECONNABORTED if the connection to the * repository is broken, ECANCELED if inst is deleted, or ENOENT if inst * has no deathrow property group. * * If deathrow/deathrow was missing or invalid, *deathrow will be -1 and a * debug message is logged. */ int libscf_get_deathrow(scf_handle_t *h, scf_instance_t *inst, int *deathrow) { scf_propertygroup_t *pg; int r; uint8_t deathrow_8; pg = safe_scf_pg_create(h); if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_DEATHROW, pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: scf_pg_destroy(pg); return (ECONNABORTED); case SCF_ERROR_DELETED: scf_pg_destroy(pg); return (ECANCELED); case SCF_ERROR_NOT_FOUND: *deathrow = -1; break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("libscf_get_deathrow", scf_error()); } } else { switch (r = get_boolean(pg, SCF_PROPERTY_DEATHROW, &deathrow_8)) { case 0: *deathrow = deathrow_8; break; case ECONNABORTED: case ECANCELED: scf_pg_destroy(pg); return (r); case ENOENT: case EINVAL: *deathrow = -1; break; default: bad_error("get_boolean", r); } } scf_pg_destroy(pg); return (0); } /* * void libscf_get_basic_instance_data() * Read enabled, enabled_ovr, and restarter_fmri (into an allocated * buffer) for inst. Returns 0, ECONNABORTED if the connection to the * repository is broken, ECANCELED if inst is deleted, or ENOENT if inst * has no general property group. * * On success, restarter_fmri may be NULL. If general/enabled was missing * or invalid, *enabledp will be -1 and a debug message is logged. */ int libscf_get_basic_instance_data(scf_handle_t *h, scf_instance_t *inst, const char *fmri, int *enabledp, int *enabled_ovrp, char **restarter_fmri) { scf_propertygroup_t *pg; int r; uint8_t enabled_8; pg = safe_scf_pg_create(h); if (enabled_ovrp == NULL) goto enabled; if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL_OVR, pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: scf_pg_destroy(pg); return (ECONNABORTED); case SCF_ERROR_DELETED: scf_pg_destroy(pg); return (ECANCELED); case SCF_ERROR_NOT_FOUND: *enabled_ovrp = -1; break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg_composed", scf_error()); } } else { switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) { case 0: *enabled_ovrp = enabled_8; break; case ECONNABORTED: case ECANCELED: scf_pg_destroy(pg); return (r); case ENOENT: case EINVAL: *enabled_ovrp = -1; break; case EACCES: default: bad_error("get_boolean", r); } } enabled: /* * Since general/restarter can be at the service level, we must do * a composed lookup. These properties are immediate, though, so we * must use the "editing" snapshot. Technically enabled shouldn't be * at the service level, but looking it up composed, too, doesn't * hurt. */ if (scf_instance_get_pg_composed(inst, NULL, SCF_PG_GENERAL, pg) != 0) { scf_pg_destroy(pg); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_FOUND: return (ENOENT); case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg_composed", scf_error()); } } switch (r = get_boolean(pg, SCF_PROPERTY_ENABLED, &enabled_8)) { case 0: *enabledp = enabled_8; break; case ECONNABORTED: case ECANCELED: scf_pg_destroy(pg); return (r); case ENOENT: /* * DEBUG because this happens when svccfg import creates * a temporary service. */ log_framework(LOG_DEBUG, "general/enabled property of %s is missing.\n", fmri); *enabledp = -1; break; case EINVAL: log_framework(LOG_ERR, "general/enabled property of %s is invalid.\n", fmri); *enabledp = -1; break; case EACCES: default: bad_error("get_boolean", r); } if (restarter_fmri != NULL) get_restarter(h, pg, restarter_fmri); scf_pg_destroy(pg); return (0); } /* * Sets pg to the name property group of s_inst. If it doesn't exist, it is * added. * * Fails with * ECONNABORTED - repository disconnection or unknown libscf error * ECANCELED - inst is deleted * EPERM - permission is denied * EACCES - backend denied access * EROFS - backend readonly */ int libscf_inst_get_or_add_pg(scf_instance_t *inst, const char *name, const char *type, uint32_t flags, scf_propertygroup_t *pg) { uint32_t f; again: if (scf_instance_get_pg(inst, name, pg) == 0) { if (scf_pg_get_flags(pg, &f) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: goto add; case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_flags", scf_error()); } } if (f == flags) return (0); if (scf_pg_delete(pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: break; case SCF_ERROR_PERMISSION_DENIED: return (EPERM); case SCF_ERROR_BACKEND_ACCESS: return (EACCES); case SCF_ERROR_BACKEND_READONLY: return (EROFS); case SCF_ERROR_NOT_SET: bad_error("scf_pg_delete", scf_error()); } } } else { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg", scf_error()); } } add: if (scf_instance_add_pg(inst, name, type, flags, pg) == 0) return (0); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_EXISTS: goto again; case SCF_ERROR_PERMISSION_DENIED: return (EPERM); case SCF_ERROR_BACKEND_ACCESS: return (EACCES); case SCF_ERROR_BACKEND_READONLY: return (EROFS); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_instance_add_pg", scf_error()); /* NOTREACHED */ } } /* * Returns * 0 - success * ECONNABORTED - repository connection broken * - unknown libscf error * ECANCELED */ static scf_error_t transaction_add_set(scf_transaction_t *tx, scf_transaction_entry_t *ent, const char *pname, scf_type_t ty) { for (;;) { if (scf_transaction_property_change_type(tx, ent, pname, ty) == 0) return (0); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_IN_USE: case SCF_ERROR_NOT_SET: bad_error("scf_transaction_property_change_type", scf_error()); } if (scf_transaction_property_new(tx, ent, pname, ty) == 0) return (0); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_EXISTS: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_IN_USE: case SCF_ERROR_NOT_SET: bad_error("scf_transaction_property_new", scf_error()); /* NOTREACHED */ } } } /* * Returns * 0 - success * ECONNABORTED - repository connection broken * - unknown libscf error * ECANCELED - pg was deleted * EPERM * EACCES * EROFS */ static int pg_set_prop_value(scf_propertygroup_t *pg, const char *pname, scf_value_t *v) { scf_handle_t *h; scf_transaction_t *tx; scf_transaction_entry_t *e; scf_type_t ty; scf_error_t scfe; int ret, r; h = scf_pg_handle(pg); tx = safe_scf_transaction_create(h); e = safe_scf_entry_create(h); ty = scf_value_type(v); assert(ty != SCF_TYPE_INVALID); for (;;) { if (scf_transaction_start(tx, pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_transaction_start", ret); } } ret = transaction_add_set(tx, e, pname, ty); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: goto out; default: bad_error("transaction_add_set", ret); } r = scf_entry_add_value(e, v); assert(r == 0); r = scf_transaction_commit(tx); if (r == 1) break; if (r != 0) { scfe = scf_error(); scf_transaction_reset(tx); switch (scfe) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_transaction_commit", scfe); } } scf_transaction_reset(tx); if (scf_pg_update(pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_pg_update", scf_error()); } } } ret = 0; out: scf_transaction_destroy(tx); scf_entry_destroy(e); return (ret); } /* * Returns * 0 - success * ECONNABORTED - repository connection broken * - unknown libscf error * ECANCELED - inst was deleted * EPERM * EACCES * EROFS */ int libscf_inst_set_boolean_prop(scf_instance_t *inst, const char *pgname, const char *pgtype, uint32_t pgflags, const char *pname, int val) { scf_handle_t *h; scf_propertygroup_t *pg = NULL; scf_value_t *v; int ret = 0; h = scf_instance_handle(inst); pg = safe_scf_pg_create(h); v = safe_scf_value_create(h); ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: goto out; default: bad_error("libscf_inst_get_or_add_pg", ret); } scf_value_set_boolean(v, val); ret = pg_set_prop_value(pg, pname, v); switch (ret) { case 0: case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: break; default: bad_error("pg_set_prop_value", ret); } out: scf_pg_destroy(pg); scf_value_destroy(v); return (ret); } /* * Returns * 0 - success * ECONNABORTED - repository connection broken * - unknown libscf error * ECANCELED - inst was deleted * EPERM * EACCES * EROFS */ int libscf_inst_set_count_prop(scf_instance_t *inst, const char *pgname, const char *pgtype, uint32_t pgflags, const char *pname, uint64_t count) { scf_handle_t *h; scf_propertygroup_t *pg = NULL; scf_value_t *v; int ret = 0; h = scf_instance_handle(inst); pg = safe_scf_pg_create(h); v = safe_scf_value_create(h); ret = libscf_inst_get_or_add_pg(inst, pgname, pgtype, pgflags, pg); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: goto out; default: bad_error("libscf_inst_get_or_add_pg", ret); } scf_value_set_count(v, count); ret = pg_set_prop_value(pg, pname, v); switch (ret) { case 0: case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: break; default: bad_error("pg_set_prop_value", ret); } out: scf_pg_destroy(pg); scf_value_destroy(v); return (ret); } /* * Returns 0 on success, ECONNABORTED if the repository connection is broken, * ECANCELED if inst is deleted, EROFS if the backend is readonly, or EPERM if * permission was denied. */ int libscf_set_enable_ovr(scf_instance_t *inst, int enable) { return (libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL_OVR, SCF_PG_GENERAL_OVR_TYPE, SCF_PG_GENERAL_OVR_FLAGS, SCF_PROPERTY_ENABLED, enable)); } /* * Returns 0 on success, ECONNABORTED if the repository connection is broken, * ECANCELED if inst is deleted, EROFS if the backend is readonly, or EPERM if * permission was denied. */ int libscf_set_deathrow(scf_instance_t *inst, int deathrow) { return (libscf_inst_set_boolean_prop(inst, SCF_PG_DEATHROW, SCF_PG_DEATHROW_TYPE, SCF_PG_DEATHROW_FLAGS, SCF_PROPERTY_DEATHROW, deathrow)); } /* * Since we're clearing the over-ridden enabled state for the service, we'll * also take the opportunity to remove any comment. * * Returns 0, ECONNABORTED, ECANCELED, or EPERM. */ int libscf_delete_enable_ovr(scf_instance_t *inst) { int r = scf_instance_delete_prop(inst, SCF_PG_GENERAL_OVR, SCF_PROPERTY_ENABLED); if (r != 0) return (r); return (scf_instance_delete_prop(inst, SCF_PG_GENERAL_OVR, SCF_PROPERTY_COMMENT)); } /* * Fails with * ECONNABORTED - repository connection was broken * ECANCELED - pg was deleted * ENOENT - pg has no milestone property * EINVAL - the milestone property is misconfigured */ static int pg_get_milestone(scf_propertygroup_t *pg, scf_property_t *prop, scf_value_t *val, char *buf, size_t buf_sz) { if (scf_pg_get_property(pg, SCF_PROPERTY_MILESTONE, prop) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_FOUND: return (ENOENT); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } if (scf_property_get_value(prop, val) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: case SCF_ERROR_CONSTRAINT_VIOLATED: case SCF_ERROR_NOT_FOUND: return (EINVAL); case SCF_ERROR_NOT_SET: case SCF_ERROR_PERMISSION_DENIED: bad_error("scf_property_get_value", scf_error()); } } if (scf_value_get_astring(val, buf, buf_sz) < 0) { switch (scf_error()) { case SCF_ERROR_TYPE_MISMATCH: return (EINVAL); case SCF_ERROR_NOT_SET: default: bad_error("scf_value_get_astring", scf_error()); } } return (0); } /* * Fails with * ECONNABORTED - repository connection was broken * ECANCELED - inst was deleted * ENOENT - inst has no milestone property * EINVAL - the milestone property is misconfigured */ int libscf_get_milestone(scf_instance_t *inst, scf_property_t *prop, scf_value_t *val, char *buf, size_t buf_sz) { scf_propertygroup_t *pg; int r; pg = safe_scf_pg_create(scf_instance_handle(inst)); if (scf_instance_get_pg(inst, SCF_PG_OPTIONS_OVR, pg) == 0) { switch (r = pg_get_milestone(pg, prop, val, buf, buf_sz)) { case 0: case ECONNABORTED: case EINVAL: goto out; case ECANCELED: case ENOENT: break; default: bad_error("pg_get_milestone", r); } } else { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: r = ECONNABORTED; goto out; case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg", scf_error()); } } if (scf_instance_get_pg(inst, SCF_PG_OPTIONS, pg) == 0) { r = pg_get_milestone(pg, prop, val, buf, buf_sz); } else { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: r = ECONNABORTED; goto out; case SCF_ERROR_DELETED: r = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: r = ENOENT; break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg", scf_error()); } } out: scf_pg_destroy(pg); return (r); } /* * Get the runlevel character from the runlevel property of the given property * group. Fails with * ECONNABORTED - repository connection was broken * ECANCELED - prop's property group was deleted * ENOENT - the property has no values * EINVAL - the property has more than one value * the property is of the wrong type * the property value is malformed */ int libscf_extract_runlevel(scf_property_t *prop, char *rlp) { scf_value_t *val; char buf[2]; val = safe_scf_value_create(scf_property_handle(prop)); if (scf_property_get_value(prop, val) != 0) { scf_value_destroy(val); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_NOT_SET: return (ENOENT); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_CONSTRAINT_VIOLATED: return (EINVAL); case SCF_ERROR_NOT_FOUND: return (ENOENT); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_PERMISSION_DENIED: default: bad_error("scf_property_get_value", scf_error()); } } if (scf_value_get_astring(val, buf, sizeof (buf)) < 0) { scf_value_destroy(val); if (scf_error() != SCF_ERROR_TYPE_MISMATCH) bad_error("scf_value_get_astring", scf_error()); return (EINVAL); } scf_value_destroy(val); if (buf[0] == '\0' || buf[1] != '\0') return (EINVAL); *rlp = buf[0]; return (0); } /* * Delete the "runlevel" property from the given property group. Also set the * "milestone" property to the given string. Fails with ECONNABORTED, * ECANCELED, EPERM, EACCES, or EROFS. */ int libscf_clear_runlevel(scf_propertygroup_t *pg, const char *milestone) { scf_handle_t *h; scf_transaction_t *tx; scf_transaction_entry_t *e_rl, *e_ms; scf_value_t *val; scf_error_t serr; boolean_t isempty = B_TRUE; int ret = 0, r; h = scf_pg_handle(pg); tx = safe_scf_transaction_create(h); e_rl = safe_scf_entry_create(h); e_ms = safe_scf_entry_create(h); val = safe_scf_value_create(h); if (milestone) { r = scf_value_set_astring(val, milestone); assert(r == 0); } for (;;) { if (scf_transaction_start(tx, pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_transaction_start", scf_error()); } } if (scf_transaction_property_delete(tx, e_rl, "runlevel") == 0) { isempty = B_FALSE; } else { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_INVALID_ARGUMENT: bad_error("scf_transaction_property_delete", scf_error()); } } if (milestone) { ret = transaction_add_set(tx, e_ms, SCF_PROPERTY_MILESTONE, SCF_TYPE_ASTRING); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: goto out; default: bad_error("transaction_add_set", ret); } isempty = B_FALSE; r = scf_entry_add_value(e_ms, val); assert(r == 0); } if (isempty) goto out; r = scf_transaction_commit(tx); if (r == 1) break; if (r != 0) { serr = scf_error(); scf_transaction_reset(tx); switch (serr) { case SCF_ERROR_CONNECTION_BROKEN: ret = ECONNABORTED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; default: bad_error("scf_transaction_commit", serr); } } scf_transaction_reset(tx); if (scf_pg_update(pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: ret = ECONNABORTED; goto out; case SCF_ERROR_NOT_SET: ret = ECANCELED; goto out; default: assert(0); abort(); } } } out: scf_transaction_destroy(tx); scf_entry_destroy(e_rl); scf_entry_destroy(e_ms); scf_value_destroy(val); return (ret); } /* * int libscf_get_template_values(scf_instance_t *, scf_snapshot_t *, * char **) * * Return template values for inst in *common_name suitable for use in * restarter_inst_t->ri_common_name. Called by restarter_insert_inst(). * * Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if * a value fetch failed for a property, ENOENT if the instance has no * tm_common_name property group or the property group is deleted, and * ECONNABORTED if the repository connection is broken. */ int libscf_get_template_values(scf_instance_t *inst, scf_snapshot_t *snap, char **common_name, char **c_common_name) { scf_handle_t *h; scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; int ret = 0, r; char *cname = startd_alloc(max_scf_value_size); char *c_cname = startd_alloc(max_scf_value_size); int common_name_initialized = B_FALSE; int c_common_name_initialized = B_FALSE; h = scf_instance_handle(inst); pg = safe_scf_pg_create(h); prop = safe_scf_property_create(h); /* * The tm_common_name property group, as with all template property * groups, is optional. */ if (scf_instance_get_pg_composed(inst, snap, SCF_PG_TM_COMMON_NAME, pg) == -1) { switch (scf_error()) { case SCF_ERROR_DELETED: ret = ECANCELED; goto template_values_out; case SCF_ERROR_NOT_FOUND: goto template_values_out; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto template_values_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg_composed", scf_error()); } } /* * The name we wish uses the current locale name as the property name. */ if (st->st_locale != NULL) { if (scf_pg_get_property(pg, st->st_locale, prop) == -1) { switch (scf_error()) { case SCF_ERROR_DELETED: case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto template_values_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } else { if ((r = libscf_read_single_astring(h, prop, &cname)) != 0) { if (r != LIBSCF_PROPERTY_ABSENT) ret = ECHILD; goto template_values_out; } *common_name = cname; common_name_initialized = B_TRUE; } } /* * Also pull out the C locale name, as a fallback for the case where * service offers no localized name. */ if (scf_pg_get_property(pg, "C", prop) == -1) { switch (scf_error()) { case SCF_ERROR_DELETED: ret = ENOENT; goto template_values_out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto template_values_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } else { if ((r = libscf_read_single_astring(h, prop, &c_cname)) != 0) { if (r != LIBSCF_PROPERTY_ABSENT) ret = ECHILD; goto template_values_out; } *c_common_name = c_cname; c_common_name_initialized = B_TRUE; } template_values_out: if (common_name_initialized == B_FALSE) startd_free(cname, max_scf_value_size); if (c_common_name_initialized == B_FALSE) startd_free(c_cname, max_scf_value_size); scf_property_destroy(prop); scf_pg_destroy(pg); return (ret); } /* * int libscf_get_startd_properties(scf_handle_t *, scf_instance_t *, * scf_snapshot_t *, uint_t *, char **) * * Return startd settings for inst in *flags suitable for use in * restarter_inst_t->ri_flags. Called by restarter_insert_inst(). * * Returns 0 on success, ECANCELED if the instance is deleted, ECHILD if * a value fetch failed for a property, ENOENT if the instance has no * general property group or the property group is deleted, and * ECONNABORTED if the repository connection is broken. */ int libscf_get_startd_properties(scf_instance_t *inst, scf_snapshot_t *snap, uint_t *flags, char **prefixp) { scf_handle_t *h; scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; int style = RINST_CONTRACT; char *style_str = startd_alloc(max_scf_value_size); int ret = 0, r; h = scf_instance_handle(inst); pg = safe_scf_pg_create(h); prop = safe_scf_property_create(h); /* * The startd property group is optional. */ if (scf_instance_get_pg_composed(inst, snap, SCF_PG_STARTD, pg) == -1) { switch (scf_error()) { case SCF_ERROR_DELETED: ret = ECANCELED; goto instance_flags_out; case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto instance_flags_out; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto instance_flags_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg_composed", scf_error()); } } /* * 1. Duration property. */ if (scf_pg_get_property(pg, SCF_PROPERTY_DURATION, prop) == -1) { switch (scf_error()) { case SCF_ERROR_DELETED: ret = ENOENT; goto instance_flags_out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto instance_flags_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } else { errno = 0; if ((r = libscf_read_single_astring(h, prop, &style_str)) != 0) { if (r != LIBSCF_PROPERTY_ABSENT) ret = ECHILD; goto instance_flags_out; } if (strcmp(style_str, "child") == 0) style = RINST_WAIT; else if (strcmp(style_str, "transient") == 0) style = RINST_TRANSIENT; } /* * 2. utmpx prefix property. */ if (scf_pg_get_property(pg, SCF_PROPERTY_UTMPX_PREFIX, prop) == 0) { errno = 0; if ((r = libscf_read_single_astring(h, prop, prefixp)) != 0) { if (r != LIBSCF_PROPERTY_ABSENT) ret = ECHILD; goto instance_flags_out; } } else { switch (scf_error()) { case SCF_ERROR_DELETED: ret = ENOENT; goto instance_flags_out; case SCF_ERROR_NOT_FOUND: goto instance_flags_out; case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto instance_flags_out; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: bad_error("scf_pg_get_property", scf_error()); } } instance_flags_out: startd_free(style_str, max_scf_value_size); *flags = (*flags & ~RINST_STYLE_MASK) | style; scf_property_destroy(prop); scf_pg_destroy(pg); return (ret); } /* * int libscf_read_method_ids(scf_handle_t *, scf_instance_t *, ctid_t *, * ctid_t *, pid_t *) * * Sets given id_t variables to primary and transient contract IDs and start * PID. Returns 0, ECONNABORTED, and ECANCELED. */ int libscf_read_method_ids(scf_handle_t *h, scf_instance_t *inst, const char *fmri, ctid_t *primary, ctid_t *transient, pid_t *start_pid) { scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; scf_value_t *val = NULL; uint64_t p, t; int ret = 0; *primary = 0; *transient = 0; *start_pid = -1; pg = safe_scf_pg_create(h); prop = safe_scf_property_create(h); val = safe_scf_value_create(h); if (scf_instance_get_pg(inst, SCF_PG_RESTARTER, pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto read_id_err; case SCF_ERROR_DELETED: ret = ECANCELED; goto read_id_err; case SCF_ERROR_NOT_FOUND: goto read_id_err; case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg", scf_error()); } } ret = get_count(pg, SCF_PROPERTY_CONTRACT, &p); switch (ret) { case 0: break; case EINVAL: log_error(LOG_NOTICE, "%s: Ignoring %s/%s: multivalued or not of type count\n", fmri, SCF_PG_RESTARTER, SCF_PROPERTY_CONTRACT); /* FALLTHROUGH */ case ENOENT: ret = 0; goto read_trans; case ECONNABORTED: case ECANCELED: goto read_id_err; case EACCES: default: bad_error("get_count", ret); } *primary = p; read_trans: ret = get_count(pg, SCF_PROPERTY_TRANSIENT_CONTRACT, &t); switch (ret) { case 0: break; case EINVAL: log_error(LOG_NOTICE, "%s: Ignoring %s/%s: multivalued or not of type count\n", fmri, SCF_PG_RESTARTER, SCF_PROPERTY_TRANSIENT_CONTRACT); /* FALLTHROUGH */ case ENOENT: ret = 0; goto read_pid_only; case ECONNABORTED: case ECANCELED: goto read_id_err; case EACCES: default: bad_error("get_count", ret); } *transient = t; read_pid_only: ret = get_count(pg, SCF_PROPERTY_START_PID, &p); switch (ret) { case 0: break; case EINVAL: log_error(LOG_NOTICE, "%s: Ignoring %s/%s: multivalued or not of type count\n", fmri, SCF_PG_RESTARTER, SCF_PROPERTY_START_PID); /* FALLTHROUGH */ case ENOENT: ret = 0; goto read_id_err; case ECONNABORTED: case ECANCELED: goto read_id_err; case EACCES: default: bad_error("get_count", ret); } *start_pid = p; read_id_err: scf_value_destroy(val); scf_property_destroy(prop); scf_pg_destroy(pg); return (ret); } /* * Returns with * 0 - success * ECONNABORTED - repository connection broken * - unknown libscf error * ECANCELED - s_inst was deleted * EPERM * EACCES * EROFS */ int libscf_write_start_pid(scf_instance_t *s_inst, pid_t pid) { scf_handle_t *h; scf_transaction_entry_t *t_pid; scf_value_t *v_pid; scf_propertygroup_t *pg; int ret = 0; h = scf_instance_handle(s_inst); pg = safe_scf_pg_create(h); t_pid = safe_scf_entry_create(h); v_pid = safe_scf_value_create(h); get_pg: ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: goto write_start_err; default: bad_error("libscf_inst_get_or_add_pg", ret); } scf_value_set_count(v_pid, pid); ret = pg_set_prop_value(pg, SCF_PROPERTY_START_PID, v_pid); switch (ret) { case 0: case ECONNABORTED: case EPERM: case EACCES: case EROFS: break; case ECANCELED: goto get_pg; default: bad_error("pg_set_prop_value", ret); } write_start_err: scf_entry_destroy(t_pid); scf_value_destroy(v_pid); scf_pg_destroy(pg); return (ret); } /* * Add a property indicating the instance log file. If the dir is * equal to LOG_PREFIX_EARLY, then the property restarter/alt_logfile * of the instance is used; otherwise, restarter/logfile is used. * * Returns * 0 - success * ECONNABORTED * ECANCELED * EPERM * EACCES * EROFS * EAGAIN */ int libscf_note_method_log(scf_instance_t *inst, const char *dir, const char *file) { scf_handle_t *h; scf_value_t *v; scf_propertygroup_t *pg; int ret = 0; char *logname; const char *propname; h = scf_instance_handle(inst); pg = safe_scf_pg_create(h); v = safe_scf_value_create(h); logname = uu_msprintf("%s%s", dir, file); if (logname == NULL) { ret = errno; goto out; } ret = libscf_inst_get_or_add_pg(inst, SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: goto out; default: bad_error("libscf_inst_get_or_add_pg", ret); } (void) scf_value_set_astring(v, logname); if (strcmp(LOG_PREFIX_EARLY, dir) == 0) propname = SCF_PROPERTY_ALT_LOGFILE; else propname = SCF_PROPERTY_LOGFILE; ret = pg_set_prop_value(pg, propname, v); switch (ret) { case 0: case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: break; default: bad_error("pg_set_prop_value", ret); } out: scf_pg_destroy(pg); scf_value_destroy(v); uu_free(logname); return (ret); } /* * Returns * 0 - success * ENAMETOOLONG - name is too long * ECONNABORTED * ECANCELED * EPERM * EACCES * EROFS */ int libscf_write_method_status(scf_instance_t *s_inst, const char *name, int status) { scf_handle_t *h; scf_transaction_t *tx; scf_transaction_entry_t *e_time, *e_stat; scf_value_t *v_time, *v_stat; scf_propertygroup_t *pg; int ret = 0, r; char pname[30]; struct timeval tv; scf_error_t scfe; if (strlen(name) + sizeof ("_method_waitstatus") > sizeof (pname)) return (ENAMETOOLONG); h = scf_instance_handle(s_inst); pg = safe_scf_pg_create(h); tx = safe_scf_transaction_create(h); e_time = safe_scf_entry_create(h); v_time = safe_scf_value_create(h); e_stat = safe_scf_entry_create(h); v_stat = safe_scf_value_create(h); get_pg: ret = libscf_inst_get_or_add_pg(s_inst, SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, pg); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: case EPERM: case EACCES: case EROFS: goto out; default: bad_error("libscf_inst_get_or_add_pg", ret); } (void) gettimeofday(&tv, NULL); r = scf_value_set_time(v_time, tv.tv_sec, tv.tv_usec * 1000); assert(r == 0); scf_value_set_integer(v_stat, status); for (;;) { if (scf_transaction_start(tx, pg) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_transaction_start", ret); } } (void) snprintf(pname, sizeof (pname), "%s_method_timestamp", name); ret = transaction_add_set(tx, e_time, pname, SCF_TYPE_TIME); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: goto out; default: bad_error("transaction_add_set", ret); } r = scf_entry_add_value(e_time, v_time); assert(r == 0); (void) snprintf(pname, sizeof (pname), "%s_method_waitstatus", name); ret = transaction_add_set(tx, e_stat, pname, SCF_TYPE_INTEGER); switch (ret) { case 0: break; case ECONNABORTED: case ECANCELED: goto out; default: bad_error("transaction_add_set", ret); } r = scf_entry_add_value(e_stat, v_stat); if (r != 0) bad_error("scf_entry_add_value", scf_error()); r = scf_transaction_commit(tx); if (r == 1) break; if (r != 0) { scfe = scf_error(); scf_transaction_reset_all(tx); switch (scfe) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto out; case SCF_ERROR_BACKEND_ACCESS: ret = EACCES; goto out; case SCF_ERROR_BACKEND_READONLY: ret = EROFS; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_transaction_commit", scfe); } } scf_transaction_reset_all(tx); if (scf_pg_update(pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_DELETED: ret = ECANCELED; goto out; case SCF_ERROR_NOT_SET: bad_error("scf_pg_update", scf_error()); } } } out: scf_transaction_destroy(tx); scf_entry_destroy(e_time); scf_value_destroy(v_time); scf_entry_destroy(e_stat); scf_value_destroy(v_stat); scf_pg_destroy(pg); return (ret); } extern int32_t stn_global; /* * Call dgraph_add_instance() for each instance in the repository. */ void libscf_populate_graph(scf_handle_t *h) { scf_scope_t *scope; scf_service_t *svc; scf_instance_t *inst; scf_iter_t *svc_iter; scf_iter_t *inst_iter; scope = safe_scf_scope_create(h); svc = safe_scf_service_create(h); inst = safe_scf_instance_create(h); svc_iter = safe_scf_iter_create(h); inst_iter = safe_scf_iter_create(h); deathrow_init(); stn_global = libscf_get_global_stn_tset(h); if (scf_handle_get_local_scope(h, scope) != SCF_SUCCESS) uu_die("retrieving local scope failed: %s\n", scf_strerror(scf_error())); if (scf_iter_scope_services(svc_iter, scope) == -1) uu_die("walking local scope's services failed\n"); while (scf_iter_next_service(svc_iter, svc) > 0) { if (scf_iter_service_instances(inst_iter, svc) == -1) uu_die("unable to walk service's instances"); while (scf_iter_next_instance(inst_iter, inst) > 0) { char *fmri; if (libscf_instance_get_fmri(inst, &fmri) == 0) { int err; err = dgraph_add_instance(fmri, inst, B_TRUE); if (err != 0 && err != EEXIST) log_error(LOG_WARNING, "Failed to add %s (%s).\n", fmri, strerror(err)); startd_free(fmri, max_scf_fmri_size); } } } deathrow_fini(); scf_iter_destroy(inst_iter); scf_iter_destroy(svc_iter); scf_instance_destroy(inst); scf_service_destroy(svc); scf_scope_destroy(scope); } /* * Monitors get handled differently since there can be multiple of them. * * Returns exec string on success. If method not defined, returns * LIBSCF_PGROUP_ABSENT; if exec property missing, returns * LIBSCF_PROPERTY_ABSENT. Returns LIBSCF_PROPERTY_ERROR on other failures. */ char * libscf_get_method(scf_handle_t *h, int type, restarter_inst_t *inst, scf_snapshot_t *snap, method_restart_t *restart_on, uint_t *cte_mask, uint8_t *need_sessionp, uint64_t *timeout, uint8_t *timeout_retry) { scf_instance_t *scf_inst = NULL; scf_propertygroup_t *pg = NULL, *pg_startd = NULL; scf_property_t *prop = NULL; const char *name; char *method = startd_alloc(max_scf_value_size); char *ig = startd_alloc(max_scf_value_size); char *restart = startd_alloc(max_scf_value_size); char *ret; int error = 0, r; scf_inst = safe_scf_instance_create(h); pg = safe_scf_pg_create(h); pg_startd = safe_scf_pg_create(h); prop = safe_scf_property_create(h); ret = NULL; *restart_on = METHOD_RESTART_UNKNOWN; switch (type) { case METHOD_START: name = "start"; break; case METHOD_STOP: name = "stop"; break; case METHOD_REFRESH: name = "refresh"; break; default: error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } if (scf_handle_decode_fmri(h, inst->ri_i.i_fmri, NULL, NULL, scf_inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) { log_error(LOG_WARNING, "%s: get_method decode instance FMRI failed: %s\n", inst->ri_i.i_fmri, scf_strerror(scf_error())); error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } if (scf_instance_get_pg_composed(scf_inst, snap, name, pg) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) error = LIBSCF_PGROUP_ABSENT; else error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } if (scf_pg_get_property(pg, SCF_PROPERTY_EXEC, prop) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) error = LIBSCF_PROPERTY_ABSENT; else error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } error = libscf_read_single_astring(h, prop, &method); if (error != 0) { log_error(LOG_WARNING, "%s: get_method failed: can't get a single astring " "from %s/%s\n", inst->ri_i.i_fmri, name, SCF_PROPERTY_EXEC); goto get_method_cleanup; } error = expand_method_tokens(method, scf_inst, snap, type, &ret); if (error != 0) { log_instance(inst, B_TRUE, "Could not expand method tokens " "in \"%s\": %s.", method, ret); error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } r = get_count(pg, SCF_PROPERTY_TIMEOUT, timeout); switch (r) { case 0: break; case ECONNABORTED: error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; case EINVAL: log_instance(inst, B_TRUE, "%s/%s is multi-valued or not of " "type count. Using infinite timeout.", name, SCF_PROPERTY_TIMEOUT); /* FALLTHROUGH */ case ECANCELED: case ENOENT: *timeout = METHOD_TIMEOUT_INFINITE; break; case EACCES: default: bad_error("get_count", r); } /* Both 0 and -1 (ugh) are considered infinite timeouts. */ if (*timeout == -1 || *timeout == 0) *timeout = METHOD_TIMEOUT_INFINITE; if (scf_instance_get_pg_composed(scf_inst, snap, SCF_PG_STARTD, pg_startd) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: case SCF_ERROR_DELETED: error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; case SCF_ERROR_NOT_FOUND: *cte_mask = 0; break; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_NOT_SET: bad_error("scf_instance_get_pg_composed", scf_error()); } } else { if (scf_pg_get_property(pg_startd, SCF_PROPERTY_IGNORE, prop) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) *cte_mask = 0; else { error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } } else { error = libscf_read_single_astring(h, prop, &ig); if (error != 0) { log_error(LOG_WARNING, "%s: get_method failed: can't get a single " "astring from %s/%s\n", inst->ri_i.i_fmri, name, SCF_PROPERTY_IGNORE); goto get_method_cleanup; } if (strcmp(ig, "core") == 0) *cte_mask = CT_PR_EV_CORE; else if (strcmp(ig, "signal") == 0) *cte_mask = CT_PR_EV_SIGNAL; else if (strcmp(ig, "core,signal") == 0 || strcmp(ig, "signal,core") == 0) *cte_mask = CT_PR_EV_CORE | CT_PR_EV_SIGNAL; else *cte_mask = 0; } r = get_boolean(pg_startd, SCF_PROPERTY_NEED_SESSION, need_sessionp); switch (r) { case 0: break; case ECONNABORTED: error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; case ECANCELED: case ENOENT: case EINVAL: *need_sessionp = 0; break; case EACCES: default: bad_error("get_boolean", r); } /* * Determine whether service has overriden retry after * method timeout. Default to retry if no value is * specified. */ r = get_boolean(pg_startd, SCF_PROPERTY_TIMEOUT_RETRY, timeout_retry); switch (r) { case 0: break; case ECONNABORTED: error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; case ECANCELED: case ENOENT: case EINVAL: *timeout_retry = 1; break; case EACCES: default: bad_error("get_boolean", r); } } if (type != METHOD_START) goto get_method_cleanup; /* Only start methods need to honor the restart_on property. */ if (scf_pg_get_property(pg, SCF_PROPERTY_RESTART_ON, prop) == -1) { if (scf_error() == SCF_ERROR_NOT_FOUND) *restart_on = METHOD_RESTART_ALL; else error = LIBSCF_PROPERTY_ERROR; goto get_method_cleanup; } error = libscf_read_single_astring(h, prop, &restart); if (error != 0) { log_error(LOG_WARNING, "%s: get_method failed: can't get a single astring " "from %s/%s\n", inst->ri_i.i_fmri, name, SCF_PROPERTY_RESTART_ON); goto get_method_cleanup; } if (strcmp(restart, "all") == 0) *restart_on = METHOD_RESTART_ALL; else if (strcmp(restart, "external_fault") == 0) *restart_on = METHOD_RESTART_EXTERNAL_FAULT; else if (strcmp(restart, "any_fault") == 0) *restart_on = METHOD_RESTART_ANY_FAULT; get_method_cleanup: startd_free(ig, max_scf_value_size); startd_free(method, max_scf_value_size); startd_free(restart, max_scf_value_size); scf_instance_destroy(scf_inst); scf_pg_destroy(pg); scf_pg_destroy(pg_startd); scf_property_destroy(prop); if (error != 0 && ret != NULL) { free(ret); ret = NULL; } errno = error; return (ret); } /* * Returns 1 if we've reached the fault threshold */ int update_fault_count(restarter_inst_t *inst, int type) { assert(type == FAULT_COUNT_INCR || type == FAULT_COUNT_RESET); if (type == FAULT_COUNT_INCR) { inst->ri_i.i_fault_count++; log_framework(LOG_INFO, "%s: Increasing fault count to %d\n", inst->ri_i.i_fmri, inst->ri_i.i_fault_count); } if (type == FAULT_COUNT_RESET) inst->ri_i.i_fault_count = 0; if (inst->ri_i.i_fault_count >= FAULT_THRESHOLD) return (1); return (0); } /* * int libscf_unset_action() * Delete any pending timestamps for the specified action which is * older than the supplied ts. * * Returns 0 on success, ECONNABORTED, EACCES, or EPERM on failure. */ int libscf_unset_action(scf_handle_t *h, scf_propertygroup_t *pg, admin_action_t a, hrtime_t ts) { scf_transaction_t *t; scf_transaction_entry_t *e; scf_property_t *prop; scf_value_t *val; hrtime_t rep_ts; int ret = 0, r; t = safe_scf_transaction_create(h); e = safe_scf_entry_create(h); prop = safe_scf_property_create(h); val = safe_scf_value_create(h); for (;;) { if (scf_pg_update(pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto unset_action_cleanup; case SCF_ERROR_DELETED: goto unset_action_cleanup; case SCF_ERROR_NOT_SET: assert(0); abort(); } } if (scf_transaction_start(t, pg) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto unset_action_cleanup; case SCF_ERROR_DELETED: goto unset_action_cleanup; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto unset_action_cleanup; case SCF_ERROR_BACKEND_ACCESS: case SCF_ERROR_BACKEND_READONLY: ret = EACCES; goto unset_action_cleanup; case SCF_ERROR_IN_USE: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: assert(0); abort(); } } /* Return failure only if the property hasn't been deleted. */ if (scf_pg_get_property(pg, admin_actions[a], prop) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto unset_action_cleanup; case SCF_ERROR_DELETED: case SCF_ERROR_NOT_FOUND: goto unset_action_cleanup; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: assert(0); abort(); } } if (scf_property_get_value(prop, val) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto unset_action_cleanup; case SCF_ERROR_DELETED: case SCF_ERROR_NOT_FOUND: goto unset_action_cleanup; case SCF_ERROR_CONSTRAINT_VIOLATED: /* * More than one value was associated with * this property -- this is incorrect. Take * the opportunity to clean up and clear the * entire property. */ rep_ts = ts; break; case SCF_ERROR_PERMISSION_DENIED: case SCF_ERROR_NOT_SET: assert(0); abort(); } } else if (scf_value_get_integer(val, &rep_ts) == -1) { assert(scf_error() == SCF_ERROR_TYPE_MISMATCH); rep_ts = 0; } /* Repository ts is more current. Don't clear the action. */ if (rep_ts > ts) goto unset_action_cleanup; r = scf_transaction_property_change_type(t, e, admin_actions[a], SCF_TYPE_INTEGER); assert(r == 0); r = scf_transaction_commit(t); if (r == 1) break; if (r != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto unset_action_cleanup; case SCF_ERROR_DELETED: break; case SCF_ERROR_PERMISSION_DENIED: ret = EPERM; goto unset_action_cleanup; case SCF_ERROR_BACKEND_ACCESS: case SCF_ERROR_BACKEND_READONLY: ret = EACCES; goto unset_action_cleanup; case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: assert(0); abort(); } } scf_transaction_reset(t); } unset_action_cleanup: scf_transaction_destroy(t); scf_entry_destroy(e); scf_property_destroy(prop); scf_value_destroy(val); return (ret); } /* * Decorates & binds hndl. hndl must be unbound. Returns * 0 - success * -1 - repository server is not running * -1 - repository server is out of resources */ static int handle_decorate_and_bind(scf_handle_t *hndl) { scf_value_t *door_dec_value; door_dec_value = safe_scf_value_create(hndl); /* * Decorate if alternate door path set. */ if (st->st_door_path) { if (scf_value_set_astring(door_dec_value, st->st_door_path) != 0) uu_die("$STARTD_ALT_DOOR is too long.\n"); if (scf_handle_decorate(hndl, "door_path", door_dec_value) != 0) bad_error("scf_handle_decorate", scf_error()); } scf_value_destroy(door_dec_value); if (scf_handle_bind(hndl) == 0) return (0); switch (scf_error()) { case SCF_ERROR_NO_SERVER: case SCF_ERROR_NO_RESOURCES: return (-1); case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_IN_USE: default: bad_error("scf_handle_bind", scf_error()); /* NOTREACHED */ } } scf_handle_t * libscf_handle_create_bound(scf_version_t v) { scf_handle_t *hndl = scf_handle_create(v); if (hndl == NULL) return (hndl); if (handle_decorate_and_bind(hndl) == 0) return (hndl); scf_handle_destroy(hndl); return (NULL); } void libscf_handle_rebind(scf_handle_t *h) { (void) scf_handle_unbind(h); MUTEX_LOCK(&st->st_configd_live_lock); /* * Try to rebind the handle before sleeping in case the server isn't * really dead. */ while (handle_decorate_and_bind(h) != 0) (void) pthread_cond_wait(&st->st_configd_live_cv, &st->st_configd_live_lock); MUTEX_UNLOCK(&st->st_configd_live_lock); } /* * Create a handle and try to bind it until it succeeds. Always returns * a bound handle. */ scf_handle_t * libscf_handle_create_bound_loop() { scf_handle_t *h; while ((h = scf_handle_create(SCF_VERSION)) == NULL) { /* This should have been caught earlier. */ assert(scf_error() != SCF_ERROR_VERSION_MISMATCH); (void) sleep(2); } if (handle_decorate_and_bind(h) != 0) libscf_handle_rebind(h); return (h); } /* * Call cb for each dependency property group of inst. cb is invoked with * a pointer to the scf_propertygroup_t and arg. If the repository connection * is broken, returns ECONNABORTED. If inst is deleted, returns ECANCELED. * If cb returns non-zero, the walk is stopped and EINTR is returned. * Otherwise returns 0. */ int walk_dependency_pgs(scf_instance_t *inst, callback_t cb, void *arg) { scf_handle_t *h; scf_snapshot_t *snap; scf_iter_t *iter; scf_propertygroup_t *pg; int r; h = scf_instance_handle(inst); iter = safe_scf_iter_create(h); pg = safe_scf_pg_create(h); snap = libscf_get_running_snapshot(inst); if (scf_iter_instance_pgs_typed_composed(iter, inst, snap, SCF_GROUP_DEPENDENCY) != 0) { scf_snapshot_destroy(snap); scf_pg_destroy(pg); scf_iter_destroy(iter); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: assert(0); abort(); } } for (;;) { r = scf_iter_next_pg(iter, pg); if (r == 0) break; if (r == -1) { scf_snapshot_destroy(snap); scf_pg_destroy(pg); scf_iter_destroy(iter); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_SET: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_HANDLE_MISMATCH: default: bad_error("scf_iter_next_pg", scf_error()); } } r = cb(pg, arg); if (r != 0) break; } scf_snapshot_destroy(snap); scf_pg_destroy(pg); scf_iter_destroy(iter); return (r == 0 ? 0 : EINTR); } /* * Call cb for each of the string values of prop. cb is invoked with * a pointer to the string and arg. If the connection to the repository is * broken, ECONNABORTED is returned. If the property is deleted, ECANCELED is * returned. If the property does not have astring type, EINVAL is returned. * If cb returns non-zero, the walk is stopped and EINTR is returned. * Otherwise 0 is returned. */ int walk_property_astrings(scf_property_t *prop, callback_t cb, void *arg) { scf_handle_t *h; scf_value_t *val; scf_iter_t *iter; char *buf; int r; ssize_t sz; if (scf_property_is_type(prop, SCF_TYPE_ASTRING) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_TYPE_MISMATCH: return (EINVAL); case SCF_ERROR_NOT_SET: assert(0); abort(); } } h = scf_property_handle(prop); val = safe_scf_value_create(h); iter = safe_scf_iter_create(h); if (scf_iter_property_values(iter, prop) != 0) { scf_iter_destroy(iter); scf_value_destroy(val); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_NOT_SET: assert(0); abort(); } } buf = startd_alloc(max_scf_value_size); for (;;) { r = scf_iter_next_value(iter, val); if (r < 0) { startd_free(buf, max_scf_value_size); scf_iter_destroy(iter); scf_value_destroy(val); switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: return (ECONNABORTED); case SCF_ERROR_DELETED: return (ECANCELED); case SCF_ERROR_NOT_SET: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_BOUND: case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_PERMISSION_DENIED: default: bad_error("scf_iter_next_value", scf_error()); } } if (r == 0) break; sz = scf_value_get_astring(val, buf, max_scf_value_size); assert(sz >= 0); r = cb(buf, arg); if (r != 0) break; } startd_free(buf, max_scf_value_size); scf_value_destroy(val); scf_iter_destroy(iter); return (r == 0 ? 0 : EINTR); } /* * Returns 0 or ECONNABORTED. */ int libscf_create_self(scf_handle_t *h) { scf_scope_t *scope; scf_service_t *svc; scf_instance_t *inst; instance_data_t idata; int ret = 0, r; ctid_t ctid; uint64_t uint64; uint_t count = 0, msecs = ALLOC_DELAY; const char * const startd_svc = "system/svc/restarter"; const char * const startd_inst = "default"; /* If SCF_SERVICE_STARTD changes, our strings must change, too. */ assert(strcmp(SCF_SERVICE_STARTD, "svc:/system/svc/restarter:default") == 0); scope = safe_scf_scope_create(h); svc = safe_scf_service_create(h); inst = safe_scf_instance_create(h); if (scf_handle_get_scope(h, SCF_SCOPE_LOCAL, scope) != 0) { assert(scf_error() == SCF_ERROR_CONNECTION_BROKEN); ret = ECONNABORTED; goto out; } get_svc: if (scf_scope_get_service(scope, startd_svc, svc) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: case SCF_ERROR_DELETED: default: ret = ECONNABORTED; goto out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_scope_get_service", scf_error()); } add_svc: if (scf_scope_add_service(scope, startd_svc, svc) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: case SCF_ERROR_DELETED: default: ret = ECONNABORTED; goto out; case SCF_ERROR_EXISTS: goto get_svc; case SCF_ERROR_PERMISSION_DENIED: case SCF_ERROR_BACKEND_ACCESS: case SCF_ERROR_BACKEND_READONLY: uu_warn("Could not create %s: %s\n", SCF_SERVICE_STARTD, scf_strerror(scf_error())); goto out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_scope_add_service", scf_error()); } } } if (scf_service_get_instance(svc, startd_inst, NULL) == 0) goto out; switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_NOT_FOUND: break; case SCF_ERROR_DELETED: goto add_svc; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_service_get_instance", scf_error()); } add_inst: if (scf_service_add_instance(svc, startd_inst, inst) != 0) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: ret = ECONNABORTED; goto out; case SCF_ERROR_EXISTS: break; case SCF_ERROR_PERMISSION_DENIED: case SCF_ERROR_BACKEND_ACCESS: uu_die("Could not create %s: %s\n", SCF_SERVICE_STARTD, scf_strerror(scf_error())); /* NOTREACHED */ case SCF_ERROR_BACKEND_READONLY: log_error(LOG_NOTICE, "Could not create %s: backend readonly.\n", SCF_SERVICE_STARTD); goto out; case SCF_ERROR_DELETED: goto add_svc; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_NOT_SET: bad_error("scf_service_add_instance", scf_error()); } } /* Set start time. */ idata.i_fmri = SCF_SERVICE_STARTD; idata.i_state = RESTARTER_STATE_NONE; idata.i_next_state = RESTARTER_STATE_NONE; set_state: switch (r = _restarter_commit_states(h, &idata, RESTARTER_STATE_ONLINE, RESTARTER_STATE_NONE, restarter_get_str_short(restarter_str_insert_in_graph))) { case 0: break; case ENOMEM: ++count; if (count < ALLOC_RETRY) { (void) poll(NULL, 0, msecs); msecs *= ALLOC_DELAY_MULT; goto set_state; } uu_die("Insufficient memory.\n"); /* NOTREACHED */ case ECONNABORTED: ret = ECONNABORTED; goto out; case ENOENT: goto add_inst; case EPERM: case EACCES: case EROFS: uu_warn("Could not timestamp %s: %s\n", idata.i_fmri, strerror(r)); break; case EINVAL: default: bad_error("_restarter_commit_states", r); } /* Set general/enabled. */ ret = libscf_inst_set_boolean_prop(inst, SCF_PG_GENERAL, SCF_PG_GENERAL_TYPE, SCF_PG_GENERAL_FLAGS, SCF_PROPERTY_ENABLED, 1); switch (ret) { case 0: case ECONNABORTED: case EPERM: case EACCES: case EROFS: break; case ECANCELED: goto add_inst; default: bad_error("libscf_inst_set_boolean_prop", ret); } ret = libscf_write_start_pid(inst, getpid()); switch (ret) { case 0: case ECONNABORTED: case EPERM: case EACCES: case EROFS: break; case ECANCELED: goto add_inst; default: bad_error("libscf_write_start_pid", ret); } ctid = proc_get_ctid(); if (ctid > 0) { uint64 = (uint64_t)ctid; ret = libscf_inst_set_count_prop(inst, SCF_PG_RESTARTER, SCF_PG_RESTARTER_TYPE, SCF_PG_RESTARTER_FLAGS, SCF_PROPERTY_CONTRACT, uint64); switch (ret) { case 0: case ECONNABORTED: case EPERM: case EACCES: case EROFS: break; case ECANCELED: goto add_inst; default: bad_error("libscf_inst_set_count_prop", ret); } } ret = libscf_note_method_log(inst, LOG_PREFIX_EARLY, STARTD_DEFAULT_LOG); if (ret == 0) { ret = libscf_note_method_log(inst, LOG_PREFIX_NORMAL, STARTD_DEFAULT_LOG); } switch (ret) { case 0: case ECONNABORTED: case EPERM: case EACCES: case EROFS: case EAGAIN: break; case ECANCELED: goto add_inst; default: bad_error("libscf_note_method_log", ret); } out: scf_instance_destroy(inst); scf_service_destroy(svc); scf_scope_destroy(scope); return (ret); } /* * Returns * 0 - success * ENOENT - SCF_SERVICE_STARTD does not exist in repository * EPERM * EACCES * EROFS */ int libscf_set_reconfig(int set) { scf_handle_t *h; scf_instance_t *inst; scf_propertygroup_t *pg; int ret = 0; h = libscf_handle_create_bound_loop(); inst = safe_scf_instance_create(h); pg = safe_scf_pg_create(h); again: if (scf_handle_decode_fmri(h, SCF_SERVICE_STARTD, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_EXACT) == -1) { switch (scf_error()) { case SCF_ERROR_CONNECTION_BROKEN: default: libscf_handle_rebind(h); goto again; case SCF_ERROR_NOT_FOUND: ret = ENOENT; goto reconfig_out; case SCF_ERROR_HANDLE_MISMATCH: case SCF_ERROR_INVALID_ARGUMENT: case SCF_ERROR_CONSTRAINT_VIOLATED: bad_error("scf_handle_decode_fmri", scf_error()); } } ret = libscf_inst_set_boolean_prop(inst, "system", SCF_GROUP_FRAMEWORK, SCF_PG_FLAG_NONPERSISTENT, "reconfigure", set); switch (ret) { case 0: case EPERM: case EACCES: case EROFS: break; case ECONNABORTED: libscf_handle_rebind(h); goto again; case ECANCELED: ret = ENOENT; break; default: bad_error("libscf_inst_set_boolean_prop", ret); } reconfig_out: scf_pg_destroy(pg); scf_instance_destroy(inst); scf_handle_destroy(h); return (ret); } /* * Set inst->ri_m_inst to the scf instance for inst. If it has been deleted, * set inst->ri_mi_deleted to true. If the repository connection is broken, it * is rebound with libscf_handle_rebound(). */ void libscf_reget_instance(restarter_inst_t *inst) { scf_handle_t *h; int r; h = scf_instance_handle(inst->ri_m_inst); again: r = libscf_lookup_instance(inst->ri_i.i_fmri, inst->ri_m_inst); switch (r) { case 0: case ENOENT: inst->ri_mi_deleted = (r == ENOENT); return; case ECONNABORTED: libscf_handle_rebind(h); goto again; case EINVAL: case ENOTSUP: default: bad_error("libscf_lookup_instance", r); } }