[SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
};
+/* For Type=idle we never want to delay any other jobs, hence we
+ * consider idle jobs active as soon as we start working on them */
+static const UnitActiveState state_translation_table_idle[_SERVICE_STATE_MAX] = {
+ [SERVICE_DEAD] = UNIT_INACTIVE,
+ [SERVICE_START_PRE] = UNIT_ACTIVE,
+ [SERVICE_START] = UNIT_ACTIVE,
+ [SERVICE_START_POST] = UNIT_ACTIVE,
+ [SERVICE_RUNNING] = UNIT_ACTIVE,
+ [SERVICE_EXITED] = UNIT_ACTIVE,
+ [SERVICE_RELOAD] = UNIT_RELOADING,
+ [SERVICE_STOP] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_STOP_POST] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGTERM] = UNIT_DEACTIVATING,
+ [SERVICE_FINAL_SIGKILL] = UNIT_DEACTIVATING,
+ [SERVICE_FAILED] = UNIT_FAILED,
+ [SERVICE_AUTO_RESTART] = UNIT_ACTIVATING
+};
+
static void service_init(Unit *u) {
Service *s = SERVICE(u);
- int i;
assert(u);
assert(u->load_state == UNIT_STUB);
- s->timeout_usec = DEFAULT_TIMEOUT_USEC;
+ s->timeout_start_usec = DEFAULT_TIMEOUT_USEC;
+ s->timeout_stop_usec = DEFAULT_TIMEOUT_USEC;
s->restart_usec = DEFAULT_RESTART_USEC;
s->type = _SERVICE_TYPE_INVALID;
s->guess_main_pid = true;
exec_context_init(&s->exec_context);
- for (i = 0; i < RLIMIT_NLIMITS; i++)
- if (UNIT(s)->manager->rlimit[i])
- s->exec_context.rlimit[i] = newdup(struct rlimit, UNIT(s)->manager->rlimit[i], 1);
+ kill_context_init(&s->kill_context);
RATELIMIT_INIT(s->start_limit, 10*USEC_PER_SEC, 5);
s->pid_file = NULL;
#ifdef HAVE_SYSV_COMPAT
- free(s->sysv_path);
- s->sysv_path = NULL;
-
free(s->sysv_runlevels);
s->sysv_runlevels = NULL;
#endif
s->control_command = NULL;
s->main_command = NULL;
+ set_free(s->restart_ignore_status.code);
+ s->restart_ignore_status.code = NULL;
+ set_free(s->restart_ignore_status.signal);
+ s->restart_ignore_status.signal = NULL;
+
+ set_free(s->success_status.code);
+ s->success_status.code = NULL;
+ set_free(s->success_status.signal);
+ s->success_status.signal = NULL;
+
/* This will leak a process, but at least no memory or any of
* our resources */
service_unwatch_main_pid(s);
ExecCommand *c;
assert(s);
- assert(s->sysv_path);
+ assert(s->is_sysv);
+ assert(UNIT(s)->source_path);
- if (!(c = exec_command_new(s->sysv_path, "start")))
+ c = exec_command_new(UNIT(s)->source_path, "start");
+ if (!c)
return -ENOMEM;
exec_command_append_list(s->exec_command+SERVICE_EXEC_START, c);
- if (!(c = exec_command_new(s->sysv_path, "stop")))
+ c = exec_command_new(UNIT(s)->source_path, "stop");
+ if (!c)
return -ENOMEM;
exec_command_append_list(s->exec_command+SERVICE_EXEC_STOP, c);
- if (!(c = exec_command_new(s->sysv_path, "reload")))
+ c = exec_command_new(UNIT(s)->source_path, "reload");
+ if (!c)
return -ENOMEM;
exec_command_append_list(s->exec_command+SERVICE_EXEC_RELOAD, c);
u = UNIT(s);
- if (!(f = fopen(path, "re"))) {
+ f = fopen(path, "re");
+ if (!f) {
r = errno == ENOENT ? 0 : -errno;
goto finish;
}
- zero(st);
if (fstat(fileno(f), &st) < 0) {
r = -errno;
goto finish;
}
- free(s->sysv_path);
- if (!(s->sysv_path = strdup(path))) {
+ free(u->source_path);
+ u->source_path = strdup(path);
+ if (!u->source_path) {
r = -ENOMEM;
goto finish;
}
-
- s->sysv_mtime = timespec_load(&st.st_mtim);
+ u->source_mtime = timespec_load(&st.st_mtim);
if (null_or_empty(&st)) {
u->load_state = UNIT_MASKED;
goto finish;
}
+ s->is_sysv = true;
+
while (!feof(f)) {
char l[LINE_MAX], *t;
char *d = NULL;
if (chkconfig_description)
- d = join(chkconfig_description, " ", j, NULL);
+ d = strjoin(chkconfig_description, " ", j, NULL);
else
d = strdup(j);
char *d = NULL;
if (long_description)
- d = join(long_description, " ", t, NULL);
+ d = strjoin(long_description, " ", t, NULL);
else
d = strdup(j);
UNIT(s)->default_dependencies = false;
/* Don't timeout special services during boot (like fsck) */
- s->timeout_usec = 0;
- } else
- s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ s->timeout_start_usec = 0;
+ s->timeout_stop_usec = 0;
+ } else {
+ s->timeout_start_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ s->timeout_stop_usec = DEFAULT_SYSV_TIMEOUT_USEC;
+ }
+
/* Special setting for all SysV services */
s->type = SERVICE_FORKING;
s->guess_main_pid = false;
s->restart = SERVICE_RESTART_NO;
s->exec_context.ignore_sigpipe = false;
-
- if (UNIT(s)->manager->sysv_console)
- s->exec_context.std_output = EXEC_OUTPUT_JOURNAL_AND_CONSOLE;
-
- s->exec_context.kill_mode = KILL_PROCESS;
+ s->kill_context.kill_mode = KILL_PROCESS;
/* We use the long description only if
* no short description is set. */
char *path;
int r;
- path = join(*p, "/", name, NULL);
+ path = strjoin(*p, "/", name, NULL);
if (!path)
return -ENOMEM;
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
/* Try SUSE style boot.* init scripts */
- path = join(*p, "/boot.", name, NULL);
+ path = strjoin(*p, "/boot.", name, NULL);
if (!path)
return -ENOMEM;
if (r >= 0 && UNIT(s)->load_state == UNIT_STUB) {
/* Try Frugalware style rc.* init scripts */
- path = join(*p, "/rc.", name, NULL);
+ path = strjoin(*p, "/rc.", name, NULL);
if (!path)
return -ENOMEM;
if (s->bus_name && s->type != SERVICE_DBUS)
log_warning("%s has a D-Bus service name specified, but is not of type dbus. Ignoring.", UNIT(s)->id);
- if (s->exec_context.pam_name && s->exec_context.kill_mode != KILL_CONTROL_GROUP) {
+ if (s->exec_context.pam_name && s->kill_context.kill_mode != KILL_CONTROL_GROUP) {
log_error("%s has PAM enabled. Kill mode must be set to 'control-group'. Refusing.", UNIT(s)->id);
return -EINVAL;
}
if (s->type == _SERVICE_TYPE_INVALID)
s->type = s->bus_name ? SERVICE_DBUS : SERVICE_SIMPLE;
+ /* Oneshot services have disabled start timeout by default */
+ if (s->type == SERVICE_ONESHOT && !s->start_timeout_defined)
+ s->timeout_start_usec = 0;
+
service_fix_output(s);
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
if (UNIT(s)->default_dependencies)
if ((r = service_add_default_dependencies(s)) < 0)
return r;
+
+ r = unit_exec_context_defaults(u, &s->exec_context);
+ if (r < 0)
+ return r;
}
return service_verify(s);
prefix, s->bus_name,
prefix, yes_no(s->bus_name_good));
+ kill_context_dump(&s->kill_context, f, prefix);
exec_context_dump(&s->exec_context, f, prefix);
for (c = 0; c < _SERVICE_EXEC_COMMAND_MAX; c++) {
}
#ifdef HAVE_SYSV_COMPAT
- if (s->sysv_path)
+ if (s->is_sysv)
fprintf(f,
- "%sSysV Init Script Path: %s\n"
"%sSysV Init Script has LSB Header: %s\n"
"%sSysVEnabled: %s\n",
- prefix, s->sysv_path,
prefix, yes_no(s->sysv_has_lsb),
prefix, yes_no(s->sysv_enabled));
static void service_set_state(Service *s, ServiceState state) {
ServiceState old_state;
+ const UnitActiveState *table;
assert(s);
+ table = s->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
+
old_state = s->state;
s->state = state;
if (old_state != state)
log_debug("%s changed %s -> %s", UNIT(s)->id, service_state_to_string(old_state), service_state_to_string(state));
- unit_notify(UNIT(s), state_translation_table[old_state], state_translation_table[state], s->reload_result == SERVICE_SUCCESS);
+ unit_notify(UNIT(s), table[old_state], table[state], s->reload_result == SERVICE_SUCCESS);
s->reload_result = SERVICE_SUCCESS;
}
s->deserialized_state == SERVICE_FINAL_SIGTERM ||
s->deserialized_state == SERVICE_FINAL_SIGKILL ||
s->deserialized_state == SERVICE_AUTO_RESTART) {
-
- if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_usec > 0) {
+ if (s->deserialized_state == SERVICE_AUTO_RESTART || s->timeout_start_usec > 0) {
usec_t k;
- k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_usec;
+ k = s->deserialized_state == SERVICE_AUTO_RESTART ? s->restart_usec : s->timeout_start_usec;
if ((r = unit_watch_timer(UNIT(s), k, &s->timer_watch)) < 0)
return r;
}
}
- if (timeout && s->timeout_usec) {
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ if (timeout && s->timeout_start_usec) {
+ r = unit_watch_timer(UNIT(s), s->timeout_start_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
} else
unit_unwatch_timer(UNIT(s), &s->timer_watch);
UNIT(s)->cgroup_bondings,
UNIT(s)->cgroup_attributes,
is_control ? "control" : NULL,
+ UNIT(s)->id,
s->type == SERVICE_IDLE ? UNIT(s)->manager->idle_pipe : NULL,
&pid);
if (f != SERVICE_SUCCESS)
s->result = f;
+ service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
+
if (allow_restart &&
!s->forbid_restart &&
(s->restart == SERVICE_RESTART_ALWAYS ||
(s->restart == SERVICE_RESTART_ON_SUCCESS && s->result == SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_FAILURE && s->result != SERVICE_SUCCESS) ||
(s->restart == SERVICE_RESTART_ON_ABORT && (s->result == SERVICE_FAILURE_SIGNAL ||
- s->result == SERVICE_FAILURE_CORE_DUMP)))) {
+ s->result == SERVICE_FAILURE_CORE_DUMP))) &&
+ (s->result != SERVICE_FAILURE_EXIT_CODE ||
+ !set_contains(s->restart_ignore_status.code, INT_TO_PTR(s->main_exec_status.status))) &&
+ (s->result != SERVICE_FAILURE_SIGNAL ||
+ !set_contains(s->restart_ignore_status.signal, INT_TO_PTR(s->main_exec_status.status)))
+ ) {
r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
if (r < 0)
goto fail;
service_set_state(s, SERVICE_AUTO_RESTART);
- } else
- service_set_state(s, s->result != SERVICE_SUCCESS ? SERVICE_FAILED : SERVICE_DEAD);
+ }
s->forbid_restart = false;
if (f != SERVICE_SUCCESS)
s->result = f;
- if (s->exec_context.kill_mode != KILL_NONE) {
- int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->exec_context.kill_signal : SIGKILL;
+ if (s->kill_context.kill_mode != KILL_NONE) {
+ int sig = (state == SERVICE_STOP_SIGTERM || state == SERVICE_FINAL_SIGTERM) ? s->kill_context.kill_signal : SIGKILL;
if (s->main_pid > 0) {
if (kill_and_sigcont(s->main_pid, sig) < 0 && errno != ESRCH)
wait_for_exit = true;
}
- if (s->exec_context.kill_mode == KILL_CONTROL_GROUP) {
+ if (s->kill_context.kill_mode == KILL_CONTROL_GROUP) {
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func))) {
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set) {
r = -ENOMEM;
goto fail;
}
}
if (wait_for_exit) {
- if (s->timeout_usec > 0)
- if ((r = unit_watch_timer(UNIT(s), s->timeout_usec, &s->timer_watch)) < 0)
+ if (s->timeout_stop_usec > 0) {
+ r = unit_watch_timer(UNIT(s), s->timeout_stop_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
+ }
service_set_state(s, state);
} else if (state == SERVICE_STOP_SIGTERM || state == SERVICE_STOP_SIGKILL)
r = service_spawn(s,
c,
- s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY,
+ s->type == SERVICE_FORKING || s->type == SERVICE_DBUS || s->type == SERVICE_NOTIFY || s->type == SERVICE_ONESHOT,
true,
true,
true,
assert(s);
dbus_error_init(&error);
- if (UNIT(s)->job) {
- log_info("Job pending for unit, delaying automatic restart.");
+ if (UNIT(s)->job && UNIT(s)->job->type == JOB_STOP) {
+ /* Don't restart things if we are going down anyway */
+ log_info("Stop job pending for unit, delaying automatic restart.");
- if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0)
+ r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch);
+ if (r < 0)
goto fail;
return;
if (r < 0)
goto fail;
+ /* Note that we stay in the SERVICE_AUTO_RESTART state here,
+ * it will be canceled as part of the service_stop() call that
+ * is executed as part of JOB_RESTART. */
+
log_debug("%s scheduled restart job.", UNIT(s)->id);
return;
r = service_spawn(s,
s->main_command,
- false,
+ true,
true,
true,
true,
s->state == SERVICE_START_POST)
return 0;
- assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED || s->state == SERVICE_AUTO_RESTART);
+ /* A service that will be restarted must be stopped first to
+ * trigger BindsTo and/or OnFailure dependencies. If a user
+ * does not want to wait for the holdoff time to elapse, the
+ * service should be manually restarted, not started. We
+ * simply return EAGAIN here, so that any start jobs stay
+ * queued, and assume that the auto restart timer will
+ * eventually trigger the restart. */
+ if (s->state == SERVICE_AUTO_RESTART)
+ return -EAGAIN;
+
+ assert(s->state == SERVICE_DEAD || s->state == SERVICE_FAILED);
/* Make sure we don't enter a busy loop of some kind. */
r = service_start_limit_test(s);
assert(s);
- /* This is a user request, so don't do restarts on this
- * shutdown. */
+ /* Don't create restart jobs from here. */
s->forbid_restart = true;
/* Already on it */
s->state == SERVICE_FINAL_SIGKILL)
return 0;
- /* Don't allow a restart */
+ /* A restart will be scheduled or is in progress. */
if (s->state == SERVICE_AUTO_RESTART) {
service_set_state(s, SERVICE_DEAD);
return 0;
}
static UnitActiveState service_active_state(Unit *u) {
+ const UnitActiveState *table;
+
assert(u);
- return state_translation_table[SERVICE(u)->state];
+ table = SERVICE(u)->type == SERVICE_IDLE ? state_translation_table_idle : state_translation_table;
+
+ return table[SERVICE(u)->state];
}
static const char *service_sub_state_to_string(Unit *u) {
return true;
#ifdef HAVE_SYSV_COMPAT
- if (s->sysv_path)
+ if (s->is_sysv)
return true;
#endif
assert(s);
assert(pid >= 0);
- if (UNIT(s)->fragment_path ? is_clean_exit(code, status) : is_clean_exit_lsb(code, status))
+ if (UNIT(s)->fragment_path ? is_clean_exit(code, status, &s->success_status) :
+ is_clean_exit_lsb(code, status, &s->success_status))
f = SERVICE_SUCCESS;
else if (code == CLD_EXITED)
f = SERVICE_FAILURE_EXIT_CODE;
break;
case SERVICE_STOP_SIGTERM:
- if (s->exec_context.send_sigkill) {
+ if (s->kill_context.send_sigkill) {
log_warning("%s stopping timed out. Killing.", u->id);
service_enter_signal(s, SERVICE_STOP_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
break;
case SERVICE_FINAL_SIGTERM:
- if (s->exec_context.send_sigkill) {
+ if (s->kill_context.send_sigkill) {
log_warning("%s stopping timed out (2). Killing.", u->id);
service_enter_signal(s, SERVICE_FINAL_SIGKILL, SERVICE_FAILURE_TIMEOUT);
} else {
case SERVICE_FINAL_SIGTERM:
case SERVICE_FINAL_SIGKILL:
if (main_pid_good(s) <= 0 && !control_pid_good(s))
- service_enter_dead(s, SERVICE_SUCCESS, SERVICE_SUCCESS);
+ service_enter_dead(s, SERVICE_SUCCESS, true);
break;
struct dirent *de;
free(path);
- path = join(*p, "/", rcnd_table[i].path, NULL);
+ path = strjoin(*p, "/", rcnd_table[i].path, NULL);
if (!path) {
r = -ENOMEM;
goto finish;
continue;
free(fpath);
- fpath = join(path, "/", de->d_name, NULL);
+ fpath = strjoin(path, "/", de->d_name, NULL);
if (!fpath) {
r = -ENOMEM;
goto finish;
s->result = SERVICE_SUCCESS;
s->reload_result = SERVICE_SUCCESS;
-}
-
-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;
+ RATELIMIT_RESET(s->start_limit);
}
-static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+static int service_kill(Unit *u, KillWho who, int signo, DBusError *error) {
Service *s = SERVICE(u);
int r = 0;
Set *pid_set = NULL;
if (kill(s->main_pid, signo) < 0)
r = -errno;
- if (who == KILL_ALL && mode == KILL_CONTROL_GROUP) {
+ if (who == KILL_ALL) {
int q;
- if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ pid_set = set_new(trivial_hash_func, trivial_compare_func);
+ if (!pid_set)
return -ENOMEM;
/* Exclude the control/main pid from being killed via the cgroup */
- if (s->control_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->control_pid))) < 0) {
+ if (s->control_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(s->control_pid));
+ if (q < 0) {
r = q;
goto finish;
}
+ }
- if (s->main_pid > 0)
- if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) {
+ if (s->main_pid > 0) {
+ q = set_put(pid_set, LONG_TO_PTR(s->main_pid));
+ if (q < 0) {
r = q;
goto finish;
}
+ }
+
q = cgroup_bonding_kill_list(UNIT(s)->cgroup_bondings, signo, false, false, pid_set, NULL);
- if (q < 0)
- if (q != -EAGAIN && q != -ESRCH && q != -ENOENT)
- r = q;
+ if (q < 0 && q != -EAGAIN && q != -ESRCH && q != -ENOENT)
+ r = q;
}
finish:
DEFINE_STRING_TABLE_LOOKUP(start_limit_action, StartLimitAction);
const UnitVTable service_vtable = {
- .suffix = ".service",
.object_size = sizeof(Service),
.sections =
"Unit\0"
.reset_failed = service_reset_failed,
- .need_daemon_reload = service_need_daemon_reload,
-
.cgroup_notify_empty = service_cgroup_notify_event,
.notify_message = service_notify_message,