{ "boot.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
#endif
-#ifdef TARGET_DEBIAN
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU) || defined(TARGET_FRUGALWARE)
/* Debian style rcS.d */
{ "rcS.d", SPECIAL_SYSINIT_TARGET, RUNLEVEL_SYSINIT },
#endif
if (!(r = new(char, strlen(name) + sizeof(".service"))))
return NULL;
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+ if (endswith(name, ".sh"))
+ /* Drop Debian-style .sh suffix */
+ strcpy(stpcpy(r, name) - 3, ".service");
+#endif
+#ifdef TARGET_SUSE
if (startswith(name, "boot."))
/* Drop SuSE-style boot. prefix */
strcpy(stpcpy(r, name + 5), ".service");
- else if (endswith(name, ".sh"))
- /* Drop Debian-style .sh suffix */
- strcpy(stpcpy(r, name) - 3, ".service");
-#ifdef TARGET_ARCH
- else if (startswith(name, "@"))
- /* Drop Arch-style background prefix */
- strcpy(stpcpy(r, name + 1), ".service");
+#endif
+#ifdef TARGET_FRUGALWARE
+ if (startswith(name, "rc."))
+ /* Drop Frugalware-style rc. prefix */
+ strcpy(stpcpy(r, name + 3), ".service");
#endif
else
/* Normal init scripts */
"syslog", SPECIAL_SYSLOG_TARGET,
"time", SPECIAL_RTC_SET_TARGET,
- /* Debian extensions */
-#ifdef TARGET_DEBIAN
- "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
-#endif
+ /* common extensions */
"mail-transfer-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
"x-display-manager", SPECIAL_DISPLAY_MANAGER_SERVICE,
+ "null", NULL,
+
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+ "mail-transport-agent", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+#endif
#ifdef TARGET_FEDORA
- /* Fedora extensions */
"MTA", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
"smtpdaemon", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
"httpd", SPECIAL_HTTP_DAEMON_TARGET,
#endif
- /* SuSE extensions */
- "null", NULL
-
+#ifdef TARGET_SUSE
+ "smtp", SPECIAL_MAIL_TRANSFER_AGENT_TARGET,
+#endif
};
unsigned i;
}
/* 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 == '$') {
+ if (!unit_prefix_is_valid(n))
+ return -EINVAL;
- if (*name == '$')
/* Facilities starting with $ are most likely targets */
r = unit_name_build(n, NULL, ".target");
- else if (filename && streq(name, filename))
+ } else if (filename && streq(name, filename))
/* Names equalling the file name of the services are redundant */
return 0;
else
}
r = sysv_translate_facility(n, file_name_from_path(path), &m);
- free(n);
- if (r < 0)
- goto finish;
+ if (r < 0) {
+ log_error("[%s:%u] Failed to translate LSB dependency %s, ignoring: %s", path, line, n, strerror(-r));
+ free(n);
+ continue;
+ }
+
+ free(n);
if (r == 0)
continue;
assert(s);
assert(name);
- /* For SysV services we strip the boot. or .sh
+ /* For SysV services we strip the boot.*, rc.* and *.sh
* prefixes/suffixes. */
- if (startswith(name, "boot.") ||
- endswith(name, ".sh.service"))
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
+ if (endswith(name, ".sh.service"))
+ return -ENOENT;
+#endif
+
+#ifdef TARGET_SUSE
+ if (startswith(name, "boot."))
+ return -ENOENT;
+#endif
+
+#ifdef TARGET_FRUGALWARE
+ if (startswith(name, "rc."))
return -ENOENT;
+#endif
STRV_FOREACH(p, s->meta.manager->lookup_paths.sysvinit_path) {
char *path;
r = service_load_sysv_path(s, path);
+#if defined(TARGET_DEBIAN) || defined(TARGET_UBUNTU)
if (r >= 0 && s->meta.load_state == UNIT_STUB) {
- /* Try Debian style xxx.sh source'able init scripts */
+ /* Try Debian style *.sh source'able init scripts */
strcat(path, ".sh");
r = service_load_sysv_path(s, path);
}
-
+#endif
free(path);
+#ifdef TARGET_SUSE
if (r >= 0 && s->meta.load_state == UNIT_STUB) {
- /* Try SUSE style boot.xxx init scripts */
+ /* Try SUSE style boot.* init scripts */
if (asprintf(&path, "%s/boot.%s", *p, name) < 0)
return -ENOMEM;
+ /* Drop .service suffix */
+ path[strlen(path)-8] = 0;
+ r = service_load_sysv_path(s, path);
+ free(path);
+ }
+#endif
+
+#ifdef TARGET_FRUGALWARE
+ if (r >= 0 && s->meta.load_state == UNIT_STUB) {
+ /* Try Frugalware style rc.* init scripts */
+
+ if (asprintf(&path, "%s/rc.%s", *p, name) < 0)
+ return -ENOMEM;
+
+ /* Drop .service suffix */
path[strlen(path)-8] = 0;
r = service_load_sysv_path(s, path);
free(path);
}
+#endif
if (r < 0)
return r;
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) {
if ((r = unit_add_exec_dependencies(u, &s->exec_context)) < 0)
return r;
- if ((r = unit_add_default_cgroup(u)) < 0)
+ if ((r = unit_add_default_cgroups(u)) < 0)
return r;
#ifdef HAVE_SYSV_COMPAT
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;
}
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;
* 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
break;
case SERVICE_STOP_SIGTERM:
- log_warning("%s stopping timed out. Killing.", u->meta.id);
- service_enter_signal(s, SERVICE_STOP_SIGKILL, false);
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out. Killing.", u->meta.id);
+ service_enter_signal(s, SERVICE_STOP_SIGKILL, false);
+ } else {
+ log_warning("%s stopping timed out. Skipping SIGKILL.", u->meta.id);
+ service_enter_stop_post(s, false);
+ }
+
break;
case SERVICE_STOP_SIGKILL:
break;
case SERVICE_FINAL_SIGTERM:
- log_warning("%s stopping timed out (2). Killing.", u->meta.id);
- service_enter_signal(s, SERVICE_FINAL_SIGKILL, false);
+ if (s->exec_context.send_sigkill) {
+ log_warning("%s stopping timed out (2). Killing.", u->meta.id);
+ service_enter_signal(s, SERVICE_FINAL_SIGKILL, false);
+ } else {
+ log_warning("%s stopping timed out (2). Skipping SIGKILL. Entering failed mode.", u->meta.id);
+ service_enter_dead(s, false, true);
+ }
+
break;
case SERVICE_FINAL_SIGKILL:
Unit *service;
Iterator j;
int r;
-#ifdef TARGET_ARCH
- Unit *previous = NULL;
- char *arch_daemons = NULL;
- char *arch_daemons_stripped = NULL;
- char **arch_daemons_split = NULL;
-#endif
assert(m);
-#ifdef TARGET_ARCH
- if ((r = parse_env_file("/etc/rc.conf", NEWLINE,
- "DAEMONS", &arch_daemons,
- NULL)) < 0) {
-
- if (r != -ENOENT)
- log_warning("Failed to read /etc/rc.conf: %s", strerror(-r));
-
- } else if (arch_daemons) {
- if (!(arch_daemons_stripped = strchr(arch_daemons, '(')))
- arch_daemons_stripped = arch_daemons;
- else
- arch_daemons_stripped++; /* strip start paren */
-
- arch_daemons_stripped[strcspn(arch_daemons_stripped, ")")] = 0; /* strip end paren */
-
- if (!(arch_daemons_split = strv_split_quoted(arch_daemons_stripped))) {
- r = -ENOMEM;
- goto finish;
- }
-
- STRV_FOREACH(p, arch_daemons_split) {
-
- free(name);
- name = NULL;
-
- if (**p == '!') /* daemons prefixed with ! are disabled, so ignore them */
- continue;
-
- if (!(name = sysv_translate_name(*p))) {
- r = -ENOMEM;
- goto finish;
- }
-
- if ((r = manager_load_unit_prepare(m, name, NULL, NULL, &service)) < 0) {
- log_warning("Failed to prepare unit %s: %s", name, strerror(-r));
- continue;
- }
-
- if ((r = unit_add_two_dependencies_by_name_inverse(service, UNIT_AFTER, UNIT_WANTS, "multi-user.target", NULL, true)) < 0)
- goto finish;
-
- if (previous)
- if ((r = unit_add_dependency(service, UNIT_AFTER, previous, true)) < 0)
- goto finish;
-
- if (**p != '@') /* daemons prefixed with @ can be started in the background */
- previous = service;
- }
- }
-#endif
-
zero(runlevel_services);
STRV_FOREACH(p, m->lookup_paths.sysvrcnd_path)
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;
}
free(path);
free(fpath);
free(name);
-#ifdef TARGET_ARCH
- free(arch_daemons);
- free(arch_daemons_split);
-#endif
for (i = 0; i < ELEMENTSOF(rcnd_table); i++)
set_free(runlevel_services[i]);
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",
.can_reload = service_can_reload,
+ .kill = service_kill,
+
.serialize = service_serialize,
.deserialize_item = service_deserialize_item,