{ "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
#endif
-#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE) || defined(TARGET_ANGSTROM)
+#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->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 void service_connection_unref(Service *s) {
assert(s);
- if (!s->accept_socket)
+ if (!UNIT_DEREF(s->accept_socket))
return;
- socket_connection_unref(s->accept_socket);
- s->accept_socket = NULL;
+ socket_connection_unref(SOCKET(UNIT_DEREF(s->accept_socket)));
+ unit_ref_unset(&s->accept_socket);
}
static void service_done(Unit *u) {
service_close_socket_fd(s);
service_connection_unref(s);
- set_free(s->configured_sockets);
+ unit_ref_unset(&s->accept_socket);
unit_unwatch_timer(u, &s->timer_watch);
}
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_JOURNAL_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 int service_get_sockets(Service *s, Set **_set) {
- Set *set;
- Iterator i;
- char *t;
- int r;
-
- assert(s);
- assert(_set);
-
- if (s->socket_fd >= 0)
- return 0;
-
- if (!set_isempty(s->configured_sockets))
- return 0;
-
- /* Collects all Socket objects that belong to this
- * service. Note that a service might have multiple sockets
- * via multiple names. */
-
- if (!(set = set_new(NULL, NULL)))
- return -ENOMEM;
-
- SET_FOREACH(t, s->meta.names, i) {
- char *k;
- Unit *p;
-
- /* Look for all socket objects that go by any of our
- * units and collect their fds */
-
- if (!(k = unit_name_change_suffix(t, ".socket"))) {
- r = -ENOMEM;
- goto fail;
- }
-
- p = manager_get_unit(s->meta.manager, k);
- free(k);
-
- if (!p)
- continue;
-
- if ((r = set_put(set, p)) < 0)
- goto fail;
- }
-
- *_set = set;
- return 0;
-
-fail:
- set_free(set);
- return r;
-}
-
-static int service_notify_sockets_dead(Service *s) {
+static void service_notify_sockets_dead(Service *s) {
Iterator i;
- Set *set, *free_set = NULL;
- Socket *sock;
- int r;
+ Unit *u;
assert(s);
/* Notifies all our sockets when we die */
if (s->socket_fd >= 0)
- return 0;
-
- if (!set_isempty(s->configured_sockets))
- set = s->configured_sockets;
- else {
- if ((r = service_get_sockets(s, &free_set)) < 0)
- return r;
+ return;
- set = free_set;
- }
+ SET_FOREACH(u, s->meta.dependencies[UNIT_TRIGGERED_BY], i)
+ if (u->meta.type == UNIT_SOCKET)
+ socket_notify_service_dead(SOCKET(u));
- SET_FOREACH(sock, set, i)
- socket_notify_service_dead(sock);
+ return;
+}
- set_free(free_set);
+static void service_unwatch_pid_file(Service *s) {
+ if (!s->pid_file_pathspec)
+ return;
- return 0;
+ log_debug("Stopping watch for %s's PID file %s", s->meta.id, s->pid_file_pathspec->path);
+ path_spec_unwatch(s->pid_file_pathspec, UNIT(s));
+ path_spec_done(s->pid_file_pathspec);
+ free(s->pid_file_pathspec);
+ s->pid_file_pathspec = NULL;
}
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 &&
int r;
int *rfds = NULL;
unsigned rn_fds = 0;
- Set *set, *free_set = NULL;
- Socket *sock;
+ Unit *u;
assert(s);
assert(fds);
if (s->socket_fd >= 0)
return 0;
- if (!set_isempty(s->configured_sockets))
- set = s->configured_sockets;
- else {
- if ((r = service_get_sockets(s, &free_set)) < 0)
- return r;
-
- set = free_set;
- }
-
- SET_FOREACH(sock, set, i) {
+ SET_FOREACH(u, s->meta.dependencies[UNIT_TRIGGERED_BY], i) {
int *cfds;
unsigned cn_fds;
+ Socket *sock;
+
+ if (u->meta.type != UNIT_SOCKET)
+ continue;
+
+ sock = SOCKET(u);
if ((r = socket_collect_fds(sock, &cfds, &cn_fds)) < 0)
goto fail;
*fds = rfds;
*n_fds = rn_fds;
- set_free(free_set);
-
return 0;
fail:
- set_free(set);
free(rfds);
return r;
else
service_unwatch_main_pid(s);
+ /* We want to ensure that nobody leaks processes from
+ * START_PRE here, so let's go on a killing spree, People
+ * should not spawn long running processes from START_PRE. */
+ cgroup_bonding_kill_list(s->meta.cgroup_bondings, SIGKILL, true, NULL);
+
if (s->type == SERVICE_FORKING) {
s->control_command_id = SERVICE_EXEC_START;
c = s->control_command = s->exec_command[SERVICE_EXEC_START];
service_unwatch_control_pid(s);
if ((s->control_command = s->exec_command[SERVICE_EXEC_START_PRE])) {
+
+ /* Before we start anything, let's clear up what might
+ * be left from previous runs. */
+ cgroup_bonding_kill_list(s->meta.cgroup_bondings, SIGKILL, true, NULL);
+
s->control_command_id = SERVICE_EXEC_START_PRE;
if ((r = service_spawn(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 = path_spec_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(path_spec_owns_inotify_fd(s->pid_file_pathspec, fd));
+
+ log_debug("inotify event for %s", u->meta.id);
+
+ if (path_spec_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:
}
int service_set_socket_fd(Service *s, int fd, Socket *sock) {
+
assert(s);
assert(fd >= 0);
s->socket_fd = fd;
s->got_socket_fd = true;
- s->accept_socket = sock;
- return 0;
+ unit_ref_set(&s->accept_socket, UNIT(sock));
+
+ return unit_add_two_dependencies(UNIT(sock), UNIT_BEFORE, UNIT_TRIGGERS, UNIT(s), false);
}
static void service_reset_failed(Unit *u) {
.sigchld_event = service_sigchld_event,
.timer_event = service_timer_event,
+ .fd_event = service_fd_event,
.reset_failed = service_reset_failed,