#define NEWLINES "\n\r"
#ifdef HAVE_SYSV_COMPAT
+
+#define DEFAULT_SYSV_TIMEOUT_USEC (3*USEC_PER_MINUTE)
+
typedef enum RunlevelType {
RUNLEVEL_UP,
RUNLEVEL_DOWN,
{ "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
#endif
-#ifdef TARGET_DEBIAN
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
/* Debian style rcS.d */
{ "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
#endif
"time", SPECIAL_RTC_SET_TARGET,
/* Debian extensions */
-#ifdef TARGET_DEBIAN
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
"mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
#endif
"mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
}
/* If we don't know this name, fallback heuristics to figure
- * out whether something is a target or an service alias. */
+ * out whether something is a target or a service alias. */
if (*name == '$')
/* Facilities starting with $ are most likely targets */
/* Don't timeout special services during boot (like fsck) */
s->timeout_usec = 0;
- }
+ } else
+ s->timeout_usec = DEFAULT_SYSV_TIMEOUT_USEC;
/* Special setting for all SysV services */
s->type = SERVICE_FORKING;
}
#endif
+static int fsck_fix_order(Service *s) {
+ Meta *other;
+ int r;
+
+ assert(s);
+
+ if (s->fsck_passno <= 0)
+ return 0;
+
+ /* 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]) {
+ Service *t;
+ UnitDependency d;
+
+ t = (Service*) other;
+
+ if (s == t)
+ continue;
+
+ if (t->meta.load_state != UNIT_LOADED)
+ continue;
+
+ if (t->fsck_passno <= 0)
+ continue;
+
+ if (t->fsck_passno < s->fsck_passno)
+ d = UNIT_AFTER;
+ else if (t->fsck_passno > s->fsck_passno)
+ d = UNIT_BEFORE;
+ else
+ continue;
+
+ if (!(r = unit_add_dependency(UNIT(s), d, UNIT(t), true)) < 0)
+ return r;
+ }
+
+ return 0;
+}
+
static int service_verify(Service *s) {
assert(s);
if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_BASIC_TARGET, NULL, true)) < 0)
return r;
- } else if (s->meta.manager->running_as == MANAGER_SESSION) {
+ } else if (s->meta.manager->running_as == MANAGER_USER) {
if ((r = unit_add_two_dependencies_by_name(UNIT(s), UNIT_AFTER, UNIT_REQUIRES, SPECIAL_SOCKETS_TARGET, NULL, true)) < 0)
return r;
}
/* Second, activate normal shutdown */
- return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTED_BY, SPECIAL_SHUTDOWN_TARGET, NULL, true);
+ return unit_add_two_dependencies_by_name(UNIT(s), UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true);
}
static int service_load(Unit *u) {
return r;
#endif
+ if ((r = fsck_fix_order(s)) < 0)
+ return r;
+
if (s->bus_name)
if ((r = unit_watch_bus_name(u, s->bus_name)) < 0)
return r;
if (s->sysv_path)
fprintf(f,
"%sSysV Init Script Path: %s\n"
- "%sSysV Init Script has LSB Header: %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_has_lsb),
+ prefix, yes_no(s->sysv_enabled));
if (s->sysv_start_priority >= 0)
fprintf(f,
- "%sSysVStartPriority: %i\n"
- "%sSysVEnabled: %s\n",
- prefix, s->sysv_start_priority,
- prefix, yes_no(s->sysv_enabled));
+ "%sSysVStartPriority: %i\n",
+ prefix, s->sysv_start_priority);
if (s->sysv_runlevels)
fprintf(f, "%sSysVRunLevels: %s\n",
prefix, s->sysv_runlevels);
#endif
+ if (s->fsck_passno > 0)
+ fprintf(f,
+ "%sFsckPassNo: %i\n",
+ prefix, s->fsck_passno);
+
if (s->status_text)
fprintf(f, "%sStatus Text: %s\n",
prefix, s->status_text);
return 0;
}
+static int service_search_main_pid(Service *s) {
+ pid_t pid;
+ int r;
+
+ assert(s);
+
+ if (s->main_pid_known)
+ return 0;
+
+ assert(s->main_pid <= 0);
+
+ if ((pid = cgroup_bonding_search_main_pid_list(s->meta.cgroup_bondings)) <= 0)
+ return -ENOENT;
+
+ if ((r = service_set_main_pid(s, pid)) < 0)
+ return r;
+
+ if ((r = unit_watch_pid(UNIT(s), pid)) < 0)
+ /* FIXME: we need to do something here */
+ return r;
+
+ return 0;
+}
+
static int service_get_sockets(Service *s, Set **_set) {
Set *set;
Iterator i;
goto fail;
}
- if (!(our_env = new0(char*, 3))) {
+ if (!(our_env = new0(char*, 4))) {
r = -ENOMEM;
goto fail;
}
if (allow_restart &&
!s->forbid_restart &&
(s->restart == SERVICE_RESTART_ALWAYS ||
- (s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure))) {
+ (s->restart == SERVICE_RESTART_ON_SUCCESS && !s->failure) ||
+ (s->restart == SERVICE_RESTART_ON_FAILURE && s->failure) ||
+ (s->restart == SERVICE_RESTART_ON_ABORT && s->failure &&
+ (s->main_exec_status.code == CLD_KILLED ||
+ s->main_exec_status.code == CLD_DUMPED)))) {
if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0)
goto fail;
assert(s);
dbus_error_init(&error);
+ if (s->meta.job) {
+ log_info("Job pending for unit, delaying automatic restart.");
+
+ if ((r = unit_watch_timer(UNIT(s), s->restart_usec, &s->timer_watch)) < 0)
+ goto fail;
+ }
+
service_enter_dead(s, true, false);
if ((r = manager_add_job(s->meta.manager, JOB_START, UNIT(s), JOB_FAIL, false, &error, NULL)) < 0)
if (s->main_exec_status.pid > 0) {
unit_serialize_item_format(u, f, "main-exec-status-pid", "%lu", (unsigned long) s->main_exec_status.pid);
+ dual_timestamp_serialize(f, "main-exec-status-start", &s->main_exec_status.start_timestamp);
+ dual_timestamp_serialize(f, "main-exec-status-exit", &s->main_exec_status.exit_timestamp);
- if (s->main_exec_status.start_timestamp.realtime > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-start-realtime",
- "%llu", (unsigned long long) s->main_exec_status.start_timestamp.realtime);
-
- unit_serialize_item_format(u, f, "main-exec-status-start-monotonic",
- "%llu", (unsigned long long) s->main_exec_status.start_timestamp.monotonic);
- }
-
- if (s->main_exec_status.exit_timestamp.realtime > 0) {
- unit_serialize_item_format(u, f, "main-exec-status-exit-realtime",
- "%llu", (unsigned long long) s->main_exec_status.exit_timestamp.realtime);
- unit_serialize_item_format(u, f, "main-exec-status-exit-monotonic",
- "%llu", (unsigned long long) s->main_exec_status.exit_timestamp.monotonic);
-
+ if (dual_timestamp_is_set(&s->main_exec_status.exit_timestamp)) {
unit_serialize_item_format(u, f, "main-exec-status-code", "%i", s->main_exec_status.code);
unit_serialize_item_format(u, f, "main-exec-status-status", "%i", s->main_exec_status.status);
}
log_debug("Failed to parse main-exec-status-status value %s", value);
else
s->main_exec_status.status = i;
- } else if (streq(key, "main-exec-status-start-realtime")) {
- uint64_t k;
-
- if (safe_atou64(value, &k) < 0)
- log_debug("Failed to parse main-exec-status-start-realtime value %s", value);
- else
- s->main_exec_status.start_timestamp.realtime = (usec_t) k;
- } else if (streq(key, "main-exec-status-start-monotonic")) {
- uint64_t k;
-
- if (safe_atou64(value, &k) < 0)
- log_debug("Failed to parse main-exec-status-start-monotonic value %s", value);
- else
- s->main_exec_status.start_timestamp.monotonic = (usec_t) k;
- } else if (streq(key, "main-exec-status-exit-realtime")) {
- uint64_t k;
-
- if (safe_atou64(value, &k) < 0)
- log_debug("Failed to parse main-exec-status-exit-realtime value %s", value);
- else
- s->main_exec_status.exit_timestamp.realtime = (usec_t) k;
- } else if (streq(key, "main-exec-status-exit-monotonic")) {
- uint64_t k;
-
- if (safe_atou64(value, &k) < 0)
- log_debug("Failed to parse main-exec-status-exit-monotonic value %s", value);
- else
- s->main_exec_status.exit_timestamp.monotonic = (usec_t) k;
- } else
+ } else if (streq(key, "main-exec-status-start"))
+ dual_timestamp_deserialize(value, &s->main_exec_status.start_timestamp);
+ else if (streq(key, "main-exec-status-exit"))
+ dual_timestamp_deserialize(value, &s->main_exec_status.exit_timestamp);
+ else
log_debug("Unknown serialization key '%s'", key);
return 0;
if (s->main_pid == pid) {
s->main_pid = 0;
- exec_status_exit(&s->main_exec_status, pid, code, status);
+ exec_status_exit(&s->main_exec_status, pid, code, status, s->exec_context.utmp_id);
if (s->type != SERVICE_FORKING && s->control_command) {
s->control_command->exec_status = s->main_exec_status;
s->control_pid = 0;
if (s->control_command) {
- exec_status_exit(&s->control_command->exec_status, pid, code, status);
+ exec_status_exit(&s->control_command->exec_status, pid, code, status, s->exec_context.utmp_id);
if (s->control_command->ignore)
success = true;
* START_POST script */
if (success) {
- if (s->pid_file)
- service_load_pid_file(s);
+ service_load_pid_file(s);
+ service_search_main_pid(s);
service_enter_start_post(s);
} else
if (service->meta.fragment_path)
continue;
- if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
+ if ((r = unit_add_two_dependencies_by_name(service, UNIT_BEFORE, UNIT_CONFLICTS, SPECIAL_SHUTDOWN_TARGET, NULL, true)) < 0)
goto finish;
}
s->failure = false;
}
+static int service_kill(Unit *u, KillWho who, KillMode mode, int signo, DBusError *error) {
+ Service *s = SERVICE(u);
+ int r = 0;
+ Set *pid_set = NULL;
+
+ assert(s);
+
+ if (s->main_pid <= 0 && who == KILL_MAIN) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No main process to kill");
+ return -EINVAL;
+ }
+
+ if (s->control_pid <= 0 && who == KILL_CONTROL) {
+ dbus_set_error(error, BUS_ERROR_NO_SUCH_PROCESS, "No control process to kill");
+ return -ENOENT;
+ }
+
+ if (s->control_pid > 0)
+ if (kill(mode == KILL_PROCESS_GROUP ? -s->control_pid : 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 (mode == KILL_CONTROL_GROUP) {
+ int q;
+
+ if (!(pid_set = set_new(trivial_hash_func, trivial_compare_func)))
+ 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) {
+ r = q;
+ goto finish;
+ }
+
+ if (s->main_pid > 0)
+ if ((q = set_put(pid_set, LONG_TO_PTR(s->main_pid))) < 0) {
+ r = q;
+ goto finish;
+ }
+
+ if ((q = cgroup_bonding_kill_list(s->meta.cgroup_bondings, signo, pid_set)) < 0)
+ if (r != -EAGAIN && r != -ESRCH && r != -ENOENT)
+ r = q;
+ }
+
+finish:
+ if (pid_set)
+ set_free(pid_set);
+
+ return r;
+}
+
static const char* const service_state_table[_SERVICE_STATE_MAX] = {
[SERVICE_DEAD] = "dead",
[SERVICE_START_PRE] = "start-pre",
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
- [SERVICE_RESTART_ALWAYS] = "always",
+ [SERVICE_RESTART_ON_FAILURE] = "on-failure",
+ [SERVICE_RESTART_ON_ABORT] = "on-abort",
+ [SERVICE_RESTART_ALWAYS] = "always"
};
DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
.can_reload = service_can_reload,
+ .kill = service_kill,
+
.serialize = service_serialize,
.deserialize_item = service_deserialize_item,