#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->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);
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.
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, "# ")) {
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 = !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;
+
+ 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
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;
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;
assert(s);
- if (s->main_pid_known)
- return 0;
-
if (!s->pid_file)
- return 0;
+ 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 &&
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, &s->exec_context, pid, code, 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;
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);
}
* 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;
free(fpath);
fpath = join(path, "/", de->d_name, NULL);
- if (!path) {
+ if (!fpath) {
r = -ENOMEM;
goto finish;
}
r = 0;
#ifdef TARGET_SUSE
- sysv_facility_in_insserv_conf (m);
+ sysv_facility_in_insserv_conf (m);
#endif
finish:
.sigchld_event = service_sigchld_event,
.timer_event = service_timer_event,
+ .fd_event = service_fd_event,
.reset_failed = service_reset_failed,