#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,
{ "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
#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] = {
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);
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);
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");
static const char * const table[] = {
/* LSB defined facilities */
"local_fs", SPECIAL_LOCAL_FS_TARGET,
+#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
/* 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;
/* 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;
}
LSB_DESCRIPTION
} state = NORMAL;
char *short_description = NULL, *long_description = NULL, *chkconfig_description = NULL, *description;
+ struct stat st;
assert(s);
assert(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;
* 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);
char *d = NULL;
if (chkconfig_description)
- asprintf(&d, "%s %s", chkconfig_description, j);
+ d = join(chkconfig_description, " ", j, NULL);
else
d = strdup(j);
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));
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, "# ")) {
char *d = NULL;
if (long_description)
- asprintf(&d, "%s %s", long_description, t);
+ d = join(long_description, " ", t, NULL);
else
d = strdup(j);
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
/* 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_SYSLOG_AND_CONSOLE;
+
+ s->exec_context.kill_mode = KILL_PROCESS;
/* We use the long description only if
* no short description is set. */
if (description) {
char *d;
- if (!(d = strappend(s->sysv_has_lsb ? "LSB: " : "SysV: ", description))) {
+ if (!(d = strappend(s->sysv_has_lsb ? "LSB: " : "SYSV: ", description))) {
r = -ENOMEM;
goto finish;
}
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;
/* 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
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"));
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");
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 */
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 */
/* 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;
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;
}
return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
+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);
/* 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;
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 (s->meta.default_dependencies)
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,
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;
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);
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;
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;
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);
old_state = s->state;
s->state = state;
+ service_unwatch_pid_file(s);
+
if (state != SERVICE_START_PRE &&
state != SERVICE_START &&
state != SERVICE_START_POST &&
/* 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)
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);
}
if (set_notify_socket)
- if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=@%s", s->meta.manager->notify_socket) < 0) {
+ if (asprintf(our_env + n_env++, "NOTIFY_SOCKET=%s", s->meta.manager->notify_socket) < 0) {
r = -ENOMEM;
goto fail;
}
apply_tty_stdin,
s->meta.manager->confirm_spawn,
s->meta.cgroup_bondings,
+ s->meta.cgroup_attributes,
&pid);
if (r < 0)
/* 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;
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;
wait_for_exit = true;
set_free(pid_set);
+ pid_set = NULL;
}
}
s->failure = false;
s->main_pid_known = false;
+ s->main_pid_alien = false;
s->forbid_restart = false;
service_enter_start_pre(s);
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;
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
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;
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);
}
* 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);
}
#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 <interactive>, not used, equivalent to X-Interactive */
+ if (parsed && !startswith_no_case (parsed[0], "<interactive>")) {
+ 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;
assert(m);
+ if (m->running_as != MANAGER_SYSTEM)
+ return 0;
+
zero(runlevel_services);
STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
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;
}
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;
}
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;
}
r = 0;
+#ifdef TARGET_SUSE
+ sysv_facility_in_insserv_conf (m);
+#endif
+
finish:
free(path);
free(fpath);
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;
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)))
}
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;
}
const UnitVTable service_vtable = {
.suffix = ".service",
+ .sections =
+ "Unit\0"
+ "Service\0"
+ "Install\0",
.show_status = true,
.init = service_init,
.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,