return PAKFIRE_WALK_OK;
}
-static int pakfire_archive_stream_payload(struct pakfire_ctx* ctx,
- struct pakfire_jail* jail, void* data, int fd) {
- char buffer[1024];
-
- struct archive* a = (struct archive*)data;
-
- // Read a block from the input archive
- ssize_t bytes_read = archive_read_data(a, buffer, sizeof(buffer));
- if (bytes_read < 0) {
+static ssize_t pakfire_archive_stream_payload(
+ struct pakfire_ctx* ctx, void* data, char* buffer, size_t length) {
+ struct archive* a = data;
+ ssize_t bytes_read;
+
+ // Fill the buffer with data from the archive
+ bytes_read = archive_read_data(a, buffer, length);
+ if (bytes_read < 0)
CTX_ERROR(ctx, "Could not read from archive: %s\n", archive_error_string(a));
- return 1;
- }
-
- // We have consumed everything
- if (bytes_read == 0)
- return EOF;
- // Write the data to the output file descriptor
- ssize_t bytes_written = write(fd, buffer, bytes_read);
- if (bytes_written < 0) {
- CTX_ERROR(ctx, "Could not stream output: %m\n");
- return 1;
- }
-
- return 0;
+ return bytes_read;
}
static int __pakfire_archive_handle_systemd_sysusers(struct pakfire_ctx* ctx,
if (r)
goto ERROR;
- // Set callback
- pakfire_jail_set_stdin_callback(jail, pakfire_archive_stream_payload, a);
-
// Run!
- r = pakfire_jail_exec(jail, argv, PAKFIRE_JAIL_NOENT_OK, NULL);
+ r = pakfire_jail_communicate(jail, argv, PAKFIRE_JAIL_NOENT_OK,
+ pakfire_archive_stream_payload, a, NULL, NULL);
ERROR:
if (jail)
static int pakfire_build_run_script(
struct pakfire_build* build,
const char* filename,
- const char* args[]) {
+ const char* args[],
+ pakfire_pty_stdin_callback stdin_callback, void* stdin_data,
+ pakfire_pty_stdout_callback stdout_callback, void* stdout_data) {
int r;
char* script = NULL;
return r;
}
-static int pakfire_build_send_filelist(struct pakfire_ctx* ctx,
- struct pakfire_jail* jail, void* data, int fd) {
+static ssize_t pakfire_build_send_filelist(
+ struct pakfire_ctx* ctx, void* data, char* buffer, size_t length) {
struct pakfire_find_deps_ctx* p = (struct pakfire_find_deps_ctx*)data;
struct pakfire_file* file = NULL;
int r = 0;
- const size_t length = pakfire_filelist_length(p->filelist);
-
// Check if we have reached the end of the filelist
- if (p->i >= length)
- return EOF;
+ if (p->i >= pakfire_filelist_length(p->filelist))
+ return 0;
// Fetch the next file
file = pakfire_filelist_get(p->filelist, p->i);
if (!file) {
CTX_DEBUG(ctx, "Could not fetch file %u: %m\n", p->i);
- r = 1;
+ r = -errno;
goto ERROR;
}
const char* path = pakfire_file_get_path(file);
if (!path) {
CTX_ERROR(ctx, "Received a file with an empty path\n");
- r = 1;
+ r = -errno;
goto ERROR;
}
// Skip files that don't match what we are looking for
- if (p->class && !pakfire_file_matches_class(file, p->class))
+ if (p->class && !pakfire_file_matches_class(file, p->class)) {
+ r = -EAGAIN;
goto SKIP;
+ }
// Write path to stdin
- r = dprintf(fd, "%s\n", path);
+ r = snprintf(buffer, length, "%s\n", path);
if (r < 0)
- return r;
+ goto ERROR;
+
+ // If the output could not be written, we ask to be called again later
+ else if (r >= (ssize_t)length) {
+ r = -EAGAIN;
+ goto ERROR;
+ }
SKIP:
// Move on to the next file
p->i++;
- // Success
- r = 0;
-
ERROR:
if (file)
pakfire_file_unref(file);
return r;
}
-static int pakfire_build_process_deps(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
- void* data, const char* buffer, const size_t length) {
+static int pakfire_build_process_deps(struct pakfire_ctx* ctx, void* data,
+ const char* buffer, const size_t length) {
const struct pakfire_find_deps_ctx* p = (struct pakfire_find_deps_ctx*)data;
char dep[PATH_MAX];
pcre2_match_data* match = NULL;
NULL,
};
- // Set callbacks
- pakfire_jail_set_stdin_callback(build->jail, pakfire_build_send_filelist, &ctx);
- pakfire_jail_set_stdout_callback(build->jail, pakfire_build_process_deps, &ctx);
-
// Run the script
- r = pakfire_build_run_script(build, script, args);
+ r = pakfire_build_run_script(build, script, args,
+ pakfire_build_send_filelist, &ctx, pakfire_build_process_deps, &ctx);
if (r)
CTX_ERROR(build->ctx, "%s returned with error %d\n", script, r);
return r;
}
-static int pakfire_build_send_scriptlet(struct pakfire_ctx* ctx, struct pakfire_jail* jail,
- void* data, int fd) {
- const struct pakfire_find_deps_ctx* __ctx = (struct pakfire_find_deps_ctx*)data;
- size_t length = 0;
+struct pakfire_build_send_scriptlet_state {
+ struct pakfire_scriptlet* scriptlet;
- // Fetch the scriptlet
- const char* p = pakfire_scriptlet_get_data(__ctx->scriptlet, &length);
- if (!p) {
- CTX_ERROR(ctx, "Could not fetch scriptlet: %m\n");
- return 1;
- }
+ const char* p;
+ size_t l;
+};
- // Write it into the pipe
- ssize_t bytes_written = write(fd, p, length);
- if (bytes_written < 0) {
- CTX_ERROR(ctx, "Could not send scriptlet: %m\n");
- return 1;
+static ssize_t pakfire_build_send_scriptlet(
+ struct pakfire_ctx* ctx, void* data, char* buffer, size_t length) {
+ struct pakfire_build_send_scriptlet_state* state = data;
+
+ // Fetch the scriptlet unless already done
+ if (!state->p) {
+ state->p = pakfire_scriptlet_get_data(state->scriptlet, &state->l);
+ if (!state->p) {
+ CTX_ERROR(ctx, "Could not fetch scriptlet: %m\n");
+ return -errno;
+ }
}
- return EOF;
+ // Return if we have nothing left to send
+ if (state->l == 0)
+ return 0;
+
+ // Cap the buffer to the maximum size of the scriptlet
+ if (state->l < length)
+ length = state->l;
+
+ // Copy the scriptlet into the buffer
+ memcpy(buffer, state->p, length);
+
+ // Forward the pointer by how much we have written
+ state->p += length;
+ state->l -= length;
+
+ return length;
}
static int pakfire_build_add_scriptlet_requires(struct pakfire_build* build,
struct pakfire_package* pkg, struct pakfire_scriptlet* scriptlet) {
int r;
+ struct pakfire_build_send_scriptlet_state send_scriptlet_state = {
+ .scriptlet = scriptlet,
+ };
+
struct pakfire_find_deps_ctx ctx = {
.build = build,
.pkg = pkg,
.dep = PAKFIRE_PKG_PREREQUIRES,
- .scriptlet = scriptlet,
};
- // Set callbacks
- pakfire_jail_set_stdin_callback(build->jail, pakfire_build_send_scriptlet, &ctx);
- pakfire_jail_set_stdout_callback(build->jail, pakfire_build_process_deps, &ctx);
-
// Find all pre-requires
- r = pakfire_build_run_script(build, "find-prerequires", NULL);
+ r = pakfire_build_run_script(build, "find-prerequires", NULL,
+ pakfire_build_send_scriptlet, &send_scriptlet_state, pakfire_build_process_deps, &ctx);
if (r)
goto ERROR;
// Run them one by one
for (const char** script = post_build_scripts; *script; script++) {
- int r = pakfire_build_run_script(build, *script, args);
+ int r = pakfire_build_run_script(build, *script, args, NULL, NULL, NULL, NULL);
if (r)
return r;
}
};
// Run the mkimage script
- r = pakfire_build_run_script(build, "mkimage", args);
+ r = pakfire_build_run_script(build, "mkimage", args, NULL, NULL, NULL, NULL);
if (r)
goto ERROR;
#ifdef PAKFIRE_PRIVATE
#include <pakfire/cgroup.h>
+#include <pakfire/pty.h>
// Resource limits
int pakfire_jail_set_cgroup(struct pakfire_jail* jail, struct pakfire_cgroup* cgroup);
+int pakfire_jail_communicate(
+ struct pakfire_jail* jail, const char* argv[], int flags,
+ pakfire_pty_stdin_callback stdin_callback, void* stdin_data,
+ pakfire_pty_stdout_callback stdout_callback, void* stdout_data);
+
// Convenience functions
int pakfire_jail_run(struct pakfire* pakfire, const char* argv[], int flags, char** output);
int pakfire_jail_run_script(struct pakfire* pakfire,
char* pakfire_pty_output(struct pakfire_pty* pty, size_t* length);
-typedef int (*pakfire_pty_stdin_callback)(
+typedef ssize_t (*pakfire_pty_stdin_callback)(
struct pakfire_ctx* ctx, void* data, char* buffer, size_t length);
typedef int (*pakfire_pty_stdout_callback)(
struct pakfire_ctx* ctx, void* data, const char* line, const size_t length);
// Environment
char* env[ENVIRON_SIZE];
- struct pakfire_jail_callbacks {
- // Standard Input
- struct pakfire_jail_callbacks_stdin {
- pakfire_jail_stdin_callback callback;
- void* data;
- } stdin;
-
- // Standard Output
- struct pakfire_jail_callbacks_stdout {
- pakfire_jail_stdout_callback callback;
- void* data;
- } stdout;
- } callbacks;
-
// Mountpoints
struct pakfire_jail_mountpoint mountpoints[MAX_MOUNTPOINTS];
unsigned int num_mountpoints;
return 0;
}
-/*
- Standard Input
-*/
-PAKFIRE_EXPORT void pakfire_jail_set_stdin_callback(struct pakfire_jail* jail,
- pakfire_jail_stdin_callback callback, void* data) {
- jail->callbacks.stdin.callback = callback;
- jail->callbacks.stdin.data = data;
-}
-
-/*
- Standard Output
-*/
-PAKFIRE_EXPORT void pakfire_jail_set_stdout_callback(struct pakfire_jail* jail,
- pakfire_jail_stdout_callback callback, void* data) {
- jail->callbacks.stdout.callback = callback;
- jail->callbacks.stdout.data = data;
-}
-
/*
This function replaces any logging in the child process.
}
#endif /* ENABLE_DEBUG */
-static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
- struct pakfire_jail_exec* ctx, const int fd) {
- const char eof = 0x04;
- int r;
-
- // Skip if the writing pipe has already been closed
- if (fd < 0)
- return 0;
-
- CTX_DEBUG(jail->ctx, "Streaming standard input...\n");
-
- // Calling the callback
- r = jail->callbacks.stdin.callback(jail->ctx, jail, jail->callbacks.stdin.data, fd);
-
- switch (r) {
- case EOF:
- // The callback signaled that it has written everything
- CTX_DEBUG(jail->ctx, "Closing standard input pipe\n");
-
- // Send EOF (Ctrl-D)
- r = write(fd, &eof, sizeof(eof));
- if (r < 0) {
- CTX_ERROR(jail->ctx, "Could not write EOF: %s\n", strerror(errno));
- return -errno;
- }
-
- return 0;
-
- case 0:
- CTX_DEBUG(jail->ctx, "Standard input callback finished\n");
- return 0;
-
- default:
- CTX_ERROR(jail->ctx, "Standard input callback failed: %s\n", strerror(-r));
- return r;
- }
-}
-
// Capabilities
// Logs all capabilities of the current process
}
// Run a command in the jail
-PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail,
- const char* argv[], int flags, char** output) {
+static int __pakfire_jail_exec(struct pakfire_jail* jail,
+ const char* argv[], int flags,
+ pakfire_pty_stdin_callback stdin_callback, void* stdin_data,
+ pakfire_pty_stdout_callback stdout_callback, void* stdout_data,
+ char** output) {
int pty_flags = 0;
int r;
// Check if argv is valid
- if (!argv || !argv[0]) {
- errno = EINVAL;
- return -1;
- }
+ if (!argv || !argv[0])
+ return -EINVAL;
+
+ // We cannot have both, an output callback and buffer
+ if (stdout_callback && output)
+ return -EINVAL;
// Initialize context for this call
struct pakfire_jail_exec ctx = {
if (r)
goto ERROR;
+ // Configure the callbacks
+ if (stdin_callback)
+ pakfire_pty_set_stdin_callback(ctx.pty, stdin_callback, stdin_data);
+ if (stdout_callback)
+ pakfire_pty_set_stdout_callback(ctx.pty, stdout_callback, stdout_data);
+
// Setup pipes for logging
// INFO
r = pakfire_log_stream_create(&ctx.log.INFO, jail->ctx, pakfire_jail_INFO, jail);
}
ERROR:
- // Reset all callbacks
- pakfire_jail_set_stdin_callback(jail, NULL, NULL);
- pakfire_jail_set_stdout_callback(jail, NULL, NULL);
-
// Destroy the temporary cgroup (if any)
if (ctx.cgroup) {
// Read cgroup stats
return ctx.exit;
}
+PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail,
+ const char* argv[], int flags, char** output) {
+ return __pakfire_jail_exec(jail, argv, flags, NULL, NULL, NULL, NULL, output);
+}
+
+int pakfire_jail_communicate(
+ struct pakfire_jail* jail, const char* argv[], int flags,
+ pakfire_pty_stdin_callback stdin_callback, void* stdin_data,
+ pakfire_pty_stdout_callback stdout_callback, void* stdout_data) {
+ return __pakfire_jail_exec(jail, argv, flags,
+ stdin_callback, stdin_data, stdout_callback, stdout_data, NULL);
+}
+
static int pakfire_jail_exec_interactive(
struct pakfire_jail* jail, const char* argv[], int flags) {
int r;
pakfire_jail_nice;
pakfire_jail_ref;
pakfire_jail_set_env;
- pakfire_jail_set_stdin_callback;
- pakfire_jail_set_stdout_callback;
pakfire_jail_set_timeout;
pakfire_jail_unref;
return r;
}
-static int callback_stdin(struct pakfire_ctx* ctx, struct pakfire_jail* pakfire, void* data, int fd) {
+static ssize_t callback_stdin(struct pakfire_ctx* ctx, void* data, char* buffer, size_t length) {
int* lines = (int*)data;
- int r;
-
- while (*lines > 0) {
- r = dprintf(fd, "LINE %d\n", *lines);
- if (r < 0) {
- switch (errno) {
- case EAGAIN:
- return 0;
-
- default:
- LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines);
- return -errno;
- }
- }
-
- // Decrement the lines counter
- (*lines)--;
+ ssize_t bytes_written;
+
+ // We are done
+ if (!*lines)
+ return 0;
+
+ // Send another line
+ bytes_written = snprintf(buffer, length, "LINE %d\n", *lines);
+ if (bytes_written < 0) {
+ LOG_ERROR("Could not write line (%u) to stdin: %m\n", *lines);
+
+ return 1;
}
- return EOF;
+ // Decrement the lines counter
+ (*lines)--;
+
+ return bytes_written;
}
static int test_communicate(const struct test* t) {
// Create a new jail
ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
- pakfire_jail_set_stdin_callback(jail, callback_stdin, &lines);
-
// Check if the mount actually works
- ASSERT_SUCCESS(pakfire_jail_exec(jail, argv, 0, NULL));
+ ASSERT_SUCCESS(pakfire_jail_communicate(jail, argv, 0,
+ callback_stdin, &lines, NULL, NULL));
// Success
r = EXIT_SUCCESS;