static void cleanupScf(scf_handle_t *h, scf_snapshot_t *snap, scf_instance_t *inst, scf_propertygroup_t *pg, scf_property_t *prop, scf_value_t *val, char *buf) { if (buf != NULL) { free(buf); } if (snap != NULL) { scf_snapshot_destroy(snap); } if (val != NULL) { scf_value_destroy(val); } if (prop != NULL) { scf_property_destroy(prop); } if (pg != NULL) { scf_pg_destroy(pg); } if (inst != NULL) { scf_instance_destroy(inst); } if (h != NULL) { scf_handle_destroy(h); } }
/* * Return a snapshot for the supplied instance and snapshot name. */ static scf_snapshot_t * get_snapshot(const scf_instance_t *inst, const char *snapshot) { scf_snapshot_t *snap = scf_snapshot_create(hndl); if (snap == NULL) scfdie(); if (scf_instance_get_snapshot(inst, snapshot, snap) == -1) { switch (scf_error()) { case SCF_ERROR_INVALID_ARGUMENT: die(gettext("Invalid snapshot name.\n")); /* NOTREACHED */ case SCF_ERROR_NOT_FOUND: if (sflag == 0) { scf_snapshot_destroy(snap); snap = NULL; } else die(gettext("No such snapshot.\n")); break; default: scfdie(); } } return (snap); }
/* * Inputs: * res is a pointer to the scf_resources_t to be released. */ static void release_scf_resources(scf_resources_t *res) { scf_value_destroy(res->sr_val); scf_property_destroy(res->sr_prop); scf_pg_destroy(res->sr_pg); scf_snapshot_destroy(res->sr_snap); scf_instance_destroy(res->sr_inst); (void) scf_handle_unbind(res->sr_handle); scf_handle_destroy(res->sr_handle); }
static void kbd_defaults(int kbd) { scf_handle_t *h = NULL; scf_snapshot_t *snap = NULL; scf_instance_t *inst = NULL; scf_propertygroup_t *pg = NULL; scf_property_t *prop = NULL; scf_value_t *val = NULL; int layout_num; char *val_layout = NULL, *val_abort = NULL; uint8_t val_click; int64_t val_delay, val_rate; int64_t val_kbd_beeper, val_console_beeper; if ((h = scf_handle_create(SCF_VERSION)) == NULL || scf_handle_bind(h) != 0 || (inst = scf_instance_create(h)) == NULL || (snap = scf_snapshot_create(h)) == NULL || (pg = scf_pg_create(h)) == NULL || (prop = scf_property_create(h)) == NULL || (val = scf_value_create(h)) == NULL) { goto out; } if (scf_handle_decode_fmri(h, KBD_FMRI, NULL, NULL, inst, NULL, NULL, SCF_DECODE_FMRI_REQUIRE_INSTANCE) != 0) { goto out; } if (scf_instance_get_snapshot(inst, "running", snap) != 0) { scf_snapshot_destroy(snap); snap = NULL; } if (scf_instance_get_pg_composed(inst, snap, KBD_PG, pg) != 0) { goto out; } if ((val_abort = malloc(KBD_MAX_NAME_LEN)) == NULL) { (void) fprintf(stderr, "Can not alloc memory for keyboard properties\n"); goto out; } if ((val_layout = malloc(KBD_MAX_NAME_LEN)) == NULL) { (void) fprintf(stderr, "Can not alloc memory for keyboard properties\n"); goto out; } if (scf_pg_get_property(pg, KBD_PROP_KEYCLICK, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_boolean(val, &val_click) == -1) { (void) fprintf(stderr, "Can not get KEYCLICK\n"); } if (val_click == 1) (void) click("on", kbd); else if (val_click == 0) (void) click("off", kbd); else (void) fprintf(stderr, BAD_DEFAULT_INT, KBD_PROP_KEYCLICK, val_click); if (scf_pg_get_property(pg, KBD_PROP_KEYBOARD_ABORT, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_astring(val, val_abort, KBD_MAX_NAME_LEN) == -1) { (void) fprintf(stderr, "Can not get KEYBOARD_ABORT\n"); } if (*val_abort != '\0') { /* * ABORT must equal "enable", "disable" or "alternate" */ if ((strcmp(val_abort, "enable") == 0) || (strcmp(val_abort, "alternate") == 0) || (strcmp(val_abort, "disable") == 0)) (void) abort_enable(val_abort, kbd); else (void) fprintf(stderr, BAD_DEFAULT_STR, KBD_PROP_KEYBOARD_ABORT, val_abort); } if (scf_pg_get_property(pg, KBD_PROP_RPTDELAY, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_delay) == -1) { (void) fprintf(stderr, "Can not get RPTDELAY\n"); } if (val_delay > 0) (void) set_rptdelay(val_delay, kbd); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_RPTDELAY, val_delay); if (scf_pg_get_property(pg, KBD_PROP_RPTRATE, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_rate) == -1) { (void) fprintf(stderr, "Can not get RPTRATE\n"); } if (val_rate > 0) (void) set_rptrate(val_rate, kbd); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_RPTRATE, val_rate); if (scf_pg_get_property(pg, KBD_PROP_LAYOUT, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_astring(val, val_layout, KBD_MAX_NAME_LEN) == -1) { (void) fprintf(stderr, "Can not get LAYOUT\n"); } if (*val_layout != '\0') { /* * LAYOUT must be one of the layouts supported in kbd_layouts */ if (get_layouts() != 0) goto out; if ((layout_num = get_layout_number(val_layout)) == -1) { (void) fprintf(stderr, BAD_DEFAULT_STR, KBD_PROP_LAYOUT, val_layout); goto out; } (void) set_layout(kbd, layout_num); } if (scf_pg_get_property(pg, KBD_PROP_FREQ, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_kbd_beeper) == -1) { (void) fprintf(stderr, "Can not get FREQ\n"); } if (val_kbd_beeper >= 0 && val_kbd_beeper <= INT16_MAX) (void) set_beep_freq(kbd, "keyboard", val_kbd_beeper); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_FREQ, val_kbd_beeper); if (scf_pg_get_property(pg, KBD_PROP_CONSFREQ, prop) != 0 || scf_property_get_value(prop, val) != 0 || scf_value_get_integer(val, &val_console_beeper) == -1) { (void) fprintf(stderr, "Can not get CONSFREQ\n"); } if (val_console_beeper >= 0 && val_console_beeper <= INT16_MAX) (void) set_beep_freq(kbd, "console", val_console_beeper); else (void) fprintf(stderr, BAD_DEFAULT_LLINT, KBD_PROP_CONSFREQ, val_console_beeper); out: if (val_layout != NULL) free(val_layout); if (val_abort != NULL) free(val_abort); if (snap != NULL) scf_snapshot_destroy(snap); scf_value_destroy(val); scf_property_destroy(prop); scf_pg_destroy(pg); scf_instance_destroy(inst); scf_handle_destroy(h); }
/* * int method_run() * Execute the type method of instp. If it requires a fork(), wait for it * to return and return its exit code in *exit_code. Otherwise set * *exit_code to 0 if the method succeeds & -1 if it fails. If the * repository connection is broken, it is rebound, but inst may not be * reset. * Returns * 0 - success * EINVAL - A correct method or method context couldn't be retrieved. * EIO - Contract kill failed. * EFAULT - Method couldn't be executed successfully. * ELOOP - Retry threshold exceeded. * ECANCELED - inst was deleted from the repository before method was run * ERANGE - Timeout retry threshold exceeded. * EAGAIN - Failed due to external cause, retry. */ int method_run(restarter_inst_t **instp, int type, int *exit_code) { char *method; int ret_status; pid_t pid; method_restart_t restart_on; uint_t cte_mask; uint8_t need_session; scf_handle_t *h; scf_snapshot_t *snap; const char *mname; const char *errstr; struct method_context *mcp; int result = 0, timeout_fired = 0; int sig, r; boolean_t transient; uint64_t timeout; uint8_t timeout_retry; ctid_t ctid; int ctfd = -1; ct_evthdl_t ctev; uint_t evtype; restarter_inst_t *inst = *instp; int id = inst->ri_id; int forkerr; assert(PTHREAD_MUTEX_HELD(&inst->ri_lock)); assert(instance_in_transition(inst)); if (inst->ri_mi_deleted) return (ECANCELED); *exit_code = 0; assert(0 <= type && type <= 2); mname = method_names[type]; if (type == METHOD_START) inst->ri_pre_online_hook(); h = scf_instance_handle(inst->ri_m_inst); snap = scf_snapshot_create(h); if (snap == NULL || scf_instance_get_snapshot(inst->ri_m_inst, "running", snap) != 0) { log_framework(LOG_DEBUG, "Could not get running snapshot for %s. " "Using editing version to run method %s.\n", inst->ri_i.i_fmri, mname); scf_snapshot_destroy(snap); snap = NULL; } /* * After this point, we may be logging to the instance log. * Make sure we've noted where that log is as a property of * the instance. */ r = libscf_note_method_log(inst->ri_m_inst, st->st_log_prefix, inst->ri_logstem); if (r != 0) { log_framework(LOG_WARNING, "%s: couldn't note log location: %s\n", inst->ri_i.i_fmri, strerror(r)); } if ((method = libscf_get_method(h, type, inst, snap, &restart_on, &cte_mask, &need_session, &timeout, &timeout_retry)) == NULL) { if (errno == LIBSCF_PGROUP_ABSENT) { log_framework(LOG_DEBUG, "%s: instance has no method property group '%s'.\n", inst->ri_i.i_fmri, mname); if (type == METHOD_REFRESH) log_instance(inst, B_TRUE, "No '%s' method " "defined. Treating as :true.", mname); else log_instance(inst, B_TRUE, "Method property " "group '%s' is not present.", mname); scf_snapshot_destroy(snap); return (0); } else if (errno == LIBSCF_PROPERTY_ABSENT) { log_framework(LOG_DEBUG, "%s: instance has no '%s/exec' method property.\n", inst->ri_i.i_fmri, mname); log_instance(inst, B_TRUE, "Method property '%s/exec " "is not present.", mname); scf_snapshot_destroy(snap); return (0); } else { log_error(LOG_WARNING, "%s: instance libscf_get_method failed\n", inst->ri_i.i_fmri); scf_snapshot_destroy(snap); return (EINVAL); } } /* open service contract if stopping a non-transient service */ if (type == METHOD_STOP && (!instance_is_transient_style(inst))) { if (inst->ri_i.i_primary_ctid == 0) { /* service is not running, nothing to stop */ log_framework(LOG_DEBUG, "%s: instance has no primary " "contract, no service to stop.\n", inst->ri_i.i_fmri); scf_snapshot_destroy(snap); return (0); } if ((ctfd = contract_open(inst->ri_i.i_primary_ctid, "process", "events", O_RDONLY)) < 0) { result = EFAULT; log_instance(inst, B_TRUE, "Could not open service " "contract %ld. Stop method not run.\n", inst->ri_i.i_primary_ctid); goto out; } } if (restarter_is_null_method(method)) { log_framework(LOG_DEBUG, "%s: null method succeeds\n", inst->ri_i.i_fmri); log_instance(inst, B_TRUE, "Executing %s method (null)", mname); if (type == METHOD_START) write_status(inst, mname, 0); goto out; } sig = restarter_is_kill_method(method); if (sig >= 0) { if (inst->ri_i.i_primary_ctid == 0) { log_error(LOG_ERR, "%s: :kill with no contract\n", inst->ri_i.i_fmri); result = EINVAL; goto out; } log_framework(LOG_DEBUG, "%s: :killing contract with signal %d\n", inst->ri_i.i_fmri, sig); log_instance(inst, B_TRUE, "Executing %s method (:kill)", mname); if (contract_kill(inst->ri_i.i_primary_ctid, sig, inst->ri_i.i_fmri) != 0) { result = EIO; goto out; } else goto assured_kill; } log_framework(LOG_DEBUG, "%s: forking to run method %s\n", inst->ri_i.i_fmri, method); errstr = restarter_get_method_context(RESTARTER_METHOD_CONTEXT_VERSION, inst->ri_m_inst, snap, mname, method, &mcp); if (errstr != NULL) { log_error(LOG_WARNING, "%s: %s\n", inst->ri_i.i_fmri, errstr); result = EINVAL; goto out; } r = method_ready_contract(inst, type, restart_on, cte_mask); if (r != 0) { assert(r == ECANCELED); assert(inst->ri_mi_deleted); restarter_free_method_context(mcp); result = ECANCELED; goto out; } /* * Validate safety of method contexts, to save children work. */ if (!restarter_rm_libs_loadable()) log_framework(LOG_DEBUG, "%s: method contexts limited " "to root-accessible libraries\n", inst->ri_i.i_fmri); /* * If the service is restarting too quickly, send it to * maintenance. */ if (type == METHOD_START) { method_record_start(inst); if (method_rate_critical(inst)) { log_instance(inst, B_TRUE, "Restarting too quickly, " "changing state to maintenance"); result = ELOOP; goto out; } } pid = startd_fork1(&forkerr); if (pid == 0) exec_method(inst, type, method, mcp, need_session); if (pid == -1) { if (forkerr == EAGAIN) result = EAGAIN; else result = EFAULT; log_error(LOG_WARNING, "%s: Couldn't fork to execute method %s: %s\n", inst->ri_i.i_fmri, method, strerror(forkerr)); goto out; } restarter_free_method_context(mcp); /* * Get the contract id, decide whether it is primary or transient, and * stash it in inst & the repository. */ method_store_contract(inst, type, &ctid); /* * Similarly for the start method PID. */ if (type == METHOD_START && !inst->ri_mi_deleted) (void) libscf_write_start_pid(inst->ri_m_inst, pid); if (instance_is_wait_style(inst) && type == METHOD_START) { /* Wait style instances don't get timeouts on start methods. */ if (wait_register(pid, inst->ri_i.i_fmri, 1, 0)) { log_error(LOG_WARNING, "%s: couldn't register %ld for wait\n", inst->ri_i.i_fmri, pid); result = EFAULT; goto contract_out; } write_status(inst, mname, 0); } else { int r, err; time_t start_time; time_t end_time; /* * Because on upgrade/live-upgrade we may have no chance * to override faulty timeout values on the way to * manifest import, all services on the path to manifest * import are treated the same as INFINITE timeout services. */ start_time = time(NULL); if (timeout != METHOD_TIMEOUT_INFINITE && !is_timeout_ovr(inst)) timeout_insert(inst, ctid, timeout); else timeout = METHOD_TIMEOUT_INFINITE; /* Unlock the instance while waiting for the method. */ MUTEX_UNLOCK(&inst->ri_lock); do r = waitpid(pid, &ret_status, NULL); while (r == -1 && errno == EINTR); if (r == -1) err = errno; /* Re-grab the lock. */ inst = inst_lookup_by_id(id); /* * inst can't be removed, as the removal thread waits * for completion of this one. */ assert(inst != NULL); *instp = inst; if (inst->ri_timeout != NULL && inst->ri_timeout->te_fired) timeout_fired = 1; timeout_remove(inst, ctid); log_framework(LOG_DEBUG, "%s method for %s exited with status %d.\n", mname, inst->ri_i.i_fmri, WEXITSTATUS(ret_status)); if (r == -1) { log_error(LOG_WARNING, "Couldn't waitpid() for %s method of %s (%s).\n", mname, inst->ri_i.i_fmri, strerror(err)); result = EFAULT; goto contract_out; } if (type == METHOD_START) write_status(inst, mname, ret_status); /* return ERANGE if this service doesn't retry on timeout */ if (timeout_fired == 1 && timeout_retry == 0) { result = ERANGE; goto contract_out; } if (!WIFEXITED(ret_status)) { /* * If method didn't exit itself (it was killed by an * external entity, etc.), consider the entire * method_run as failed. */ if (WIFSIGNALED(ret_status)) { char buf[SIG2STR_MAX]; (void) sig2str(WTERMSIG(ret_status), buf); log_error(LOG_WARNING, "%s: Method \"%s\" " "failed due to signal %s.\n", inst->ri_i.i_fmri, method, buf); log_instance(inst, B_TRUE, "Method \"%s\" " "failed due to signal %s", mname, buf); } else { log_error(LOG_WARNING, "%s: Method \"%s\" " "failed with exit status %d.\n", inst->ri_i.i_fmri, method, WEXITSTATUS(ret_status)); log_instance(inst, B_TRUE, "Method \"%s\" " "failed with exit status %d", mname, WEXITSTATUS(ret_status)); } result = EAGAIN; goto contract_out; } *exit_code = WEXITSTATUS(ret_status); if (*exit_code != 0) { log_error(LOG_WARNING, "%s: Method \"%s\" failed with exit status %d.\n", inst->ri_i.i_fmri, method, WEXITSTATUS(ret_status)); } log_instance(inst, B_TRUE, "Method \"%s\" exited with status " "%d", mname, *exit_code); if (*exit_code != 0) goto contract_out; end_time = time(NULL); /* Give service contract remaining seconds to empty */ if (timeout != METHOD_TIMEOUT_INFINITE) timeout -= (end_time - start_time); } assured_kill: /* * For stop methods, assure that the service contract has emptied * before returning. */ if (type == METHOD_STOP && (!instance_is_transient_style(inst)) && !(contract_is_empty(inst->ri_i.i_primary_ctid))) { if (timeout != METHOD_TIMEOUT_INFINITE) timeout_insert(inst, inst->ri_i.i_primary_ctid, timeout); for (;;) { do { r = ct_event_read_critical(ctfd, &ctev); } while (r == EINTR); if (r != 0) break; evtype = ct_event_get_type(ctev); ct_event_free(ctev); if (evtype == CT_PR_EV_EMPTY) break; } if (r) { result = EFAULT; log_instance(inst, B_TRUE, "Error reading service " "contract %ld.\n", inst->ri_i.i_primary_ctid); } if (timeout != METHOD_TIMEOUT_INFINITE) if (inst->ri_timeout->te_fired) result = EFAULT; timeout_remove(inst, inst->ri_i.i_primary_ctid); } contract_out: /* Abandon contracts for transient methods & methods that fail. */ transient = method_is_transient(inst, type); if ((transient || *exit_code != 0 || result != 0) && (restarter_is_kill_method(method) < 0)) method_remove_contract(inst, !transient, B_TRUE); out: if (ctfd >= 0) (void) close(ctfd); scf_snapshot_destroy(snap); free(method); return (result); }
/* * Entity (service or instance): If there are -p options, * display_{pg,prop}() the named property groups and/or properties. Otherwise * display_pg() all property groups. */ static void process_ent(scf_entityp_t ent) { scf_snapshot_t *snap = NULL; scf_propertygroup_t *pg; scf_property_t *prop; scf_iter_t *iter; svcprop_prop_node_t *spn; int ret, err; if (uu_list_numnodes(prop_list) == 0) { if (quiet) return; if ((pg = scf_pg_create(hndl)) == NULL || (iter = scf_iter_create(hndl)) == NULL) scfdie(); if (cflag || Cflag || ent.type != ENT_INSTANCE) { if (scf_iter_entity_pgs(iter, ent) == -1) scfdie(); } else { if (snapshot != NULL) snap = get_snapshot(ent.u.inst, snapshot); if (scf_iter_instance_pgs_composed(iter, ent.u.inst, snap) == -1) scfdie(); if (snap) scf_snapshot_destroy(snap); } while ((ret = scf_iter_next_pg(iter, pg)) == 1) display_pg(pg); if (ret == -1) scfdie(); /* * In normal usage, i.e. against the running snapshot, * we must iterate over the current non-persistent * pg's. */ if (sflag == 0 && snap != NULL) { scf_iter_reset(iter); if (scf_iter_instance_pgs_composed(iter, ent.u.inst, NULL) == -1) scfdie(); while ((ret = scf_iter_next_pg(iter, pg)) == 1) { uint32_t flags; if (scf_pg_get_flags(pg, &flags) == -1) scfdie(); if (flags & SCF_PG_FLAG_NONPERSISTENT) display_pg(pg); } } if (ret == -1) scfdie(); scf_iter_destroy(iter); scf_pg_destroy(pg); return; } if ((pg = scf_pg_create(hndl)) == NULL || (prop = scf_property_create(hndl)) == NULL) scfdie(); if (ent.type == ENT_INSTANCE && snapshot != NULL) snap = get_snapshot(ent.u.inst, snapshot); for (spn = uu_list_first(prop_list); spn != NULL; spn = uu_list_next(prop_list, spn)) { if (ent.type == ENT_INSTANCE) { if (Cflag) ret = scf_instance_get_pg(ent.u.inst, spn->spn_comp1, pg); else ret = scf_instance_get_pg_composed(ent.u.inst, snap, spn->spn_comp1, pg); err = scf_error(); /* * If we didn't find it in the specified snapshot, use * the current values if the pg is nonpersistent. */ if (ret == -1 && !Cflag &&snap != NULL && err == SCF_ERROR_NOT_FOUND) { ret = scf_instance_get_pg_composed( ent.u.inst, NULL, spn->spn_comp1, pg); if (ret == 0) { uint32_t flags; if (scf_pg_get_flags(pg, &flags) == -1) scfdie(); if ((flags & SCF_PG_FLAG_NONPERSISTENT) == 0) { ret = -1; } } } } else { /* * If we are displaying properties for a service, * treat it as though it were a composed, current * lookup. (implicit cflag) However, if a snapshot * was specified, fail. */ if (sflag) die(gettext("Only instances have " "snapshots.\n")); ret = scf_entity_get_pg(ent, spn->spn_comp1, pg); err = scf_error(); } if (ret == -1) { if (err != SCF_ERROR_NOT_FOUND) scfdie(); if (PRINT_NOPROP_ERRORS) { char *buf; buf = safe_malloc(max_scf_fmri_length + 1); if (scf_entity_to_fmri(ent, buf, max_scf_fmri_length + 1) == -1) scfdie(); uu_warn(gettext("Couldn't find property group " "`%s' for %s `%s'.\n"), spn->spn_comp1, SCF_ENTITY_TYPE_NAME(ent), buf); free(buf); } noprop_common_action(); continue; } if (spn->spn_comp2 == NULL) { if (!quiet) display_pg(pg); continue; } if (scf_pg_get_property(pg, spn->spn_comp2, prop) == -1) { if (scf_error() != SCF_ERROR_NOT_FOUND) scfdie(); if (PRINT_NOPROP_ERRORS) { char *buf; buf = safe_malloc(max_scf_fmri_length + 1); if (scf_entity_to_fmri(ent, buf, max_scf_fmri_length + 1) == -1) scfdie(); /* FMRI syntax knowledge */ uu_warn(gettext("Couldn't find property " "`%s/%s' for %s `%s'.\n"), spn->spn_comp1, spn->spn_comp2, SCF_ENTITY_TYPE_NAME(ent), buf); free(buf); } noprop_common_action(); continue; } if (!quiet) display_prop(pg, prop); } scf_property_destroy(prop); scf_pg_destroy(pg); if (snap) scf_snapshot_destroy(snap); }