// Log pipes
struct pakfire_jail_pipes {
- int stdin[2];
- int stdout[2];
- int stderr[2];
-
// Logging
int log_INFO[2];
int log_ERROR[2];
// Standard Input
struct pakfire_jail_pty_stdio {
int fd;
- char buffer[LINE_MAX];
+ struct pakfire_log_buffer buffer;
struct termios attrs;
int fdflags;
enum pakfire_jail_pty_flags flags;
return 0;
}
+#if 0
static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
struct pakfire_jail_exec* ctx, const int fd) {
int r;
return r;
}
+#endif
static int pakfire_jail_recv_fd(struct pakfire_jail* jail, int socket, int* fd) {
const size_t payload_length = sizeof(fd);
return 0;
}
+static int pakfire_jail_fill_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
+ int r;
+
+ // Skip this if there is not space left in the buffer
+ if (buffer->used >= sizeof(buffer->data))
+ return 0;
+
+ // Fill the buffer
+ r = read(fd, buffer->data + buffer->used, sizeof(buffer->data) - buffer->used);
+
+ // Handle errors
+ if (r < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EIO:
+ break;
+
+ default:
+ return -errno;
+ }
+
+ // EOF
+ } else if (r == 0) {
+ // XXX What to do here?
+
+ // Successful read
+ } else {
+ buffer->used += r;
+ }
+
+ return 0;
+}
+
+static int pakfire_jail_drain_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
+ int r;
+
+ // Nothing to do if the buffer is empty
+ if (!buffer->used)
+ return 0;
+
+ // Drain the buffer
+ r = write(fd, buffer->data, buffer->used);
+
+ // Handle errors
+ if (r < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EIO:
+ break;
+
+ default:
+ return -errno;
+ }
+
+ // Successful write
+ } else {
+ memmove(buffer->data, buffer->data + r, buffer->used - r);
+
+ buffer->used -= r;
+ }
+
+ return 0;
+}
+
+static int pakfire_jail_forward_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+ int r;
+
+ // Read from standard input
+ if (ctx->pty.stdin.flags & PAKFIRE_JAIL_PTY_READY_TO_READ) {
+ r = pakfire_jail_fill_buffer(jail, ctx->pty.stdin.fd, &ctx->pty.stdin.buffer);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed reading from standard input: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done reading for now
+ ctx->pty.stdin.flags &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
+
+ // But we may have data to write
+ if (ctx->pty.stdin.buffer.used)
+ ctx->pty.master.flags |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+ }
+
+ // Write to the master
+ if (ctx->pty.master.flags & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
+ r = pakfire_jail_drain_buffer(jail, ctx->pty.master.fd, &ctx->pty.stdin.buffer);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed writing to the PTY: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done writing for now
+ ctx->pty.master.flags &= ~PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+ }
+
+ // Read from the master
+ if (ctx->pty.master.flags & PAKFIRE_JAIL_PTY_READY_TO_READ) {
+ r = pakfire_jail_fill_buffer(jail, ctx->pty.master.fd, &ctx->pty.stdout.buffer);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed reading from the PTY: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done reading for now
+ ctx->pty.master.flags &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
+
+ // But we may have data to write
+ if (ctx->pty.stdout.buffer.used)
+ ctx->pty.stdout.flags |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+ }
+
+ // Write to standard output
+ if (ctx->pty.stdout.flags & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
+ r = pakfire_jail_drain_buffer(jail, ctx->pty.stdout.fd, &ctx->pty.stdout.buffer);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed writing to standard output: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done writing for now
+ ctx->pty.stdout.flags &= ~PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+ }
+
+ return 0;
+}
+
static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
int epollfd = -1;
struct epoll_event events[EPOLL_MAX_EVENTS];
int r = 0;
// Fetch file descriptors from context
- const int stdin = pakfire_jail_get_pipe_to_write(jail, &ctx->pipes.stdin);
- const int stdout = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stdout);
- const int stderr = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr);
const int pidfd = ctx->pidfd;
// Fetch the UNIX domain socket
const int fd;
const int events;
} fds[] = {
- // Standard input/output
- { stdin, EPOLLOUT },
- { stdout, EPOLLIN },
- { stderr, EPOLLIN },
-
// Timer
{ timerfd, EPOLLIN },
void* data = NULL;
int priority;
+ // Handle PTY forwarding events
+ if (ctx->pty.master.fd == fd) {
+ if (e & (EPOLLIN|EPOLLHUP))
+ ctx->pty.master.flags |= PAKFIRE_JAIL_PTY_READY_TO_READ;
+
+ if (e & (EPOLLOUT|EPOLLHUP))
+ ctx->pty.master.flags |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+
+ // Perform the work
+ r = pakfire_jail_forward_pty(jail, ctx);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // Handle standard input
+ } else if (ctx->pty.stdin.fd == fd) {
+ if (e & (EPOLLIN|EPOLLHUP))
+ ctx->pty.stdin.flags |= PAKFIRE_JAIL_PTY_READY_TO_READ;
+
+ // Perform the work
+ r = pakfire_jail_forward_pty(jail, ctx);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
+ goto ERROR;
+ }
+
+ // Handle standard output
+ } else if (ctx->pty.stdout.fd == fd) {
+ if (e & (EPOLLOUT|EPOLLHUP))
+ ctx->pty.stdout.flags |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
+
+ // Perform the work
+ r = pakfire_jail_forward_pty(jail, ctx);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
+ goto ERROR;
+ }
+ }
+
// Check if there is any data to be read
if (e & EPOLLIN) {
// Handle any changes to the PIDFD
callback = pakfire_jail_log;
#endif /* ENABLE_DEBUG */
- // Handle anything from the log pipes
- } else if (fd == stdout) {
- buffer = &ctx->buffers.stdout;
- priority = LOG_INFO;
-
- // Send any output to the default logger if no callback is set
- if (ctx->communicate.out) {
- callback = ctx->communicate.out;
- data = ctx->communicate.data;
- } else {
- callback = jail->callbacks.log;
- data = jail->callbacks.log_data;
- }
-
- } else if (fd == stderr) {
- buffer = &ctx->buffers.stderr;
- priority = LOG_ERR;
-
- // Send any output to the default logger if no callback is set
- if (ctx->communicate.out) {
- callback = ctx->communicate.out;
- data = ctx->communicate.data;
- } else {
- callback = jail->callbacks.log;
- data = jail->callbacks.log_data;
- }
-
// Handle socket messages
} else if (fd == socket_recv) {
// Receive the passed FD
goto ERROR;
}
- if (e & EPOLLOUT) {
- // Handle standard input
- if (fd == stdin) {
- r = pakfire_jail_stream_stdin(jail, ctx, fd);
- if (r) {
- switch (errno) {
- // Ignore if we filled up the buffer
- case EAGAIN:
- break;
-
- default:
- ERROR(jail->pakfire, "Could not write to stdin: %m\n");
- goto ERROR;
- }
- }
- }
- }
-
// Check if any file descriptors have been closed
if (e & EPOLLHUP) {
// Remove the file descriptor
CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->pty.console, ctx->pty.master.fd);
-#if 0
+ // Unlock the master device
+ r = unlockpt(ctx->pty.master.fd);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Could not unlock the PTY: %s\n", strerror(errno));
+ return -errno;
+ }
+
// Create a symlink
- r = pakfire_symlink(jail->ctx, "/dev/console", ctx->pty.console);
+ r = pakfire_symlink(jail->ctx, ctx->pty.console, "/dev/console");
if (r)
return r;
-#endif
return r;
}
+static int pakfire_jail_setup_terminal(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+ int fd;
+ int r;
+
+ // Open a new terminal
+ fd = open("/dev/console", O_RDWR|O_NOCTTY);
+ if (fd < 0) {
+ CTX_ERROR(jail->ctx, "Failed to open a new terminal: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ CTX_DEBUG(jail->ctx, "Opened a new terminal %d\n", fd);
+
+ // Connect the new terminal to standard input
+ r = dup2(fd, STDIN_FILENO);
+ if (r < 0) {
+ CTX_ERROR(jail->ctx, "Failed to open standard input: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Connect the new terminal to standard output
+ r = dup2(fd, STDOUT_FILENO);
+ if (r < 0) {
+ CTX_ERROR(jail->ctx, "Failed to open standard output: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Connect the new terminal to standard error
+ r = dup2(fd, STDERR_FILENO);
+ if (r < 0) {
+ CTX_ERROR(jail->ctx, "Failed to open standard error: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
const char* argv[]) {
int r;
}
}
-#if 0
// Create a new session
r = setsid();
if (r < 0) {
CTX_ERROR(jail->ctx, "Could not create a new session: %s\n", strerror(errno));
return r;
}
-#endif
// Allocate a new PTY
r = pakfire_jail_open_pty(jail, ctx);
return r;
}
+ // Setup the terminal
+ r = pakfire_jail_setup_terminal(jail, ctx);
+ if (r)
+ return r;
+
// Close the master of the PTY
close(ctx->pty.master.fd);
ctx->pty.master.fd = -1;
close(ctx->pipes.log_DEBUG[0]);
#endif /* ENABLE_DEBUG */
- // Connect standard input
- if (ctx->pipes.stdin[0] >= 0) {
- r = dup2(ctx->pipes.stdin[0], STDIN_FILENO);
- if (r < 0) {
- ERROR(jail->pakfire, "Could not connect fd %d to stdin: %m\n",
- ctx->pipes.stdin[0]);
-
- return 1;
- }
- }
-
- // Connect standard output and error
- if (ctx->pipes.stdout[1] >= 0 && ctx->pipes.stderr[1] >= 0) {
- r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
- if (r < 0) {
- ERROR(jail->pakfire, "Could not connect fd %d to stdout: %m\n",
- ctx->pipes.stdout[1]);
-
- return 1;
- }
-
- r = dup2(ctx->pipes.stderr[1], STDERR_FILENO);
- if (r < 0) {
- ERROR(jail->pakfire, "Could not connect fd %d to stderr: %m\n",
- ctx->pipes.stderr[1]);
-
- return 1;
- }
-
- // Close the pipe (as we have moved the original file descriptors)
- pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
- pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
- pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
- }
-
// Reset open file limit (http://0pointer.net/blog/file-descriptor-limits.html)
r = pakfire_rlimit_reset_nofile(jail->pakfire);
if (r)
.socket = { -1, -1 },
.pipes = {
- .stdin = { -1, -1 },
- .stdout = { -1, -1 },
- .stderr = { -1, -1 },
.log_INFO = { -1, -1 },
.log_ERROR = { -1, -1 },
#ifdef ENABLE_DEBUG
goto ERROR;
}
- // Create pipes to communicate with child process if we are not running interactively
- if (!interactive) {
- // stdin (only if callback is set)
- if (ctx.communicate.in) {
- r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdin, 0);
- if (r)
- goto ERROR;
- }
-
- // stdout
- r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, 0);
- if (r)
- goto ERROR;
-
- // stderr
- r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, 0);
- if (r)
- goto ERROR;
- }
-
// Setup pipes for logging
// INFO
r = pakfire_jail_setup_pipe(jail, &ctx.pipes.log_INFO, O_CLOEXEC);
}
// Close any file descriptors
- pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
- pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
- pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
if (ctx.pidfd >= 0)
close(ctx.pidfd);
if (ctx.pty.master.fd >= 0)