return 0;
}
+static int pakfire_pty_buffer_is_full(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
+ if (stdio->buffered >= sizeof(stdio->buffer))
+ return 1;
+
+ return 0;
+}
+
+static int pakfire_pty_buffer_is_empty(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
+ if (stdio->buffered == 0)
+ return 1;
+
+ return 0;
+}
+
+static int pakfire_pty_buffer_has_data(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
+ if (stdio->buffered)
+ return 1;
+
+ return 0;
+}
+
static int pakfire_pty_disconnect(struct pakfire_pty* pty) {
+ DEBUG(pty->ctx, "Disconnecting the PTY\n");
+
// Clear events
if (pty->master.event)
pty->master.event = sd_event_source_unref(pty->master.event);
pty->stdout.event = sd_event_source_unref(pty->stdout.event);
if (pty->sigwinch_event)
pty->sigwinch_event = sd_event_source_unref(pty->sigwinch_event);
- if (pty->exit_event)
- pty->exit_event = sd_event_source_unref(pty->exit_event);
// Close the PTY
if (pty->master.fd >= 0) {
int q;
int r;
+ // We are drained if the file descriptor is closed
+ if (pty->master.fd < 0)
+ return 1;
+
// We still have data in the buffer
- if (pty->stdout.buffered)
+ if (pakfire_pty_buffer_has_data(pty, &pty->stdout))
return 0;
// There is still data in the PTY buffer
if (pty->master.io & PAKFIRE_PTY_READY_TO_READ)
return 0;
- // We are drained if the file descriptor is closed
- if (pty->master.fd < 0)
- return 1;
-
// Is there anything in the input buffer?
r = ioctl(pty->master.fd, TIOCINQ, &q);
if (r < 0) {
if (q)
return 0;
+ DEBUG(pty->ctx, "We are fully drained\n");
+
// We are fully drained
return 1;
}
if (pty->state == PAKFIRE_PTY_STATE_DONE)
return 0;
+ // Mark as done
+ pty->state = PAKFIRE_PTY_STATE_DONE;
+
+ DEBUG(pty->ctx, "PTY is done, code=%d\n", code);
+
// Close the output
if (pty->output) {
fclose(pty->output);
}
// Disconnect
- pakfire_pty_disconnect(pty);
+ //pakfire_pty_disconnect(pty);
+
+ // If there has been an error, we will terminate the event loop
+ if (code)
+ return sd_event_exit(pty->loop, code < 0 ? EXIT_FAILURE : code);
return 0;
}
return 0;
}
-static int pakfire_pty_buffer_is_full(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
- if (stdio->buffered >= sizeof(stdio->buffer))
- return 1;
-
- return 0;
-}
-
-static int pakfire_pty_buffer_is_empty(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
- if (stdio->buffered == 0)
- return 1;
-
- return 0;
-}
-
-static int pakfire_pty_buffer_has_data(struct pakfire_pty* pty, const struct pakfire_pty_stdio* stdio) {
- if (stdio->buffered)
- return 1;
-
- return 0;
-}
-
/*
Maps any CRNL in the buffer to just NL
*/
This function handles the forwarding between the different pipes...
*/
static int pakfire_pty_forward(struct pakfire_pty* pty) {
+ int did_something;
int r;
- // Make sure we are in the correct state
- switch (pty->state) {
- case PAKFIRE_PTY_STATE_FORWARDING:
- case PAKFIRE_PTY_STATE_DRAINING:
- break;
+ // Forward data for as long as we can...
+ for (;;) {
+ did_something = 0;
+
+#if 0
+ // Print status
+ DEBUG(pty->ctx,
+ "PTY forward drained=%d\n"
+ " master = %c%c%c\n"
+ " stdin = %c%c%c buffered = %zu\n"
+ " stdout = %c%c%c buffered = %zu\n",
+ pakfire_pty_drained(pty),
+ (pty->master.io & PAKFIRE_PTY_READY_TO_READ ) ? 'R' : '-',
+ (pty->master.io & PAKFIRE_PTY_READY_TO_WRITE) ? 'W' : '-',
+ (pty->master.io & PAKFIRE_PTY_HANGUP) ? 'H' : '-',
+ (pty->stdin.io & PAKFIRE_PTY_READY_TO_READ ) ? 'R' : '-',
+ (pty->stdin.io & PAKFIRE_PTY_READY_TO_WRITE) ? 'W' : '-',
+ (pty->stdin.io & PAKFIRE_PTY_HANGUP) ? 'H' : '-',
+ pty->stdin.buffered,
+ (pty->stdout.io & PAKFIRE_PTY_READY_TO_READ ) ? 'R' : '-',
+ (pty->stdout.io & PAKFIRE_PTY_READY_TO_WRITE) ? 'W' : '-',
+ (pty->stdout.io & PAKFIRE_PTY_HANGUP) ? 'H' : '-',
+ pty->stdout.buffered
+ );
+#endif
+
+ // Read from standard input if we are asked to read and
+ // there is still space in the buffer..
+ if ((pty->stdin.io & PAKFIRE_PTY_READY_TO_READ)
+ && !pakfire_pty_buffer_is_full(pty, &pty->stdin)) {
+ DEBUG(pty->ctx, "Reading from standard input...\n");
+
+ // Fill up the buffer
+ r = pakfire_pty_fill_buffer(pty, pty->stdin.fd, &pty->stdin);
+ if (r < 0) {
+ switch (-r) {
+ // We have been signaled, that currently there is no data available to read
+ case EAGAIN:
+ pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ break;
- default:
- return -EINVAL;
- }
+ // There has been a problem reading the data
+ case EIO:
+ // We are no longer readable
+ pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
- while (
- ((pty->stdin.io & PAKFIRE_PTY_READY_TO_READ) && !pakfire_pty_buffer_is_full(pty, &pty->stdin)) ||
- ((pty->master.io & PAKFIRE_PTY_READY_TO_WRITE) && pakfire_pty_buffer_has_data(pty, &pty->stdin)) ||
- ((pty->master.io & PAKFIRE_PTY_READY_TO_READ) && !pakfire_pty_buffer_is_full(pty, &pty->stdout)) ||
- ((pty->stdout.io & PAKFIRE_PTY_READY_TO_WRITE) && pakfire_pty_buffer_has_data(pty, &pty->stdout))
- ) {
- // DEBUG(pty->ctx, "PTY forward stdin=%x %zu, stdout=%x %zu, %x, draining=%d, drained=%d\n",
- // pty->stdin.io, pty->stdin.buffered, pty->stdout.io, pty->stdout.buffered, pty->master.io,
- // pty->state == PAKFIRE_PTY_STATE_DRAINING, pakfire_pty_drained(pty));
-
- // Read from standard input
- if (pty->stdin.io & PAKFIRE_PTY_READY_TO_READ) {
- if (!pakfire_pty_buffer_is_full(pty, &pty->stdin)) {
- r = pakfire_pty_fill_buffer(pty, pty->stdin.fd, &pty->stdin);
- if (r < 0) {
- switch (-r) {
- case EAGAIN:
- pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
- break;
-
- case EIO:
- pty->stdin.io |= PAKFIRE_PTY_HANGUP;
- break;
-
- default:
- ERROR(pty->ctx, "Failed reading from standard input: %s\n",
- strerror(-r));
- goto ERROR;
- }
+ // We want to terminate as soon as possible
+ pty->stdin.io |= PAKFIRE_PTY_HANGUP;
- // EOF?
- } else if (r == 0) {
- DEBUG(pty->ctx, "Received EOF from standard input\n");
+ // We don't want to be called again
+ if (pty->stdin.event) {
+ sd_event_source_unref(pty->stdin.event);
+ pty->stdin.event = NULL;
+ }
+ break;
- // We are done reading
- pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ // Any other unexpected error
+ default:
+ ERROR(pty->ctx, "Failed reading from standard input: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // EOF?
+ } else if (r == 0) {
+ DEBUG(pty->ctx, "Received EOF from standard input\n");
+
+ // We are done reading
+ pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
- // And we have reached EOF
- pty->stdin.io |= PAKFIRE_PTY_EOF;
+ // We want to terminate as soon as possible
+ pty->stdin.io |= PAKFIRE_PTY_HANGUP;
+
+ // And we have reached EOF
+ pty->stdin.io |= PAKFIRE_PTY_EOF;
+
+ // We don't want to be called again
+ if (pty->stdin.event) {
+ sd_event_source_unref(pty->stdin.event);
+ pty->stdin.event = NULL;
}
}
+
+ did_something = 1;
}
- // Write to the master
- if (pty->master.io & PAKFIRE_PTY_READY_TO_WRITE) {
- if (pakfire_pty_buffer_has_data(pty, &pty->stdin)) {
- r = pakfire_pty_drain_buffer(pty, pty->master.fd, &pty->stdin);
- if (r < 0) {
- switch (-r) {
- case EAGAIN:
- pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
- break;
-
- case EIO:
- case EPIPE:
- case ECONNRESET:
- pty->master.io |= PAKFIRE_PTY_HANGUP;
- break;
-
- default:
- ERROR(pty->ctx, "Failed writing to the PTY: %s\n", strerror(-r));
- goto ERROR;
- }
- }
- }
+ // Write to the master if there is any input data
+ if ((pty->master.io & PAKFIRE_PTY_READY_TO_WRITE)
+ && pakfire_pty_buffer_has_data(pty, &pty->stdin)) {
+ DEBUG(pty->ctx, "Writing to master...\n");
+
+ // Drain the input buffer...
+ r = pakfire_pty_drain_buffer(pty, pty->master.fd, &pty->stdin);
+ if (r < 0) {
+ switch (-r) {
+ // We have been signaled that we cannot write any more data...
+ case EAGAIN:
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+ break;
- // If the buffer is full drained and we may send EOF
- if (pty->stdin.io & PAKFIRE_PTY_EOF) {
- if (pakfire_pty_buffer_is_empty(pty, &pty->stdin)) {
- r = pakfire_pty_send_eof(pty, pty->master.fd);
- if (r < 0)
- goto ERROR;
+ // There hs been a problem writing the data
+ case EIO:
+ case EPIPE:
+ case ECONNRESET:
+ // We are no longer readable or writable
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+
+ // We want to hang up as soon as possible
+ pty->master.io |= PAKFIRE_PTY_HANGUP;
+
+ // We don't want to be called again
+ if (pty->master.event) {
+ sd_event_source_unref(pty->master.event);
+ pty->master.event = NULL;
+ }
+ break;
- // Don't send EOF again
- pty->stdin.io &= ~PAKFIRE_PTY_EOF;
+ // Log any other errors
+ default:
+ ERROR(pty->ctx, "Failed writing to the PTY: %s\n", strerror(-r));
+ goto ERROR;
}
}
+
+ did_something = 1;
}
- // Read from the master
- if (pty->master.io & PAKFIRE_PTY_READY_TO_READ) {
- if (!pakfire_pty_buffer_is_full(pty, &pty->stdout)) {
- r = pakfire_pty_fill_buffer(pty, pty->master.fd, &pty->stdout);
- if (r < 0) {
- switch (-r) {
- case EAGAIN:
- pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
- break;
-
- case EIO:
- case EPIPE:
- case ECONNRESET:
- pty->master.io |= PAKFIRE_PTY_HANGUP;
- break;
-
- default:
- ERROR(pty->ctx, "Failed reading from the PTY: %s\n", strerror(-r));
- goto ERROR;
- }
+ // If the buffer is full drained and we may send EOF
+ if ((pty->stdin.io & PAKFIRE_PTY_EOF)
+ && pakfire_pty_buffer_is_empty(pty, &pty->stdin)) {
+ // Send EOF
+ r = pakfire_pty_send_eof(pty, pty->master.fd);
+ if (r < 0)
+ goto ERROR;
+
+ // Don't send EOF again
+ pty->stdin.io &= ~PAKFIRE_PTY_EOF;
+ }
+
+ // Read from the master until the buffer is full
+ if ((pty->master.io & PAKFIRE_PTY_READY_TO_READ)
+ && !pakfire_pty_buffer_is_full(pty, &pty->stdout)) {
+ DEBUG(pty->ctx, "Reading from master...\n");
+
+ r = pakfire_pty_fill_buffer(pty, pty->master.fd, &pty->stdout);
+ if (r < 0) {
+ switch (-r) {
+ // We have been signalled that we cannot send any more data...
+ case EAGAIN:
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ break;
+
+ // An problem has occurred...
+ case EIO:
+ case EPIPE:
+ case ECONNRESET:
+ // We are no longer readable or writable
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+
+ // We want to hang up as soon as possible
+ pty->master.io |= PAKFIRE_PTY_HANGUP;
+
+ // We don't want to be called again
+ if (pty->master.event) {
+ sd_event_source_unref(pty->master.event);
+ pty->master.event = NULL;
+ }
+ break;
+
+ default:
+ ERROR(pty->ctx, "Failed reading from the PTY: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // EOF?
+ } else if (r == 0) {
+ // We are no longer readable
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+
+ // We want to hang up as soon as possible
+ pty->master.io |= PAKFIRE_PTY_HANGUP;
+
+ // We don't want to be called again
+ if (pty->master.event) {
+ sd_event_source_unref(pty->master.event);
+ pty->master.event = NULL;
}
}
+
+ did_something = 1;
}
- // Write to standard output
- if (pty->stdout.io & PAKFIRE_PTY_READY_TO_WRITE) {
- if (pakfire_pty_buffer_has_data(pty, &pty->stdout)) {
- r = pakfire_pty_drain_buffer(pty, pty->stdout.fd, &pty->stdout);
- if (r < 0) {
- switch (-r) {
- case EAGAIN:
- pty->stdout.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
- break;
-
- case EIO:
- pty->stdout.io |= PAKFIRE_PTY_HANGUP;
- break;
-
- // This is a special hack for when we have data in the buffer
- // but cannot send it to the callback, yet, because we have not
- // read to the end of the line.
- // To avoid getting stuck in this loop for forever, we simply
- // exit and let the loop call us again.
- case ENOMSG:
- return 0;
-
- default:
- ERROR(pty->ctx, "Failed writing to standard output: %s\n", strerror(-r));
- goto ERROR;
- }
+ // Write to standard output until the buffer is drained
+ if ((pty->stdout.io & PAKFIRE_PTY_READY_TO_WRITE)
+ && pakfire_pty_buffer_has_data(pty, &pty->stdout)) {
+ DEBUG(pty->ctx, "Writing to standard output...\n");
+
+ r = pakfire_pty_drain_buffer(pty, pty->stdout.fd, &pty->stdout);
+ if (r < 0) {
+ switch (-r) {
+ // We have been signalled that we are no longer able to write...
+ case EAGAIN:
+ pty->stdout.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+ break;
+
+ // A problem has occurred...
+ case EIO:
+ // We are no longer readable or writable
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+
+ // We want to hang up as soon as possible
+ pty->master.io |= PAKFIRE_PTY_HANGUP;
+
+ // We don't want to be called again
+ if (pty->master.event) {
+ sd_event_source_unref(pty->master.event);
+ pty->master.event = NULL;
+ }
+ break;
+
+ // This is a special hack for when we have data in the buffer
+ // but cannot send it to the callback, yet, because we have not
+ // read to the end of the line.
+ // To avoid getting stuck in this loop for forever, we simply
+ // exit and let the loop call us again.
+ case ENOMSG:
+ break;
+ // return 0;
+
+ default:
+ ERROR(pty->ctx, "Failed writing to standard output: %s\n", strerror(-r));
+ goto ERROR;
}
}
+
+ did_something = 1;
}
- if ((pty->master.io|pty->stdin.io|pty->stdout.io) & PAKFIRE_PTY_HANGUP)
+ // Terminate the loop if we found nothing to do
+ if (!did_something)
+ break;
+ }
+
+ if ((pty->master.io|pty->stdin.io|pty->stdout.io) & PAKFIRE_PTY_HANGUP) {
+ DEBUG(pty->ctx, "Something hung up...\n");
+
+ // Terminate the event loop after all buffers have been emptied or cannot be emptied
+ if ((!pakfire_pty_buffer_has_data(pty, &pty->stdout)
+ || (pty->stdout.io & PAKFIRE_PTY_HANGUP))
+ && (!pakfire_pty_buffer_has_data(pty, &pty->stdin)
+ || (pty->stdin.io & PAKFIRE_PTY_HANGUP)))
return pakfire_pty_done(pty, 0);
}
DEBUG(self->ctx, "Exiting the PTY...\n");
// Unless we are already drained, try to drain all buffers
- if (!pakfire_pty_drain(self)) {
+ if (pakfire_pty_drain(self)) {
if (!(self->master.io & PAKFIRE_PTY_HANGUP))
self->master.io |= PAKFIRE_PTY_READY_TO_READ | PAKFIRE_PTY_READY_TO_WRITE;
if (pty->socket[1] >= 0)
close(pty->socket[1]);
+ if (pty->exit_event)
+ sd_event_source_unref(pty->exit_event);
+
if (pty->loop)
sd_event_unref(pty->loop);
if (pty->ctx)