#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
static void service_connection_unref(Service *s) {
assert(s);
- if (!s->socket)
+ if (!s->accept_socket)
return;
- socket_connection_unref(s->socket);
- s->socket = NULL;
+ socket_connection_unref(s->accept_socket);
+ s->accept_socket = NULL;
}
static void service_done(Unit *u) {
service_close_socket_fd(s);
service_connection_unref(s);
+ set_free(s->configured_sockets);
+
unit_unwatch_timer(u, &s->timer_watch);
}
return r;
}
-static int sysv_translate_facility(const char *name, char **_r) {
+static int sysv_translate_facility(const char *name, const char *filename, char **_r) {
/* We silently ignore the $ prefix here. According to the LSB
* spec it simply indicates whether something is a
"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,
unsigned i;
char *r;
+ const char *n;
- for (i = 0; i < ELEMENTSOF(table); i += 2)
+ assert(name);
+ assert(_r);
- if (streq(table[i], name) ||
- (*name == '$' && streq(table[i], name+1))) {
+ n = *name == '$' ? name + 1 : name;
- if (!table[i+1])
- return 0;
+ for (i = 0; i < ELEMENTSOF(table); i += 2) {
- if (!(r = strdup(table[i+1])))
- return -ENOMEM;
+ if (!streq(table[i], n))
+ continue;
- goto finish;
- }
+ if (!table[i+1])
+ return 0;
+
+ if (!(r = strdup(table[i+1])))
+ return -ENOMEM;
+
+ goto finish;
+ }
/* 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 == '$')
- r = unit_name_build(name+1, NULL, ".target");
+ /* Facilities starting with $ are most likely targets */
+ r = unit_name_build(n, NULL, ".target");
+ else if (filename && streq(name, filename))
+ /* Names equalling the file name of the services are redundant */
+ return 0;
else
- r = sysv_translate_name(name);
+ /* Everything else we assume to be normal service names */
+ r = sysv_translate_name(n);
if (!r)
return -ENOMEM;
goto finish;
}
- if (streq(n, file_name_from_path(path))) {
- free(n);
- continue;
- }
-
- r = sysv_translate_facility(n, &m);
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
free(n);
if (r < 0)
goto finish;
}
- if (streq(n, file_name_from_path(path))) {
- free(n);
- continue;
- }
-
- r = sysv_translate_facility(n, &m);
+ r = sysv_translate_facility(n, file_name_from_path(path), &m);
free(n);
if (r < 0)
/* 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;
s->remain_after_exit = true;
- s->restart = SERVICE_ONCE;
+ 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 : EXEC_OUTPUT_NULL;
}
#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);
}
/* 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;
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. */
static int service_notify_sockets_dead(Service *s) {
Iterator i;
- Set *set;
+ Set *set, *free_set = NULL;
Socket *sock;
int r;
assert(s);
+ /* Notifies all our sockets when we die */
+
if (s->socket_fd >= 0)
return 0;
- /* Notifies all our sockets when we die */
- if ((r = service_get_sockets(s, &set)) < 0)
- return r;
+ 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)
socket_notify_service_dead(sock);
- set_free(set);
+ set_free(free_set);
return 0;
}
int r;
int *rfds = NULL;
unsigned rn_fds = 0;
- Set *set;
+ Set *set, *free_set = NULL;
Socket *sock;
assert(s);
if (s->socket_fd >= 0)
return 0;
- if ((r = service_get_sockets(s, &set)) < 0)
- return r;
+ 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) {
int *cfds;
*fds = rfds;
*n_fds = rn_fds;
- set_free(set);
+ set_free(free_set);
return 0;
goto fail;
}
- if (!(our_env = new0(char*, 3))) {
+ if (!(our_env = new0(char*, 4))) {
r = -ENOMEM;
goto fail;
}
goto fail;
}
+#ifdef HAVE_SYSV_COMPAT
+ /* Make sure we set TERM=linux for SysV scripts, since some
+ * require it to be set from the kernel */
+ if (s->sysv_path && !strv_env_get(s->meta.manager->environment, "TERM"))
+ if (!(our_env[n_env++] = strdup("TERM=linux"))) {
+ r = -ENOMEM;
+ goto fail;
+ }
+#endif
+
if (!(final_env = strv_env_merge(2,
s->meta.manager->environment,
our_env,
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)
return -ECANCELED;
}
- if ((s->exec_context.std_input == EXEC_INPUT_SOCKET ||
- s->exec_context.std_output == EXEC_OUTPUT_SOCKET ||
- s->exec_context.std_error == EXEC_OUTPUT_SOCKET) &&
- s->socket_fd < 0) {
- log_warning("%s can only be started with a per-connection socket.", u->meta.id);
- return -EINVAL;
- }
-
s->failure = false;
s->main_pid_known = false;
s->forbid_restart = false;
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->socket_fd = fd;
s->got_socket_fd = true;
- s->socket = sock;
+ s->accept_socket = sock;
return 0;
}
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",
DEFINE_STRING_TABLE_LOOKUP(service_state, ServiceState);
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
- [SERVICE_ONCE] = "once",
- [SERVICE_RESTART_ON_SUCCESS] = "restart-on-success",
- [SERVICE_RESTART_ALWAYS] = "restart-always",
+ [SERVICE_RESTART_NO] = "no",
+ [SERVICE_RESTART_ON_SUCCESS] = "on-success",
+ [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,