/** * service_enabled - Should the service run? * @svc: Pointer to &svc_t object * @event: Dynamic event, opaque flag passed to callback * @arg: Event argument, used only by external service plugins. * * This method calls an associated service callback, if registered by a * plugin, and returns the &svc_cmd_t status. If no plugin is registered * the service is statically enabled in /etc/finit.conf and the result * will always be %SVC_START. * * Returns: * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD. */ svc_cmd_t service_enabled(svc_t *svc, int event, void *arg) { if (!svc) { errno = EINVAL; return SVC_STOP; } if (!svc_in_runlevel(svc, runlevel)) return SVC_STOP; /* * Event conditions for services are ignored during bootstrap. */ if (runlevel && !event_service_cond(svc->events)) return SVC_STOP; /* Is there a service plugin registered? */ if (svc->cb) { int status; pid_t pid; /* Let callback run in separate process so it doesn't crash PID 1 */ pid = fork(); if (-1 == pid) { _pe("Failed in %s callback", svc->cmd); return SVC_STOP; } if (!pid) { status = svc->cb(svc, event, arg); exit(status); } if (waitpid(pid, &status, 0) == -1) { _pe("Failed reading status from %s callback", svc->cmd); return SVC_STOP; } /* Callback normally exits here. */ if (WIFEXITED(status)) return WEXITSTATUS(status); /* Check for SEGFAULT or other error ... */ if (WIFSIGNALED(status) && WCOREDUMP(status)) _e("Callback to %s crashed!\n", svc->cmd); else _e("Callback to %s did not exit normally!\n", svc->cmd); return SVC_STOP; } /* No service plugin, default to start, since listed in finit.conf */ return SVC_START; }
/** * service_runlevel - Change to a new runlevel * @newlevel: New runlevel to activate * * Stops all services not in @newlevel and starts, or lets continue to run, * those in @newlevel. Also updates @prevlevel and active @runlevel. */ void service_runlevel(int newlevel) { svc_t *svc; if (runlevel == newlevel) return; if (newlevel < 0 || newlevel > 9) return; prevlevel = runlevel; runlevel = newlevel; _d("Setting new runlevel --> %d <-- previous %d", runlevel, prevlevel); runlevel_set(prevlevel, newlevel); /* Make sure to (re)load all *.conf in /etc/finit.d/ */ conf_reload_dynamic(); _d("Stopping services services not allowed in new runlevel ..."); for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { if (!svc_in_runlevel(svc, runlevel)) { #ifndef INETD_DISABLED if (svc_is_inetd(svc)) inetd_stop(&svc->inetd); else #endif service_stop(svc); } /* ... or disabled/removed services from /etc/finit.d/ */ if (svc_is_dynamic(svc) && svc_is_changed(svc)) service_stop(svc); } /* Prev runlevel services stopped, call hooks before starting new runlevel ... */ _d("All services have been stoppped, calling runlevel change hooks ..."); plugin_run_hooks(HOOK_RUNLEVEL_CHANGE); /* Reconfigure HW/VLANs/etc here */ _d("Starting services services new to this runlevel ..."); for (svc = svc_iterator(1); svc; svc = svc_iterator(0)) { #ifndef INETD_DISABLED /* Inetd services have slightly different semantics */ if (svc_is_inetd(svc)) { if (svc_in_runlevel(svc, runlevel)) inetd_start(&svc->inetd); continue; } #endif /* All other services consult their callback here */ svc_dance(svc); } /* Cleanup stale services */ svc_clean_dynamic(service_unregister); if (0 == runlevel) { do_shutdown(SIGUSR2); return; } if (6 == runlevel) { do_shutdown(SIGUSR1); return; } if (runlevel == 1) touch("/etc/nologin"); /* Disable login in single-user mode */ else erase("/etc/nologin"); if (0 != prevlevel) tty_runlevel(runlevel); }
/** * service_enabled - Should the service run? * @svc: Pointer to &svc_t object * @event: Dynamic event, opaque flag passed to callback * @arg: Event argument, used only by external service plugins. * * This method calls an associated service callback, if registered by a * plugin, and returns the &svc_cmd_t status. If no plugin is registered * the service is statically enabled in /etc/finit.conf and the result * will always be %SVC_START. * * Returns: * Either one of %SVC_START, %SVC_STOP, %SVC_RELOAD. */ svc_cmd_t service_enabled(svc_t *svc, int event, void *arg) { svc_cmd_t cmd = SVC_START; /* Default to start, since listed in finit.conf */ if (!svc) { errno = EINVAL; return SVC_STOP; } if (!svc_in_runlevel(svc, runlevel)) return SVC_STOP; /* * Event conditions for services are ignored during bootstrap. */ _d("Checking %s runlevel %d and events %s", svc->cmd, runlevel, svc->events); if (runlevel && !event_service_cond(svc->events)) return SVC_STOP; if (svc->state == SVC_RELOAD_STATE) cmd = SVC_RELOAD; if (svc->state == SVC_PAUSED_STATE) cmd = SVC_STOP; /* Is there a service plugin registered? */ if (svc->cb) { int status; pid_t pid; /* Let callback run in separate process so it doesn't crash PID 1 */ pid = fork(); if (-1 == pid) { _pe("Failed in %s callback", svc->cmd); return SVC_STOP; } if (!pid) _exit(svc->cb(svc, event, arg)); if (waitpid(pid, &status, 0) == -1) { _pe("Failed reading status from %s callback", svc->cmd); return SVC_STOP; } /* Callback normally exits here. */ if (WIFEXITED(status)) { svc_cmd_t tmp = WEXITSTATUS(status); return tmp == SVC_START ? cmd : tmp; } /* Check for SEGFAULT or other error ... */ if (WIFSIGNALED(status) && WCOREDUMP(status)) _e("Callback to %s crashed!\n", svc->cmd); else _e("Callback to %s did not exit normally!\n", svc->cmd); return SVC_STOP; } _d("%s => %s", svc->cmd, (cmd == SVC_START ? "SVC_START" : (cmd == SVC_RELOAD ? "SVC_RELOAD" : "SVC_STOP"))); return cmd; }