#include <pakfire/ctx.h>
#include <pakfire/file.h>
#include <pakfire/filelist.h>
-#include <pakfire/log_stream.h>
#include <pakfire/pty.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
// IO Flags
enum pakfire_pty_io {
- PAKFIRE_PTY_CONNECT = (1 << 0),
- PAKFIRE_PTY_READY_TO_READ = (1 << 1),
- PAKFIRE_PTY_READY_TO_WRITE = (1 << 2),
- PAKFIRE_PTY_HANGUP = (1 << 3),
- PAKFIRE_PTY_EOF = (1 << 4),
- PAKFIRE_PTY_MAP_CRNL = (1 << 5),
+ PAKFIRE_PTY_READY_TO_READ = (1 << 0),
+ PAKFIRE_PTY_READY_TO_WRITE = (1 << 1),
+ PAKFIRE_PTY_HANGUP = (1 << 2),
+ PAKFIRE_PTY_MAP_CRNL = (1 << 3),
} io;
// Event Source
sd_event_source* event;
-
- // Callbacks
- struct pakfire_pty_callback {
- pakfire_pty_stdin_callback stdin_callback;
- pakfire_pty_stdout_callback stdout_callback;
- void* data;
- } callbacks;
};
struct pakfire_pty {
PAKFIRE_PTY_STATE_DRAINING,
PAKFIRE_PTY_STATE_DONE,
} state;
-
- // Stream for output
- FILE* output;
-
- // Log Stream
- struct pakfire_log_stream* stream;
};
static int pakfire_pty_same_inode(struct pakfire_pty* pty, int fd1, int fd2) {
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;
DEBUG(pty->ctx, "PTY is done, code=%d\n", code);
- // Close the output
- if (pty->output) {
- fclose(pty->output);
- pty->output = NULL;
- }
-
// Disconnect
//pakfire_pty_disconnect(pty);
return 0;
}
-static int pakfire_pty_send_eof(struct pakfire_pty* pty, int fd) {
- const char eof = VEOF;
- int r;
-
- // Send EOF (Ctrl-D)
- r = write(fd, &eof, sizeof(eof));
- if (r < 0) {
- ERROR(pty->ctx, "Could not write EOF: %s\n", strerror(errno));
- return -errno;
- }
-
- return 0;
-}
-
/*
Maps any CRNL in the buffer to just NL
*/
static int pakfire_pty_fill_buffer(struct pakfire_pty* pty, int fd, struct pakfire_pty_stdio* stdio) {
ssize_t bytes_read = 0;
- // Call the callback (if we have one)
- if (stdio->callbacks.stdin_callback) {
- bytes_read = stdio->callbacks.stdin_callback(pty->ctx, stdio->callbacks.data,
- stdio->buffer + stdio->buffered, sizeof(stdio->buffer) - stdio->buffered);
-
- // Abort on errors
- if (bytes_read < 0)
- return bytes_read;
-
- DEBUG(pty->ctx, "Read %zd byte(s) from input callback\n", bytes_read);
-
- // Otherwise read from the file descriptor
- } else if (fd >= 0) {
- bytes_read = read(fd, stdio->buffer + stdio->buffered, sizeof(stdio->buffer) - stdio->buffered);
+ // Read from the file descriptor
+ bytes_read = read(fd, stdio->buffer + stdio->buffered, sizeof(stdio->buffer) - stdio->buffered);
- // Abort on errors
- if (bytes_read < 0)
- return -errno;
-
- DEBUG(pty->ctx, "Read %zd byte(s) from fd=%d\n", bytes_read, fd);
- }
+ // Abort on errors
+ if (bytes_read < 0)
+ return -errno;
// Successful read
stdio->buffered += bytes_read;
if (stdio->io & PAKFIRE_PTY_MAP_CRNL)
pakfire_pty_map_crnl(stdio);
- // Call the callback if possible
- if (stdio->callbacks.stdout_callback) {
- // Send the line
- bytes_written = stdio->callbacks.stdout_callback(pty->ctx,
- stdio->callbacks.data, stdio->buffer, stdio->buffered);
-
- // Abort on error
- if (bytes_written < 0)
- return bytes_written;
-
- DEBUG(pty->ctx, "Sent %zd byte(s) to output callback\n", bytes_written);
+ // Write to the file descriptor
+ bytes_written = write(fd, stdio->buffer, stdio->buffered);
- // Otherwise we write to the file descriptor
- } else if (fd >= 0) {
- bytes_written = write(fd, stdio->buffer, stdio->buffered);
-
- // Abort on error
- if (bytes_written < 0)
- return -errno;
-
- DEBUG(pty->ctx, "Wrote %zd byte(s) to fd=%d\n", bytes_written, fd);
- }
+ // Abort on error
+ if (bytes_written < 0)
+ return -errno;
// Successful write
memmove(stdio->buffer, stdio->buffer + bytes_written, stdio->buffered - bytes_written);
// 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);
did_something = 1;
}
- // 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)) {
// Mark as forwarding
pty->state = PAKFIRE_PTY_STATE_FORWARDING;
+ // Open standard input
+ pty->stdin.fd = pakfire_pty_reopen(pty, STDIN_FILENO, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (pty->stdin.fd < 0) {
+ DEBUG(pty->ctx, "Could not re-open standard input: %s. Ignoring.\n", strerror(-pty->stdin.fd));
+
+ // Use the original file descriptor
+ pty->stdin.fd = STDIN_FILENO;
+
+ // Request to close the file descriptor afterwards
+ } else {
+ pty->stdin.close_fd = 1;
+ }
+
+ // Open standard output
+ pty->stdout.fd = pakfire_pty_reopen(pty, STDOUT_FILENO, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
+ if (pty->stdout.fd < 0) {
+ DEBUG(pty->ctx, "Could not re-open standard output: %s. Ignoring.\n", strerror(-pty->stdout.fd));
+
+ // Use the original file descriptor
+ pty->stdout.fd = STDOUT_FILENO;
+
+ // Request to close the file descriptor afterwards
+ } else {
+ pty->stdout.close_fd = 1;
+ }
+
// Copy the terminal dimensions to the PTY
if (isatty(pty->stdout.fd)) {
// Fetch dimensions
if (pty->exit_event)
sd_event_source_unref(pty->exit_event);
- if (pty->stream)
- pakfire_log_stream_unref(pty->stream);
if (pty->loop)
sd_event_unref(pty->loop);
if (pty->ctx)
pakfire_ctx_unref(pty->ctx);
- if (pty->output)
- fclose(pty->output);
free(pty);
}
return NULL;
}
-static int pakfire_pty_connect_null(struct pakfire_pty* pty, int fileno) {
- int fd = -EBADF;
- int flags = 0;
- int r;
-
- switch (fileno) {
- case STDIN_FILENO:
- flags |= O_RDONLY;
- break;
-
- case STDOUT_FILENO:
- case STDERR_FILENO:
- flags |= O_WRONLY;
- break;
-
- default:
- return -EINVAL;
- }
-
-
- // Open /dev/null
- fd = open("/dev/null", flags);
- if (fd < 0) {
- ERROR(pty->ctx, "Failed to open /dev/null: %m\n");
- r = -errno;
- goto ERROR;
- }
-
- // Copy to the desired file descriptor
- r = dup2(fd, fileno);
- if (r < 0) {
- ERROR(pty->ctx, "Failed to duplicate the file descriptor: %m\n");
- r = -errno;
- goto ERROR;
- }
-
-ERROR:
- if (fd >= 0)
- close(fd);
-
- return r;
-}
-
-static int pakfire_pty_disable_echo(struct pakfire_pty* self, int fd) {
- struct termios t = {};
- int r;
-
- // Fetch current attributes
- r = tcgetattr(fd, &t);
- if (r < 0) {
- ERROR(self->ctx, "Failed to fetch terminal attributes from %d: %m\n", fd);
- return -errno;
- }
-
- // Disable echo
- t.c_lflag &= ~ECHO;
-
- // Restore the changed attributes
- r = tcsetattr(fd, TCSANOW, &t);
- if (r < 0) {
- ERROR(self->ctx, "Failed to restore terminal attributes to %d: %m\n", fd);
- return -errno;
- }
-
- return 0;
-}
-
/*
Sets up the terminal in the child process...
*/
DEBUG(pty->ctx, "Opened a new terminal %d\n", fd);
- // Disable echo
- if (pty->stdin.callbacks.stdin_callback) {
- r = pakfire_pty_disable_echo(pty, fd);
- if (r < 0)
- goto ERROR;
- }
-
// Connect the new terminal to standard input
- if (pty->stdin.io & PAKFIRE_PTY_CONNECT) {
- r = dup2(fd, STDIN_FILENO);
- if (r < 0) {
- ERROR(pty->ctx, "Failed to open standard input: %s\n", strerror(errno));
- r = -errno;
- goto ERROR;
- }
-
- // Otherwise we connect standard input to /dev/null
- } else {
- r = pakfire_pty_connect_null(pty, STDIN_FILENO);
- if (r < 0)
- goto ERROR;
+ r = dup2(fd, STDIN_FILENO);
+ if (r < 0) {
+ ERROR(pty->ctx, "Failed to open standard input: %s\n", strerror(errno));
+ r = -errno;
+ goto ERROR;
}
// Connect the new terminal to standard output
- if (pty->stdout.io & PAKFIRE_PTY_CONNECT) {
- r = dup2(fd, STDOUT_FILENO);
- if (r < 0) {
- ERROR(pty->ctx, "Failed to open standard output: %s\n", strerror(errno));
- r = -errno;
- goto ERROR;
- }
-
- // Connect stderr, too
- r = dup2(fd, STDERR_FILENO);
- if (r < 0) {
- ERROR(pty->ctx, "Failed to open standard error: %s\n", strerror(errno));
- r = -errno;
- goto ERROR;
- }
-
- // Otherwise we connect standard output to /dev/null
- } else {
- r = pakfire_pty_connect_null(pty, STDOUT_FILENO);
- if (r < 0)
- goto ERROR;
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0) {
+ ERROR(pty->ctx, "Failed to open standard output: %s\n", strerror(errno));
+ r = -errno;
+ goto ERROR;
+ }
- // Also redirect stderr
- r = pakfire_pty_connect_null(pty, STDERR_FILENO);
- if (r < 0)
- goto ERROR;
+ // Connect stderr, too
+ r = dup2(fd, STDERR_FILENO);
+ if (r < 0) {
+ ERROR(pty->ctx, "Failed to open standard error: %s\n", strerror(errno));
+ r = -errno;
+ goto ERROR;
}
ERROR:
return 0;
}
-static int __pakfire_pty_capture_output(struct pakfire_ctx* ctx,
- void* data, const char* line, const size_t length) {
- struct pakfire_pty* self = data;
-
- // Write to the buffer
- return fwrite(line, 1, length, self->output);
-}
-
-int pakfire_pty_capture_output(struct pakfire_pty* self, char** output, size_t* length) {
- if (!output || !length)
- return -EINVAL;
-
- // Open the memory stream
- self->output = open_memstream(output, length);
- if (!self->output) {
- ERROR(self->ctx, "Failed to open memory stream: %m\n");
- return -errno;
- }
-
- // Set the callback
- pakfire_pty_set_stdout_callback(self, __pakfire_pty_capture_output, self);
-
- return 0;
-}
-
-/*
- Log Stream
-*/
-int pakfire_pty_log_stream(struct pakfire_pty* self,
- pakfire_log_stream_callback callback, void* data) {
- int r;
-
- // Create a new log stream
- r = pakfire_log_stream_create(&self->stream, self->ctx, callback, data);
- if (r < 0)
- return r;
-
- // Register the callback
- pakfire_pty_set_stdout_callback(self, pakfire_log_stream_pty, self->stream);
-
- return 0;
-}
-
-/*
- Interactive Mode
-*/
-int pakfire_pty_interactive(struct pakfire_pty* self) {
- // Fail if something is already open
- if (self->stdin.fd >= 0 || self->stdout.fd >= 0)
- return -ENOTSUP;
-
- // Open standard input
- self->stdin.fd = pakfire_pty_reopen(self, STDIN_FILENO, O_RDONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (self->stdin.fd < 0) {
- DEBUG(self->ctx, "Could not re-open standard input: %s. Ignoring.\n", strerror(-self->stdin.fd));
-
- // Use the original file descriptor
- self->stdin.fd = STDIN_FILENO;
-
- // Request to close the file descriptor afterwards
- } else {
- self->stdin.close_fd = 1;
- }
-
- // Open standard output
- self->stdout.fd = pakfire_pty_reopen(self, STDOUT_FILENO, O_WRONLY|O_CLOEXEC|O_NOCTTY|O_NONBLOCK);
- if (self->stdout.fd < 0) {
- DEBUG(self->ctx, "Could not re-open standard output: %s. Ignoring.\n", strerror(-self->stdout.fd));
-
- // Use the original file descriptor
- self->stdout.fd = STDOUT_FILENO;
-
- // Request to close the file descriptor afterwards
- } else {
- self->stdout.close_fd = 1;
- }
-
- // Connect everything
- self->stdin.io |= PAKFIRE_PTY_CONNECT;
- self->stdout.io |= PAKFIRE_PTY_CONNECT;
-
- return 0;
-}
-
-/*
- Standard Input/Output Callbacks
-*/
-void pakfire_pty_set_stdin_callback(struct pakfire_pty* pty,
- pakfire_pty_stdin_callback callback, void* data) {
- pty->stdin.callbacks.stdin_callback = callback;
- pty->stdin.callbacks.data = data;
-
- // We are now ready to read
- pty->stdin.io |=
- PAKFIRE_PTY_CONNECT |
- PAKFIRE_PTY_READY_TO_READ;
-}
-
-void pakfire_pty_set_stdout_callback(struct pakfire_pty* pty,
- pakfire_pty_stdout_callback callback, void* data) {
- pty->stdout.callbacks.stdout_callback = callback;
- pty->stdout.callbacks.data = data;
-
- // We are now ready to write
- pty->stdout.io |=
- PAKFIRE_PTY_CONNECT |
- PAKFIRE_PTY_READY_TO_WRITE |
- PAKFIRE_PTY_MAP_CRNL;
-}
-
ssize_t pakfire_pty_send_buffer(struct pakfire_ctx* ctx,
void* data, char* buffer, size_t length) {
struct pakfire_pty_buffer* input = data;