/* * void wait_remove(wait_info_t *, int) * Remove the given wait_info structure from our list, performing various * cleanup operations along the way. If the direct flag is false (meaning * that we are being called with from restarter instance list context) and * the instance should not be ignored, then notify the restarter that the * associated instance has exited. If the wi_ignore flag is true then it * means that the stop was initiated from within svc.startd, rather than * from outside it. * * Since we may no longer be the startd that started this process, we only are * concerned with a waitpid(3C) failure if the wi_parent field is non-zero. */ static void wait_remove(wait_info_t *wi, int direct) { int status; stop_cause_t cause = RSTOP_EXIT; if (waitpid(wi->wi_pid, &status, 0) == -1) { if (wi->wi_parent) log_framework(LOG_INFO, "instance %s waitpid failure: %s\n", wi->wi_fmri, strerror(errno)); } else { if (WEXITSTATUS(status) != 0) { log_framework(LOG_NOTICE, "instance %s exited with status %d\n", wi->wi_fmri, WEXITSTATUS(status)); if (WEXITSTATUS(status) == SMF_EXIT_ERR_CONFIG) cause = RSTOP_ERR_CFG; else cause = RSTOP_ERR_EXIT; } } MUTEX_LOCK(&wait_info_lock); if (wi->wi_fd != -1) { startd_close(wi->wi_fd); wi->wi_fd = -1; } uu_list_remove(wait_info_list, wi); MUTEX_UNLOCK(&wait_info_lock); /* * Make an attempt to clear out any utmpx record associated with this * PID. */ utmpx_mark_dead(wi->wi_pid, status, B_FALSE); if (!direct && !wi->wi_ignore) { /* * Bind wait_hndl lazily. */ if (wait_hndl == NULL) { for (wait_hndl = libscf_handle_create_bound(SCF_VERSION); wait_hndl == NULL; wait_hndl = libscf_handle_create_bound(SCF_VERSION)) { log_error(LOG_INFO, "[wait_remove] Unable to " "bind a new repository handle: %s\n", scf_strerror(scf_error())); (void) sleep(2); } } log_framework(LOG_DEBUG, "wait_remove requesting stop of %s\n", wi->wi_fmri); (void) stop_instance_fmri(wait_hndl, wi->wi_fmri, cause); } uu_list_node_fini(wi, &wi->wi_link, wait_info_pool); startd_free(wi, sizeof (wait_info_t)); }
/* * The method thread executes a service method to effect a state transition. * The next_state of info->sf_id should be non-_NONE on entrance, and it will * be _NONE on exit (state will either be what next_state was (on success), or * it will be _MAINT (on error)). * * There are six classes of methods to consider: start & other (stop, refresh) * for each of "normal" services, wait services, and transient services. For * each, the method must be fetched from the repository & executed. fork()ed * methods must be waited on, except for the start method of wait services * (which must be registered with the wait subsystem via wait_register()). If * the method succeeded (returned 0), then for start methods its contract * should be recorded as the primary contract for the service. For other * methods, it should be abandoned. If the method fails, then depending on * the failure, either the method should be reexecuted or the service should * be put into maintenance. Either way the contract should be abandoned. */ void * method_thread(void *arg) { fork_info_t *info = arg; restarter_inst_t *inst; scf_handle_t *local_handle; scf_instance_t *s_inst = NULL; int r, exit_code; boolean_t retryable; const char *aux; assert(0 <= info->sf_method_type && info->sf_method_type <= 2); /* Get (and lock) the restarter_inst_t. */ inst = inst_lookup_by_id(info->sf_id); assert(inst->ri_method_thread != 0); assert(instance_in_transition(inst) == 1); /* * We cannot leave this function with inst in transition, because * protocol.c withholds messages for inst otherwise. */ log_framework(LOG_DEBUG, "method_thread() running %s method for %s.\n", method_names[info->sf_method_type], inst->ri_i.i_fmri); local_handle = libscf_handle_create_bound_loop(); rebind_retry: /* get scf_instance_t */ switch (r = libscf_fmri_get_instance(local_handle, inst->ri_i.i_fmri, &s_inst)) { case 0: break; case ECONNABORTED: libscf_handle_rebind(local_handle); goto rebind_retry; case ENOENT: /* * It's not there, but we need to call this so protocol.c * doesn't think it's in transition anymore. */ (void) restarter_instance_update_states(local_handle, inst, inst->ri_i.i_state, RESTARTER_STATE_NONE, RERR_NONE, NULL); goto out; case EINVAL: case ENOTSUP: default: bad_error("libscf_fmri_get_instance", r); } inst->ri_m_inst = s_inst; inst->ri_mi_deleted = B_FALSE; retry: if (info->sf_method_type == METHOD_START) log_transition(inst, START_REQUESTED); r = method_run(&inst, info->sf_method_type, &exit_code); if (r == 0 && exit_code == 0) { /* Success! */ assert(inst->ri_i.i_next_state != RESTARTER_STATE_NONE); /* * When a stop method succeeds, remove the primary contract of * the service, unless we're going to offline, in which case * retain the contract so we can transfer inherited contracts to * the replacement service. */ if (info->sf_method_type == METHOD_STOP && inst->ri_i.i_primary_ctid != 0) { if (inst->ri_i.i_next_state == RESTARTER_STATE_OFFLINE) inst->ri_i.i_primary_ctid_stopped = 1; else method_remove_contract(inst, B_TRUE, B_TRUE); } /* * We don't care whether the handle was rebound because this is * the last thing we do with it. */ (void) restarter_instance_update_states(local_handle, inst, inst->ri_i.i_next_state, RESTARTER_STATE_NONE, info->sf_event_type, NULL); (void) update_fault_count(inst, FAULT_COUNT_RESET); goto out; } /* Failure. Retry or go to maintenance. */ if (r != 0 && r != EAGAIN) { retryable = B_FALSE; } else { switch (exit_code) { case SMF_EXIT_ERR_CONFIG: case SMF_EXIT_ERR_NOSMF: case SMF_EXIT_ERR_PERM: case SMF_EXIT_ERR_FATAL: retryable = B_FALSE; break; default: retryable = B_TRUE; } } if (retryable && update_fault_count(inst, FAULT_COUNT_INCR) != 1) goto retry; /* maintenance */ if (r == ELOOP) log_transition(inst, START_FAILED_REPEATEDLY); else if (r == ERANGE) log_transition(inst, START_FAILED_TIMEOUT_FATAL); else if (exit_code == SMF_EXIT_ERR_CONFIG) log_transition(inst, START_FAILED_CONFIGURATION); else if (exit_code == SMF_EXIT_ERR_FATAL) log_transition(inst, START_FAILED_FATAL); else log_transition(inst, START_FAILED_OTHER); if (r == ELOOP) aux = "restarting_too_quickly"; else if (retryable) aux = "fault_threshold_reached"; else aux = "method_failed"; (void) restarter_instance_update_states(local_handle, inst, RESTARTER_STATE_MAINT, RESTARTER_STATE_NONE, RERR_FAULT, (char *)aux); if (!method_is_transient(inst, info->sf_method_type) && inst->ri_i.i_primary_ctid != 0) method_remove_contract(inst, B_TRUE, B_TRUE); out: inst->ri_method_thread = 0; MUTEX_UNLOCK(&inst->ri_lock); (void) pthread_cond_broadcast(&inst->ri_method_cv); scf_instance_destroy(s_inst); scf_handle_destroy(local_handle); startd_free(info, sizeof (fork_info_t)); return (NULL); }
/* * init_env() * A clone of the work init.c does to provide as much compatibility * for startup scripts as possible. */ void init_env() { int i; char line[MAXCMDL]; FILE *fp; int inquotes, length, wslength; char *tokp, *cp1, *cp2; char **newp; glob_env_n = 16; glob_envp = startd_alloc(sizeof (*glob_envp) * glob_env_n); glob_envp[0] = startd_alloc((unsigned)(strlen(DEF_PATH)+2)); (void) strcpy(glob_envp[0], DEF_PATH); if ((fp = fopen(ENVFILE, "r")) == NULL) { uu_warn("Cannot open %s. Environment not initialized.\n", ENVFILE); glob_envp[1] = NULL; return; } i = 1; while (fgets(line, MAXCMDL - 1, fp) != NULL) { /* * Toss newline */ length = strlen(line); if (line[length - 1] == '\n') line[length - 1] = '\0'; /* * Ignore blank or comment lines. */ if (line[0] == '#' || line[0] == '\0' || (wslength = strspn(line, " \t\n")) == strlen(line) || strchr(line, '#') == line + wslength) continue; /* * First make a pass through the line and change * any non-quoted semi-colons to blanks so they * will be treated as token separators below. */ inquotes = 0; for (cp1 = line; *cp1 != '\0'; cp1++) { if (*cp1 == '"') { if (inquotes == 0) inquotes = 1; else inquotes = 0; } else if (*cp1 == ';') { if (inquotes == 0) *cp1 = ' '; } } /* * Tokens within the line are separated by blanks * and tabs. For each token in the line which * contains a '=' we strip out any quotes and then * stick the token in the environment array. */ if ((tokp = strtok(line, " \t")) == NULL) continue; do { cp1 = strchr(tokp, '='); if (cp1 == NULL || cp1 == tokp) continue; length = strlen(tokp); while ((cp1 = strpbrk(tokp, "\"\'")) != NULL) { for (cp2 = cp1; cp2 < &tokp[length]; cp2++) *cp2 = *(cp2 + 1); length--; } /* * init already started us with this umask, and we * handled it in startd.c, so just skip it. */ if (strncmp(tokp, "CMASK=", 6) == 0 || strncmp(tokp, "SMF_", 4) == 0) continue; glob_envp[i] = startd_alloc((unsigned)(length + 1)); (void) strcpy(glob_envp[i], tokp); /* * Double the environment size whenever it is * full. */ if (++i == glob_env_n) { glob_env_n *= 2; newp = startd_alloc(sizeof (*glob_envp) * glob_env_n); (void) memcpy(newp, glob_envp, sizeof (*glob_envp) * glob_env_n / 2); startd_free(glob_envp, sizeof (*glob_envp) * glob_env_n / 2); glob_envp = newp; } } while ((tokp = strtok(NULL, " \t")) != NULL); } startd_fclose(fp); /* Append a null pointer to the environment array to mark its end. */ glob_envp[i] = NULL; /* * Get the zonename once; it is used to set SMF_ZONENAME for methods. */ (void) getzonenamebyid(getzoneid(), zonename, sizeof (zonename)); }