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" #include "unit.h"
#define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC) #define IDLE_TIMEOUT_USEC (5*USEC_PER_SEC)
#define IDLE_TIMEOUT2_USEC (1*USEC_PER_SEC)
/* This assumes there is a 'tty' group */ /* This assumes there is a 'tty' group */
#define TTY_MODE 0620 #define TTY_MODE 0620
@ -977,6 +978,35 @@ static int apply_seccomp(uint32_t *syscall_filter) {
return 0; 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, int exec_spawn(ExecCommand *command,
char **argv, char **argv,
ExecContext *context, ExecContext *context,
@ -989,7 +1019,7 @@ int exec_spawn(ExecCommand *command,
CGroupControllerMask cgroup_mask, CGroupControllerMask cgroup_mask,
const char *cgroup_path, const char *cgroup_path,
const char *unit_id, const char *unit_id,
int idle_pipe[2], int idle_pipe[4],
pid_t *ret) { pid_t *ret) {
_cleanup_strv_free_ char **files_env = NULL; _cleanup_strv_free_ char **files_env = NULL;
@ -1083,14 +1113,8 @@ int exec_spawn(ExecCommand *command,
goto fail_child; goto fail_child;
} }
if (idle_pipe) { if (idle_pipe)
if (idle_pipe[1] >= 0) do_idle_pipe_dance(idle_pipe);
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]);
}
}
/* Close sockets very early to make sure we don't /* Close sockets very early to make sure we don't
* block init reexecution because it cannot bind its * 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++; 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) { static int manager_setup_time_change(Manager *m) {
struct epoll_event ev = { struct epoll_event ev = {
.events = EPOLLIN, .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->name_data_slot = m->conn_data_slot = m->subscribed_data_slot = -1;
m->exit_code = _MANAGER_EXIT_CODE_INVALID; m->exit_code = _MANAGER_EXIT_CODE_INVALID;
m->pin_cgroupfs_fd = -1; 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->signal_watch);
watch_init(&m->mount_watch); watch_init(&m->mount_watch);
@ -657,6 +709,11 @@ static void manager_clear_jobs_and_units(Manager *m) {
m->n_running_jobs = 0; 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) { void manager_free(Manager *m) {
UnitType c; UnitType c;
int i; int i;
@ -701,7 +758,7 @@ void manager_free(Manager *m) {
hashmap_free(m->cgroup_unit); hashmap_free(m->cgroup_unit);
set_free_free(m->unit_path_cache); set_free_free(m->unit_path_cache);
close_pipe(m->idle_pipe); close_idle_pipe(m);
free(m->switch_root); free(m->switch_root);
free(m->switch_root_init); free(m->switch_root_init);
@ -1138,6 +1195,9 @@ unsigned manager_dispatch_run_queue(Manager *m) {
if (m->n_running_jobs > 0) if (m->n_running_jobs > 0)
manager_watch_jobs_in_progress(m); manager_watch_jobs_in_progress(m);
if (m->n_on_console > 0)
manager_watch_idle_pipe(m);
return n; return n;
} }
@ -1691,6 +1751,14 @@ static int process_event(Manager *m, struct epoll_event *ev) {
break; break;
} }
case WATCH_IDLE_PIPE: {
m->no_console_output = true;
manager_unwatch_idle_pipe(m);
close_idle_pipe(m);
break;
}
default: default:
log_error("event type=%i", w->type); log_error("event type=%i", w->type);
assert_not_reached("Unknown epoll event 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 */ /* 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 */ /* Turn off confirm spawn now */
m->confirm_spawn = false; m->confirm_spawn = false;
@ -2660,6 +2729,9 @@ static bool manager_get_show_status(Manager *m) {
if (m->running_as != SYSTEMD_SYSTEM) if (m->running_as != SYSTEMD_SYSTEM)
return false; return false;
if (m->no_console_output)
return false;
if (m->show_status) if (m->show_status)
return true; return true;

View file

@ -63,7 +63,8 @@ enum WatchType {
WATCH_DBUS_WATCH, WATCH_DBUS_WATCH,
WATCH_DBUS_TIMEOUT, WATCH_DBUS_TIMEOUT,
WATCH_TIME_CHANGE, WATCH_TIME_CHANGE,
WATCH_JOBS_IN_PROGRESS WATCH_JOBS_IN_PROGRESS,
WATCH_IDLE_PIPE,
}; };
struct Watch { struct Watch {
@ -135,6 +136,7 @@ struct Manager {
Watch signal_watch; Watch signal_watch;
Watch time_change_watch; Watch time_change_watch;
Watch jobs_in_progress_watch; Watch jobs_in_progress_watch;
Watch idle_pipe_watch;
int epoll_fd; int epoll_fd;
@ -227,6 +229,7 @@ struct Manager {
bool show_status; bool show_status;
bool confirm_spawn; bool confirm_spawn;
bool no_console_output;
ExecOutput default_std_output, default_std_error; ExecOutput default_std_output, default_std_error;
@ -244,7 +247,7 @@ struct Manager {
unsigned jobs_in_progress_iteration; unsigned jobs_in_progress_iteration;
/* Type=idle pipes */ /* Type=idle pipes */
int idle_pipe[2]; int idle_pipe[4];
char *switch_root; char *switch_root;
char *switch_root_init; 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 * feature for cosmetics, not actually useful for
* anything beyond that. */ * 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, O_NONBLOCK|O_CLOEXEC);
pipe2(m->idle_pipe + 2, O_NONBLOCK|O_CLOEXEC);
}
} }
return 0; 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)) { if (UNIT_IS_INACTIVE_OR_FAILED(os) != UNIT_IS_INACTIVE_OR_FAILED(ns)) {
ExecContext *ec = unit_get_exec_context(u); ExecContext *ec = unit_get_exec_context(u);
if (ec && exec_context_may_touch_console(ec)) { if (ec && exec_context_may_touch_console(ec)) {
if (UNIT_IS_INACTIVE_OR_FAILED(ns)) if (UNIT_IS_INACTIVE_OR_FAILED(ns)) {
m->n_on_console--; m->n_on_console --;
else
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 ++;
} }
} }