1
0
mirror of https://github.com/systemd/systemd.git synced 2024-10-30 23:21:22 +03:00

Merge pull request #20123 from keszybz/extended-job-status

Nested job status for systemd+user service managers
This commit is contained in:
Luca Boccassi 2021-07-21 11:39:10 +01:00 committed by GitHub
commit cac38a9803
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 801 additions and 720 deletions

View File

@ -1065,11 +1065,11 @@ static bool automount_supported(void) {
}
static const char* const automount_result_table[_AUTOMOUNT_RESULT_MAX] = {
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources",
[AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[AUTOMOUNT_SUCCESS] = "success",
[AUTOMOUNT_FAILURE_RESOURCES] = "resources",
[AUTOMOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[AUTOMOUNT_FAILURE_MOUNT_START_LIMIT_HIT] = "mount-start-limit-hit",
[AUTOMOUNT_FAILURE_UNMOUNTED] = "unmounted",
[AUTOMOUNT_FAILURE_UNMOUNTED] = "unmounted",
};
DEFINE_STRING_TABLE_LOOKUP(automount_result, AutomountResult);

View File

@ -1511,44 +1511,44 @@ static const char* const job_state_table[_JOB_STATE_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(job_state, JobState);
static const char* const job_type_table[_JOB_TYPE_MAX] = {
[JOB_START] = "start",
[JOB_VERIFY_ACTIVE] = "verify-active",
[JOB_STOP] = "stop",
[JOB_RELOAD] = "reload",
[JOB_START] = "start",
[JOB_VERIFY_ACTIVE] = "verify-active",
[JOB_STOP] = "stop",
[JOB_RELOAD] = "reload",
[JOB_RELOAD_OR_START] = "reload-or-start",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
[JOB_TRY_RELOAD] = "try-reload",
[JOB_NOP] = "nop",
[JOB_RESTART] = "restart",
[JOB_TRY_RESTART] = "try-restart",
[JOB_TRY_RELOAD] = "try-reload",
[JOB_NOP] = "nop",
};
DEFINE_STRING_TABLE_LOOKUP(job_type, JobType);
static const char* const job_mode_table[_JOB_MODE_MAX] = {
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
[JOB_FAIL] = "fail",
[JOB_REPLACE] = "replace",
[JOB_REPLACE_IRREVERSIBLY] = "replace-irreversibly",
[JOB_ISOLATE] = "isolate",
[JOB_FLUSH] = "flush",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
[JOB_TRIGGERING] = "triggering",
[JOB_ISOLATE] = "isolate",
[JOB_FLUSH] = "flush",
[JOB_IGNORE_DEPENDENCIES] = "ignore-dependencies",
[JOB_IGNORE_REQUIREMENTS] = "ignore-requirements",
[JOB_TRIGGERING] = "triggering",
};
DEFINE_STRING_TABLE_LOOKUP(job_mode, JobMode);
static const char* const job_result_table[_JOB_RESULT_MAX] = {
[JOB_DONE] = "done",
[JOB_CANCELED] = "canceled",
[JOB_TIMEOUT] = "timeout",
[JOB_FAILED] = "failed",
[JOB_DEPENDENCY] = "dependency",
[JOB_SKIPPED] = "skipped",
[JOB_INVALID] = "invalid",
[JOB_ASSERT] = "assert",
[JOB_DONE] = "done",
[JOB_CANCELED] = "canceled",
[JOB_TIMEOUT] = "timeout",
[JOB_FAILED] = "failed",
[JOB_DEPENDENCY] = "dependency",
[JOB_SKIPPED] = "skipped",
[JOB_INVALID] = "invalid",
[JOB_ASSERT] = "assert",
[JOB_UNSUPPORTED] = "unsupported",
[JOB_COLLECTED] = "collected",
[JOB_ONCE] = "once",
[JOB_COLLECTED] = "collected",
[JOB_ONCE] = "once",
};
DEFINE_STRING_TABLE_LOOKUP(job_result, JobResult);

View File

@ -55,6 +55,7 @@
#include "machine-id-setup.h"
#include "manager.h"
#include "manager-dump.h"
#include "manager-serialize.h"
#include "mkdir.h"
#include "mount-setup.h"
#include "os-util.h"

View File

@ -0,0 +1,536 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "clean-ipc.h"
#include "dbus.h"
#include "fd-util.h"
#include "fileio.h"
#include "format-util.h"
#include "macro.h"
#include "manager-serialize.h"
#include "manager.h"
#include "parse-util.h"
#include "serialize.h"
#include "syslog-util.h"
#include "unit-serialize.h"
#include "user-util.h"
int manager_open_serialization(Manager *m, FILE **ret_f) {
_cleanup_close_ int fd = -1;
FILE *f;
assert(ret_f);
fd = open_serialization_fd("systemd-state");
if (fd < 0)
return fd;
f = take_fdopen(&fd, "w+");
if (!f)
return -errno;
*ret_f = f;
return 0;
}
static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
if (!in_initrd())
return true;
/* The following timestamps only apply to the host system, hence only serialize them there */
return !IN_SET(t,
MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
MANAGER_TIMESTAMP_GENERATORS_START, MANAGER_TIMESTAMP_GENERATORS_FINISH,
MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
}
static void manager_serialize_uid_refs_internal(
FILE *f,
Hashmap *uid_refs,
const char *field_name) {
void *p, *k;
assert(f);
assert(field_name);
/* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as
* the actual counter of it is better rebuild after a reload/reexec. */
HASHMAP_FOREACH_KEY(p, k, uid_refs) {
uint32_t c;
uid_t uid;
uid = PTR_TO_UID(k);
c = PTR_TO_UINT32(p);
if (!(c & DESTROY_IPC_FLAG))
continue;
(void) serialize_item_format(f, field_name, UID_FMT, uid);
}
}
static void manager_serialize_uid_refs(Manager *m, FILE *f) {
manager_serialize_uid_refs_internal(f, m->uid_refs, "destroy-ipc-uid");
}
static void manager_serialize_gid_refs(Manager *m, FILE *f) {
manager_serialize_uid_refs_internal(f, m->gid_refs, "destroy-ipc-gid");
}
int manager_serialize(
Manager *m,
FILE *f,
FDSet *fds,
bool switching_root) {
const char *t;
Unit *u;
int r;
assert(m);
assert(f);
assert(fds);
_cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
(void) serialize_item_format(f, "current-job-id", "%" PRIu32, m->current_job_id);
(void) serialize_item_format(f, "n-installed-jobs", "%u", m->n_installed_jobs);
(void) serialize_item_format(f, "n-failed-jobs", "%u", m->n_failed_jobs);
(void) serialize_bool(f, "taint-usr", m->taint_usr);
(void) serialize_bool(f, "ready-sent", m->ready_sent);
(void) serialize_bool(f, "taint-logged", m->taint_logged);
(void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
/* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
(void) serialize_bool(f, "honor-device-enumeration", !switching_root);
if (m->show_status_overridden != _SHOW_STATUS_INVALID)
(void) serialize_item(f, "show-status-overridden",
show_status_to_string(m->show_status_overridden));
if (m->log_level_overridden)
(void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level());
if (m->log_target_overridden)
(void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target()));
(void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]);
(void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]);
(void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]);
for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
_cleanup_free_ char *joined = NULL;
if (!manager_timestamp_shall_serialize(q))
continue;
joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
if (!joined)
return log_oom();
(void) serialize_dual_timestamp(f, joined, m->timestamps + q);
}
if (!switching_root)
(void) serialize_strv(f, "env", m->client_environment);
if (m->notify_fd >= 0) {
r = serialize_fd(f, fds, "notify-fd", m->notify_fd);
if (r < 0)
return r;
(void) serialize_item(f, "notify-socket", m->notify_socket);
}
if (m->cgroups_agent_fd >= 0) {
r = serialize_fd(f, fds, "cgroups-agent-fd", m->cgroups_agent_fd);
if (r < 0)
return r;
}
if (m->user_lookup_fds[0] >= 0) {
int copy0, copy1;
copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
if (copy0 < 0)
return log_error_errno(copy0, "Failed to add user lookup fd to serialization: %m");
copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
if (copy1 < 0)
return log_error_errno(copy1, "Failed to add user lookup fd to serialization: %m");
(void) serialize_item_format(f, "user-lookup", "%i %i", copy0, copy1);
}
bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
if (r < 0)
return r;
manager_serialize_uid_refs(m, f);
manager_serialize_gid_refs(m, f);
r = exec_runtime_serialize(m, f, fds);
if (r < 0)
return r;
(void) fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units) {
if (u->id != t)
continue;
r = unit_serialize(u, f, fds, switching_root);
if (r < 0)
return r;
}
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to flush serialization: %m");
r = bus_fdset_add_all(m, fds);
if (r < 0)
return log_error_errno(r, "Failed to add bus sockets to serialization: %m");
return 0;
}
static int manager_deserialize_one_unit(Manager *m, const char *name, FILE *f, FDSet *fds) {
Unit *u;
int r;
r = manager_load_unit(m, name, NULL, NULL, &u);
if (r < 0) {
if (r == -ENOMEM)
return r;
return log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", name);
}
r = unit_deserialize(u, f, fds);
if (r < 0) {
if (r == -ENOMEM)
return r;
return log_notice_errno(r, "Failed to deserialize unit \"%s\", skipping: %m", name);
}
return 0;
}
static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) {
const char *unit_name;
int r;
for (;;) {
_cleanup_free_ char *line = NULL;
/* Start marker */
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
break;
unit_name = strstrip(line);
r = manager_deserialize_one_unit(m, unit_name, f, fds);
if (r == -ENOMEM)
return r;
if (r < 0) {
r = unit_deserialize_skip(f);
if (r < 0)
return r;
}
}
return 0;
}
static void manager_deserialize_uid_refs_one_internal(
Hashmap** uid_refs,
const char *value) {
uid_t uid;
uint32_t c;
int r;
assert(uid_refs);
assert(value);
r = parse_uid(value, &uid);
if (r < 0 || uid == 0) {
log_debug("Unable to parse UID/GID reference serialization: " UID_FMT, uid);
return;
}
if (hashmap_ensure_allocated(uid_refs, &trivial_hash_ops) < 0) {
log_oom();
return;
}
c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
if (c & DESTROY_IPC_FLAG)
return;
c |= DESTROY_IPC_FLAG;
r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
if (r < 0) {
log_debug_errno(r, "Failed to add UID/GID reference entry: %m");
return;
}
}
static void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
manager_deserialize_uid_refs_one_internal(&m->uid_refs, value);
}
static void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
manager_deserialize_uid_refs_one_internal(&m->gid_refs, value);
}
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int r = 0;
assert(m);
assert(f);
if (DEBUG_LOGGING) {
if (fdset_isempty(fds))
log_debug("No file descriptors passed");
else {
int fd;
FDSET_FOREACH(fd, fds) {
_cleanup_free_ char *fn = NULL;
r = fd_get_path(fd, &fn);
if (r < 0)
log_debug_errno(r, "Received serialized fd %i → %m", fd);
else
log_debug("Received serialized fd %i → %s", fd, strna(fn));
}
}
}
log_debug("Deserializing state...");
/* If we are not in reload mode yet, enter it now. Not that this is recursive, a caller might already have
* increased it to non-zero, which is why we just increase it by one here and down again at the end of this
* call. */
_cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
for (;;) {
_cleanup_free_ char *line = NULL;
const char *val, *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
break;
l = strstrip(line);
if (isempty(l)) /* end marker */
break;
if ((val = startswith(l, "current-job-id="))) {
uint32_t id;
if (safe_atou32(val, &id) < 0)
log_notice("Failed to parse current job id value '%s', ignoring.", val);
else
m->current_job_id = MAX(m->current_job_id, id);
} else if ((val = startswith(l, "n-installed-jobs="))) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
log_notice("Failed to parse installed jobs counter '%s', ignoring.", val);
else
m->n_installed_jobs += n;
} else if ((val = startswith(l, "n-failed-jobs="))) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
log_notice("Failed to parse failed jobs counter '%s', ignoring.", val);
else
m->n_failed_jobs += n;
} else if ((val = startswith(l, "taint-usr="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse taint /usr flag '%s', ignoring.", val);
else
m->taint_usr = m->taint_usr || b;
} else if ((val = startswith(l, "ready-sent="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse ready-sent flag '%s', ignoring.", val);
else
m->ready_sent = m->ready_sent || b;
} else if ((val = startswith(l, "taint-logged="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse taint-logged flag '%s', ignoring.", val);
else
m->taint_logged = m->taint_logged || b;
} else if ((val = startswith(l, "service-watchdogs="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse service-watchdogs flag '%s', ignoring.", val);
else
m->service_watchdogs = b;
} else if ((val = startswith(l, "honor-device-enumeration="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse honor-device-enumeration flag '%s', ignoring.", val);
else
m->honor_device_enumeration = b;
} else if ((val = startswith(l, "show-status-overridden="))) {
ShowStatus s;
s = show_status_from_string(val);
if (s < 0)
log_notice("Failed to parse show-status-overridden flag '%s', ignoring.", val);
else
manager_override_show_status(m, s, "deserialize");
} else if ((val = startswith(l, "log-level-override="))) {
int level;
level = log_level_from_string(val);
if (level < 0)
log_notice("Failed to parse log-level-override value '%s', ignoring.", val);
else
manager_override_log_level(m, level);
} else if ((val = startswith(l, "log-target-override="))) {
LogTarget target;
target = log_target_from_string(val);
if (target < 0)
log_notice("Failed to parse log-target-override value '%s', ignoring.", val);
else
manager_override_log_target(m, target);
} else if ((val = startswith(l, "runtime-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_RUNTIME, t);
} else if ((val = startswith(l, "reboot-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_REBOOT, t);
} else if ((val = startswith(l, "kexec-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_KEXEC, t);
} else if (startswith(l, "env=")) {
r = deserialize_environment(l + 4, &m->client_environment);
if (r < 0)
log_notice_errno(r, "Failed to parse environment entry: \"%s\", ignoring: %m", l);
} else if ((val = startswith(l, "notify-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_notice("Failed to parse notify fd, ignoring: \"%s\"", val);
else {
m->notify_event_source = sd_event_source_disable_unref(m->notify_event_source);
safe_close(m->notify_fd);
m->notify_fd = fdset_remove(fds, fd);
}
} else if ((val = startswith(l, "notify-socket="))) {
r = free_and_strdup(&m->notify_socket, val);
if (r < 0)
return r;
} else if ((val = startswith(l, "cgroups-agent-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_notice("Failed to parse cgroups agent fd, ignoring.: %s", val);
else {
m->cgroups_agent_event_source = sd_event_source_disable_unref(m->cgroups_agent_event_source);
safe_close(m->cgroups_agent_fd);
m->cgroups_agent_fd = fdset_remove(fds, fd);
}
} else if ((val = startswith(l, "user-lookup="))) {
int fd0, fd1;
if (sscanf(val, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
log_notice("Failed to parse user lookup fd, ignoring: %s", val);
else {
m->user_lookup_event_source = sd_event_source_disable_unref(m->user_lookup_event_source);
safe_close_pair(m->user_lookup_fds);
m->user_lookup_fds[0] = fdset_remove(fds, fd0);
m->user_lookup_fds[1] = fdset_remove(fds, fd1);
}
} else if ((val = startswith(l, "dynamic-user=")))
dynamic_user_deserialize_one(m, val, fds);
else if ((val = startswith(l, "destroy-ipc-uid=")))
manager_deserialize_uid_refs_one(m, val);
else if ((val = startswith(l, "destroy-ipc-gid=")))
manager_deserialize_gid_refs_one(m, val);
else if ((val = startswith(l, "exec-runtime=")))
(void) exec_runtime_deserialize_one(m, val, fds);
else if ((val = startswith(l, "subscribed="))) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
return -ENOMEM;
} else {
ManagerTimestamp q;
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
val = startswith(l, manager_timestamp_to_string(q));
if (!val)
continue;
val = startswith(val, "-timestamp=");
if (val)
break;
}
if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
(void) deserialize_dual_timestamp(val, m->timestamps + q);
else if (!startswith(l, "kdbus-fd=")) /* ignore kdbus */
log_notice("Unknown serialization item '%s', ignoring.", l);
}
}
return manager_deserialize_units(m, f, fds);
}

View File

@ -0,0 +1,13 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <stdbool.h>
#include "manager.h"
#include "fdset.h"
#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
int manager_open_serialization(Manager *m, FILE **ret_f);
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);

View File

@ -56,6 +56,7 @@
#include "macro.h"
#include "manager.h"
#include "manager-dump.h"
#include "manager-serialize.h"
#include "memory-util.h"
#include "mkdir.h"
#include "parse-util.h"
@ -66,7 +67,6 @@
#include "rlimit-util.h"
#include "rm-rf.h"
#include "selinux-util.h"
#include "serialize.h"
#include "signal-util.h"
#include "socket-util.h"
#include "special.h"
@ -82,7 +82,6 @@
#include "transaction.h"
#include "umask-util.h"
#include "unit-name.h"
#include "unit-serialize.h"
#include "user-util.h"
#include "virt.h"
#include "watchdog.h"
@ -118,9 +117,19 @@ static int manager_run_generators(Manager *m);
static void manager_vacuum(Manager *m);
static usec_t manager_watch_jobs_next_time(Manager *m) {
return usec_add(now(CLOCK_MONOTONIC),
show_status_on(m->show_status) ? JOBS_IN_PROGRESS_WAIT_USEC :
JOBS_IN_PROGRESS_QUIET_WAIT_USEC);
usec_t timeout;
if (MANAGER_IS_USER(m))
/* Let the user manager without a timeout show status quickly, so the system manager can make
* use of it, if it wants to. */
timeout = JOBS_IN_PROGRESS_WAIT_USEC * 2 / 3;
else if (show_status_on(m->show_status))
/* When status is on, just use the usual timeout. */
timeout = JOBS_IN_PROGRESS_WAIT_USEC;
else
timeout = JOBS_IN_PROGRESS_QUIET_WAIT_USEC;
return usec_add(now(CLOCK_MONOTONIC), timeout);
}
static void manager_watch_jobs_in_progress(Manager *m) {
@ -200,7 +209,6 @@ static void manager_flip_auto_status(Manager *m, bool enable, const char *reason
}
static void manager_print_jobs_in_progress(Manager *m) {
_cleanup_free_ char *job_of_n = NULL;
Job *j;
unsigned counter = 0, print_nr;
char cylon[6 + CYLON_BUFFER_EXTRA + 1];
@ -230,26 +238,51 @@ static void manager_print_jobs_in_progress(Manager *m) {
m->jobs_in_progress_iteration++;
char job_of_n[STRLEN("( of ) ") + DECIMAL_STR_MAX(unsigned)*2] = "";
if (m->n_running_jobs > 1)
if (asprintf(&job_of_n, "(%u of %u) ", counter, m->n_running_jobs) < 0)
job_of_n = NULL;
xsprintf(job_of_n, "(%u of %u) ", counter, m->n_running_jobs);
bool have_timeout = job_get_timeout(j, &x) > 0;
/* We want to use enough information for the user to identify previous lines talking about the same
* unit, but keep the message as short as possible. So if 'Starting foo.service' or 'Starting
* foo.service (Description)' were used, 'foo.service' is enough here. On the other hand, if we used
* foo.service - Description' were used, 'foo.service' is enough here. On the other hand, if we used
* 'Starting Description' before, then we shall also use 'Description' here. So we pass NULL as the
* second argument to unit_status_string(). */
const char *ident = unit_status_string(j->unit, NULL);
manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon,
"%sA %s job is running for %s (%s / %s)",
strempty(job_of_n),
job_type_to_string(j->type),
ident,
FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC),
have_timeout ? FORMAT_TIMESPAN(x - j->begin_usec, 1*USEC_PER_SEC) : "no limit");
const char *time = FORMAT_TIMESPAN(now(CLOCK_MONOTONIC) - j->begin_usec, 1*USEC_PER_SEC);
const char *limit = have_timeout ? FORMAT_TIMESPAN(x - j->begin_usec, 1*USEC_PER_SEC) : "no limit";
if (m->status_unit_format == STATUS_UNIT_FORMAT_DESCRIPTION)
/* When using 'Description', we effectively don't have enough space to show the nested status
* without ellipsization, so let's not even try. */
manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon,
"%sA %s job is running for %s (%s / %s)",
job_of_n,
job_type_to_string(j->type),
ident,
time, limit);
else {
const char *status_text = unit_status_text(j->unit);
manager_status_printf(m, STATUS_TYPE_EPHEMERAL, cylon,
"%sJob %s/%s running (%s / %s)%s%s",
job_of_n,
ident,
job_type_to_string(j->type),
time, limit,
status_text ? ": " : "",
strempty(status_text));
}
sd_notifyf(false,
"STATUS=%sUser job %s/%s running (%s / %s)...",
job_of_n,
ident,
job_type_to_string(j->type),
time, limit);
m->status_ready = false;
}
static int have_ask_password(void) {
@ -621,9 +654,7 @@ static char** sanitize_environment(char **l) {
NULL);
/* Let's order the environment alphabetically, just to make it pretty */
strv_sort(l);
return l;
return strv_sort(l);
}
int manager_default_environment(Manager *m) {
@ -1686,11 +1717,11 @@ static void manager_ready(Manager *m) {
m->honor_device_enumeration = true;
}
static Manager* manager_reloading_start(Manager *m) {
Manager* manager_reloading_start(Manager *m) {
m->n_reloading++;
return m;
}
static void manager_reloading_stopp(Manager **m) {
void manager_reloading_stopp(Manager **m) {
if (*m) {
assert((*m)->n_reloading > 0);
(*m)->n_reloading--;
@ -2616,15 +2647,19 @@ turn_off:
return 0;
}
static void manager_start_target(Manager *m, const char *name, JobMode mode) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
int r;
static void manager_start_special(Manager *m, const char *name, JobMode mode) {
Job *job;
log_debug("Activating special unit %s", name);
if (manager_add_job_by_name_and_warn(m, JOB_START, name, mode, NULL, &job) < 0)
return;
r = manager_add_job_by_name(m, JOB_START, name, mode, NULL, &error, NULL);
if (r < 0)
log_error("Failed to enqueue %s job: %s", name, bus_error_message(&error, r));
const char *s = unit_status_string(job->unit, NULL);
log_info("Activating special unit %s...", s);
sd_notifyf(false,
"STATUS=Activating special unit %s...", s);
m->status_ready = false;
}
static void manager_handle_ctrl_alt_del(Manager *m) {
@ -2633,7 +2668,7 @@ static void manager_handle_ctrl_alt_del(Manager *m) {
* unless it was disabled in system.conf */
if (ratelimit_below(&m->ctrl_alt_del_ratelimit) || m->cad_burst_action == EMERGENCY_ACTION_NONE)
manager_start_target(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
manager_start_special(m, SPECIAL_CTRL_ALT_DEL_TARGET, JOB_REPLACE_IRREVERSIBLY);
else
emergency_action(m, m->cad_burst_action, EMERGENCY_ACTION_WARN, NULL, -1,
"Ctrl-Alt-Del was pressed more than 7 times within 2s");
@ -2697,21 +2732,20 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
if (MANAGER_IS_SYSTEM(m))
manager_handle_ctrl_alt_del(m);
else
manager_start_target(m, SPECIAL_EXIT_TARGET,
JOB_REPLACE_IRREVERSIBLY);
manager_start_special(m, SPECIAL_EXIT_TARGET, JOB_REPLACE_IRREVERSIBLY);
break;
case SIGWINCH:
/* This is a nop on non-init */
if (MANAGER_IS_SYSTEM(m))
manager_start_target(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
manager_start_special(m, SPECIAL_KBREQUEST_TARGET, JOB_REPLACE);
break;
case SIGPWR:
/* This is a nop on non-init */
if (MANAGER_IS_SYSTEM(m))
manager_start_target(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
manager_start_special(m, SPECIAL_SIGPWR_TARGET, JOB_REPLACE);
break;
@ -2723,10 +2757,8 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
if (MANAGER_IS_SYSTEM(m))
(void) bus_init_system(m);
} else {
log_info("Starting D-Bus service...");
manager_start_target(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
}
} else
manager_start_special(m, SPECIAL_DBUS_SERVICE, JOB_REPLACE);
break;
@ -2777,8 +2809,7 @@ static int manager_dispatch_signal_fd(sd_event_source *source, int fd, uint32_t
if ((int) sfsi.ssi_signo >= SIGRTMIN+0 &&
(int) sfsi.ssi_signo < SIGRTMIN+(int) ELEMENTSOF(target_table)) {
int idx = (int) sfsi.ssi_signo - SIGRTMIN;
manager_start_target(m, target_table[idx].target,
target_table[idx].mode);
manager_start_special(m, target_table[idx].target, target_table[idx].mode);
break;
}
@ -3143,10 +3174,8 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
return;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0) {
log_oom();
return;
}
if (asprintf(&message, "U\002%c%s%n", (int) (strlen(u->id) + 1), u->id, &n) < 0)
return (void) log_oom();
errno = 0;
if (write(fd, message, n + 1) != n + 1)
@ -3154,242 +3183,6 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
log_error_errno(errno, "Failed to write Plymouth message: %m");
}
int manager_open_serialization(Manager *m, FILE **_f) {
_cleanup_close_ int fd = -1;
FILE *f;
assert(_f);
fd = open_serialization_fd("systemd-state");
if (fd < 0)
return fd;
f = take_fdopen(&fd, "w+");
if (!f)
return -errno;
*_f = f;
return 0;
}
static bool manager_timestamp_shall_serialize(ManagerTimestamp t) {
if (!in_initrd())
return true;
/* The following timestamps only apply to the host system, hence only serialize them there */
return !IN_SET(t,
MANAGER_TIMESTAMP_USERSPACE, MANAGER_TIMESTAMP_FINISH,
MANAGER_TIMESTAMP_SECURITY_START, MANAGER_TIMESTAMP_SECURITY_FINISH,
MANAGER_TIMESTAMP_GENERATORS_START, MANAGER_TIMESTAMP_GENERATORS_FINISH,
MANAGER_TIMESTAMP_UNITS_LOAD_START, MANAGER_TIMESTAMP_UNITS_LOAD_FINISH);
}
#define DESTROY_IPC_FLAG (UINT32_C(1) << 31)
static void manager_serialize_uid_refs_internal(
FILE *f,
Hashmap *uid_refs,
const char *field_name) {
void *p, *k;
assert(f);
assert(field_name);
/* Serialize the UID reference table. Or actually, just the IPC destruction flag of it, as
* the actual counter of it is better rebuild after a reload/reexec. */
HASHMAP_FOREACH_KEY(p, k, uid_refs) {
uint32_t c;
uid_t uid;
uid = PTR_TO_UID(k);
c = PTR_TO_UINT32(p);
if (!(c & DESTROY_IPC_FLAG))
continue;
(void) serialize_item_format(f, field_name, UID_FMT, uid);
}
}
static void manager_serialize_uid_refs(Manager *m, FILE *f) {
manager_serialize_uid_refs_internal(f, m->uid_refs, "destroy-ipc-uid");
}
static void manager_serialize_gid_refs(Manager *m, FILE *f) {
manager_serialize_uid_refs_internal(f, m->gid_refs, "destroy-ipc-gid");
}
int manager_serialize(
Manager *m,
FILE *f,
FDSet *fds,
bool switching_root) {
const char *t;
Unit *u;
int r;
assert(m);
assert(f);
assert(fds);
_cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
(void) serialize_item_format(f, "current-job-id", "%" PRIu32, m->current_job_id);
(void) serialize_item_format(f, "n-installed-jobs", "%u", m->n_installed_jobs);
(void) serialize_item_format(f, "n-failed-jobs", "%u", m->n_failed_jobs);
(void) serialize_bool(f, "taint-usr", m->taint_usr);
(void) serialize_bool(f, "ready-sent", m->ready_sent);
(void) serialize_bool(f, "taint-logged", m->taint_logged);
(void) serialize_bool(f, "service-watchdogs", m->service_watchdogs);
/* After switching root, udevd has not been started yet. So, enumeration results should not be emitted. */
(void) serialize_bool(f, "honor-device-enumeration", !switching_root);
if (m->show_status_overridden != _SHOW_STATUS_INVALID)
(void) serialize_item(f, "show-status-overridden",
show_status_to_string(m->show_status_overridden));
if (m->log_level_overridden)
(void) serialize_item_format(f, "log-level-override", "%i", log_get_max_level());
if (m->log_target_overridden)
(void) serialize_item(f, "log-target-override", log_target_to_string(log_get_target()));
(void) serialize_usec(f, "runtime-watchdog-overridden", m->watchdog_overridden[WATCHDOG_RUNTIME]);
(void) serialize_usec(f, "reboot-watchdog-overridden", m->watchdog_overridden[WATCHDOG_REBOOT]);
(void) serialize_usec(f, "kexec-watchdog-overridden", m->watchdog_overridden[WATCHDOG_KEXEC]);
for (ManagerTimestamp q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
_cleanup_free_ char *joined = NULL;
if (!manager_timestamp_shall_serialize(q))
continue;
joined = strjoin(manager_timestamp_to_string(q), "-timestamp");
if (!joined)
return log_oom();
(void) serialize_dual_timestamp(f, joined, m->timestamps + q);
}
if (!switching_root)
(void) serialize_strv(f, "env", m->client_environment);
if (m->notify_fd >= 0) {
r = serialize_fd(f, fds, "notify-fd", m->notify_fd);
if (r < 0)
return r;
(void) serialize_item(f, "notify-socket", m->notify_socket);
}
if (m->cgroups_agent_fd >= 0) {
r = serialize_fd(f, fds, "cgroups-agent-fd", m->cgroups_agent_fd);
if (r < 0)
return r;
}
if (m->user_lookup_fds[0] >= 0) {
int copy0, copy1;
copy0 = fdset_put_dup(fds, m->user_lookup_fds[0]);
if (copy0 < 0)
return log_error_errno(copy0, "Failed to add user lookup fd to serialization: %m");
copy1 = fdset_put_dup(fds, m->user_lookup_fds[1]);
if (copy1 < 0)
return log_error_errno(copy1, "Failed to add user lookup fd to serialization: %m");
(void) serialize_item_format(f, "user-lookup", "%i %i", copy0, copy1);
}
bus_track_serialize(m->subscribed, f, "subscribed");
r = dynamic_user_serialize(m, f, fds);
if (r < 0)
return r;
manager_serialize_uid_refs(m, f);
manager_serialize_gid_refs(m, f);
r = exec_runtime_serialize(m, f, fds);
if (r < 0)
return r;
(void) fputc('\n', f);
HASHMAP_FOREACH_KEY(u, t, m->units) {
if (u->id != t)
continue;
r = unit_serialize(u, f, fds, switching_root);
if (r < 0)
return r;
}
r = fflush_and_check(f);
if (r < 0)
return log_error_errno(r, "Failed to flush serialization: %m");
r = bus_fdset_add_all(m, fds);
if (r < 0)
return log_error_errno(r, "Failed to add bus sockets to serialization: %m");
return 0;
}
static int manager_deserialize_one_unit(Manager *m, const char *name, FILE *f, FDSet *fds) {
Unit *u;
int r;
r = manager_load_unit(m, name, NULL, NULL, &u);
if (r < 0) {
if (r == -ENOMEM)
return r;
return log_notice_errno(r, "Failed to load unit \"%s\", skipping deserialization: %m", name);
}
r = unit_deserialize(u, f, fds);
if (r < 0) {
if (r == -ENOMEM)
return r;
return log_notice_errno(r, "Failed to deserialize unit \"%s\", skipping: %m", name);
}
return 0;
}
static int manager_deserialize_units(Manager *m, FILE *f, FDSet *fds) {
const char *unit_name;
int r;
for (;;) {
_cleanup_free_ char *line = NULL;
/* Start marker */
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
break;
unit_name = strstrip(line);
r = manager_deserialize_one_unit(m, unit_name, f, fds);
if (r == -ENOMEM)
return r;
if (r < 0) {
r = unit_deserialize_skip(f);
if (r < 0)
return r;
}
}
return 0;
}
usec_t manager_get_watchdog(Manager *m, WatchdogType t) {
assert(m);
@ -3474,294 +3267,6 @@ void manager_retry_runtime_watchdog(Manager *m) {
m->runtime_watchdog_running = true;
}
static void manager_deserialize_uid_refs_one_internal(
Hashmap** uid_refs,
const char *value) {
uid_t uid;
uint32_t c;
int r;
assert(uid_refs);
assert(value);
r = parse_uid(value, &uid);
if (r < 0 || uid == 0) {
log_debug("Unable to parse UID/GID reference serialization: " UID_FMT, uid);
return;
}
if (hashmap_ensure_allocated(uid_refs, &trivial_hash_ops) < 0) {
log_oom();
return;
}
c = PTR_TO_UINT32(hashmap_get(*uid_refs, UID_TO_PTR(uid)));
if (c & DESTROY_IPC_FLAG)
return;
c |= DESTROY_IPC_FLAG;
r = hashmap_replace(*uid_refs, UID_TO_PTR(uid), UINT32_TO_PTR(c));
if (r < 0) {
log_debug_errno(r, "Failed to add UID/GID reference entry: %m");
return;
}
}
static void manager_deserialize_uid_refs_one(Manager *m, const char *value) {
manager_deserialize_uid_refs_one_internal(&m->uid_refs, value);
}
static void manager_deserialize_gid_refs_one(Manager *m, const char *value) {
manager_deserialize_uid_refs_one_internal(&m->gid_refs, value);
}
int manager_deserialize(Manager *m, FILE *f, FDSet *fds) {
int r = 0;
assert(m);
assert(f);
if (DEBUG_LOGGING) {
if (fdset_isempty(fds))
log_debug("No file descriptors passed");
else {
int fd;
FDSET_FOREACH(fd, fds) {
_cleanup_free_ char *fn = NULL;
r = fd_get_path(fd, &fn);
if (r < 0)
log_debug_errno(r, "Received serialized fd %i → %m", fd);
else
log_debug("Received serialized fd %i → %s", fd, strna(fn));
}
}
}
log_debug("Deserializing state...");
/* If we are not in reload mode yet, enter it now. Not that this is recursive, a caller might already have
* increased it to non-zero, which is why we just increase it by one here and down again at the end of this
* call. */
_cleanup_(manager_reloading_stopp) _unused_ Manager *reloading = manager_reloading_start(m);
for (;;) {
_cleanup_free_ char *line = NULL;
const char *val, *l;
r = read_line(f, LONG_LINE_MAX, &line);
if (r < 0)
return log_error_errno(r, "Failed to read serialization line: %m");
if (r == 0)
break;
l = strstrip(line);
if (isempty(l)) /* end marker */
break;
if ((val = startswith(l, "current-job-id="))) {
uint32_t id;
if (safe_atou32(val, &id) < 0)
log_notice("Failed to parse current job id value '%s', ignoring.", val);
else
m->current_job_id = MAX(m->current_job_id, id);
} else if ((val = startswith(l, "n-installed-jobs="))) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
log_notice("Failed to parse installed jobs counter '%s', ignoring.", val);
else
m->n_installed_jobs += n;
} else if ((val = startswith(l, "n-failed-jobs="))) {
uint32_t n;
if (safe_atou32(val, &n) < 0)
log_notice("Failed to parse failed jobs counter '%s', ignoring.", val);
else
m->n_failed_jobs += n;
} else if ((val = startswith(l, "taint-usr="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse taint /usr flag '%s', ignoring.", val);
else
m->taint_usr = m->taint_usr || b;
} else if ((val = startswith(l, "ready-sent="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse ready-sent flag '%s', ignoring.", val);
else
m->ready_sent = m->ready_sent || b;
} else if ((val = startswith(l, "taint-logged="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse taint-logged flag '%s', ignoring.", val);
else
m->taint_logged = m->taint_logged || b;
} else if ((val = startswith(l, "service-watchdogs="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse service-watchdogs flag '%s', ignoring.", val);
else
m->service_watchdogs = b;
} else if ((val = startswith(l, "honor-device-enumeration="))) {
int b;
b = parse_boolean(val);
if (b < 0)
log_notice("Failed to parse honor-device-enumeration flag '%s', ignoring.", val);
else
m->honor_device_enumeration = b;
} else if ((val = startswith(l, "show-status-overridden="))) {
ShowStatus s;
s = show_status_from_string(val);
if (s < 0)
log_notice("Failed to parse show-status-overridden flag '%s', ignoring.", val);
else
manager_override_show_status(m, s, "deserialize");
} else if ((val = startswith(l, "log-level-override="))) {
int level;
level = log_level_from_string(val);
if (level < 0)
log_notice("Failed to parse log-level-override value '%s', ignoring.", val);
else
manager_override_log_level(m, level);
} else if ((val = startswith(l, "log-target-override="))) {
LogTarget target;
target = log_target_from_string(val);
if (target < 0)
log_notice("Failed to parse log-target-override value '%s', ignoring.", val);
else
manager_override_log_target(m, target);
} else if ((val = startswith(l, "runtime-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse runtime-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_RUNTIME, t);
} else if ((val = startswith(l, "reboot-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse reboot-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_REBOOT, t);
} else if ((val = startswith(l, "kexec-watchdog-overridden="))) {
usec_t t;
if (deserialize_usec(val, &t) < 0)
log_notice("Failed to parse kexec-watchdog-overridden value '%s', ignoring.", val);
else
manager_override_watchdog(m, WATCHDOG_KEXEC, t);
} else if (startswith(l, "env=")) {
r = deserialize_environment(l + 4, &m->client_environment);
if (r < 0)
log_notice_errno(r, "Failed to parse environment entry: \"%s\", ignoring: %m", l);
} else if ((val = startswith(l, "notify-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_notice("Failed to parse notify fd, ignoring: \"%s\"", val);
else {
m->notify_event_source = sd_event_source_disable_unref(m->notify_event_source);
safe_close(m->notify_fd);
m->notify_fd = fdset_remove(fds, fd);
}
} else if ((val = startswith(l, "notify-socket="))) {
r = free_and_strdup(&m->notify_socket, val);
if (r < 0)
return r;
} else if ((val = startswith(l, "cgroups-agent-fd="))) {
int fd;
if (safe_atoi(val, &fd) < 0 || fd < 0 || !fdset_contains(fds, fd))
log_notice("Failed to parse cgroups agent fd, ignoring.: %s", val);
else {
m->cgroups_agent_event_source = sd_event_source_disable_unref(m->cgroups_agent_event_source);
safe_close(m->cgroups_agent_fd);
m->cgroups_agent_fd = fdset_remove(fds, fd);
}
} else if ((val = startswith(l, "user-lookup="))) {
int fd0, fd1;
if (sscanf(val, "%i %i", &fd0, &fd1) != 2 || fd0 < 0 || fd1 < 0 || fd0 == fd1 || !fdset_contains(fds, fd0) || !fdset_contains(fds, fd1))
log_notice("Failed to parse user lookup fd, ignoring: %s", val);
else {
m->user_lookup_event_source = sd_event_source_disable_unref(m->user_lookup_event_source);
safe_close_pair(m->user_lookup_fds);
m->user_lookup_fds[0] = fdset_remove(fds, fd0);
m->user_lookup_fds[1] = fdset_remove(fds, fd1);
}
} else if ((val = startswith(l, "dynamic-user=")))
dynamic_user_deserialize_one(m, val, fds);
else if ((val = startswith(l, "destroy-ipc-uid=")))
manager_deserialize_uid_refs_one(m, val);
else if ((val = startswith(l, "destroy-ipc-gid=")))
manager_deserialize_gid_refs_one(m, val);
else if ((val = startswith(l, "exec-runtime=")))
(void) exec_runtime_deserialize_one(m, val, fds);
else if ((val = startswith(l, "subscribed="))) {
if (strv_extend(&m->deserialized_subscribed, val) < 0)
return -ENOMEM;
} else {
ManagerTimestamp q;
for (q = 0; q < _MANAGER_TIMESTAMP_MAX; q++) {
val = startswith(l, manager_timestamp_to_string(q));
if (!val)
continue;
val = startswith(val, "-timestamp=");
if (val)
break;
}
if (q < _MANAGER_TIMESTAMP_MAX) /* found it */
(void) deserialize_dual_timestamp(val, m->timestamps + q);
else if (!startswith(l, "kdbus-fd=")) /* ignore kdbus */
log_notice("Unknown serialization item '%s', ignoring.", l);
}
}
return manager_deserialize_units(m, f, fds);
}
int manager_reload(Manager *m) {
_cleanup_(manager_reloading_stopp) Manager *reloading = NULL;
_cleanup_fdset_free_ FDSet *fds = NULL;
@ -3974,28 +3479,32 @@ static void manager_notify_finished(Manager *m) {
bus_manager_send_finished(m, firmware_usec, loader_usec, kernel_usec, initrd_usec, userspace_usec, total_usec);
sd_notifyf(false,
m->ready_sent ? "STATUS=Startup finished in %s."
: "READY=1\n"
"STATUS=Startup finished in %s.",
FORMAT_TIMESPAN(total_usec, USEC_PER_MSEC));
m->ready_sent = true;
log_taint_string(m);
}
static void manager_send_ready(Manager *m) {
static void user_manager_send_ready(Manager *m) {
assert(m);
/* We send READY=1 on reaching basic.target only when running in --user mode. */
if (!MANAGER_IS_USER(m) || m->ready_sent)
return;
m->ready_sent = true;
sd_notifyf(false,
"READY=1\n"
"STATUS=Reached " SPECIAL_BASIC_TARGET ".");
m->ready_sent = true;
m->status_ready = false;
}
static void manager_send_ready(Manager *m) {
if (m->ready_sent && m->status_ready)
/* Skip the notification if nothing changed. */
return;
sd_notifyf(false,
"%sSTATUS=Ready.",
m->ready_sent ? "READY=1\n" : "");
m->ready_sent = m->status_ready = true;
}
static void manager_check_basic_target(Manager *m) {
@ -4012,7 +3521,7 @@ static void manager_check_basic_target(Manager *m) {
return;
/* For user managers, send out READY=1 as soon as we reach basic.target */
manager_send_ready(m);
user_manager_send_ready(m);
/* Log the taint string as soon as we reach basic.target */
log_taint_string(m);
@ -4043,6 +3552,11 @@ void manager_check_finished(Manager *m) {
if (hashmap_buckets(m->jobs) > hashmap_size(m->units) / 10)
m->jobs = hashmap_free(m->jobs);
manager_send_ready(m);
if (MANAGER_IS_FINISHED(m))
return;
manager_flip_auto_status(m, false, "boot finished");
/* Notify Type=idle units that we are done now */
@ -4057,9 +3571,6 @@ void manager_check_finished(Manager *m) {
/* This is no longer the first boot */
manager_set_first_boot(m, false);
if (MANAGER_IS_FINISHED(m))
return;
dual_timestamp_get(m->timestamps + MANAGER_TIMESTAMP_FINISH);
manager_notify_finished(m);
@ -4929,33 +4440,33 @@ ManagerTimestamp manager_timestamp_initrd_mangle(ManagerTimestamp s) {
static const char *const manager_state_table[_MANAGER_STATE_MAX] = {
[MANAGER_INITIALIZING] = "initializing",
[MANAGER_STARTING] = "starting",
[MANAGER_RUNNING] = "running",
[MANAGER_DEGRADED] = "degraded",
[MANAGER_MAINTENANCE] = "maintenance",
[MANAGER_STOPPING] = "stopping",
[MANAGER_STARTING] = "starting",
[MANAGER_RUNNING] = "running",
[MANAGER_DEGRADED] = "degraded",
[MANAGER_MAINTENANCE] = "maintenance",
[MANAGER_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP(manager_state, ManagerState);
static const char *const manager_timestamp_table[_MANAGER_TIMESTAMP_MAX] = {
[MANAGER_TIMESTAMP_FIRMWARE] = "firmware",
[MANAGER_TIMESTAMP_LOADER] = "loader",
[MANAGER_TIMESTAMP_KERNEL] = "kernel",
[MANAGER_TIMESTAMP_INITRD] = "initrd",
[MANAGER_TIMESTAMP_USERSPACE] = "userspace",
[MANAGER_TIMESTAMP_FINISH] = "finish",
[MANAGER_TIMESTAMP_SECURITY_START] = "security-start",
[MANAGER_TIMESTAMP_SECURITY_FINISH] = "security-finish",
[MANAGER_TIMESTAMP_GENERATORS_START] = "generators-start",
[MANAGER_TIMESTAMP_GENERATORS_FINISH] = "generators-finish",
[MANAGER_TIMESTAMP_UNITS_LOAD_START] = "units-load-start",
[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH] = "units-load-finish",
[MANAGER_TIMESTAMP_INITRD_SECURITY_START] = "initrd-security-start",
[MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH] = "initrd-security-finish",
[MANAGER_TIMESTAMP_INITRD_GENERATORS_START] = "initrd-generators-start",
[MANAGER_TIMESTAMP_FIRMWARE] = "firmware",
[MANAGER_TIMESTAMP_LOADER] = "loader",
[MANAGER_TIMESTAMP_KERNEL] = "kernel",
[MANAGER_TIMESTAMP_INITRD] = "initrd",
[MANAGER_TIMESTAMP_USERSPACE] = "userspace",
[MANAGER_TIMESTAMP_FINISH] = "finish",
[MANAGER_TIMESTAMP_SECURITY_START] = "security-start",
[MANAGER_TIMESTAMP_SECURITY_FINISH] = "security-finish",
[MANAGER_TIMESTAMP_GENERATORS_START] = "generators-start",
[MANAGER_TIMESTAMP_GENERATORS_FINISH] = "generators-finish",
[MANAGER_TIMESTAMP_UNITS_LOAD_START] = "units-load-start",
[MANAGER_TIMESTAMP_UNITS_LOAD_FINISH] = "units-load-finish",
[MANAGER_TIMESTAMP_INITRD_SECURITY_START] = "initrd-security-start",
[MANAGER_TIMESTAMP_INITRD_SECURITY_FINISH] = "initrd-security-finish",
[MANAGER_TIMESTAMP_INITRD_GENERATORS_START] = "initrd-generators-start",
[MANAGER_TIMESTAMP_INITRD_GENERATORS_FINISH] = "initrd-generators-finish",
[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START] = "initrd-units-load-start",
[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_START] = "initrd-units-load-start",
[MANAGER_TIMESTAMP_INITRD_UNITS_LOAD_FINISH] = "initrd-units-load-finish",
};
@ -4963,8 +4474,8 @@ DEFINE_STRING_TABLE_LOOKUP(manager_timestamp, ManagerTimestamp);
static const char* const oom_policy_table[_OOM_POLICY_MAX] = {
[OOM_CONTINUE] = "continue",
[OOM_STOP] = "stop",
[OOM_KILL] = "kill",
[OOM_STOP] = "stop",
[OOM_KILL] = "kill",
};
DEFINE_STRING_TABLE_LOOKUP(oom_policy, OOMPolicy);

View File

@ -329,6 +329,9 @@ struct Manager {
/* Have we already sent out the READY=1 notification? */
bool ready_sent;
/* Was the last status sent "STATUS=Ready."? */
bool status_ready;
/* Have we already printed the taint line if necessary? */
bool taint_logged;
@ -500,12 +503,9 @@ int manager_set_default_rlimits(Manager *m, struct rlimit **default_rlimit);
int manager_loop(Manager *m);
int manager_open_serialization(Manager *m, FILE **_f);
int manager_serialize(Manager *m, FILE *f, FDSet *fds, bool switching_root);
int manager_deserialize(Manager *m, FILE *f, FDSet *fds);
int manager_reload(Manager *m);
Manager* manager_reloading_start(Manager *m);
void manager_reloading_stopp(Manager **m);
void manager_reset_failed(Manager *m);

View File

@ -87,6 +87,8 @@ libcore_sources = '''
locale-setup.h
manager-dump.c
manager-dump.h
manager-serialize.c
manager-serialize.h
manager.c
manager.h
mount.c

View File

@ -2139,7 +2139,7 @@ static int mount_can_clean(Unit *u, ExecCleanMask *ret) {
}
static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_MOUNT] = "ExecMount",
[MOUNT_EXEC_UNMOUNT] = "ExecUnmount",
[MOUNT_EXEC_REMOUNT] = "ExecRemount",
};
@ -2147,14 +2147,14 @@ static const char* const mount_exec_command_table[_MOUNT_EXEC_COMMAND_MAX] = {
DEFINE_STRING_TABLE_LOOKUP(mount_exec_command, MountExecCommand);
static const char* const mount_result_table[_MOUNT_RESULT_MAX] = {
[MOUNT_SUCCESS] = "success",
[MOUNT_FAILURE_RESOURCES] = "resources",
[MOUNT_FAILURE_TIMEOUT] = "timeout",
[MOUNT_FAILURE_EXIT_CODE] = "exit-code",
[MOUNT_FAILURE_SIGNAL] = "signal",
[MOUNT_FAILURE_CORE_DUMP] = "core-dump",
[MOUNT_SUCCESS] = "success",
[MOUNT_FAILURE_RESOURCES] = "resources",
[MOUNT_FAILURE_TIMEOUT] = "timeout",
[MOUNT_FAILURE_EXIT_CODE] = "exit-code",
[MOUNT_FAILURE_SIGNAL] = "signal",
[MOUNT_FAILURE_CORE_DUMP] = "core-dump",
[MOUNT_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[MOUNT_FAILURE_PROTOCOL] = "protocol",
[MOUNT_FAILURE_PROTOCOL] = "protocol",
};
DEFINE_STRING_TABLE_LOOKUP(mount_result, MountResult);

View File

@ -813,19 +813,19 @@ static void path_reset_failed(Unit *u) {
}
static const char* const path_type_table[_PATH_TYPE_MAX] = {
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
[PATH_EXISTS] = "PathExists",
[PATH_EXISTS_GLOB] = "PathExistsGlob",
[PATH_DIRECTORY_NOT_EMPTY] = "DirectoryNotEmpty",
[PATH_CHANGED] = "PathChanged",
[PATH_MODIFIED] = "PathModified",
[PATH_CHANGED] = "PathChanged",
[PATH_MODIFIED] = "PathModified",
};
DEFINE_STRING_TABLE_LOOKUP(path_type, PathType);
static const char* const path_result_table[_PATH_RESULT_MAX] = {
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
[PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[PATH_SUCCESS] = "success",
[PATH_FAILURE_RESOURCES] = "resources",
[PATH_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[PATH_FAILURE_UNIT_START_LIMIT_HIT] = "unit-start-limit-hit",
};

View File

@ -632,9 +632,9 @@ static void scope_enumerate_perpetual(Manager *m) {
}
static const char* const scope_result_table[_SCOPE_RESULT_MAX] = {
[SCOPE_SUCCESS] = "success",
[SCOPE_SUCCESS] = "success",
[SCOPE_FAILURE_RESOURCES] = "resources",
[SCOPE_FAILURE_TIMEOUT] = "timeout",
[SCOPE_FAILURE_TIMEOUT] = "timeout",
};
DEFINE_STRING_TABLE_LOOKUP(scope_result, ScopeResult);

View File

@ -4379,6 +4379,14 @@ static int service_exit_status(Unit *u) {
return s->main_exec_status.status;
}
static const char* service_status_text(Unit *u) {
Service *s = SERVICE(u);
assert(s);
return s->status_text;
}
static int service_clean(Unit *u, ExecCleanMask mask) {
_cleanup_strv_free_ char **l = NULL;
Service *s = SERVICE(u);
@ -4440,82 +4448,82 @@ static const char *service_finished_job(Unit *u, JobType t, JobResult result) {
}
static const char* const service_restart_table[_SERVICE_RESTART_MAX] = {
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
[SERVICE_RESTART_ON_FAILURE] = "on-failure",
[SERVICE_RESTART_NO] = "no",
[SERVICE_RESTART_ON_SUCCESS] = "on-success",
[SERVICE_RESTART_ON_FAILURE] = "on-failure",
[SERVICE_RESTART_ON_ABNORMAL] = "on-abnormal",
[SERVICE_RESTART_ON_WATCHDOG] = "on-watchdog",
[SERVICE_RESTART_ON_ABORT] = "on-abort",
[SERVICE_RESTART_ALWAYS] = "always",
[SERVICE_RESTART_ON_ABORT] = "on-abort",
[SERVICE_RESTART_ALWAYS] = "always",
};
DEFINE_STRING_TABLE_LOOKUP(service_restart, ServiceRestart);
static const char* const service_type_table[_SERVICE_TYPE_MAX] = {
[SERVICE_SIMPLE] = "simple",
[SERVICE_SIMPLE] = "simple",
[SERVICE_FORKING] = "forking",
[SERVICE_ONESHOT] = "oneshot",
[SERVICE_DBUS] = "dbus",
[SERVICE_NOTIFY] = "notify",
[SERVICE_IDLE] = "idle",
[SERVICE_EXEC] = "exec",
[SERVICE_DBUS] = "dbus",
[SERVICE_NOTIFY] = "notify",
[SERVICE_IDLE] = "idle",
[SERVICE_EXEC] = "exec",
};
DEFINE_STRING_TABLE_LOOKUP(service_type, ServiceType);
static const char* const service_exec_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
[SERVICE_EXEC_CONDITION] = "ExecCondition",
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
[SERVICE_EXEC_START] = "ExecStart",
[SERVICE_EXEC_CONDITION] = "ExecCondition",
[SERVICE_EXEC_START_PRE] = "ExecStartPre",
[SERVICE_EXEC_START] = "ExecStart",
[SERVICE_EXEC_START_POST] = "ExecStartPost",
[SERVICE_EXEC_RELOAD] = "ExecReload",
[SERVICE_EXEC_STOP] = "ExecStop",
[SERVICE_EXEC_STOP_POST] = "ExecStopPost",
[SERVICE_EXEC_RELOAD] = "ExecReload",
[SERVICE_EXEC_STOP] = "ExecStop",
[SERVICE_EXEC_STOP_POST] = "ExecStopPost",
};
DEFINE_STRING_TABLE_LOOKUP(service_exec_command, ServiceExecCommand);
static const char* const service_exec_ex_command_table[_SERVICE_EXEC_COMMAND_MAX] = {
[SERVICE_EXEC_CONDITION] = "ExecConditionEx",
[SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
[SERVICE_EXEC_START] = "ExecStartEx",
[SERVICE_EXEC_CONDITION] = "ExecConditionEx",
[SERVICE_EXEC_START_PRE] = "ExecStartPreEx",
[SERVICE_EXEC_START] = "ExecStartEx",
[SERVICE_EXEC_START_POST] = "ExecStartPostEx",
[SERVICE_EXEC_RELOAD] = "ExecReloadEx",
[SERVICE_EXEC_STOP] = "ExecStopEx",
[SERVICE_EXEC_STOP_POST] = "ExecStopPostEx",
[SERVICE_EXEC_RELOAD] = "ExecReloadEx",
[SERVICE_EXEC_STOP] = "ExecStopEx",
[SERVICE_EXEC_STOP_POST] = "ExecStopPostEx",
};
DEFINE_STRING_TABLE_LOOKUP(service_exec_ex_command, ServiceExecCommand);
static const char* const notify_state_table[_NOTIFY_STATE_MAX] = {
[NOTIFY_UNKNOWN] = "unknown",
[NOTIFY_READY] = "ready",
[NOTIFY_UNKNOWN] = "unknown",
[NOTIFY_READY] = "ready",
[NOTIFY_RELOADING] = "reloading",
[NOTIFY_STOPPING] = "stopping",
[NOTIFY_STOPPING] = "stopping",
};
DEFINE_STRING_TABLE_LOOKUP(notify_state, NotifyState);
static const char* const service_result_table[_SERVICE_RESULT_MAX] = {
[SERVICE_SUCCESS] = "success",
[SERVICE_FAILURE_RESOURCES] = "resources",
[SERVICE_FAILURE_PROTOCOL] = "protocol",
[SERVICE_FAILURE_TIMEOUT] = "timeout",
[SERVICE_FAILURE_EXIT_CODE] = "exit-code",
[SERVICE_FAILURE_SIGNAL] = "signal",
[SERVICE_FAILURE_CORE_DUMP] = "core-dump",
[SERVICE_FAILURE_WATCHDOG] = "watchdog",
[SERVICE_SUCCESS] = "success",
[SERVICE_FAILURE_RESOURCES] = "resources",
[SERVICE_FAILURE_PROTOCOL] = "protocol",
[SERVICE_FAILURE_TIMEOUT] = "timeout",
[SERVICE_FAILURE_EXIT_CODE] = "exit-code",
[SERVICE_FAILURE_SIGNAL] = "signal",
[SERVICE_FAILURE_CORE_DUMP] = "core-dump",
[SERVICE_FAILURE_WATCHDOG] = "watchdog",
[SERVICE_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SERVICE_FAILURE_OOM_KILL] = "oom-kill",
[SERVICE_SKIP_CONDITION] = "exec-condition",
[SERVICE_FAILURE_OOM_KILL] = "oom-kill",
[SERVICE_SKIP_CONDITION] = "exec-condition",
};
DEFINE_STRING_TABLE_LOOKUP(service_result, ServiceResult);
static const char* const service_timeout_failure_mode_table[_SERVICE_TIMEOUT_FAILURE_MODE_MAX] = {
[SERVICE_TIMEOUT_TERMINATE] = "terminate",
[SERVICE_TIMEOUT_ABORT] = "abort",
[SERVICE_TIMEOUT_KILL] = "kill",
[SERVICE_TIMEOUT_ABORT] = "abort",
[SERVICE_TIMEOUT_KILL] = "kill",
};
DEFINE_STRING_TABLE_LOOKUP(service_timeout_failure_mode, ServiceTimeoutFailureMode);
@ -4590,6 +4598,7 @@ const UnitVTable service_vtable = {
.get_timeout = service_get_timeout,
.needs_console = service_needs_console,
.exit_status = service_exit_status,
.status_text = service_status_text,
.status_message_formats = {
.finished_start_job = {

View File

@ -3425,24 +3425,24 @@ static int socket_can_clean(Unit *u, ExecCleanMask *ret) {
}
static const char* const socket_exec_command_table[_SOCKET_EXEC_COMMAND_MAX] = {
[SOCKET_EXEC_START_PRE] = "ExecStartPre",
[SOCKET_EXEC_START_PRE] = "ExecStartPre",
[SOCKET_EXEC_START_CHOWN] = "ExecStartChown",
[SOCKET_EXEC_START_POST] = "ExecStartPost",
[SOCKET_EXEC_STOP_PRE] = "ExecStopPre",
[SOCKET_EXEC_STOP_POST] = "ExecStopPost"
[SOCKET_EXEC_START_POST] = "ExecStartPost",
[SOCKET_EXEC_STOP_PRE] = "ExecStopPre",
[SOCKET_EXEC_STOP_POST] = "ExecStopPost"
};
DEFINE_STRING_TABLE_LOOKUP(socket_exec_command, SocketExecCommand);
static const char* const socket_result_table[_SOCKET_RESULT_MAX] = {
[SOCKET_SUCCESS] = "success",
[SOCKET_FAILURE_RESOURCES] = "resources",
[SOCKET_FAILURE_TIMEOUT] = "timeout",
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_SUCCESS] = "success",
[SOCKET_FAILURE_RESOURCES] = "resources",
[SOCKET_FAILURE_TIMEOUT] = "timeout",
[SOCKET_FAILURE_EXIT_CODE] = "exit-code",
[SOCKET_FAILURE_SIGNAL] = "signal",
[SOCKET_FAILURE_CORE_DUMP] = "core-dump",
[SOCKET_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
[SOCKET_FAILURE_TRIGGER_LIMIT_HIT] = "trigger-limit-hit",
[SOCKET_FAILURE_SERVICE_START_LIMIT_HIT] = "service-start-limit-hit"
};
@ -3450,8 +3450,8 @@ DEFINE_STRING_TABLE_LOOKUP(socket_result, SocketResult);
static const char* const socket_timestamping_table[_SOCKET_TIMESTAMPING_MAX] = {
[SOCKET_TIMESTAMPING_OFF] = "off",
[SOCKET_TIMESTAMPING_US] = "us",
[SOCKET_TIMESTAMPING_NS] = "ns",
[SOCKET_TIMESTAMPING_US] = "us",
[SOCKET_TIMESTAMPING_NS] = "ns",
};
DEFINE_STRING_TABLE_LOOKUP(socket_timestamping, SocketTimestamping);

View File

@ -1589,19 +1589,19 @@ static int swap_can_clean(Unit *u, ExecCleanMask *ret) {
}
static const char* const swap_exec_command_table[_SWAP_EXEC_COMMAND_MAX] = {
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_ACTIVATE] = "ExecActivate",
[SWAP_EXEC_DEACTIVATE] = "ExecDeactivate",
};
DEFINE_STRING_TABLE_LOOKUP(swap_exec_command, SwapExecCommand);
static const char* const swap_result_table[_SWAP_RESULT_MAX] = {
[SWAP_SUCCESS] = "success",
[SWAP_FAILURE_RESOURCES] = "resources",
[SWAP_FAILURE_TIMEOUT] = "timeout",
[SWAP_FAILURE_EXIT_CODE] = "exit-code",
[SWAP_FAILURE_SIGNAL] = "signal",
[SWAP_FAILURE_CORE_DUMP] = "core-dump",
[SWAP_SUCCESS] = "success",
[SWAP_FAILURE_RESOURCES] = "resources",
[SWAP_FAILURE_TIMEOUT] = "timeout",
[SWAP_FAILURE_EXIT_CODE] = "exit-code",
[SWAP_FAILURE_SIGNAL] = "signal",
[SWAP_FAILURE_CORE_DUMP] = "core-dump",
[SWAP_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};

View File

@ -891,19 +891,19 @@ static int timer_can_clean(Unit *u, ExecCleanMask *ret) {
}
static const char* const timer_base_table[_TIMER_BASE_MAX] = {
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
[TIMER_STARTUP] = "OnStartupSec",
[TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
[TIMER_ACTIVE] = "OnActiveSec",
[TIMER_BOOT] = "OnBootSec",
[TIMER_STARTUP] = "OnStartupSec",
[TIMER_UNIT_ACTIVE] = "OnUnitActiveSec",
[TIMER_UNIT_INACTIVE] = "OnUnitInactiveSec",
[TIMER_CALENDAR] = "OnCalendar"
[TIMER_CALENDAR] = "OnCalendar"
};
DEFINE_STRING_TABLE_LOOKUP(timer_base, TimerBase);
static const char* const timer_result_table[_TIMER_RESULT_MAX] = {
[TIMER_SUCCESS] = "success",
[TIMER_FAILURE_RESOURCES] = "resources",
[TIMER_SUCCESS] = "success",
[TIMER_FAILURE_RESOURCES] = "resources",
[TIMER_FAILURE_START_LIMIT_HIT] = "start-limit-hit",
};

View File

@ -631,6 +631,9 @@ typedef struct UnitVTable {
* exit code of the "main" process of the service or similar. */
int (*exit_status)(Unit *u);
/* Return a copy of the status string pointer. */
const char* (*status_text)(Unit *u);
/* Like the enumerate() callback further down, but only enumerates the perpetual units, i.e. all units that
* unconditionally exist and are always active. The main reason to keep both enumeration functions separate is
* philosophical: the state of perpetual units should be put in place by coldplug(), while the state of those
@ -744,6 +747,12 @@ static inline bool unit_is_extrinsic(Unit *u) {
(UNIT_VTABLE(u)->is_extrinsic && UNIT_VTABLE(u)->is_extrinsic(u));
}
static inline const char* unit_status_text(Unit *u) {
if (u && UNIT_VTABLE(u)->status_text)
return UNIT_VTABLE(u)->status_text(u);
return NULL;
}
void unit_add_to_load_queue(Unit *u);
void unit_add_to_dbus_queue(Unit *u);
void unit_add_to_cleanup_queue(Unit *u);