From: Michael Tremer Date: Sun, 6 Oct 2024 13:46:21 +0000 (+0000) Subject: pty: Implement draining the buffers X-Git-Tag: 0.9.30~1138 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=93bd0b9559f56796127317408dfcc4f763a26233;p=pakfire.git pty: Implement draining the buffers Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/include/pakfire/pty.h b/src/libpakfire/include/pakfire/pty.h index f3f394227..0a49965a2 100644 --- a/src/libpakfire/include/pakfire/pty.h +++ b/src/libpakfire/include/pakfire/pty.h @@ -41,5 +41,7 @@ struct pakfire_pty* pakfire_pty_unref(struct pakfire_pty* pty); int pakfire_pty_open(struct pakfire_pty* pty); +int pakfire_pty_drain(struct pakfire_pty* pty); + #endif /* PAKFIRE_PRIVATE */ #endif /* PAKFIRE_PTY_H */ diff --git a/src/libpakfire/jail.c b/src/libpakfire/jail.c index 99838e12c..f8a1040f7 100644 --- a/src/libpakfire/jail.c +++ b/src/libpakfire/jail.c @@ -1196,6 +1196,9 @@ static int pakfire_jail_exited(sd_event_source* source, const siginfo_t* si, voi break; } + // Drain the PTY + pakfire_pty_drain(ctx->pty); + // Terminate the event loop return sd_event_exit(sd_event_source_get_event(source), 0); } diff --git a/src/libpakfire/pty.c b/src/libpakfire/pty.c index c1e963508..047477a5f 100644 --- a/src/libpakfire/pty.c +++ b/src/libpakfire/pty.c @@ -80,8 +80,66 @@ struct pakfire_pty { // SIGWINCH Event sd_event_source* sigwinch_event; + + // State + enum { + PAKFIRE_PTY_STATE_INIT = 0, + PAKFIRE_PTY_STATE_FORWARDING, + PAKFIRE_PTY_STATE_DRAINING, + PAKFIRE_PTY_STATE_DONE, + } state; }; +static int pakfire_pty_drained(struct pakfire_pty* pty) { + int q; + int r; + + // We still have data in the buffer + if (pty->stdout.buffered) + return 0; + + // There is still data in the PTY buffer + if (pty->master.io & PAKFIRE_PTY_READY_TO_READ) + return 0; + + // Is there anything in the input buffer? + r = ioctl(pty->master.fd, TIOCINQ, &q); + if (r < 0) { + CTX_DEBUG(pty->ctx, "TIOCINQ failed on master fd %d: %m\n", pty->master.fd); + return -errno; + } + + if (q) + return 0; + + // Is there anything in the output buffer? + r = ioctl(pty->master.fd, TIOCOUTQ, &q); + if (r < 0) { + CTX_DEBUG(pty->ctx, "TIOCOUTQ failed on master fd %d: %m\n", pty->master.fd); + return -errno; + } + + if (q) + return 0; + + // We are fully drained + return 1; +} + +static int pakfire_pty_done(struct pakfire_pty* pty, int code) { + sd_event* loop = NULL; + + // Don't run this more than once + if (pty->state == PAKFIRE_PTY_STATE_DONE) + return 0; + + // Fetch a reference to the event loop + loop = sd_event_ref(pty->loop); + + // Terminate the event loop + return sd_event_exit(loop, code < 0 ? EXIT_FAILURE : code); +} + /* Reads as much data as possible into the buffer */ @@ -158,6 +216,16 @@ static int pakfire_pty_drain_buffer(struct pakfire_pty* pty, int fd, struct pakf static int pakfire_pty_forward(struct pakfire_pty* pty) { int r; + // Make sure we are in the correct state + switch (pty->state) { + case PAKFIRE_PTY_STATE_FORWARDING: + case PAKFIRE_PTY_STATE_DRAINING: + break; + + default: + return -EINVAL; + } + while (pty->master.io || pty->stdin.io || pty->stdout.io) { // Read from standard input if (pty->stdin.io & PAKFIRE_PTY_READY_TO_READ) { @@ -218,6 +286,10 @@ static int pakfire_pty_forward(struct pakfire_pty* pty) { } } + // If we have been requested to drain, and are fully drained, we are done + if (pty->state == PAKFIRE_PTY_STATE_DRAINING && pakfire_pty_drained(pty)) + return pakfire_pty_done(pty, 0); + return 0; } @@ -374,6 +446,9 @@ static int pakfire_pty_setup_forwarding(struct pakfire_pty* pty) { CTX_DEBUG(pty->ctx, "Setting up PTY Forwarding...\n"); + // Mark as forwarding + pty->state = PAKFIRE_PTY_STATE_FORWARDING; + // Connect to standard input/output pty->stdin.fd = STDIN_FILENO; pty->stdout.fd = STDOUT_FILENO; @@ -728,3 +803,9 @@ int pakfire_pty_open(struct pakfire_pty* pty) { return 0; } + +int pakfire_pty_drain(struct pakfire_pty* pty) { + pty->state = PAKFIRE_PTY_STATE_DRAINING; + + return pakfire_pty_drained(pty); +}