From: Michael Tremer Date: Sat, 12 Oct 2024 13:10:56 +0000 (+0000) Subject: jail: Replace standard input/output callbacks with the PTY X-Git-Tag: 0.9.30~1090 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cb8162d92a278c17989f8afb3e10d370d34b8dd7;p=pakfire.git jail: Replace standard input/output callbacks with the PTY Signed-off-by: Michael Tremer --- diff --git a/src/libpakfire/archive.c b/src/libpakfire/archive.c index 7808e0a69..b00474a8e 100644 --- a/src/libpakfire/archive.c +++ b/src/libpakfire/archive.c @@ -1398,31 +1398,17 @@ static int __pakfire_archive_filter_systemd_sysusers(struct pakfire_ctx* ctx, 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, @@ -1448,11 +1434,9 @@ 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) diff --git a/src/libpakfire/build.c b/src/libpakfire/build.c index c3ed131d7..d790f3d65 100644 --- a/src/libpakfire/build.c +++ b/src/libpakfire/build.c @@ -275,7 +275,9 @@ ERROR: 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; @@ -339,23 +341,21 @@ ERROR: 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; } @@ -363,26 +363,31 @@ static int pakfire_build_send_filelist(struct pakfire_ctx* ctx, 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); @@ -390,8 +395,8 @@ ERROR: 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; @@ -513,12 +518,9 @@ static int pakfire_build_find_deps(struct pakfire_build* build, 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); @@ -702,45 +704,61 @@ ERROR: 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; @@ -1295,7 +1313,7 @@ static int pakfire_build_run_post_build_scripts(struct pakfire_build* build) { // 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; } @@ -2365,7 +2383,7 @@ PAKFIRE_EXPORT int pakfire_build_mkimage(struct pakfire_build* build, }; // 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; diff --git a/src/libpakfire/include/pakfire/jail.h b/src/libpakfire/include/pakfire/jail.h index 9aee36230..084f434de 100644 --- a/src/libpakfire/include/pakfire/jail.h +++ b/src/libpakfire/include/pakfire/jail.h @@ -69,10 +69,16 @@ int pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[], int flags, #ifdef PAKFIRE_PRIVATE #include +#include // 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, diff --git a/src/libpakfire/include/pakfire/pty.h b/src/libpakfire/include/pakfire/pty.h index e1c8355e9..bf7a78b28 100644 --- a/src/libpakfire/include/pakfire/pty.h +++ b/src/libpakfire/include/pakfire/pty.h @@ -48,7 +48,7 @@ int pakfire_pty_drain(struct pakfire_pty* pty); 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); diff --git a/src/libpakfire/jail.c b/src/libpakfire/jail.c index c6c0d6b9e..0b420be0f 100644 --- a/src/libpakfire/jail.c +++ b/src/libpakfire/jail.c @@ -112,20 +112,6 @@ struct pakfire_jail { // 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; @@ -446,24 +432,6 @@ PAKFIRE_EXPORT int pakfire_jail_set_timeout( 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. @@ -524,44 +492,6 @@ static int pakfire_jail_DEBUG(struct pakfire_log_stream* stream, const char* lin } #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 @@ -1422,16 +1352,21 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe } // 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 = { @@ -1495,6 +1430,12 @@ PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* jail, 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); @@ -1593,10 +1534,6 @@ PAKFIRE_EXPORT int pakfire_jail_exec(struct pakfire_jail* 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 @@ -1630,6 +1567,19 @@ ERROR: 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; diff --git a/src/libpakfire/libpakfire.sym b/src/libpakfire/libpakfire.sym index ce27d9210..11e364f97 100644 --- a/src/libpakfire/libpakfire.sym +++ b/src/libpakfire/libpakfire.sym @@ -180,8 +180,6 @@ global: 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; diff --git a/tests/libpakfire/jail.c b/tests/libpakfire/jail.c index 533aa3317..a3c73edc6 100644 --- a/tests/libpakfire/jail.c +++ b/tests/libpakfire/jail.c @@ -346,28 +346,26 @@ FAIL: 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) { @@ -384,10 +382,9 @@ 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;