summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/execute.c42
-rw-r--r--src/core/manager.c78
-rw-r--r--src/core/manager.h7
-rw-r--r--src/core/transaction.c5
-rw-r--r--src/core/unit.c12
5 files changed, 125 insertions, 19 deletions
diff --git a/src/core/execute.c b/src/core/execute.c
index 50d2d49ba8..43b571e043 100644
--- a/src/core/execute.c
+++ b/src/core/execute.c
@@ -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
diff --git a/src/core/manager.c b/src/core/manager.c
index 2e98181b37..ad1a8d6179 100644
--- a/src/core/manager.c
+++ b/src/core/manager.c
@@ -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;
diff --git a/src/core/manager.h b/src/core/manager.h
index 6d5241497d..bd068ac3c9 100644
--- a/src/core/manager.h
+++ b/src/core/manager.h
@@ -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;
diff --git a/src/core/transaction.c b/src/core/transaction.c
index 5259a5b7ca..27efef7cc6 100644
--- a/src/core/transaction.c
+++ b/src/core/transaction.c
@@ -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;
diff --git a/src/core/unit.c b/src/core/unit.c
index b245356887..f4f92f0fca 100644
--- a/src/core/unit.c
+++ b/src/core/unit.c
@@ -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 ++;
}
}