]> git.ipfire.org Git - pakfire.git/commitdiff
pty: Implement draining the buffers
authorMichael Tremer <michael.tremer@ipfire.org>
Sun, 6 Oct 2024 13:46:21 +0000 (13:46 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sun, 6 Oct 2024 13:46:21 +0000 (13:46 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/include/pakfire/pty.h
src/libpakfire/jail.c
src/libpakfire/pty.c

index f3f394227e6978b30180a3092baf38dad2b07c2a..0a49965a20c3a49d2438fe1c1efedc52d3443a7b 100644 (file)
@@ -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 */
index 99838e12c7670da3ddcae215258887eb50e25664..f8a1040f7d4c7b3f8cdc44bc19b20b338cd5e7ba 100644 (file)
@@ -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);
 }
index c1e963508adfaf257d89df13d4bf1ee0262b1cb1..047477a5f6026910537cdafcb339ded779488cd0 100644 (file)
@@ -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);
+}