#include <fcntl.h>
#include <linux/capability.h>
#include <linux/sched.h>
-#include <sys/wait.h>
-#include <linux/wait.h>
#include <sched.h>
#include <signal.h>
#include <stdlib.h>
#include <syscall.h>
#include <sys/capability.h>
-#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <sys/mount.h>
#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
#include <sys/types.h>
-#include <sys/wait.h>
-#include <termios.h>
// libnl3
#include <net/if.h>
#include <pakfire/pakfire.h>
#include <pakfire/path.h>
#include <pakfire/private.h>
+#include <pakfire/pty.h>
#include <pakfire/pwd.h>
#include <pakfire/string.h>
#include <pakfire/util.h>
#define BUFFER_SIZE 1024 * 64
#define ENVIRON_SIZE 128
-#define EPOLL_MAX_EVENTS 2
#define MAX_MOUNTPOINTS 8
// The default environment that will be set for every command
struct pakfire_log_buffer {
char data[BUFFER_SIZE];
size_t used;
- int priority;
-};
-
-enum pakfire_jail_pty_io {
- PAKFIRE_JAIL_PTY_READY_TO_READ = (1 << 0),
- PAKFIRE_JAIL_PTY_READY_TO_WRITE = (1 << 1),
-};
-
-struct pakfire_jail_pty_stdio {
- // File Descriptor
- int fd;
-
- // Buffer
- struct pakfire_log_buffer buffer;
-
- // Terminal Attributes
- struct termios attrs;
-
- // File Descriptor Flags
- int flags;
-
- // IO Flags
- enum pakfire_jail_pty_io io;
};
struct pakfire_jail_mountpoint {
pid_t pid;
int pidfd;
- // Socket to pass FDs
- int socket[2];
-
// FD to notify the client that the parent has finished initialization
int completed_fd;
+ // PTY
+ struct pakfire_pty* pty;
+
// Log pipes
struct pakfire_jail_pipes {
// Logging
struct pakfire_cgroup* cgroup;
struct pakfire_cgroup_stats cgroup_stats;
-
- // PTY
- struct pakfire_jail_pty {
- // The path to the console
- char console[PATH_MAX];
-
- // The master device
- struct pakfire_jail_pty_master {
- int fd;
- enum pakfire_jail_pty_io io;
- } master;
-
- // Standard Input/Output
- struct pakfire_jail_pty_stdio stdin;
- struct pakfire_jail_pty_stdio stdout;
- } pty;
};
static int pivot_root(const char* new_root, const char* old_root) {
}
}
-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_with_callback(struct pakfire_jail* jail,
struct pakfire_log_buffer* buffer, pakfire_jail_stdout_callback callback, void* data) {
const char* eol = NULL;
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;
-
- // Do not try to write to an invalid file descriptor
- if (fd < 0)
- 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;
-}
-
/*
Passes any log messages on to the context logger
*/
}
}
-static int pakfire_jail_recv_fd(struct pakfire_jail* jail, int socket, int* fd) {
- const size_t payload_length = sizeof(fd);
- char buffer[CMSG_SPACE(payload_length)];
- int r;
-
- struct msghdr msg = {
- .msg_control = buffer,
- .msg_controllen = sizeof(buffer),
- };
-
- // Receive the message
- r = recvmsg(socket, &msg, 0);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not receive file descriptor: %s\n", strerror(errno));
- return -errno;
- }
-
- // Fetch the payload
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- if (!cmsg)
- return -EBADMSG;
-
- *fd = *((int*)CMSG_DATA(cmsg));
-
- CTX_DEBUG(jail->ctx, "Received fd %d from socket %d\n", *fd, socket);
-
- return 0;
-}
-
-static int pakfire_jail_send_fd(struct pakfire_jail* jail, int socket, int fd) {
- const size_t payload_length = sizeof(fd);
- char buffer[CMSG_SPACE(payload_length)];
- int r;
-
- CTX_DEBUG(jail->ctx, "Sending fd %d to socket %d\n", fd, socket);
-
- // Header
- struct msghdr msg = {
- .msg_control = buffer,
- .msg_controllen = sizeof(buffer),
- };
-
- // Payload
- struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
- cmsg->cmsg_level = SOL_SOCKET;
- cmsg->cmsg_type = SCM_RIGHTS;
- cmsg->cmsg_len = CMSG_LEN(payload_length);
-
- // Set payload
- *((int*)CMSG_DATA(cmsg)) = fd;
-
- // Send the message
- r = sendmsg(socket, &msg, 0);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not send file descriptor: %s\n", strerror(errno));
- return -errno;
- }
-
- return 0;
-}
-
-static void pakfire_jail_close_pipe(struct pakfire_jail* jail, int fds[2]) {
- for (unsigned int i = 0; i < 2; i++)
- if (fds[i] >= 0)
- close(fds[i]);
-}
-
-/*
- This is a convenience function to fetch the reading end of a pipe and
- closes the write end.
-*/
-static int pakfire_jail_get_pipe_to_read(struct pakfire_jail* jail, int (*fds)[2]) {
- // Give the variables easier names to avoid confusion
- int* fd_read = &(*fds)[0];
- int* fd_write = &(*fds)[1];
-
- // Close the write end of the pipe
- if (*fd_write >= 0) {
- close(*fd_write);
- *fd_write = -1;
- }
-
- // Return the read end
- if (*fd_read >= 0)
- return *fd_read;
-
- return -1;
-}
-
-static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
- // Give the variables easier names to avoid confusion
- int* fd_read = &(*fds)[0];
- int* fd_write = &(*fds)[1];
-
- // Close the read end of the pipe
- if (*fd_read >= 0) {
- close(*fd_read);
- *fd_read = -1;
- }
-
- // Return the write end
- if (*fd_write >= 0)
- return *fd_write;
-
- return -1;
-}
-
-static int pakfire_jail_epoll_add_fd(struct pakfire_jail* jail, int epollfd, int fd, int events) {
- struct epoll_event event = {
- .events = events|EPOLLHUP,
- .data = {
- .fd = fd,
- },
- };
- int r;
-
- // Read flags
- int flags = fcntl(fd, F_GETFL, 0);
-
- // Set modified flags
- r = fcntl(fd, F_SETFL, flags|O_NONBLOCK);
- if (r < 0) {
- CTX_ERROR(jail->ctx, "Could not set file descriptor %d into non-blocking mode: %s\n",
- fd, strerror(errno));
- return -errno;
- }
-
- // Add the file descriptor to the loop
- r = epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &event);
- if (r < 0) {
- CTX_ERROR(jail->ctx, "Could not add file descriptor %d to epoll(): %s\n",
- fd, strerror(errno));
- return -errno;
- }
-
- return 0;
-}
-
-// PTY Forwarding
-
-static int pakfire_jail_enable_raw_mode(struct pakfire_jail* jail,
- struct pakfire_jail_pty_stdio* stdio) {
- struct termios raw_attrs;
- int r;
-
- // Skip if we don't know the file descriptor
- if (stdio->fd < 0)
- return 0;
-
- // Skip everything if fd is not a TTY
- if (!isatty(stdio->fd))
- return 0;
-
- // Store flags
- stdio->flags = fcntl(stdio->fd, F_GETFL);
- if (stdio->flags < 0) {
- CTX_ERROR(jail->ctx, "Could not fetch flags from fd %d: %s\n",
- stdio->fd, strerror(errno));
- return -errno;
- }
-
- // Fetch all attributes
- r = tcgetattr(stdio->fd, &stdio->attrs);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not fetch terminal attributes from fd %d: %s\n",
- stdio->fd, strerror(errno));
- return -errno;
- }
-
- // Copy all attributes
- raw_attrs = stdio->attrs;
-
- // Make it RAW
- cfmakeraw(&raw_attrs);
-
- switch (stdio->fd) {
- case STDIN_FILENO:
- raw_attrs.c_oflag = stdio->attrs.c_oflag;
- break;
-
- case STDOUT_FILENO:
- raw_attrs.c_iflag = stdio->attrs.c_iflag;
- raw_attrs.c_lflag = stdio->attrs.c_lflag;
- break;
- }
-
- // Restore the attributes
- r = tcsetattr(stdio->fd, TCSANOW, &raw_attrs);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not restore terminal attributes for fd %d: %s\n",
- stdio->fd, strerror(errno));
- return -errno;
- }
-
- return 0;
-}
-
-static int pakfire_jail_restore_attrs(struct pakfire_jail* jail,
- const struct pakfire_jail_pty_stdio* stdio) {
- int r;
-
- // Skip if we don't know the file descriptor
- if (stdio->fd < 0)
- return 0;
-
- // Skip everything if fd is not a TTY
- if (!isatty(stdio->fd))
- return 0;
-
- // Restore the flags
- r = fcntl(stdio->fd, F_SETFL, stdio->flags);
- if (r < 0) {
- CTX_ERROR(jail->ctx, "Could not set flags for file descriptor %d: %s\n",
- stdio->fd, strerror(errno));
- return -errno;
- }
-
- // Restore the attributes
- r = tcsetattr(stdio->fd, TCSANOW, &stdio->attrs);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not restore terminal attributes for %d, ignoring: %s\n",
- stdio->fd, strerror(errno));
- return -errno;
- }
-
- return 0;
-}
-
-static int pakfire_jail_setup_pty_forwarding(struct pakfire_jail* jail,
- struct pakfire_jail_exec* ctx, const int epollfd, const int fd) {
- struct winsize size;
- int r;
-
- CTX_DEBUG(jail->ctx, "Setting up PTY forwarding on fd %d\n", fd);
-
- // Store the file descriptor
- ctx->pty.master.fd = fd;
-
- // Add the master to the event loop
- r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.master.fd, EPOLLIN|EPOLLOUT|EPOLLET);
- if (r)
- return r;
-
- if (ctx->flags & PAKFIRE_JAIL_PTY_FORWARDING) {
- // Configure stdin/stdout
- ctx->pty.stdin.fd = STDIN_FILENO;
- ctx->pty.stdout.fd = STDOUT_FILENO;
-
- // Fetch dimensions
- if (isatty(ctx->pty.stdout.fd)) {
- r = ioctl(ctx->pty.stdout.fd, TIOCGWINSZ, &size);
- if (r) {
- CTX_ERROR(jail->ctx, "Failed to determine terminal dimensions: %s\n", strerror(errno));
- return -errno;
- }
-
- // Set dimensions
- r = ioctl(ctx->pty.master.fd, TIOCSWINSZ, &size);
- if (r) {
- CTX_ERROR(jail->ctx, "Failed setting dimensions: %s\n", strerror(errno));
- return -errno;
- }
- }
-
- // Enable RAW mode on standard input
- r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdin);
- if (r)
- return r;
-
- // Enable RAW mode on standard output
- r = pakfire_jail_enable_raw_mode(jail, &ctx->pty.stdout);
- if (r)
- return r;
-
- // Add standard input to the event loop
- r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdin.fd, EPOLLIN|EPOLLET);
- if (r)
- return r;
-
- // Add standard output to the event loop
- r = pakfire_jail_epoll_add_fd(jail, epollfd, ctx->pty.stdout.fd, EPOLLOUT|EPOLLET);
- if (r)
- return r;
- }
-
- return 0;
-}
-
static int pakfire_jail_command_output(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
void* data, const char* line, const size_t length) {
CTX_INFO(ctx, "Command Output: %.*s", (int)length, line);
return 0;
}
-static int pakfire_jail_forward_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
- int r;
-
- while (ctx->pty.master.io || ctx->pty.stdin.io || ctx->pty.stdout.io) {
- // Read from standard input
- if (ctx->pty.stdin.io & 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.io &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
-
- // But we may have data to write
- if (ctx->pty.stdin.buffer.used)
- ctx->pty.master.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
- }
-
- // Write to the master
- if (ctx->pty.master.io & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
- if (jail->callbacks.stdin.callback) {
- r = pakfire_jail_stream_stdin(jail, ctx, ctx->pty.master.fd);
- if (r)
- return r;
-
- } else {
- 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.io &= ~PAKFIRE_JAIL_PTY_READY_TO_WRITE;
- }
-
- // Read from the master
- if (ctx->pty.master.io & 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.io &= ~PAKFIRE_JAIL_PTY_READY_TO_READ;
-
- // But we may have data to write
- if (ctx->pty.stdout.buffer.used)
- ctx->pty.stdout.io |= PAKFIRE_JAIL_PTY_READY_TO_WRITE;
- }
-
- // Write to standard output
- if (ctx->pty.stdout.io & PAKFIRE_JAIL_PTY_READY_TO_WRITE) {
- // If we have a callback, we will send any output to the callback
- if (jail->callbacks.stdout.callback) {
- r = pakfire_jail_drain_buffer_with_callback(jail, &ctx->pty.stdout.buffer,
- jail->callbacks.stdout.callback, jail->callbacks.stdout.data);
- if (r)
- return r;
-
- // If we have a file descriptor, we will forward any output
- } else if (ctx->pty.stdout.fd >= 0) {
- 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;
- }
-
- // Otherwise send the output to the default logger
- } else {
- r = pakfire_jail_drain_buffer_with_callback(jail, &ctx->pty.stdout.buffer,
- pakfire_jail_command_output, NULL);
- if (r)
- return r;
- }
-
- // We are done writing for now
- ctx->pty.stdout.io &= ~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 the UNIX domain socket
- const int socket_recv = pakfire_jail_get_pipe_to_read(jail, &ctx->socket);
-
- // Make a list of all file descriptors we are interested in
- const struct pakfire_wait_fds {
- const int fd;
- const int events;
- } fds[] = {
- // UNIX Domain Socket
- { socket_recv, EPOLLIN },
-
- // Sentinel
- { -1, 0 },
- };
-
- // Setup epoll
- epollfd = epoll_create1(0);
- if (epollfd < 0) {
- CTX_ERROR(jail->ctx, "Could not initialize epoll(): %m\n");
- r = 1;
- goto ERROR;
- }
-
- // Turn file descriptors into non-blocking mode and add them to epoll()
- for (const struct pakfire_wait_fds* fd = fds; fd->events; fd++) {
- // Skip fds which were not initialized
- if (fd->fd < 0)
- continue;
-
- // Add the FD to the event loop
- r = pakfire_jail_epoll_add_fd(jail, epollfd, fd->fd, fd->events);
- if (r)
- goto ERROR;
- }
-
- int ended = 0;
-
- // Loop for as long as the process is alive
- while (!ended) {
- int num = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1);
- if (num < 1) {
- // Ignore if epoll_wait() has been interrupted
- if (errno == EINTR)
- continue;
-
- CTX_ERROR(jail->ctx, "epoll_wait() failed: %m\n");
- r = 1;
-
- goto ERROR;
- }
-
- for (int i = 0; i < num; i++) {
- int e = events[i].events;
- int fd = events[i].data.fd;
-
- // Handle PTY forwarding events
- if (ctx->pty.master.fd == fd) {
- if (e & (EPOLLIN|EPOLLHUP))
- ctx->pty.master.io |= PAKFIRE_JAIL_PTY_READY_TO_READ;
-
- if (e & (EPOLLOUT|EPOLLHUP))
- ctx->pty.master.io |= 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.io |= 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.io |= 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 socket messages
- } else if (socket_recv == fd) {
- if (e & EPOLLIN) {
- // Receive the passed FD
- r = pakfire_jail_recv_fd(jail, socket_recv, &fd);
- if (r)
- goto ERROR;
-
- // Setup PTY forwarding
- if (ctx->pty.master.fd < 0) {
- r = pakfire_jail_setup_pty_forwarding(jail, ctx, epollfd, fd);
- if (r) {
- CTX_ERROR(jail->ctx, "Failed setting up PTY forwarding: %s\n", strerror(-r));
- goto ERROR;
- }
- }
- }
-
- // Log a message for anything else
- } else {
- CTX_DEBUG(jail->ctx, "Received invalid file descriptor %d\n", fd);
- continue;
- }
-
- // Check if any file descriptors have been closed
- if (e & EPOLLHUP) {
- // Remove the file descriptor
- r = epoll_ctl(epollfd, EPOLL_CTL_DEL, fd, NULL);
- if (r) {
- CTX_ERROR(jail->ctx, "Could not remove closed file-descriptor %d: %m\n", fd);
- goto ERROR;
- }
- }
- }
- }
-
-ERROR:
- if (epollfd >= 0)
- close(epollfd);
-
- // Restore any changed terminal attributes
- if (ctx->pty.stdin.fd >= 0)
- pakfire_jail_restore_attrs(jail, &ctx->pty.stdin);
- if (ctx->pty.stdout.fd >= 0)
- pakfire_jail_restore_attrs(jail, &ctx->pty.stdout);
-
- return r;
-}
-
int pakfire_jail_capture_stdout(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
void* data, const char* line, size_t length) {
char** output = (char**)data;
return 0;
}
-static int pakfire_jail_open_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
- int r;
-
- // Allocate a new PTY
- ctx->pty.master.fd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
- if (ctx->pty.master.fd < 0)
- return -errno;
-
- // Fetch the path
- r = ptsname_r(ctx->pty.master.fd, ctx->pty.console, sizeof(ctx->pty.console));
- if (r)
- return -r;
-
- CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->pty.console, ctx->pty.master.fd);
-
- // 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, ctx->pty.console, "/dev/console");
- if (r)
- return r;
-
- 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;
return 126;
}
- const int socket_send = pakfire_jail_get_pipe_to_write(jail, &ctx->socket);
-
// Mount all default stuff
r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_INNER, 0);
if (r)
return r;
}
- // Allocate a new PTY
- r = pakfire_jail_open_pty(jail, ctx);
+ // Open a new PTY
+ r = pakfire_pty_open(ctx->pty);
if (r) {
- CTX_ERROR(jail->ctx, "Could not allocate a new PTY: %s\n", strerror(-r));
+ CTX_ERROR(jail->ctx, "Could not open a new PTY: %s\n", strerror(-r));
return r;
}
- // Send the PTY master to the parent process
- r = pakfire_jail_send_fd(jail, socket_send, ctx->pty.master.fd);
- if (r) {
- CTX_ERROR(jail->ctx, "Failed sending the PTY master to the parent: %s\n", strerror(-r));
- 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 the socket
- close(socket_send);
-
// Setup logging
r = pakfire_log_stream_in_child(ctx->log.INFO);
if (r)
// Run a command in the jail
PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[], int flags) {
+ int pty_flags = 0;
int r;
// Check if argv is valid
struct pakfire_jail_exec ctx = {
.jail = jail,
.flags = flags,
-
- // Exit Code
- .exit = -1,
-
- .socket = { -1, -1 },
-
.pidfd = -1,
- // PTY
- .pty = {
- .master = {
- .fd = -1,
- },
- .stdin = {
- .fd = -1,
- },
- .stdout = {
- .fd = -1,
- },
- },
+ // Exit Code
+ .exit = -1,
};
CTX_DEBUG(jail->ctx, "Executing jail...\n");
if (ctx.flags & PAKFIRE_JAIL_PTY_FORWARDING)
ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
+ // Enable PTY forwarding
+ if (ctx.flags & PAKFIRE_JAIL_PTY_FORWARDING)
+ pty_flags |= PAKFIRE_PTY_FORWARD;
+
/*
Setup a file descriptor which can be used to notify the client that the parent
has completed configuration.
return -1;
}
- // Create a UNIX domain socket
- r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, ctx.socket);
- if (r < 0) {
- CTX_ERROR(jail->ctx, "Could not create UNIX socket: %s\n", strerror(errno));
- r = -errno;
+ // Setup the PTY
+ r = pakfire_pty_create(&ctx.pty, jail->ctx, ctx.loop, pty_flags);
+ if (r)
goto ERROR;
- }
// Setup pipes for logging
// INFO
// Close any file descriptors
if (ctx.pidfd >= 0)
close(ctx.pidfd);
- if (ctx.pty.master.fd >= 0)
- close(ctx.pty.master.fd);
+
+ // PTY
+ if (ctx.pty)
+ pakfire_pty_unref(ctx.pty);
// Logging
if (ctx.log.INFO)
if (ctx.log.DEBUG)
pakfire_log_stream_unref(ctx.log.DEBUG);
#endif /* ENABLE_DEBUG */
- pakfire_jail_close_pipe(jail, ctx.socket);
if (ctx.loop)
sd_event_unref(ctx.loop);
--- /dev/null
+/*#############################################################################
+# #
+# Pakfire - The IPFire package management system #
+# Copyright (C) 2024 Pakfire development team #
+# #
+# This program is free software: you can redistribute it and/or modify #
+# it under the terms of the GNU General Public License as published by #
+# the Free Software Foundation, either version 3 of the License, or #
+# (at your option) any later version. #
+# #
+# This program is distributed in the hope that it will be useful, #
+# but WITHOUT ANY WARRANTY; without even the implied warranty of #
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
+# GNU General Public License for more details. #
+# #
+# You should have received a copy of the GNU General Public License #
+# along with this program. If not, see <http://www.gnu.org/licenses/>. #
+# #
+#############################################################################*/
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include <systemd/sd-event.h>
+
+#include <pakfire/ctx.h>
+#include <pakfire/pty.h>
+#include <pakfire/util.h>
+
+struct pakfire_pty_stdio {
+ // File Descriptor
+ int fd;
+
+ // Buffer
+ char buffer[64 * 1024];
+ size_t buffered;
+
+ // Terminal Attributes
+ struct termios attrs;
+
+ // File Descriptor Flags
+ int flags;
+
+ // IO Flags
+ enum pakfire_pty_io {
+ PAKFIRE_PTY_READY_TO_READ = (1 << 0),
+ PAKFIRE_PTY_READY_TO_WRITE = (1 << 1),
+ } io;
+};
+
+struct pakfire_pty {
+ struct pakfire_ctx* ctx;
+ int nrefs;
+
+ // Flags
+ int flags;
+
+ // Event Loop
+ sd_event* loop;
+
+ // A UNIX socket to pass the file descriptor
+ int socket[2];
+
+ // The master PTY
+ struct pakfire_pty_stdio master;
+
+ // The path to the PTY
+ char path[PATH_MAX];
+
+ // Standard Input
+ struct pakfire_pty_stdio stdin;
+ struct pakfire_pty_stdio stdout;
+};
+
+/*
+ Reads as much data as possible into the buffer
+*/
+static int pakfire_pty_fill_buffer(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio) {
+ int r;
+
+ for (;;) {
+ // Skip this if there is not space left in the buffer
+ if (stdio->buffered >= sizeof(stdio->buffer))
+ return 0;
+
+ // Fill the buffer
+ r = read(stdio->fd, stdio->buffer + stdio->buffered, sizeof(stdio->buffer) - stdio->buffered);
+ if (r < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EIO:
+ return 0;
+
+ default:
+ return -errno;
+ }
+
+ // EOF
+ } else if (r == 0) {
+ return 0;
+
+ // Successful read
+ } else {
+ stdio->buffered += r;
+ }
+ }
+}
+
+/*
+ Writes as much data as possible from the buffer
+*/
+static int pakfire_pty_drain_buffer(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio) {
+ int r;
+
+ // Do not try to write to an invalid file descriptor
+ if (stdio->fd < 0)
+ return 0;
+
+ for (;;) {
+ // Nothing to do if the buffer is empty
+ if (!stdio->buffered)
+ return 0;
+
+ // Drain the buffer
+ r = write(stdio->fd, stdio->buffer, stdio->buffered);
+ if (r < 0) {
+ switch (errno) {
+ case EAGAIN:
+ case EIO:
+ return 0;
+
+ default:
+ return -errno;
+ }
+
+ // Successful write
+ } else {
+ memmove(stdio->buffer, stdio->buffer + r, stdio->buffered - r);
+
+ stdio->buffered -= r;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ This function handles the forwarding between the different pipes...
+*/
+static int pakfire_pty_forward(struct pakfire_pty* pty) {
+ int r;
+
+ while (pty->master.io || pty->stdin.io || pty->stdout.io) {
+ // Read from standard input
+ if (pty->stdin.io & PAKFIRE_PTY_READY_TO_READ) {
+ r = pakfire_pty_fill_buffer(pty, &pty->stdin);
+ if (r < 0) {
+ CTX_ERROR(pty->ctx, "Failed reading from standard input: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done reading for now
+ pty->stdin.io &= ~PAKFIRE_PTY_READY_TO_READ;
+
+ // But we may have data to write
+ if (pty->stdin.buffered)
+ pty->master.io |= PAKFIRE_PTY_READY_TO_WRITE;
+ }
+
+ // Write to the master
+ if (pty->master.io & PAKFIRE_PTY_READY_TO_WRITE) {
+ r = pakfire_pty_drain_buffer(pty, &pty->master);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Failed writing to the PTY: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done writing for now
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+ }
+
+ // Read from the master
+ if (pty->master.io & PAKFIRE_PTY_READY_TO_READ) {
+ r = pakfire_pty_fill_buffer(pty, &pty->master);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Failed reading from the PTY: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done reading for now
+ pty->master.io &= ~PAKFIRE_PTY_READY_TO_READ;
+
+ // But we may have data to write
+ if (pty->stdout.buffered)
+ pty->stdout.io |= PAKFIRE_PTY_READY_TO_WRITE;
+ }
+
+ // Write to standard output
+ if (pty->stdout.io & PAKFIRE_PTY_READY_TO_WRITE) {
+ r = pakfire_pty_drain_buffer(pty, &pty->stdout);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Failed writing to standard output: %s\n", strerror(-r));
+ return r;
+ }
+
+ // We are done writing for now
+ pty->stdout.io &= ~PAKFIRE_PTY_READY_TO_WRITE;
+ }
+ }
+
+ return 0;
+}
+
+static int pakfire_pty_restore_attrs(struct pakfire_pty* pty,
+ const struct pakfire_pty_stdio* stdio) {
+ int r;
+
+ // Skip if we don't know the file descriptor
+ if (stdio->fd < 0)
+ return 0;
+
+ // Skip everything if fd is not a TTY
+ if (!isatty(stdio->fd))
+ return 0;
+
+ // Restore the flags
+ r = fcntl(stdio->fd, F_SETFL, stdio->flags);
+ if (r < 0) {
+ CTX_ERROR(pty->ctx, "Could not set flags for file descriptor %d: %s\n",
+ stdio->fd, strerror(errno));
+ return -errno;
+ }
+
+ // Restore the attributes
+ r = tcsetattr(stdio->fd, TCSANOW, &stdio->attrs);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not restore terminal attributes for %d, ignoring: %s\n",
+ stdio->fd, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+static int pakfire_pty_activity(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio, uint32_t events) {
+ // Do we have data to read?
+ if (events & (EPOLLIN|EPOLLHUP))
+ stdio->io |= PAKFIRE_PTY_READY_TO_READ;
+
+ // Do we have data to write?
+ if (events & (EPOLLOUT|EPOLLHUP))
+ stdio->io |= PAKFIRE_PTY_READY_TO_WRITE;
+
+ return pakfire_pty_forward(pty);
+}
+
+static int pakfire_pty_master(sd_event_source* source, int fd, uint32_t events, void* data) {
+ struct pakfire_pty* pty = data;
+
+ return pakfire_pty_activity(pty, &pty->master, events);
+}
+
+static int pakfire_pty_stdin(sd_event_source* source, int fd, uint32_t events, void* data) {
+ struct pakfire_pty* pty = data;
+
+ return pakfire_pty_activity(pty, &pty->stdin, events);
+}
+
+static int pakfire_pty_stdout(sd_event_source* source, int fd, uint32_t events, void* data) {
+ struct pakfire_pty* pty = data;
+
+ return pakfire_pty_activity(pty, &pty->stdout, events);
+}
+
+static int pakfire_pty_enable_raw_mode(struct pakfire_pty* pty, struct pakfire_pty_stdio* stdio) {
+ struct termios raw_attrs;
+ int r;
+
+ // Skip if we don't know the file descriptor
+ if (stdio->fd < 0)
+ return 0;
+
+ // Skip everything if fd is not a TTY
+ if (!isatty(stdio->fd))
+ return 0;
+
+ // Store flags
+ stdio->flags = fcntl(stdio->fd, F_GETFL);
+ if (stdio->flags < 0) {
+ CTX_ERROR(pty->ctx, "Could not fetch flags from fd %d: %s\n",
+ stdio->fd, strerror(errno));
+ return -errno;
+ }
+
+ // Fetch all attributes
+ r = tcgetattr(stdio->fd, &stdio->attrs);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not fetch terminal attributes from fd %d: %s\n",
+ stdio->fd, strerror(errno));
+ return -errno;
+ }
+
+ // Copy all attributes
+ raw_attrs = stdio->attrs;
+
+ // Make it RAW
+ cfmakeraw(&raw_attrs);
+
+ switch (stdio->fd) {
+ case STDIN_FILENO:
+ raw_attrs.c_oflag = stdio->attrs.c_oflag;
+ break;
+
+ case STDOUT_FILENO:
+ raw_attrs.c_iflag = stdio->attrs.c_iflag;
+ raw_attrs.c_lflag = stdio->attrs.c_lflag;
+ break;
+ }
+
+ // Restore the attributes
+ r = tcsetattr(stdio->fd, TCSANOW, &raw_attrs);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not restore terminal attributes for fd %d: %s\n",
+ stdio->fd, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+/*
+ Sets up PTY forwarding...
+*/
+static int pakfire_pty_setup_forwarding(struct pakfire_pty* pty) {
+ struct winsize size;
+ int r;
+
+ CTX_DEBUG(pty->ctx, "Setting up PTY Forwarding...\n");
+
+ // Connect to standard input/output
+ pty->stdin.fd = STDIN_FILENO;
+ pty->stdout.fd = STDOUT_FILENO;
+
+ // Copy the terminal dimensions to the PTY
+ if (isatty(pty->stdout.fd)) {
+ // Fetch dimensions
+ r = ioctl(pty->stdout.fd, TIOCGWINSZ, &size);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Failed to determine terminal dimensions: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Set dimensions
+ r = ioctl(pty->master.fd, TIOCSWINSZ, &size);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Failed setting dimensions: %s\n", strerror(errno));
+ return -errno;
+ }
+ }
+
+ // Enable RAW mode on standard input
+ r = pakfire_pty_enable_raw_mode(pty, &pty->stdin);
+ if (r)
+ return r;
+
+ // Enable RAW mode on standard output
+ r = pakfire_pty_enable_raw_mode(pty, &pty->stdout);
+ if (r)
+ return r;
+
+ // Add standard input to the event loop
+ r = sd_event_add_io(pty->loop, NULL,
+ pty->stdin.fd, EPOLLIN|EPOLLET, pakfire_pty_stdin, pty);
+ if (r)
+ return r;
+
+ // Add standard output to the event loop
+ r = sd_event_add_io(pty->loop, NULL,
+ pty->stdout.fd, EPOLLOUT|EPOLLET, pakfire_pty_stdout, pty);
+ if (r)
+ return r;
+
+ return 0;
+}
+
+/*
+ Sends the master file descriptor to the parent process...
+*/
+static int pakfire_pty_send_master(struct pakfire_pty* pty) {
+ const size_t payload_length = sizeof(pty->master.fd);
+ char buffer[CMSG_SPACE(payload_length)];
+ int r;
+
+ CTX_DEBUG(pty->ctx, "Sending fd %d to parent\n", pty->master.fd);
+
+ // Header
+ struct msghdr msg = {
+ .msg_control = buffer,
+ .msg_controllen = sizeof(buffer),
+ };
+
+ // Payload
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(payload_length);
+
+ // Set payload
+ *((int*)CMSG_DATA(cmsg)) = pty->master.fd;
+
+ // Send the message
+ r = sendmsg(pty->socket[1], &msg, 0);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not send file descriptor: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // We can close the socket now
+ close(pty->socket[1]);
+ pty->socket[1] = -1;
+
+ return 0;
+}
+
+/*
+ Received the master file descriptor from the child process...
+*/
+static int pakfire_pty_recv_master(struct pakfire_pty* pty) {
+ const size_t payload_length = sizeof(pty->master.fd);
+ char buffer[CMSG_SPACE(payload_length)];
+ int r;
+
+ struct msghdr msg = {
+ .msg_control = buffer,
+ .msg_controllen = sizeof(buffer),
+ };
+
+ // Receive the message
+ r = recvmsg(pty->socket[0], &msg, 0);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not receive file descriptor: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Fetch the payload
+ struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
+ if (!cmsg)
+ return -EBADMSG;
+
+ // Store the file descriptor
+ pty->master.fd = *((int*)CMSG_DATA(cmsg));
+
+ CTX_DEBUG(pty->ctx, "Received fd %d from socket %d\n", pty->master.fd, pty->socket[0]);
+
+ // We can close the socket now
+ close(pty->socket[0]);
+ pty->socket[0] = -1;
+
+ return 0;
+}
+
+/*
+ Called when the master socket is being received from the child process.
+*/
+static int pakfire_pty_setup(sd_event_source* source, int fd, uint32_t events, void* data) {
+ struct pakfire_pty* pty = data;
+ int r;
+
+ // Receive the master file descriptor
+ r = pakfire_pty_recv_master(pty);
+ if (r)
+ return r;
+
+ // Add the master file descriptor to the event loop
+ r = sd_event_add_io(pty->loop, NULL, pty->master.fd,
+ EPOLLIN|EPOLLOUT|EPOLLET, pakfire_pty_master, pty);
+ if (r < 0) {
+ CTX_ERROR(pty->ctx, "Could not add the master file descriptor: %s\n", strerror(-r));
+ return r;
+ }
+
+ // Do we need to set up PTY forwarding?
+ if (pty->flags & PAKFIRE_PTY_FORWARD) {
+ r = pakfire_pty_setup_forwarding(pty);
+ if (r)
+ return r;
+ }
+
+ return r;
+}
+
+static void pakfire_pty_free(struct pakfire_pty* pty) {
+ // Restore any changed terminal attributes
+ if (pty->stdin.fd >= 0)
+ pakfire_pty_restore_attrs(pty, &pty->stdin);
+ if (pty->stdout.fd >= 0)
+ pakfire_pty_restore_attrs(pty, &pty->stdout);
+
+ if (pty->socket[0] >= 0)
+ close(pty->socket[0]);
+ if (pty->socket[1] >= 0)
+ close(pty->socket[1]);
+ if (pty->master.fd >= 0)
+ close(pty->master.fd);
+ if (pty->loop)
+ sd_event_unref(pty->loop);
+ if (pty->ctx)
+ pakfire_ctx_unref(pty->ctx);
+ free(pty);
+}
+
+int pakfire_pty_create(struct pakfire_pty** pty, struct pakfire_ctx* ctx,
+ sd_event* loop, int flags) {
+ struct pakfire_pty* p = NULL;
+ int r;
+
+ // Allocate a new object
+ p = calloc(1, sizeof(*p));
+ if (!p)
+ return -errno;
+
+ // Initialize the reference counter
+ p->nrefs = 1;
+
+ // Store a reference to the context
+ p->ctx = pakfire_ctx_ref(ctx);
+
+ // Store a reference to the event loop
+ p->loop = sd_event_ref(loop);
+
+ // Store the flags
+ p->flags = flags;
+
+ // Initialize the master file descriptor
+ p->master.fd = -1;
+
+ // Initialize standard input/output
+ p->stdin.fd = -1;
+ p->stdout.fd = -1;
+
+ // Create a UNIX domain socket
+ r = socketpair(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC, 0, p->socket);
+ if (r < 0) {
+ CTX_ERROR(p->ctx, "Could not create a UNIX socket: %m\n");
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Register the socket to receive the master socket
+ r = sd_event_add_io(p->loop, NULL, p->socket[0], EPOLLIN|EPOLLHUP,
+ pakfire_pty_setup, p);
+ if (r < 0) {
+ CTX_ERROR(p->ctx, "Could not listen to socket: %s\n", strerror(-r));
+ r = -errno;
+ goto ERROR;
+ }
+
+ // Return the pointer
+ *pty = p;
+
+ return 0;
+
+ERROR:
+ if (p)
+ pakfire_pty_free(p);
+
+ return r;
+}
+
+struct pakfire_pty* pakfire_pty_ref(struct pakfire_pty* pty) {
+ ++pty->nrefs;
+
+ return pty;
+}
+
+struct pakfire_pty* pakfire_pty_unref(struct pakfire_pty* pty) {
+ if (--pty->nrefs > 0)
+ return pty;
+
+ pakfire_pty_free(pty);
+ return NULL;
+}
+
+/*
+ Sets up the terminal in the child process...
+*/
+static int pakfire_pty_setup_terminal(struct pakfire_pty* pty) {
+ int fd = -1;
+ int r;
+
+ // Open a new terminal
+ fd = open("/dev/console", O_RDWR|O_NOCTTY);
+ if (fd < 0) {
+ CTX_ERROR(pty->ctx, "Failed to open a new terminal: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ CTX_DEBUG(pty->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(pty->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(pty->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(pty->ctx, "Failed to open standard error: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ return 0;
+}
+
+/*
+ Allocates a new PTY and must be called from the child process...
+*/
+int pakfire_pty_open(struct pakfire_pty* pty) {
+ int r;
+
+ // Allocate a new PTY
+ pty->master.fd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
+ if (pty->master.fd < 0)
+ return -errno;
+
+ // Fetch the path
+ r = ptsname_r(pty->master.fd, pty->path, sizeof(pty->path));
+ if (r)
+ return -r;
+
+ CTX_DEBUG(pty->ctx, "Allocated console at %s (%d)\n", pty->path, pty->master.fd);
+
+ // Unlock the master device
+ r = unlockpt(pty->master.fd);
+ if (r) {
+ CTX_ERROR(pty->ctx, "Could not unlock the PTY: %s\n", strerror(errno));
+ return -errno;
+ }
+
+ // Create a symlink
+ r = pakfire_symlink(pty->ctx, pty->path, "/dev/console");
+ if (r)
+ return r;
+
+ // Send the master to the parent process
+ r = pakfire_pty_send_master(pty);
+ if (r)
+ return r;
+
+ // Setup the terminal
+ r = pakfire_pty_setup_terminal(pty);
+ if (r)
+ return r;
+
+ // We are done with the master and close it now
+ close(pty->master.fd);
+ pty->master.fd = -1;
+
+ return 0;
+}