X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~ianmdlvl/git?p=elogind.git;a=blobdiff_plain;f=src%2Fservice.c;h=c5a796623f18708daaae8f63d248f20144d2b8a4;hp=e7a9e7c586c15c0f106f0cd4d62c583d983043cd;hb=73aa0c00df8b101bad4c3a038148a633df88610c;hpb=91b22f21f3824c1766d34f622c5bbb70cbe881a8 diff --git a/src/service.c b/src/service.c index e7a9e7c58..c5a796623 100644 --- a/src/service.c +++ b/src/service.c @@ -35,13 +35,12 @@ #include "special.h" #include "bus-errors.h" #include "exit-status.h" - -#define COMMENTS "#;\n" -#define NEWLINES "\n\r" +#include "def.h" +#include "util.h" #ifdef HAVE_SYSV_COMPAT -#define DEFAULT_SYSV_TIMEOUT_USEC (3*USEC_PER_MINUTE) +#define DEFAULT_SYSV_TIMEOUT_USEC (5*USEC_PER_MINUTE) typedef enum RunlevelType { RUNLEVEL_UP, @@ -66,7 +65,7 @@ static const struct { { "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, #endif -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) /* Debian style rcS.d */ { "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT }, #endif @@ -84,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] = { @@ -116,13 +115,12 @@ static void service_init(Unit *u) { s->timer_watch.type = WATCH_INVALID; #ifdef HAVE_SYSV_COMPAT s->sysv_start_priority = -1; + s->sysv_start_priority_from_rcnd = -1; #endif s->socket_fd = -1; s->guess_main_pid = true; exec_context_init(&s->exec_context); - s->exec_context.std_output = u->meta.manager->default_std_output; - s->exec_context.std_error = u->meta.manager->default_std_error; RATELIMIT_INIT(s->ratelimit, 10*USEC_PER_SEC, 5); @@ -160,12 +158,16 @@ static int service_set_main_pid(Service *s, pid_t pid) { if (pid == getpid()) return -EINVAL; - if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) + s->main_pid = pid; + s->main_pid_known = true; + + if (get_parent_of_pid(pid, &ppid) >= 0 && ppid != getpid()) { log_warning("%s: Supervising process %lu which is not our child. We'll most likely not notice when it exits.", s->meta.id, (unsigned long) pid); - s->main_pid = pid; - s->main_pid_known = true; + s->main_pid_alien = true; + } else + s->main_pid_alien = false; exec_status_start(&s->main_exec_status, pid); @@ -242,7 +244,7 @@ static char *sysv_translate_name(const char *name) { if (!(r = new(char, strlen(name) + sizeof(".service")))) return NULL; -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (endswith(name, ".sh")) /* Drop Debian-style .sh suffix */ strcpy(stpcpy(r, name) - 3, ".service"); @@ -275,25 +277,26 @@ 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 - /* Due to unfortunate name selection in Mandriva, - * $network is provided by network-up which is ordered - * after network which actually starts interfaces. - * To break the loop, just ignore it */ +#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. + * To break the loop, just ignore it */ "network", SPECIAL_NETWORK_TARGET, #endif "named", SPECIAL_NSS_LOOKUP_TARGET, "portmap", SPECIAL_RPCBIND_TARGET, "remote_fs", SPECIAL_REMOTE_FS_TARGET, "syslog", SPECIAL_SYSLOG_TARGET, - "time", SPECIAL_RTC_SET_TARGET, + "time", SPECIAL_TIME_SYNC_TARGET, /* common extensions */ "mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, "x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE, "null", NULL, -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET, #endif @@ -368,7 +371,7 @@ static int sysv_fix_order(Service *s) { /* For each pair of services where at least one lacks a LSB * header, we use the start priority value to order things. */ - LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) { + LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) { Service *t; UnitDependency d; bool special_s, special_t; @@ -406,7 +409,7 @@ static int sysv_fix_order(Service *s) { /* FIXME: Maybe we should compare the name here lexicographically? */ - if (!(r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) + if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) return r; } @@ -466,6 +469,7 @@ static int service_load_sysv_path(Service *s, const char *path) { LSB_DESCRIPTION } state = NORMAL; char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description; + struct stat st; assert(s); assert(path); @@ -477,12 +481,26 @@ static int service_load_sysv_path(Service *s, const char *path) { goto finish; } + zero(st); + if (fstat(fileno(f), &st) < 0) { + r = -errno; + goto finish; + } + free(s->sysv_path); if (!(s->sysv_path = strdup(path))) { r = -ENOMEM; goto finish; } + s->sysv_mtime = timespec_load(&st.st_mtim); + + if (null_or_empty(&st)) { + u->meta.load_state = UNIT_MASKED; + r = 0; + goto finish; + } + while (!feof(f)) { char l[LINE_MAX], *t; @@ -538,7 +556,7 @@ static int service_load_sysv_path(Service *s, const char *path) { * data from the LSB header. */ if (start_priority < 0 || start_priority > 99) log_warning("[%s:%u] Start priority out of range. Ignoring.", path, line); - else if (s->sysv_start_priority < 0) + else s->sysv_start_priority = start_priority; char_array_0(runlevels); @@ -616,7 +634,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (chkconfig_description) - asprintf(&d, "%s %s", chkconfig_description, j); + d = join(chkconfig_description, " ", j, NULL); else d = strdup(j); @@ -656,16 +674,21 @@ static int service_load_sysv_path(Service *s, const char *path) { if (unit_name_to_type(m) == UNIT_SERVICE) r = unit_add_name(u, m); - else { - r = unit_add_dependency_by_name(u, UNIT_BEFORE, m, NULL, true); - - if (s->sysv_enabled) { - int k; - - if ((k = unit_add_dependency_by_name_inverse(u, UNIT_WANTS, m, NULL, true)) < 0) - r = k; - } - } + else + /* NB: SysV targets + * which are provided + * by a service are + * pulled in by the + * services, as an + * indication that the + * generic service is + * now available. This + * is strictly + * one-way. The + * targets do NOT pull + * in the SysV + * services! */ + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, UNIT_WANTS, m, NULL, true); if (r < 0) log_error("[%s:%u] Failed to add LSB Provides name %s, ignoring: %s", path, line, m, strerror(-r)); @@ -759,19 +782,6 @@ static int service_load_sysv_path(Service *s, const char *path) { free(short_description); short_description = d; - } else if (startswith_no_case(t, "X-Interactive:")) { - int b; - - if ((b = parse_boolean(strstrip(t+14))) < 0) { - log_warning("[%s:%u] Couldn't parse interactive flag. Ignoring.", path, line); - continue; - } - - if (b) - s->exec_context.std_input = EXEC_INPUT_TTY; - else - s->exec_context.std_input = EXEC_INPUT_NULL; - } else if (state == LSB_DESCRIPTION) { if (startswith(l, "#\t") || startswith(l, "# ")) { @@ -781,7 +791,7 @@ static int service_load_sysv_path(Service *s, const char *path) { char *d = NULL; if (long_description) - asprintf(&d, "%s %s", long_description, t); + d = join(long_description, " ", t, NULL); else d = strdup(j); @@ -802,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 @@ -819,12 +836,14 @@ 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 = true; + s->remain_after_exit = !s->pid_file; + s->guess_main_pid = false; s->restart = SERVICE_RESTART_NO; - s->exec_context.std_output = - (s->meta.manager->sysv_console || s->exec_context.std_input == EXEC_INPUT_TTY) - ? EXEC_OUTPUT_TTY : s->meta.manager->default_std_output; - s->exec_context.kill_mode = KILL_PROCESS_GROUP; + + if (s->meta.manager->sysv_console) + s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE; + + s->exec_context.kill_mode = KILL_PROCESS; /* We use the long description only if * no short description is set. */ @@ -849,6 +868,12 @@ static int service_load_sysv_path(Service *s, const char *path) { u->meta.description = d; } + /* The priority that has been set in /etc/rcN.d/ hierarchies + * takes precedence over what is stored as default in the LSB + * header */ + if (s->sysv_start_priority_from_rcnd >= 0) + s->sysv_start_priority = s->sysv_start_priority_from_rcnd; + u->meta.load_state = UNIT_LOADED; r = 0; @@ -872,7 +897,7 @@ static int service_load_sysv_name(Service *s, const char *name) { /* For SysV services we strip the boot.*, rc.* and *.sh * prefixes/suffixes. */ -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (endswith(name, ".sh.service")) return -ENOENT; #endif @@ -891,7 +916,8 @@ static int service_load_sysv_name(Service *s, const char *name) { char *path; int r; - if (asprintf(&path, "%s/%s", *p, name) < 0) + path = join(*p, "/", name, NULL); + if (!path) return -ENOMEM; assert(endswith(path, ".service")); @@ -899,7 +925,7 @@ static int service_load_sysv_name(Service *s, const char *name) { r = service_load_sysv_path(s, path); -#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) +#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_ANGSTROM) if (r >= 0 && s->meta.load_state == UNIT_STUB) { /* Try Debian style *.sh source'able init scripts */ strcat(path, ".sh"); @@ -912,7 +938,8 @@ static int service_load_sysv_name(Service *s, const char *name) { if (r >= 0 && s->meta.load_state == UNIT_STUB) { /* Try SUSE style boot.* init scripts */ - if (asprintf(&path, "%s/boot.%s", *p, name) < 0) + path = join(*p, "/boot.", name, NULL); + if (!path) return -ENOMEM; /* Drop .service suffix */ @@ -926,7 +953,8 @@ static int service_load_sysv_name(Service *s, const char *name) { if (r >= 0 && s->meta.load_state == UNIT_STUB) { /* Try Frugalware style rc.* init scripts */ - if (asprintf(&path, "%s/rc.%s", *p, name) < 0) + path = join(*p, "/rc.", name, NULL); + if (!path) return -ENOMEM; /* Drop .service suffix */ @@ -991,7 +1019,7 @@ static int fsck_fix_order(Service *s) { /* For each pair of services where both have an fsck priority * we order things based on it. */ - LIST_FOREACH(units_per_type, other, s->meta.manager->units_per_type[UNIT_SERVICE]) { + LIST_FOREACH(units_by_type, other, s->meta.manager->units_by_type[UNIT_SERVICE]) { Service *t; UnitDependency d; @@ -1013,7 +1041,7 @@ static int fsck_fix_order(Service *s) { else continue; - if (!(r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) + if ((r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0) return r; } @@ -1080,6 +1108,40 @@ static int service_add_default_dependencies(Service *s) { return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true); } +static int service_add_socket_dependencies(Service *s) { + Iterator i; + Unit *u; + int r; + + /* Make sure we pull in all explicitly configured sockets */ + + SET_FOREACH(u, s->configured_sockets, i) { + r = unit_add_two_dependencies(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, u, true); + if (r < 0) + return r; + } + + return 0; +} + +static void service_fix_output(Service *s) { + assert(s); + + /* If nothing has been explicitly configured, patch default + * output in. If input is socket/tty we avoid this however, + * since in that case we want output to default to the same + * place as we read input from. */ + + if (s->exec_context.std_error == EXEC_OUTPUT_INHERIT && + s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_error = s->meta.manager->default_std_error; + + if (s->exec_context.std_output == EXEC_OUTPUT_INHERIT && + s->exec_context.std_input == EXEC_INPUT_NULL) + s->exec_context.std_output = s->meta.manager->default_std_output; +} + static int service_load(Unit *u) { int r; Service *s = SERVICE(u); @@ -1108,6 +1170,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; @@ -1130,8 +1194,14 @@ static int service_load(Unit *u) { s->notify_access = NOTIFY_MAIN; if (s->type == SERVICE_DBUS || s->bus_name) - if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_TARGET, NULL, true)) < 0) + if ((r = unit_add_two_dependencies_by_name(u, UNIT_AFTER, UNIT_REQUIRES, SPECIAL_DBUS_SOCKET, NULL, true)) < 0) + return r; + + if (!set_isempty(s->configured_sockets)) { + r = service_add_socket_dependencies(s); + if (r < 0) return r; + } if (s->meta.default_dependencies) if ((r = service_add_default_dependencies(s)) < 0) @@ -1178,8 +1248,12 @@ static void service_dump(Unit *u, FILE *f, const char *prefix) { if (s->main_pid > 0) fprintf(f, - "%sMain PID: %lu\n", - prefix, (unsigned long) s->main_pid); + "%sMain PID: %lu\n" + "%sMain PID Known: %s\n" + "%sMain PID Alien: %s\n", + prefix, (unsigned long) s->main_pid, + prefix, yes_no(s->main_pid_known), + prefix, yes_no(s->main_pid_alien)); if (s->pid_file) fprintf(f, @@ -1238,7 +1312,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; @@ -1248,8 +1322,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); @@ -1258,11 +1336,23 @@ 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; } + if (s->main_pid_known) { + if (pid == s->main_pid) + return 0; + + log_debug("Main PID changing: %lu -> %lu", + (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; @@ -1292,6 +1382,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; @@ -1384,6 +1475,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); @@ -1391,6 +1493,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 && @@ -1459,7 +1563,7 @@ static void service_set_state(Service *s, ServiceState state) { /* For the inactive states unit_notify() will trim the cgroup, * but for exit we have to do that ourselves... */ - if (state == SERVICE_EXITED) + if (state == SERVICE_EXITED && s->meta.manager->n_reloading <= 0) cgroup_bonding_trim_list(s->meta.cgroup_bondings, true); if (old_state != state) @@ -1581,8 +1685,8 @@ static int service_collect_fds(Service *s, int **fds, unsigned *n_fds) { goto fail; } - memcpy(t, rfds, rn_fds); - memcpy(t+rn_fds, cfds, cn_fds); + memcpy(t, rfds, rn_fds * sizeof(int)); + memcpy(t+rn_fds, cfds, cn_fds * sizeof(int)); free(rfds); free(cfds); @@ -1688,6 +1792,7 @@ static int service_spawn( apply_tty_stdin, s->meta.manager->confirm_spawn, s->meta.cgroup_bondings, + s->meta.cgroup_attributes, &pid); if (r < 0) @@ -1727,8 +1832,18 @@ static int main_pid_good(Service *s) { /* If we know the pid file, then lets just check if it is * still valid */ - if (s->main_pid_known) + if (s->main_pid_known) { + + /* If it's an alien child let's check if it is still + * alive ... */ + if (s->main_pid_alien) + return kill(s->main_pid, 0) >= 0 || errno != ESRCH; + + /* .. otherwise assume we'll get a SIGCHLD for it, + * which we really should wait for to collect exit + * status and code */ return s->main_pid > 0; + } /* We don't know the pid */ return -EAGAIN; @@ -1834,20 +1949,14 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL; if (s->main_pid > 0) { - if (kill_and_sigcont(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? - -s->main_pid : - s->main_pid, sig) < 0 && errno != ESRCH) - + if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH) log_warning("Failed to kill main process %li: %m", (long) s->main_pid); else - wait_for_exit = true; + wait_for_exit = !s->main_pid_alien; } if (s->control_pid > 0) { - if (kill_and_sigcont(s->exec_context.kill_mode == KILL_PROCESS_GROUP ? - -s->control_pid : - s->control_pid, sig) < 0 && errno != ESRCH) - + if (kill_and_sigcont(s->control_pid, sig) < 0 && errno != ESRCH) log_warning("Failed to kill control process %li: %m", (long) s->control_pid); else wait_for_exit = true; @@ -1876,6 +1985,7 @@ static void service_enter_signal(Service *s, ServiceState state, bool success) { wait_for_exit = true; set_free(pid_set); + pid_set = NULL; } } @@ -2270,6 +2380,7 @@ static int service_start(Unit *u) { s->failure = false; s->main_pid_known = false; + s->main_pid_alien = false; s->forbid_restart = false; service_enter_start_pre(s); @@ -2528,6 +2639,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; @@ -2541,9 +2741,14 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { success = is_clean_exit(code, status); if (s->main_pid == pid) { + /* 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, false) == 0) + return; s->main_pid = 0; - exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id); + exec_status_exit(&s->main_exec_status, &s->exec_context, pid, code, status); /* If this is not a forking service than the main * process got started and hence we copy the exit @@ -2622,13 +2827,13 @@ static void service_sigchld_event(Unit *u, pid_t pid, int code, int status) { s->control_pid = 0; if (s->control_command) { - exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id); + exec_status_exit(&s->control_command->exec_status, &s->exec_context, pid, code, status); if (s->control_command->ignore) 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; @@ -2663,43 +2868,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 && s->pid_file && !s->main_pid_known) { - int r; - - /* Hmm, let's see if we can - * load the pid now after the - * start-post scripts got - * executed. */ - - if ((r = service_load_pid_file(s)) < 0) - log_warning("%s: failed to load PID file %s: %s", s->meta.id, s->pid_file, strerror(-r)); + 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); } @@ -2835,12 +3049,27 @@ 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; case SERVICE_STOP_SIGTERM: case SERVICE_STOP_SIGKILL: + if (main_pid_good(s) <= 0 && !control_pid_good(s)) service_enter_stop_post(s, true); @@ -2928,6 +3157,72 @@ static void service_notify_message(Unit *u, pid_t pid, char **tags) { } #ifdef HAVE_SYSV_COMPAT + +#ifdef TARGET_SUSE +static void sysv_facility_in_insserv_conf(Manager *mgr) { + FILE *f=NULL; + int r; + + if (!(f = fopen("/etc/insserv.conf", "re"))) { + r = errno == ENOENT ? 0 : -errno; + goto finish; + } + + while (!feof(f)) { + char l[LINE_MAX], *t; + char **parsed = NULL; + + if (!fgets(l, sizeof(l), f)) { + if (feof(f)) + break; + + r = -errno; + log_error("Failed to read configuration file '/etc/insserv.conf': %s", strerror(-r)); + goto finish; + } + + t = strstrip(l); + if (*t != '$' && *t != '<') + continue; + + parsed = strv_split(t,WHITESPACE); + /* we ignore , not used, equivalent to X-Interactive */ + if (parsed && !startswith_no_case (parsed[0], "")) { + char *facility; + Unit *u; + if (sysv_translate_facility(parsed[0], NULL, &facility) < 0) + continue; + if ((u = manager_get_unit(mgr, facility)) && (u->meta.type == UNIT_TARGET)) { + UnitDependency e; + char *dep = NULL, *name, **j; + + STRV_FOREACH (j, parsed+1) { + if (*j[0]=='+') { + e = UNIT_WANTS; + name = *j+1; + } + else { + e = UNIT_REQUIRES; + name = *j; + } + if (sysv_translate_facility(name, NULL, &dep) < 0) + continue; + + r = unit_add_two_dependencies_by_name(u, UNIT_BEFORE, e, dep, NULL, true); + free(dep); + } + } + free(facility); + } + strv_free(parsed); + } +finish: + if (f) + fclose(f); + +} +#endif + static int service_enumerate(Manager *m) { char **p; unsigned i; @@ -2940,6 +3235,9 @@ static int service_enumerate(Manager *m) { assert(m); + if (m->running_as != MANAGER_SYSTEM) + return 0; + zero(runlevel_services); STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path) @@ -2947,8 +3245,8 @@ static int service_enumerate(Manager *m) { struct dirent *de; free(path); - path = NULL; - if (asprintf(&path, "%s/%s", *p, rcnd_table[i].path) < 0) { + path = join(*p, "/", rcnd_table[i].path, NULL); + if (!path) { r = -ENOMEM; goto finish; } @@ -2982,8 +3280,8 @@ static int service_enumerate(Manager *m) { continue; free(fpath); - fpath = NULL; - if (asprintf(&fpath, "%s/%s/%s", *p, rcnd_table[i].path, de->d_name) < 0) { + fpath = join(path, "/", de->d_name, NULL); + if (!fpath) { r = -ENOMEM; goto finish; } @@ -3010,8 +3308,8 @@ static int service_enumerate(Manager *m) { if (de->d_name[0] == 'S') { if (rcnd_table[i].type == RUNLEVEL_UP || rcnd_table[i].type == RUNLEVEL_SYSINIT) { - SERVICE(service)->sysv_start_priority = - MAX(a*10 + b, SERVICE(service)->sysv_start_priority); + SERVICE(service)->sysv_start_priority_from_rcnd = + MAX(a*10 + b, SERVICE(service)->sysv_start_priority_from_rcnd); SERVICE(service)->sysv_enabled = true; } @@ -3073,6 +3371,10 @@ static int service_enumerate(Manager *m) { r = 0; +#ifdef TARGET_SUSE + sysv_facility_in_insserv_conf (m); +#endif + finish: free(path); free(fpath); @@ -3190,6 +3492,29 @@ static void service_reset_failed(Unit *u) { s->failure = false; } +static bool service_need_daemon_reload(Unit *u) { + Service *s = SERVICE(u); + + assert(s); + +#ifdef HAVE_SYSV_COMPAT + if (s->sysv_path) { + struct stat st; + + zero(st); + if (stat(s->sysv_path, &st) < 0) + /* What, cannot access this anymore? */ + return true; + + if (s->sysv_mtime > 0 && + timespec_load(&st.st_mtim) != s->sysv_mtime) + return true; + } +#endif + + return false; +} + static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) { Service *s = SERVICE(u); int r = 0; @@ -3199,23 +3524,25 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro if (s->main_pid <= 0 && who == KILL_MAIN) { dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill"); - return -EINVAL; + return -ESRCH; } if (s->control_pid <= 0 && who == KILL_CONTROL) { dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill"); - return -ENOENT; + return -ESRCH; } - if (s->control_pid > 0) - if (kill(mode == KILL_PROCESS_GROUP ? -s->control_pid : s->control_pid, signo) < 0) - r = -errno; + if (who == KILL_CONTROL || who == KILL_ALL) + if (s->control_pid > 0) + if (kill(s->control_pid, signo) < 0) + r = -errno; - if (s->main_pid > 0) - if (kill(mode == KILL_PROCESS_GROUP ? -s->main_pid : s->main_pid, signo) < 0) - r = -errno; + if (who == KILL_MAIN || who == KILL_ALL) + if (s->main_pid > 0) + if (kill(s->main_pid, signo) < 0) + r = -errno; - if (mode == KILL_CONTROL_GROUP) { + if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) { int q; if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) @@ -3235,7 +3562,7 @@ static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusErro } if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, false, pid_set)) < 0) - if (r != -EAGAIN && r != -ESRCH && r != -ENOENT) + if (q != -EAGAIN && q != -ESRCH && q != -ENOENT) r = q; } @@ -3307,6 +3634,10 @@ DEFINE_STRING_TABLE_LOOKUP(notify_access, NotifyAccess); const UnitVTable service_vtable = { .suffix = ".service", + .sections = + "Unit\0" + "Service\0" + "Install\0", .show_status = true, .init = service_init, @@ -3336,9 +3667,12 @@ const UnitVTable service_vtable = { .sigchld_event = service_sigchld_event, .timer_event = service_timer_event, + .fd_event = service_fd_event, .reset_failed = service_reset_failed, + .need_daemon_reload = service_need_daemon_reload, + .cgroup_notify_empty = service_cgroup_notify_event, .notify_message = service_notify_message,