Merge pull request #31739 from poettering/pid1-sd-notify-tweaks

pid1: send various notifications via sd_notify() reporting boot progress
This commit is contained in:
Lennart Poettering
2024-03-14 18:43:57 +01:00
committed by GitHub
18 changed files with 393 additions and 120 deletions
+4 -2
View File
@@ -3282,7 +3282,8 @@ finish:
#endif
if (r < 0)
(void) sd_notifyf(0, "ERRNO=%i", -r);
(void) sd_notifyf(/* unset_environment= */ false,
"ERRNO=%i", -r);
/* Try to invoke the shutdown binary unless we already failed.
* If we failed above, we want to freeze after finishing cleanup. */
@@ -3295,7 +3296,8 @@ finish:
/* This is primarily useful when running systemd in a VM, as it provides the user running the VM with
* a mechanism to pick up systemd's exit status in the VM. */
(void) sd_notifyf(0, "EXIT_STATUS=%i", retval);
(void) sd_notifyf(/* unset_environment= */ false,
"EXIT_STATUS=%i", retval);
watchdog_free_device();
arg_watchdog_device = mfree(arg_watchdog_device);
+74 -37
View File
@@ -264,12 +264,11 @@ static void manager_print_jobs_in_progress(Manager *m) {
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);
(void) sd_notifyf(/* unset_environment= */ 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;
}
@@ -483,21 +482,19 @@ static int enable_special_signals(Manager *m) {
if (MANAGER_IS_TEST_RUN(m))
return 0;
/* Enable that we get SIGINT on control-alt-del. In containers
* this will fail with EPERM (older) or EINVAL (newer), so
* ignore that. */
/* Enable that we get SIGINT on control-alt-del. In containers this will fail with EPERM (older) or
* EINVAL (newer), so ignore that. */
if (reboot(RB_DISABLE_CAD) < 0 && !IN_SET(errno, EPERM, EINVAL))
log_warning_errno(errno, "Failed to enable ctrl-alt-del handling: %m");
log_warning_errno(errno, "Failed to enable ctrl-alt-del handling, ignoring: %m");
fd = open_terminal("/dev/tty0", O_RDWR|O_NOCTTY|O_CLOEXEC);
if (fd < 0) {
/* Support systems without virtual console */
if (fd != -ENOENT)
log_warning_errno(errno, "Failed to open /dev/tty0: %m");
} else {
if (fd < 0)
/* Support systems without virtual console (ENOENT) gracefully */
log_full_errno(fd == -ENOENT ? LOG_DEBUG : LOG_WARNING, fd, "Failed to open /dev/tty0, ignoring: %m");
else {
/* Enable that we get SIGWINCH on kbrequest */
if (ioctl(fd, KDSIGACCEPT, SIGWINCH) < 0)
log_warning_errno(errno, "Failed to enable kbrequest handling: %m");
log_warning_errno(errno, "Failed to enable kbrequest handling, ignoring: %m");
}
return 0;
@@ -597,6 +594,17 @@ static int manager_setup_signals(Manager *m) {
if (r < 0)
return r;
/* Report to supervisor that we now process the above signals. We report this as level "2", to
* indicate that we support more than sysvinit's signals (of course, sysvinit never sent this
* message, but conceptually it makes sense to consider level "1" to be equivalent to sysvinit's
* signal handling). Also, by setting this to "2" people looking for this hopefully won't
* misunderstand this as a boolean concept. Signal level 2 shall refer to the signals PID 1
* understands at the time of release of systemd v256, i.e. including basic SIGRTMIN+18 handling for
* memory pressure and stuff. When more signals are hooked up (or more SIGRTMIN+18 multiplex
* operations added, this level should be increased). */
(void) sd_notify(/* unset_environment= */ false,
"X_SYSTEMD_SIGNALS_LEVEL=2");
if (MANAGER_IS_SYSTEM(m))
return enable_special_signals(m);
@@ -2869,8 +2877,8 @@ static void manager_start_special(Manager *m, const char *name, JobMode mode) {
log_info("Activating special unit %s...", s);
sd_notifyf(false,
"STATUS=Activating special unit %s...", s);
(void) sd_notifyf(/* unset_environment= */ false,
"STATUS=Activating special unit %s...", s);
m->status_ready = false;
}
@@ -3362,18 +3370,20 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
const char *msg;
int audit_fd, r;
assert(m);
assert(u);
if (!MANAGER_IS_SYSTEM(m))
return;
/* Don't generate audit events if the service was already started and we're just deserializing */
if (MANAGER_IS_RELOADING(m))
return;
audit_fd = get_audit_fd();
if (audit_fd < 0)
return;
/* Don't generate audit events if the service was already
* started and we're just deserializing */
if (MANAGER_IS_RELOADING(m))
return;
r = unit_name_to_prefix_and_instance(u->id, &p);
if (r < 0) {
log_warning_errno(r, "Failed to extract prefix and instance of unit name, ignoring: %m");
@@ -3390,21 +3400,22 @@ void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success) {
log_warning_errno(errno, "Failed to send audit message, ignoring: %m");
}
#endif
}
void manager_send_unit_plymouth(Manager *m, Unit *u) {
_cleanup_free_ char *message = NULL;
int c, r;
/* Don't generate plymouth events if the service was already
* started and we're just deserializing */
if (MANAGER_IS_RELOADING(m))
return;
assert(m);
assert(u);
if (!MANAGER_IS_SYSTEM(m))
return;
/* Don't generate plymouth events if the service was already started and we're just deserializing */
if (MANAGER_IS_RELOADING(m))
return;
if (detect_container() > 0)
return;
@@ -3422,6 +3433,27 @@ void manager_send_unit_plymouth(Manager *m, Unit *u) {
"Failed to communicate with plymouth: %m");
}
void manager_send_unit_supervisor(Manager *m, Unit *u, bool active) {
assert(m);
assert(u);
/* Notify a "supervisor" process about our progress, i.e. a container manager, hypervisor, or
* surrounding service manager. */
if (MANAGER_IS_RELOADING(m))
return;
if (!UNIT_VTABLE(u)->notify_supervisor)
return;
if (in_initrd()) /* Only send these once we left the initrd */
return;
(void) sd_notifyf(/* unset_environment= */ false,
active ? "X_SYSTEMD_UNIT_ACTIVE=%s" : "X_SYSTEMD_UNIT_INACTIVE=%s",
u->id);
}
usec_t manager_get_watchdog(Manager *m, WatchdogType t) {
assert(m);
@@ -3731,7 +3763,7 @@ static void manager_notify_finished(Manager *m) {
log_taint_string(m);
}
static void user_manager_send_ready(Manager *m) {
static void manager_send_ready_user_scope(Manager *m) {
int r;
assert(m);
@@ -3740,7 +3772,7 @@ static void user_manager_send_ready(Manager *m) {
if (!MANAGER_IS_USER(m) || m->ready_sent)
return;
r = sd_notify(false,
r = sd_notify(/* unset_environment= */ false,
"READY=1\n"
"STATUS=Reached " SPECIAL_BASIC_TARGET ".");
if (r < 0)
@@ -3750,14 +3782,19 @@ static void user_manager_send_ready(Manager *m) {
m->status_ready = false;
}
static void manager_send_ready(Manager *m) {
static void manager_send_ready_system_scope(Manager *m) {
int r;
if (m->ready_sent && m->status_ready)
/* Skip the notification if nothing changed. */
assert(m);
if (!MANAGER_IS_SYSTEM(m))
return;
r = sd_notify(false,
/* Skip the notification if nothing changed. */
if (m->ready_sent && m->status_ready)
return;
r = sd_notify(/* unset_environment= */ false,
"READY=1\n"
"STATUS=Ready.");
if (r < 0)
@@ -3781,7 +3818,7 @@ static void manager_check_basic_target(Manager *m) {
return;
/* For user managers, send out READY=1 as soon as we reach basic.target */
user_manager_send_ready(m);
manager_send_ready_user_scope(m);
/* Log the taint string as soon as we reach basic.target */
log_taint_string(m);
@@ -3812,7 +3849,7 @@ 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);
manager_send_ready_system_scope(m);
/* Notify Type=idle units that we are done now */
manager_close_idle_pipe(m);
@@ -3842,7 +3879,7 @@ void manager_send_reloading(Manager *m) {
assert(m);
/* Let whoever invoked us know that we are now reloading */
(void) sd_notifyf(/* unset= */ false,
(void) sd_notifyf(/* unset_environment= */ false,
"RELOADING=1\n"
"MONOTONIC_USEC=" USEC_FMT "\n", now(CLOCK_MONOTONIC));
+1
View File
@@ -575,6 +575,7 @@ void manager_reset_failed(Manager *m);
void manager_send_unit_audit(Manager *m, Unit *u, int type, bool success);
void manager_send_unit_plymouth(Manager *m, Unit *u);
void manager_send_unit_supervisor(Manager *m, Unit *u, bool active);
bool manager_unit_inactive_or_pending(Manager *m, const char *name);
+2
View File
@@ -213,4 +213,6 @@ const UnitVTable target_vtable = {
[JOB_DONE] = "Stopped target %s.",
},
},
.notify_supervisor = true,
};
+2
View File
@@ -2678,12 +2678,14 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
unit_emit_audit_start(u);
manager_send_unit_plymouth(m, u);
manager_send_unit_supervisor(m, u, /* active= */ true);
}
if (UNIT_IS_INACTIVE_OR_FAILED(ns) && !UNIT_IS_INACTIVE_OR_FAILED(os)) {
/* This unit just stopped/failed. */
unit_emit_audit_stop(u, ns);
manager_send_unit_supervisor(m, u, /* active= */ false);
unit_log_resources(u);
}
+3
View File
@@ -731,6 +731,9 @@ typedef struct UnitVTable {
/* If true, we'll notify plymouth about this unit */
bool notify_plymouth;
/* If true, we'll notify a surrounding VMM/container manager about this unit becoming available */
bool notify_supervisor;
/* The audit events to generate on start + stop (or 0 if none shall be generated) */
int audit_start_message_type;
int audit_stop_message_type;
+11
View File
@@ -4408,6 +4408,17 @@ static int nspawn_dispatch_notify_fd(sd_event_source *source, int fd, uint32_t r
if (!tags)
return log_oom();
if (DEBUG_LOGGING) {
_cleanup_free_ char *joined = strv_join(tags, " ");
if (joined) {
_cleanup_free_ char *j = cescape(joined);
free_and_replace(joined, j);
}
log_debug("Got sd_notify() message: %s", strnull(joined));
}
if (strv_contains(tags, "READY=1")) {
r = sd_notify(false, "READY=1\n");
if (r < 0)
+35 -38
View File
@@ -6,6 +6,8 @@
#include <sys/utsname.h>
#include <unistd.h>
#include "sd-daemon.h"
#include "alloc-util.h"
#include "creds-util.h"
#include "fd-util.h"
@@ -13,6 +15,7 @@
#include "fs-util.h"
#include "hostname-setup.h"
#include "hostname-util.h"
#include "initrd-util.h"
#include "log.h"
#include "macro.h"
#include "proc-cmdline.h"
@@ -24,7 +27,8 @@ static int sethostname_idempotent_full(const char *s, bool really) {
assert(s);
assert_se(uname(&u) >= 0);
if (uname(&u) < 0)
return -errno;
if (streq_ptr(s, u.nodename))
return 0;
@@ -41,34 +45,33 @@ int sethostname_idempotent(const char *s) {
}
int shorten_overlong(const char *s, char **ret) {
char *h, *p;
_cleanup_free_ char *h = NULL;
/* Shorten an overlong name to HOST_NAME_MAX or to the first dot,
* whatever comes earlier. */
assert(s);
assert(ret);
h = strdup(s);
if (!h)
return -ENOMEM;
if (hostname_is_valid(h, 0)) {
*ret = h;
*ret = TAKE_PTR(h);
return 0;
}
p = strchr(h, '.');
char *p = strchr(h, '.');
if (p)
*p = 0;
strshorten(h, HOST_NAME_MAX);
if (!hostname_is_valid(h, 0)) {
free(h);
if (!hostname_is_valid(h, /* flags= */ 0))
return -EDOM;
}
*ret = h;
*ret = TAKE_PTR(h);
return 1;
}
@@ -147,74 +150,64 @@ void hostname_update_source_hint(const char *hostname, HostnameSource source) {
r = write_string_file("/run/systemd/default-hostname", hostname,
WRITE_STRING_FILE_CREATE | WRITE_STRING_FILE_ATOMIC);
if (r < 0)
log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\": %m");
log_warning_errno(r, "Failed to create \"/run/systemd/default-hostname\", ignoring: %m");
} else
unlink_or_warn("/run/systemd/default-hostname");
}
int hostname_setup(bool really) {
_cleanup_free_ char *b = NULL;
const char *hn = NULL;
_cleanup_free_ char *hn = NULL;
HostnameSource source;
bool enoent = false;
int r;
r = proc_cmdline_get_key("systemd.hostname", 0, &b);
r = proc_cmdline_get_key("systemd.hostname", 0, &hn);
if (r < 0)
log_warning_errno(r, "Failed to retrieve system hostname from kernel command line, ignoring: %m");
else if (r > 0) {
if (hostname_is_valid(b, VALID_HOSTNAME_TRAILING_DOT)) {
hn = b;
if (hostname_is_valid(hn, VALID_HOSTNAME_TRAILING_DOT))
source = HOSTNAME_TRANSIENT;
} else {
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", b);
b = mfree(b);
else {
log_warning("Hostname specified on kernel command line is invalid, ignoring: %s", hn);
hn = mfree(hn);
}
}
if (!hn) {
r = read_etc_hostname(NULL, &b);
if (r < 0) {
if (r == -ENOENT)
enoent = true;
else
log_warning_errno(r, "Failed to read configured hostname: %m");
} else {
hn = b;
r = read_etc_hostname(NULL, &hn);
if (r == -ENOENT)
enoent = true;
else if (r < 0)
log_warning_errno(r, "Failed to read configured hostname, ignoring: %m");
else
source = HOSTNAME_STATIC;
}
}
if (!hn) {
r = acquire_hostname_from_credential(&b);
if (r >= 0) {
hn = b;
r = acquire_hostname_from_credential(&hn);
if (r >= 0)
source = HOSTNAME_TRANSIENT;
}
}
if (!hn) {
_cleanup_free_ char *buf = NULL;
/* Don't override the hostname if it is already set and not explicitly configured */
r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &buf);
r = gethostname_full(GET_HOSTNAME_ALLOW_LOCALHOST, &hn);
if (r == -ENOMEM)
return log_oom();
if (r >= 0) {
log_debug("No hostname configured, leaving existing hostname <%s> in place.", buf);
return 0;
log_debug("No hostname configured, leaving existing hostname <%s> in place.", hn);
goto finish;
}
if (enoent)
log_info("No hostname configured, using default hostname.");
hn = b = get_default_hostname();
hn = get_default_hostname();
if (!hn)
return log_oom();
source = HOSTNAME_DEFAULT;
}
r = sethostname_idempotent_full(hn, really);
@@ -230,7 +223,11 @@ int hostname_setup(bool really) {
if (really)
hostname_update_source_hint(hn, source);
return r;
finish:
if (!in_initrd())
(void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_HOSTNAME=%s", hn);
return 0;
}
static const char* const hostname_source_table[] = {
+7 -2
View File
@@ -5,6 +5,7 @@
#include <sys/mount.h>
#include <unistd.h>
#include "sd-daemon.h"
#include "sd-id128.h"
#include "alloc-util.h"
@@ -12,6 +13,7 @@
#include "creds-util.h"
#include "fd-util.h"
#include "id128-util.h"
#include "initrd-util.h"
#include "io-util.h"
#include "log.h"
#include "machine-id-setup.h"
@@ -141,8 +143,8 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
if (sd_id128_is_null(machine_id)) {
/* Try to read any existing machine ID */
if (id128_read_fd(fd, ID128_FORMAT_PLAIN, ret) >= 0)
return 0;
if (id128_read_fd(fd, ID128_FORMAT_PLAIN, &machine_id) >= 0)
goto finish;
/* Hmm, so, the id currently stored is not useful, then let's generate one */
r = generate_machine_id(root, &machine_id);
@@ -207,6 +209,9 @@ int machine_id_setup(const char *root, bool force_transient, sd_id128_t machine_
return r;
finish:
if (!in_initrd())
(void) sd_notifyf(/* unset_environment= */ false, "X_SYSTEMD_MACHINE_ID=" SD_ID128_FORMAT_STR, SD_ID128_FORMAT_VAL(machine_id));
if (ret)
*ret = machine_id;
+20 -7
View File
@@ -128,7 +128,8 @@ static int write_socket_unit(
const char *dest,
const char *unit,
const char *listen_stream,
const char *comment) {
const char *comment,
bool with_ssh_access_target_dependency) {
int r;
@@ -149,13 +150,21 @@ static int write_socket_unit(
fprintf(f,
"[Unit]\n"
"Description=OpenSSH Server Socket (systemd-ssh-generator, %s)\n"
"Documentation=man:systemd-ssh-generator(8)\n"
"Documentation=man:systemd-ssh-generator(8)\n",
comment);
/* When this is a remotely accessible socket let's mark this with a milestone: ssh-access.target */
if (with_ssh_access_target_dependency)
fputs("Wants=ssh-access.target\n"
"Before=ssh-access.target\n",
f);
fprintf(f,
"\n[Socket]\n"
"ListenStream=%s\n"
"Accept=yes\n"
"PollLimitIntervalSec=30s\n"
"PollLimitBurst=50\n",
comment,
listen_stream);
r = fflush_and_check(f);
@@ -230,7 +239,8 @@ static int add_vsock_socket(
dest,
"sshd-vsock.socket",
"vsock::22",
"AF_VSOCK");
"AF_VSOCK",
/* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;
@@ -264,7 +274,8 @@ static int add_local_unix_socket(
dest,
"sshd-unix-local.socket",
"/run/ssh-unix-local/socket",
"AF_UNIX Local");
"AF_UNIX Local",
/* with_ssh_access_target_dependency= */ false);
if (r < 0)
return r;
@@ -320,7 +331,8 @@ static int add_export_unix_socket(
dest,
"sshd-unix-export.socket",
"/run/host/unix-export/ssh",
"AF_UNIX Export");
"AF_UNIX Export",
/* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;
@@ -370,7 +382,8 @@ static int add_extra_sockets(
dest,
socket ?: "sshd-extra.socket",
*i,
*i);
*i,
/* with_ssh_access_target_dependency= */ true);
if (r < 0)
return r;