]> git.ipfire.org Git - pakfire.git/commitdiff
jail: Replace standard input/output callbacks with the PTY
authorMichael Tremer <michael.tremer@ipfire.org>
Sat, 12 Oct 2024 13:10:56 +0000 (13:10 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Sat, 12 Oct 2024 13:10:56 +0000 (13:10 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/libpakfire/archive.c
src/libpakfire/build.c
src/libpakfire/include/pakfire/jail.h
src/libpakfire/include/pakfire/pty.h
src/libpakfire/jail.c
src/libpakfire/libpakfire.sym
tests/libpakfire/jail.c

index 7808e0a691779db7b268645d6706ee2be34e32cd..b00474a8ef5248d7a8d528af3822d66c33482a47 100644 (file)
@@ -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)
index c3ed131d7122c736f8ff633f07f0d8fff3948e05..d790f3d655f320a0809b96172a5ac3ec4db40080 100644 (file)
@@ -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;
 
index 9aee36230a76edab9b91d5ea65b6dea40dacd481..084f434de4801bcf01e00ce5454d1262fbc34dd4 100644 (file)
@@ -69,10 +69,16 @@ int pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[], int flags,
 #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,
index e1c8355e9cf9bfc8776d838fdc087556e3116aa9..bf7a78b282d4dbd5de4f1b3efaf4744e82d79810 100644 (file)
@@ -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);
index c6c0d6b9e4c2b922854ff467b74e0030e7f1d82f..0b420be0f7402f47a003903da5b7fabc353b7067 100644 (file)
@@ -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;
index ce27d9210dd711cefccf2776ccf02579f541b798..11e364f9733bead7e6bcefb2c44352f97d82616d 100644 (file)
@@ -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;
 
index 533aa3317f1e33cfc365ccce6ea10dc866b67249..a3c73edc6df2be0b0df52d41631a3cc300a3675a 100644 (file)
@@ -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;