systemd: do not output status messages once gettys are running

Make Type=idle communication bidirectional: when bootup is finished,
the manager, as before, signals idling Type=idle jobs to continue.
However, if the boot takes too long, idling jobs signal the manager
that they have had enough, wait a tiny bit more, and continue, taking
ownership of the console. The manager, when signalled that Type=idle
jobs are done, makes a note and will not write to the console anymore.

This is a cosmetic issue, but quite noticable, so let's just fix it.

Based on Harald Hoyer's patch.

https://bugs.freedesktop.org/show_bug.cgi?id=54247
http://unix.stackexchange.com/questions/51805/systemd-messages-after-starting-login/
This commit is contained in:
Zbigniew Jędrzejewski-Szmek 2013-07-15 21:34:57 -04:00
parent 77a9e8de65
commit 31a7eb86f1
5 changed files with 125 additions and 19 deletions

View File

@ -69,6 +69,7 @@
#include "unit.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
/* This assumes there is a 'tty' group */
#define TTY_MODE 0620
@ -977,6 +978,35 @@ static int apply_seccomp(uint32_t *syscall_filter) {
return 0;
}
static void do_idle_pipe_dance(int idle_pipe[4]) {
assert(idle_pipe);
if (idle_pipe[1] >= 0)
close_nointr_nofail(idle_pipe[1]);
if (idle_pipe[2] >= 0)
close_nointr_nofail(idle_pipe[2]);
if (idle_pipe[0] >= 0) {
int r;
r = fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
if (idle_pipe[3] >= 0 && r == 0 /* timeout */) {
/* Signal systemd that we are bored and want to continue. */
write(idle_pipe[3], "x", 1);
/* Wait for systemd to react to the signal above. */
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT2_USEC);
}
close_nointr_nofail(idle_pipe[0]);
}
if (idle_pipe[3] >= 0)
close_nointr_nofail(idle_pipe[3]);
}
int exec_spawn(ExecCommand *command,
char **argv,
ExecContext *context,
@ -989,7 +1019,7 @@ int exec_spawn(ExecCommand *command,
CGroupControllerMask cgroup_mask,
const char *cgroup_path,
const char *unit_id,
int idle_pipe[2],
int idle_pipe[4],
pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL;
@ -1083,14 +1113,8 @@ int exec_spawn(ExecCommand *command,
goto fail_child;
}
if (idle_pipe) {
if (idle_pipe[1] >= 0)
close_nointr_nofail(idle_pipe[1]);
if (idle_pipe[0] >= 0) {
fd_wait_for_event(idle_pipe[0], POLLHUP, IDLE_TIMEOUT_USEC);
close_nointr_nofail(idle_pipe[0]);
}
}
if (idle_pipe)
do_idle_pipe_dance(idle_pipe);
/* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its

View File

@ -273,6 +273,58 @@ static void manager_print_jobs_in_progress(Manager *m) {
m->jobs_in_progress_iteration++;
}
static int manager_watch_idle_pipe(Manager *m) {
struct epoll_event ev = {
.events = EPOLLIN,
.data.ptr = &m->idle_pipe_watch,
};
int r;
if (m->idle_pipe_watch.type != WATCH_INVALID)
return 0;
if (m->idle_pipe[2] < 0)
return 0;
m->idle_pipe_watch.type = WATCH_IDLE_PIPE;
m->idle_pipe_watch.fd = m->idle_pipe[2];
if (m->idle_pipe_watch.fd < 0) {
log_error("Failed to create timerfd: %m");
r = -errno;
goto err;
}
if (epoll_ctl(m->epoll_fd, EPOLL_CTL_ADD, m->idle_pipe_watch.fd, &ev) < 0) {
log_error("Failed to add idle_pipe fd to epoll: %m");
r = -errno;
goto err;
}
log_debug("Set up idle_pipe watch.");
log_debug("m->epoll_fd=%d m->idle_pipe_watch.fd=%d",
m->epoll_fd, m->idle_pipe_watch.fd);
return 0;
err:
if (m->idle_pipe_watch.fd >= 0)
close_nointr_nofail(m->idle_pipe_watch.fd);
watch_init(&m->idle_pipe_watch);
return r;
}
static void manager_unwatch_idle_pipe(Manager *m) {
if (m->idle_pipe_watch.type != WATCH_IDLE_PIPE)
return;
log_debug("m->epoll_fd=%d m->idle_pipe_watch.fd=%d",
m->epoll_fd, m->idle_pipe_watch.fd);
assert_se(epoll_ctl(m->epoll_fd, EPOLL_CTL_DEL, m->idle_pipe_watch.fd, NULL) >= 0);
watch_init(&m->idle_pipe_watch);
log_debug("Closed idle_pipe watch.");
}
static int manager_setup_time_change(Manager *m) {
struct epoll_event ev = {
.events = EPOLLIN,
@ -445,7 +497,7 @@ int manager_new(SystemdRunningAs running_as, bool reexecuting, Manager **_m) {
m->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->pin_cgroupfs_fd = -1;
m->idle_pipe[0] = m->idle_pipe[1] = -1;
m->idle_pipe[0] = m->idle_pipe[1] = m->idle_pipe[2] = m->idle_pipe[3] = -1;
watch_init(&m->signal_watch);
watch_init(&m->mount_watch);
@ -657,6 +709,11 @@ static void manager_clear_jobs_and_units(Manager *m) {
m->n_running_jobs = 0;
}
static void close_idle_pipe(Manager *m) {
close_pipe(m->idle_pipe);
close_pipe(m->idle_pipe + 2);
}
void manager_free(Manager *m) {
UnitType c;
int i;
@ -701,7 +758,7 @@ void manager_free(Manager *m) {
hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache);
close_pipe(m->idle_pipe);
close_idle_pipe(m);
free(m->switch_root);
free(m->switch_root_init);
@ -1138,6 +1195,9 @@ unsigned manager_dispatch_run_queue(Manager *m) {
if (m->n_running_jobs > 0)
manager_watch_jobs_in_progress(m);
if (m->n_on_console > 0)
manager_watch_idle_pipe(m);
return n;
}
@ -1691,6 +1751,14 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break;
}
case WATCH_IDLE_PIPE: {
m->no_console_output = true;
manager_unwatch_idle_pipe(m);
close_idle_pipe(m);
break;
}
default:
log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event type.");
@ -2384,7 +2452,8 @@ void manager_check_finished(Manager *m) {
}
/* Notify Type=idle units that we are done now */
close_pipe(m->idle_pipe);
manager_unwatch_idle_pipe(m);
close_idle_pipe(m);
/* Turn off confirm spawn now */
m->confirm_spawn = false;
@ -2660,6 +2729,9 @@ static bool manager_get_show_status(Manager *m) {
if (m->running_as != SYSTEMD_SYSTEM)
return false;
if (m->no_console_output)
return false;
if (m->show_status)
return true;

View File

@ -63,7 +63,8 @@ enum WatchType {
WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT,
WATCH_TIME_CHANGE,
WATCH_JOBS_IN_PROGRESS
WATCH_JOBS_IN_PROGRESS,
WATCH_IDLE_PIPE,
};
struct Watch {
@ -135,6 +136,7 @@ struct Manager {
Watch signal_watch;
Watch time_change_watch;
Watch jobs_in_progress_watch;
Watch idle_pipe_watch;
int epoll_fd;
@ -227,6 +229,7 @@ struct Manager {
bool show_status;
bool confirm_spawn;
bool no_console_output;
ExecOutput default_std_output, default_std_error;
@ -244,7 +247,7 @@ struct Manager {
unsigned jobs_in_progress_iteration;
/* Type=idle pipes */
int idle_pipe[2];
int idle_pipe[4];
char *switch_root;
char *switch_root_init;

View File

@ -733,8 +733,11 @@ int transaction_activate(Transaction *tr, Manager *m, JobMode mode, DBusError *e
* feature for cosmetics, not actually useful for
* anything beyond that. */
if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0)
if (m->idle_pipe[0] < 0 && m->idle_pipe[1] < 0 &&
m->idle_pipe[2] < 0 && m->idle_pipe[3] < 0) {
pipe2(m->idle_pipe, O_NONBLOCK|O_CLOEXEC);
pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
}
}
return 0;

View File

@ -1425,10 +1425,14 @@ void unit_notify(Unit *u, UnitActiveState os, UnitActiveState ns, bool reload_su
if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ExecContext *ec = unit_get_exec_context(u);
if (ec && exec_context_may_touch_console(ec)) {
if (UNIT_IS_INACTIVE_OR_FAILED(ns))
m->n_on_console--;
else
m->n_on_console++;
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
m->n_on_console --;
if (m->n_on_console == 0)
/* unset no_console_output flag, since the console is free */
m->no_console_output = 0;
} else
m->n_on_console ++;
}
}