#include <sys/personality.h>
#include <sys/prctl.h>
#include <sys/resource.h>
-#include <sys/signalfd.h>
#include <sys/timerfd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <pakfire/logging.h>
#include <pakfire/mount.h>
#include <pakfire/pakfire.h>
+#include <pakfire/path.h>
#include <pakfire/private.h>
#include <pakfire/pwd.h>
#include <pakfire/string.h>
};
struct pakfire_jail {
+ struct pakfire_ctx* ctx;
struct pakfire* pakfire;
int nrefs;
// Logging
int log_INFO[2];
int log_ERROR[2];
+#ifdef ENABLE_DEBUG
int log_DEBUG[2];
+#endif /* ENABLE_DEBUG */
} pipes;
// Communicate
// Logging
struct pakfire_log_buffer log_INFO;
struct pakfire_log_buffer log_ERROR;
+#ifdef ENABLE_DEBUG
struct pakfire_log_buffer log_DEBUG;
+#endif /* ENABLE_DEBUG */
} buffers;
struct pakfire_cgroup* cgroup;
struct pakfire_cgroup_stats cgroup_stats;
+
+ // Console
+ char console[PATH_MAX];
+ int consolefd;
};
static int clone3(struct clone_args* args, size_t size) {
if (jail->cgroup)
pakfire_cgroup_unref(jail->cgroup);
-
- pakfire_unref(jail->pakfire);
+ if (jail->pakfire)
+ pakfire_unref(jail->pakfire);
+ if (jail->ctx)
+ pakfire_ctx_unref(jail->ctx);
free(jail);
}
if (!j)
return 1;
+ // Reference context
+ j->ctx = pakfire_ctx(pakfire);
+
// Reference Pakfire
j->pakfire = pakfire_ref(pakfire);
jail->timeout.it_value.tv_sec = timeout;
if (timeout > 0)
- DEBUG(jail->pakfire, "Timeout set to %d second(s)\n", timeout);
+ DEBUG(jail->pakfire, "Timeout set to %u second(s)\n", timeout);
else
DEBUG(jail->pakfire, "Timeout disabled\n");
return fd;
ERROR:
- if (fd > 0)
+ if (fd >= 0)
close(fd);
return -1;
}
-// Signals
-
-static int pakfire_jail_handle_signals(struct pakfire_jail* jail) {
- sigset_t mask;
- int r;
-
- sigemptyset(&mask);
- sigaddset(&mask, SIGINT);
-
- // Block signals
- r = sigprocmask(SIG_BLOCK, &mask, NULL);
- if (r < 0) {
- ERROR(jail->pakfire, "Failed to block signals: %m\n");
- return r;
- }
-
- // Create a file descriptor
- r = signalfd(-1, &mask, SFD_NONBLOCK|SFD_CLOEXEC);
- if (r < 0) {
- ERROR(jail->pakfire, "Failed to create signalfd: %m\n");
- return r;
- }
-
- return r;
-}
-
/*
This function replaces any logging in the child process.
All log messages will be sent to the parent process through their respective pipes.
*/
-static void pakfire_jail_log(void* data, int priority, const char* file,
+static void pakfire_jail_log_redirect(void* data, int priority, const char* file,
int line, const char* fn, const char* format, va_list args) {
struct pakfire_jail_pipes* pipes = (struct pakfire_jail_pipes*)data;
int fd;
}
// Send the log message
- if (fd)
+ if (fd >= 0)
vdprintf(fd, format, args);
}
close(fd);
// Reset the file-descriptor so it won't be closed again later
- ctx->pipes.stdin[1] = 0;
+ ctx->pipes.stdin[1] = -1;
// Report success
r = 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])
+ if (fds[i] >= 0)
close(fds[i]);
}
int* fd_write = &(*fds)[1];
// Close the write end of the pipe
- if (*fd_write) {
+ if (*fd_write >= 0) {
close(*fd_write);
*fd_write = -1;
}
// Return the read end
- return *fd_read;
+ if (*fd_read >= 0)
+ return *fd_read;
+
+ return -1;
}
static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* jail, int (*fds)[2]) {
int* fd_write = &(*fds)[1];
// Close the read end of the pipe
- if (*fd_read) {
+ if (*fd_read >= 0) {
close(*fd_read);
*fd_read = -1;
}
// Return the write end
- return *fd_write;
+ if (*fd_write >= 0)
+ return *fd_write;
+
+ return -1;
+}
+
+static int pakfire_jail_log(struct pakfire* pakfire, void* data, int priority,
+ const char* line, const size_t length) {
+ // Pass everything to the parent logger
+ pakfire_log_condition(pakfire, priority, 0, "%.*s", (int)length, line);
+
+ return 0;
+}
+
+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) {
+ ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %s\n",
+ fd, strerror(errno));
+ return -errno;
+ }
+
+ return 0;
}
static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
int epollfd = -1;
- struct epoll_event ev;
struct epoll_event events[EPOLL_MAX_EVENTS];
- struct signalfd_siginfo siginfo;
char garbage[8];
int r = 0;
// Logging
const int log_INFO = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_INFO);
const int log_ERROR = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_ERROR);
+#ifdef ENABLE_DEBUG
const int log_DEBUG = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.log_DEBUG);
-
- // Signals
- const int signalfd = pakfire_jail_handle_signals(jail);
+#endif /* ENABLE_DEBUG */
// Make a list of all file descriptors we are interested in
- const int fds[] = {
- stdin, stdout, stderr, pidfd, timerfd, signalfd, log_INFO, log_ERROR, log_DEBUG,
+ const struct pakfire_wait_fds {
+ const int fd;
+ const int events;
+ } fds[] = {
+ // Standard input/output
+ { stdin, EPOLLOUT },
+ { stdout, EPOLLIN },
+ { stderr, EPOLLIN },
+
+ // Timer
+ { timerfd, EPOLLIN },
+
+ // Child Process
+ { ctx->pidfd, EPOLLIN },
+
+ // Log Pipes
+ { log_INFO, EPOLLIN },
+ { log_ERROR, EPOLLIN },
+#ifdef ENABLE_DEBUG
+ { log_DEBUG, EPOLLIN },
+#endif /* ENABLE_DEBUG */
+
+ // Sentinel
+ { -1, 0 },
};
// Setup epoll
}
// Turn file descriptors into non-blocking mode and add them to epoll()
- for (unsigned int i = 0; i < sizeof(fds) / sizeof(*fds); i++) {
- int fd = fds[i];
-
+ for (const struct pakfire_wait_fds* fd = fds; fd->events; fd++) {
// Skip fds which were not initialized
- if (fd < 0)
+ if (fd->fd < 0)
continue;
- ev.events = EPOLLHUP;
-
- if (fd == stdin)
- ev.events |= EPOLLOUT;
- else
- ev.events |= EPOLLIN;
-
- // Read flags
- int flags = fcntl(fd, F_GETFL, 0);
-
- // Set modified flags
- if (fcntl(fd, F_SETFL, flags|O_NONBLOCK) < 0) {
- ERROR(jail->pakfire,
- "Could not set file descriptor %d into non-blocking mode: %m\n", fd);
- r = 1;
- goto ERROR;
- }
-
- ev.data.fd = fd;
-
- if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) {
- ERROR(jail->pakfire, "Could not add file descriptor %d to epoll(): %m\n", fd);
- r = 1;
+ // 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;
}
}
- // There is nothing else to do
- continue;
-
- // Handle signals
- } else if (fd == signalfd) {
- // Read the signal
- r = read(signalfd, &siginfo, sizeof(siginfo));
- if (r < 1) {
- ERROR(jail->pakfire, "Could not read signal: %m\n");
- goto ERROR;
- }
-
- DEBUG(jail->pakfire, "Received signal %d\n", siginfo.ssi_signo);
-
- // Handle signals
- switch (siginfo.ssi_signo) {
- // Pass SIGINT down to the child process
- case SIGINT:
- r = pidfd_send_signal(pidfd, siginfo.ssi_signo, NULL, 0);
- if (r) {
- ERROR(jail->pakfire, "Could not send signal to process: %m\n");
- goto ERROR;
- }
- break;
-
- default:
- ERROR(jail->pakfire, "Received unhandled signal %d\n",
- siginfo.ssi_signo);
- break;
- }
-
// Don't fall through to log processing
continue;
buffer = &ctx->buffers.log_INFO;
priority = LOG_INFO;
- callback = jail->callbacks.log;
- data = jail->callbacks.log_data;
+ callback = pakfire_jail_log;
} else if (fd == log_ERROR) {
buffer = &ctx->buffers.log_ERROR;
priority = LOG_ERR;
- callback = jail->callbacks.log;
- data = jail->callbacks.log_data;
+ callback = pakfire_jail_log;
+#ifdef ENABLE_DEBUG
} else if (fd == log_DEBUG) {
buffer = &ctx->buffers.log_DEBUG;
priority = LOG_DEBUG;
- callback = jail->callbacks.log;
- data = jail->callbacks.log_data;
+ callback = pakfire_jail_log;
+#endif /* ENABLE_DEBUG */
// Handle anything from the log pipes
} else if (fd == stdout) {
}
ERROR:
- if (epollfd > 0)
+ if (epollfd >= 0)
close(epollfd);
- if (timerfd > 0)
+ if (timerfd >= 0)
close(timerfd);
- if (signalfd > 0)
- close(signalfd);
return r;
}
flags |= PAKFIRE_MOUNT_LOOP_DEVICES;
// Mount all default stuff
- r = pakfire_mount_all(jail->pakfire, flags);
+ r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_OUTER, flags);
+ if (r)
+ return r;
+
+ // Populate /dev
+ r = pakfire_populate_dev(jail->pakfire, flags);
+ if (r)
+ return r;
+
+ // Mount the interpreter (if needed)
+ r = pakfire_mount_interpreter(jail->pakfire);
if (r)
return r;
return r;
}
- // Log all mountpoints
- pakfire_mount_list(jail->pakfire);
-
return 0;
}
"0 %lu %lu\n", subgid->id, subgid->length);
} else {
r = pakfire_file_write(jail->pakfire, path, 0, 0, 0,
- "0 %lu 1\n%1 %lu %lu\n", gid, subgid->id, subgid->length);
+ "0 %lu 1\n1 %lu %lu\n", gid, subgid->id, subgid->length);
}
if (r) {
static int pakfire_jail_setgroups(struct pakfire_jail* jail, pid_t pid) {
char path[PATH_MAX];
- int r = 1;
+ int r;
// Make path
r = pakfire_string_format(path, "/proc/%d/setgroups", pid);
if (r)
return r;
- // Open file for writing
- FILE* f = fopen(path, "w");
- if (!f) {
- ERROR(jail->pakfire, "Could not open %s for writing: %m\n", path);
- goto ERROR;
- }
-
- // Write content
- int bytes_written = fprintf(f, "deny\n");
- if (bytes_written <= 0) {
- ERROR(jail->pakfire, "Could not write to %s: %m\n", path);
- goto ERROR;
- }
-
- r = fclose(f);
- f = NULL;
+ r = pakfire_file_write(jail->pakfire, path, 0, 0, 0, "deny\n");
if (r) {
- ERROR(jail->pakfire, "Could not close %s: %m\n", path);
- goto ERROR;
+ CTX_ERROR(jail->ctx, "Could not set setgroups to deny: %s\n", strerror(errno));
+ r = -errno;
}
-ERROR:
- if (f)
- fclose(f);
-
return r;
}
DEBUG(jail->pakfire, "Sending signal...\n");
// Write to the file descriptor
- ssize_t bytes_written = write(fd, &val, sizeof(val));
- if (bytes_written < 0 || (size_t)bytes_written < sizeof(val)) {
- ERROR(jail->pakfire, "Could not send signal: %m\n");
- r = 1;
+ r = eventfd_write(fd, val);
+ if (r < 0) {
+ ERROR(jail->pakfire, "Could not send signal: %s\n", strerror(errno));
+ r = -errno;
}
// Close the file descriptor
DEBUG(jail->pakfire, "Waiting for signal...\n");
- ssize_t bytes_read = read(fd, &val, sizeof(val));
- if (bytes_read < 0 || (size_t)bytes_read < sizeof(val)) {
- ERROR(jail->pakfire, "Error waiting for signal: %m\n");
- r = 1;
+ r = eventfd_read(fd, &val);
+ if (r < 0) {
+ ERROR(jail->pakfire, "Error waiting for signal: %s\n", strerror(errno));
+ r = -errno;
}
// Close the file descriptor
return 0;
}
+#if 0
+static int pakfire_jail_open_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+ int r;
+
+ // Allocate a new PTY
+ ctx->consolefd = posix_openpt(O_RDWR|O_NONBLOCK|O_NOCTTY|O_CLOEXEC);
+ if (ctx->consolefd < 0)
+ return -errno;
+
+ // Fetch the path
+ r = ptsname_r(ctx->consolefd, ctx->console, sizeof(ctx->console));
+ if (r)
+ return -r;
+
+ CTX_DEBUG(jail->ctx, "Allocated console at %s (%d)\n", ctx->console, ctx->consolefd);
+
+ // Create a symlink
+ r = pakfire_symlink(jail->ctx, "/dev/console", ctx->console);
+ if (r)
+ return r;
+
+ return r;
+}
+#endif
+
static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
const char* argv[]) {
int r;
// Redirect any logging to our log pipe
- pakfire_set_log_callback(jail->pakfire, pakfire_jail_log, &ctx->pipes);
+ pakfire_ctx_set_log_callback(jail->ctx, pakfire_jail_log_redirect, &ctx->pipes);
// Fetch my own PID
pid_t pid = getpid();
uid_t euid = geteuid();
gid_t egid = getegid();
- DEBUG(jail->pakfire, " UID: %d (effective %d)\n", uid, euid);
- DEBUG(jail->pakfire, " GID: %d (effective %d)\n", gid, egid);
+ DEBUG(jail->pakfire, " UID: %u (effective %u)\n", uid, euid);
+ DEBUG(jail->pakfire, " GID: %u (effective %u)\n", gid, egid);
+
+ // Log all mountpoints
+ pakfire_mount_list(jail->ctx);
- // Check if we are (effectively running as root)
+ // Fail if we are not PID 1
+ if (pid != 1) {
+ CTX_ERROR(jail->ctx, "Child process is not PID 1\n");
+ return 126;
+ }
+
+ // Fail if we are not running as root
if (uid || gid || euid || egid) {
ERROR(jail->pakfire, "Child process is not running as root\n");
return 126;
}
+ // Mount all default stuff
+ r = pakfire_mount_all(jail->pakfire, PAKFIRE_MNTNS_INNER, 0);
+ if (r)
+ return 126;
+
+#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 126;
+ }
+
+ // Allocate a new PTY
+ r = pakfire_jail_open_pty(jail, ctx);
+ if (r) {
+ CTX_ERROR(jail->ctx, "Could not allocate a new PTY: %s\n", strerror(-r));
+ return 126;
+ }
+#endif
+
const char* root = pakfire_get_path(jail->pakfire);
const char* arch = pakfire_get_effective_arch(jail->pakfire);
// Change mount propagation to slave to receive anything from the parent namespace
- r = pakfire_mount_change_propagation(jail->pakfire, MS_SLAVE, "/");
+ r = pakfire_mount_change_propagation(jail->ctx, "/", MS_SLAVE);
if (r)
return r;
return r;
// Change mount propagation to private
- r = pakfire_mount_change_propagation(jail->pakfire, MS_PRIVATE, root);
+ r = pakfire_mount_change_propagation(jail->ctx, root, MS_PRIVATE);
if (r)
return r;
// Log argv
for (unsigned int i = 0; argv[i]; i++)
- DEBUG(jail->pakfire, " argv[%d] = %s\n", i, argv[i]);
+ DEBUG(jail->pakfire, " argv[%u] = %s\n", i, argv[i]);
// exec() command
r = execvpe(argv[0], (char**)argv, jail->env);
.flags = flags,
.pipes = {
- .stdin = { -1, -1 },
- .stdout = { -1, -1 },
- .stderr = { -1, -1 },
+ .stdin = { -1, -1 },
+ .stdout = { -1, -1 },
+ .stderr = { -1, -1 },
+ .log_INFO = { -1, -1 },
+ .log_ERROR = { -1, -1 },
+#ifdef ENABLE_DEBUG
+ .log_DEBUG = { -1, -1 },
+#endif /* ENABLE_DEBUG */
},
.communicate = {
// Destroy the temporary cgroup (if any)
if (ctx.cgroup) {
// Read cgroup stats
- r = pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
- if (r) {
- ERROR(jail->pakfire, "Could not read cgroup stats: %m\n");
- } else {
- pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
- }
-
+ pakfire_cgroup_stat(ctx.cgroup, &ctx.cgroup_stats);
+ pakfire_cgroup_stat_dump(ctx.cgroup, &ctx.cgroup_stats);
pakfire_cgroup_destroy(ctx.cgroup);
pakfire_cgroup_unref(ctx.cgroup);
}
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)
+ if (ctx.pidfd >= 0)
close(ctx.pidfd);
pakfire_jail_close_pipe(jail, ctx.pipes.log_INFO);
pakfire_jail_close_pipe(jail, ctx.pipes.log_ERROR);
+#ifdef ENABLE_DEBUG
pakfire_jail_close_pipe(jail, ctx.pipes.log_DEBUG);
+#endif /* ENABLE_DEBUG */
return exit;
}
const char* root = pakfire_get_path(jail->pakfire);
// Write the scriptlet to disk
- r = pakfire_path_join(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
+ r = pakfire_path_append(path, root, PAKFIRE_TMP_DIR "/pakfire-script.XXXXXX");
if (r)
goto ERROR;