X-Git-Url: https://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=feecbbe2b6a93ad919e8bf32aec71f2069aefe25;hp=a40e01b88818b99978c11da68c8ba81a9fdfd422;hb=bad75c274a488715a845ff8e5ec3140036929e94;hpb=4dfc092a7176f74102b6be205f11008e1de59bb3 diff --git a/src/service.c b/src/service.c index a40e01b88..feecbbe2b 100644 --- a/src/service.c +++ b/src/service.c @@ -83,7 +83,7 @@ static const struct { #define RUNLEVELS_UP "12345" /* #define RUNLEVELS_DOWN "06" */ -/* #define RUNLEVELS_BOOT "bBsS" */ +#define RUNLEVELS_BOOT "bBsS" #endif static const UnitActiveState state_translation_table[_SERVICE_STATE_MAX] = { @@ -277,7 +277,8 @@ static int sysv_translate_facility(const char *name, const char *filename, char static const char * const table[] = { /* LSB defined facilities */ "local_fs", SPECIAL_LOCAL_FS_TARGET, -#ifndef TARGET_MANDRIVA +#if defined(TARGET_MANDRIVA) || defined(TARGET_MAGEIA) +#else /* Due to unfortunate name selection in Mandriva, * $network is provided by network-up which is ordered * after network which actually starts interfaces. @@ -811,6 +812,13 @@ static int service_load_sysv_path(Service *s, const char *path) { if ((r = sysv_exec_commands(s)) < 0) goto finish; + if (s->sysv_runlevels && + chars_intersect(RUNLEVELS_BOOT, s->sysv_runlevels) && + chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { + /* Service has both boot and "up" runlevels + configured. Kill the "up" ones. */ + delete_chars(s->sysv_runlevels, RUNLEVELS_UP); + } if (s->sysv_runlevels && !chars_intersect(RUNLEVELS_UP, s->sysv_runlevels)) { /* If there a runlevels configured for this service @@ -829,10 +837,11 @@ static int service_load_sysv_path(Service *s, const char *path) { /* Special setting for all SysV services */ s->type = SERVICE_FORKING; s->remain_after_exit = !s->pid_file; + s->guess_main_pid = false; s->restart = SERVICE_RESTART_NO; if (s->meta.manager->sysv_console) - s->exec_context.std_output = EXEC_OUTPUT_TTY; + s->exec_context.std_output = EXEC_OUTPUT_SYSLOG_AND_CONSOLE; s->exec_context.kill_mode = KILL_PROCESS; @@ -1145,6 +1154,8 @@ static int service_load(Unit *u) { /* This is a new unit? Then let's add in some extras */ if (u->meta.load_state == UNIT_LOADED) { + service_fix_output(s); + if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0) return r; @@ -1173,8 +1184,6 @@ static int service_load(Unit *u) { if (s->meta.default_dependencies) if ((r = service_add_default_dependencies(s)) < 0) return r; - - service_fix_output(s); } return service_verify(s); @@ -1281,7 +1290,7 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { free(p2); } -static int service_load_pid_file(Service *s) { +static int service_load_pid_file(Service *s, bool may_warn) { char *k; int r; pid_t pid; @@ -1291,8 +1300,12 @@ static int service_load_pid_file(Service *s) { if (!s->pid_file) return -ENOENT; - if ((r = read_one_line_file(s->pid_file, &k)) < 0) + if ((r = read_one_line_file(s->pid_file, &k)) < 0) { + if (may_warn) + log_info("PID file %s not readable (yet?) after %s.", + s->pid_file, service_state_to_string(s->state)); return r; + } r = parse_pid(k, &pid); free(k); @@ -1301,8 +1314,9 @@ static int service_load_pid_file(Service *s) { return r; if (kill(pid, 0) < 0 && errno != EPERM) { - log_warning("PID %lu read from file %s does not exist. Your service or init script might be broken.", - (unsigned long) pid, s->pid_file); + if (may_warn) + log_info("PID %lu read from file %s does not exist.", + (unsigned long) pid, s->pid_file); return -ESRCH; } @@ -1314,7 +1328,8 @@ static int service_load_pid_file(Service *s) { (unsigned long) s->main_pid, (unsigned long) pid); service_unwatch_main_pid(s); s->main_pid_known = false; - } + } else + log_debug("Main PID loaded: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1345,6 +1360,7 @@ static int service_search_main_pid(Service *s) { if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0) return -ENOENT; + log_debug("Main PID guessed: %lu", (unsigned long) pid); if ((r = service_set_main_pid(s, pid)) < 0) return r; @@ -1437,6 +1453,17 @@ static int service_notify_sockets_dead(Service *s) { return 0; } +static void service_unwatch_pid_file(Service *s) { + if (!s->pid_file_pathspec) + return; + + log_debug("Stopping watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + pathspec_unwatch(s->pid_file_pathspec, UNIT(s)); + pathspec_done(s->pid_file_pathspec); + free(s->pid_file_pathspec); + s->pid_file_pathspec = NULL; +} + static void service_set_state(Service *s, ServiceState state) { ServiceState old_state; assert(s); @@ -1444,6 +1471,8 @@ static void service_set_state(Service *s, ServiceState state) { old_state = s->state; s->state = state; + service_unwatch_pid_file(s); + if (state != SERVICE_START_PRE && state != SERVICE_START && state != SERVICE_START_POST && @@ -2588,6 +2617,95 @@ static bool service_check_snapshot(Unit *u) { return !s->got_socket_fd; } +static int service_retry_pid_file(Service *s) { + int r; + + assert(s->pid_file); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + + r = service_load_pid_file(s, false); + if (r < 0) + return r; + + service_unwatch_pid_file(s); + + service_enter_running(s, true); + return 0; +} + +static int service_watch_pid_file(Service *s) { + int r; + + log_debug("Setting watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path); + r = pathspec_watch(s->pid_file_pathspec, UNIT(s)); + if (r < 0) + goto fail; + + /* the pidfile might have appeared just before we set the watch */ + service_retry_pid_file(s); + + return 0; +fail: + log_error("Failed to set a watch for %s's PID file %s: %s", + s->meta.id, s->pid_file_pathspec->path, strerror(-r)); + service_unwatch_pid_file(s); + return r; +} + +static int service_demand_pid_file(Service *s) { + PathSpec *ps; + + assert(s->pid_file); + assert(!s->pid_file_pathspec); + + ps = new0(PathSpec, 1); + if (!ps) + return -ENOMEM; + + ps->path = strdup(s->pid_file); + if (!ps->path) { + free(ps); + return -ENOMEM; + } + + path_kill_slashes(ps->path); + + /* PATH_CHANGED would not be enough. There are daemons (sendmail) that + * keep their PID file open all the time. */ + ps->type = PATH_MODIFIED; + ps->inotify_fd = -1; + + s->pid_file_pathspec = ps; + + return service_watch_pid_file(s); +} + +static void service_fd_event(Unit *u, int fd, uint32_t events, Watch *w) { + Service *s = SERVICE(u); + + assert(s); + assert(fd >= 0); + assert(s->state == SERVICE_START || s->state == SERVICE_START_POST); + assert(s->pid_file_pathspec); + assert(pathspec_owns_inotify_fd(s->pid_file_pathspec, fd)); + + log_debug("inotify event for %s", u->meta.id); + + if (pathspec_fd_event(s->pid_file_pathspec, events) < 0) + goto fail; + + if (service_retry_pid_file(s) == 0) + return; + + if (service_watch_pid_file(s) < 0) + goto fail; + + return; +fail: + service_unwatch_pid_file(s); + service_enter_signal(s, SERVICE_STOP_SIGTERM, false); +} + static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { Service *s = SERVICE(u); bool success; @@ -2604,7 +2722,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { /* Forking services may occasionally move to a new PID. * As long as they update the PID file before exiting the old * PID, they're fine. */ - if (service_load_pid_file(s) == 0) + if (service_load_pid_file(s, false) == 0) return; s->main_pid = 0; @@ -2693,7 +2811,7 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { success = true; } - log_full(success ? LOG_DEBUG : LOG_NOTICE, + log_full(success ? LOG_DEBUG : LOG_NOTICE, "%s: control process exited, code=%s status=%i", u->meta.id, sigchld_code_to_string(code), status); s->failure = s->failure || !success; @@ -2728,36 +2846,52 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { case SERVICE_START: assert(s->type == SERVICE_FORKING); - /* Let's try to load the pid - * file here if we can. We - * ignore the return value, - * since the PID file might - * actually be created by a - * START_POST script */ - - if (success) { - service_load_pid_file(s); - service_search_main_pid(s); + if (!success) { + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } - service_enter_start_post(s); + if (s->pid_file) { + /* Let's try to load the pid file here if we can. + * The PID file might actually be created by a START_POST + * script. In that case don't worry if the loading fails. */ + bool has_start_post = !!s->exec_command[SERVICE_EXEC_START_POST]; + int r = service_load_pid_file(s, !has_start_post); + if (!has_start_post && r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + break; + } } else - service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + service_search_main_pid(s); + service_enter_start_post(s); break; case SERVICE_START_POST: - if (success) { - service_load_pid_file(s); - service_search_main_pid(s); + if (!success) { + service_enter_stop(s, false); + break; } - s->reload_failure = !success; + if (s->pid_file) { + int r = service_load_pid_file(s, true); + if (r < 0) { + r = service_demand_pid_file(s); + if (r < 0 || !cgroup_good(s)) + service_enter_stop(s, false); + break; + } + } else + service_search_main_pid(s); + service_enter_running(s, true); break; case SERVICE_RELOAD: if (success) { - service_load_pid_file(s); + service_load_pid_file(s, true); service_search_main_pid(s); } @@ -2893,6 +3027,20 @@ static void service_cgroup_notify_event(Unit *u) { * except when we don't know pid which to expect the * SIGCHLD for. */ + case SERVICE_START: + case SERVICE_START_POST: + /* If we were hoping for the daemon to write its PID file, + * we can give up now. */ + if (s->pid_file_pathspec) { + log_warning("%s never wrote its PID file. Failing.", s->meta.id); + service_unwatch_pid_file(s); + if (s->state == SERVICE_START) + service_enter_signal(s, SERVICE_FINAL_SIGTERM, false); + else + service_enter_stop(s, false); + } + break; + case SERVICE_RUNNING: service_enter_running(s, true); break; @@ -3111,7 +3259,7 @@ static int service_enumerate(Manager *m) { free(fpath); fpath = join(path, "/", de->d_name, NULL); - if (!path) { + if (!fpath) { r = -ENOMEM; goto finish; } @@ -3202,7 +3350,7 @@ static int service_enumerate(Manager *m) { r = 0; #ifdef TARGET_SUSE - sysv_facility_in_insserv_conf (m); + sysv_facility_in_insserv_conf (m); #endif finish: @@ -3497,6 +3645,7 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .fd_event = service_fd_event, .reset_failed = service_reset_failed,