]> git.ipfire.org Git - pakfire.git/commitdiff
jail: Bring back pipes for non-interactive sessions
authorMichael Tremer <michael.tremer@ipfire.org>
Mon, 24 Mar 2025 15:58:33 +0000 (15:58 +0000)
committerMichael Tremer <michael.tremer@ipfire.org>
Mon, 24 Mar 2025 15:58:33 +0000 (15:58 +0000)
Signed-off-by: Michael Tremer <michael.tremer@ipfire.org>
src/pakfire/jail.c
src/pakfire/log_stream.c
src/pakfire/log_stream.h
tests/libpakfire/jail.c

index 339dcc67dcf76877b170ee83d73aadb48a28620a..85bbbf706c537e69dd7f175239f86ecca51718b7 100644 (file)
@@ -130,8 +130,19 @@ struct pakfire_jail_exec {
        // PTY
        struct pakfire_pty* pty;
 
-       // Log pipes
+       // Pipes
        struct pakfire_jail_pipes {
+               int stdin[2];
+               int stdout[2];
+               int stderr[2];
+       } pipes;
+
+       // Streams
+       struct pakfire_jail_streams {
+               // Output
+               FILE* stdout;
+               FILE* stderr;
+
                // Logging
                struct pakfire_log_stream* INFO;
                struct pakfire_log_stream* WARN;
@@ -139,7 +150,7 @@ struct pakfire_jail_exec {
 #ifdef ENABLE_DEBUG
                struct pakfire_log_stream* DEBUG;
 #endif /* ENABLE_DEBUG */
-       } log;
+       } streams;
 
        // Timeout
        sd_event_source* timeout;
@@ -150,6 +161,17 @@ struct pakfire_jail_exec {
        // The function to be called once the jail has been set up
        pakfire_jail_callback callback;
        void* data;
+
+       // Callbacks
+       struct pakfire_jail_exec_callbacks {
+               // Input
+               pakfire_jail_input_callback input;
+               void* input_data;
+
+               // Output
+               pakfire_jail_output_callback output;
+               void* output_data;
+       } callbacks;
 };
 
 static int pivot_root(const char* new_root, const char* old_root) {
@@ -312,6 +334,143 @@ int pakfire_jail_set_timeout(
        return 0;
 }
 
+/*
+       Pipes
+*/
+
+static int pakfire_jail_setup_pipe(struct pakfire_jail* self, int (*fds)[2], const int flags) {
+       int r;
+
+       // Setup the pipe
+       r = pipe2(*fds, flags);
+       if (r < 0) {
+               ERROR(self->ctx, "Could not setup pipe: %m\n");
+               return -errno;
+       }
+
+       return 0;
+}
+
+static void pakfire_jail_close_pipe(struct pakfire_jail* self, 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* self, 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 = -EBADF;
+       }
+
+       // Return the read end
+       if (*fd_read >= 0)
+               return *fd_read;
+
+       return -EBADF;
+}
+
+static int pakfire_jail_get_pipe_to_write(struct pakfire_jail* self, 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 = -EBADF;
+       }
+
+       // Return the write end
+       if (*fd_write >= 0)
+               return *fd_write;
+
+       return -EBADF;
+}
+
+static int pakfire_jail_stdin(sd_event_source* source, int fd, unsigned int events, void* data) {
+       struct pakfire_jail_exec* exec = data;
+       struct pakfire_jail* self = exec->jail;
+       ssize_t bytes_written = 0;
+
+       // Fail if no callback is set
+       if (unlikely(!exec->callbacks.input)) {
+               ERROR(self->ctx, "Tried to call the standard input callback, but it is not set\n");
+               goto CLOSE;
+       }
+
+       // Call the callback
+       bytes_written = exec->callbacks.input(self->ctx, fd, exec->callbacks.input_data);
+       if (bytes_written < 0) {
+               ERROR(self->ctx, "The input callback failed: %s\n", strerror(-bytes_written));
+               return bytes_written;
+
+       // Close the file descriptor if we are done reading
+       } else if (bytes_written == 0) {
+               goto CLOSE;
+       }
+
+       return 0;
+
+CLOSE:
+       pakfire_jail_close_pipe(self, exec->pipes.stdin);
+
+       return 0;
+}
+
+static int pakfire_jail_output(struct pakfire_jail* self, int fd, unsigned int events, FILE* f) {
+       ssize_t bytes_written = 0;
+       ssize_t bytes_read = 0;
+       char buffer[4096];
+
+       for (;;) {
+               bytes_read = read(fd, buffer, sizeof(buffer));
+               if (bytes_read < 0) {
+                       switch (errno) {
+                               case EAGAIN:
+                                       return 0;
+
+                               default:
+                                       return -errno;
+                       }
+
+               // XXX Should we close the stream here?
+               } else if (bytes_read == 0) {
+                       return 0;
+               }
+
+               // Write the buffer to the output
+               if (f) {
+                       bytes_written = fwrite(buffer, 1, bytes_read, f);
+                       if (bytes_written < bytes_read) {
+                               ERROR(self->ctx, "Failed to write output: %m\n");
+                               return -errno;
+                       }
+               }
+       }
+}
+
+static int pakfire_jail_stdout(sd_event_source* source, int fd, unsigned int events, void* data) {
+       struct pakfire_jail_exec* exec = data;
+
+       return pakfire_jail_output(exec->jail, fd, events, exec->streams.stdout);
+}
+
+static int pakfire_jail_stderr(sd_event_source* source, int fd, unsigned int events, void* data) {
+       struct pakfire_jail_exec* exec = data;
+
+       return pakfire_jail_output(exec->jail, fd, events, exec->streams.stderr);
+}
+
 /*
        This function replaces any logging in the child process.
 
@@ -327,20 +486,20 @@ static void pakfire_jail_log_redirect(void* data, int priority, const char* file
 
        switch (priority) {
                case LOG_INFO:
-                       pakfire_log_stream_vprintf(ctx->log.INFO, format, args);
+                       pakfire_log_stream_vprintf(ctx->streams.INFO, format, args);
                        break;
 
                case LOG_WARNING:
-                       pakfire_log_stream_vprintf(ctx->log.WARN, format, args);
+                       pakfire_log_stream_vprintf(ctx->streams.WARN, format, args);
                        break;
 
                case LOG_ERR:
-                       pakfire_log_stream_vprintf(ctx->log.ERROR, format, args);
+                       pakfire_log_stream_vprintf(ctx->streams.ERROR, format, args);
                        break;
 
 #ifdef ENABLE_DEBUG
                case LOG_DEBUG:
-                       pakfire_log_stream_vprintf(ctx->log.DEBUG, format, args);
+                       pakfire_log_stream_vprintf(ctx->streams.DEBUG, format, args);
                        break;
 #endif /* ENABLE_DEBUG */
 
@@ -1061,6 +1220,7 @@ ERROR:
        Performs the initialisation that needs to happen in the parent part
 */
 static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
+       int fd = -EBADF;
        int r;
 
        // Register the PID file descriptor
@@ -1070,17 +1230,47 @@ static int pakfire_jail_parent(struct pakfire_jail* jail, struct pakfire_jail_ex
                return r;
        }
 
+       // Prepare standard input for writing
+       fd = pakfire_jail_get_pipe_to_write(jail, &ctx->pipes.stdin);
+       if (fd >= 0) {
+               r = sd_event_add_io(ctx->loop, NULL, fd, EPOLLOUT|EPOLLHUP|EPOLLET, pakfire_jail_stdin, ctx);
+               if (r < 0) {
+                       ERROR(jail->ctx, "Failed to register standard input for writing: %s\n", strerror(-r));
+                       return r;
+               }
+       }
+
+       // Prepare standard output for reading
+       fd = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stdout);
+       if (fd >= 0) {
+               r = sd_event_add_io(ctx->loop, NULL, fd, EPOLLIN|EPOLLHUP|EPOLLET, pakfire_jail_stdout, ctx);
+               if (r < 0) {
+                       ERROR(jail->ctx, "Failed to register standard output for reading: %s\n", strerror(-r));
+                       return r;
+               }
+       }
+
+       // Prepare standard error for reading
+       fd = pakfire_jail_get_pipe_to_read(jail, &ctx->pipes.stderr);
+       if (fd >= 0) {
+               r = sd_event_add_io(ctx->loop, NULL, fd, EPOLLIN|EPOLLHUP|EPOLLET, pakfire_jail_stderr, ctx);
+               if (r < 0) {
+                       ERROR(jail->ctx, "Failed to register standard error for reading: %s\n", strerror(-r));
+                       return r;
+               }
+       }
+
        // Setup logging
-       r = pakfire_log_stream_in_parent(ctx->log.INFO, ctx->loop);
+       r = pakfire_log_stream_in_parent(ctx->streams.INFO, ctx->loop);
        if (r)
                return r;
 
-       r = pakfire_log_stream_in_parent(ctx->log.ERROR, ctx->loop);
+       r = pakfire_log_stream_in_parent(ctx->streams.ERROR, ctx->loop);
        if (r)
                return r;
 
 #ifdef ENABLE_DEBUG
-       r = pakfire_log_stream_in_parent(ctx->log.DEBUG, ctx->loop);
+       r = pakfire_log_stream_in_parent(ctx->streams.DEBUG, ctx->loop);
        if (r)
                return r;
 #endif /* ENABLE_DEBUG */
@@ -1138,6 +1328,49 @@ static int pakfire_jail_switch_root(struct pakfire_jail* jail, const char* root)
        return 0;
 }
 
+static int pakfire_jail_connect_null(struct pakfire_jail* self, int fileno) {
+       int fd = -EBADF;
+       int flags = 0;
+       int r;
+
+       switch (fileno) {
+               case STDIN_FILENO:
+                       flags |= O_RDONLY;
+                       break;
+
+               case STDOUT_FILENO:
+               case STDERR_FILENO:
+                       flags |= O_WRONLY;
+                       break;
+
+               default:
+                       return -EINVAL;
+       }
+
+
+       // Open /dev/null
+       fd = open("/dev/null", flags);
+       if (fd < 0) {
+               ERROR(self->ctx, "Failed to open /dev/null: %m\n");
+               r = -errno;
+               goto ERROR;
+       }
+
+       // Copy to the desired file descriptor
+       r = dup2(fd, fileno);
+       if (r < 0) {
+               ERROR(self->ctx, "Failed to duplicate the file descriptor: %m\n");
+               r = -errno;
+               goto ERROR;
+       }
+
+ERROR:
+       if (fd >= 0)
+               close(fd);
+
+       return r;
+}
+
 static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
        int r;
 
@@ -1279,27 +1512,76 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
        }
 
        // Open a new PTY
-       r = pakfire_pty_open(ctx->pty);
-       if (r < 0) {
-               ERROR(jail->ctx, "Could not open a new PTY: %s\n", strerror(-r));
-               return r;
+       if (ctx->pty) {
+               r = pakfire_pty_open(ctx->pty);
+               if (r < 0) {
+                       ERROR(jail->ctx, "Could not open a new PTY: %s\n", strerror(-r));
+                       return r;
+               }
+
+       // If we don't have a PTY, we will connect the logging pipes
+       } else {
+               // Standard input
+               if (ctx->pipes.stdin[0] >= 0) {
+                       r = dup2(ctx->pipes.stdin[0], STDIN_FILENO);
+                       if (r < 0) {
+                               ERROR(jail->ctx, "Failed to connect %d to stdin: %m\n", ctx->pipes.stdin[0]);
+                               return -errno;
+                       }
+               } else {
+                       r = pakfire_jail_connect_null(jail, STDIN_FILENO);
+                       if (r < 0)
+                               return r;
+               }
+
+               // Standard output
+               if (ctx->pipes.stdout[1] >= 1) {
+                       r = dup2(ctx->pipes.stdout[1], STDOUT_FILENO);
+                       if (r < 0) {
+                               ERROR(jail->ctx, "Failed to connect %d to stdout: %m\n", ctx->pipes.stdout[1]);
+                               return -errno;
+                       }
+               } else {
+                       r = pakfire_jail_connect_null(jail, STDOUT_FILENO);
+                       if (r < 0)
+                               return r;
+               }
+
+               // Standard error
+               if (ctx->pipes.stderr[1] >= 1) {
+                       r = dup2(ctx->pipes.stderr[1], STDERR_FILENO);
+                       if (r < 0) {
+                               ERROR(jail->ctx, "Failed to connect %d to stderr: %m\n", ctx->pipes.stderr[1]);
+                               return -errno;
+                       }
+               } else {
+                       r = pakfire_jail_connect_null(jail, STDERR_FILENO);
+                       if (r < 0)
+                               return r;
+               }
+
+               // Close the pipes entirely as the reading/writing ends
+               // that we need have been moved to stdin/stdout/stderr.
+               pakfire_jail_close_pipe(jail, ctx->pipes.stdin);
+               pakfire_jail_close_pipe(jail, ctx->pipes.stdout);
+               pakfire_jail_close_pipe(jail, ctx->pipes.stderr);
        }
 
        // Setup logging
-       r = pakfire_log_stream_in_child(ctx->log.INFO);
+       r = pakfire_log_stream_in_child(ctx->streams.INFO);
        if (r)
                return r;
 
-       r = pakfire_log_stream_in_child(ctx->log.WARN);
+       r = pakfire_log_stream_in_child(ctx->streams.WARN);
        if (r)
                return r;
 
-       r = pakfire_log_stream_in_child(ctx->log.ERROR);
+       r = pakfire_log_stream_in_child(ctx->streams.ERROR);
        if (r)
                return r;
 
 #ifdef ENABLE_DEBUG
-       r = pakfire_log_stream_in_child(ctx->log.DEBUG);
+       r = pakfire_log_stream_in_child(ctx->streams.DEBUG);
        if (r)
                return r;
 #endif /* ENABLE_DEBUG */
@@ -1342,6 +1624,8 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail,
                pakfire_pty_stdin_callback stdin_callback, void* stdin_data,
                pakfire_pty_stdout_callback stdout_callback, void* stdout_data,
                char** output, size_t* output_length) {
+       struct pakfire_log_stream* stdout = NULL;
+       struct pakfire_log_stream* stderr = NULL;
        int r;
 
        // We cannot have both, an output callback and buffer
@@ -1352,11 +1636,18 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail,
        struct pakfire_jail_exec ctx = {
                .jail  = jail,
                .flags = flags,
-               .pidfd = -1,
+               .pidfd = -EBADF,
 
                // Callback & Data
                .callback = callback,
                .data     = data,
+
+               // Pipes
+               .pipes = {
+                       .stdin  = { -EBADF, -EBADF },
+                       .stdout = { -EBADF, -EBADF },
+                       .stderr = { -EBADF, -EBADF },
+               },
        };
 
        DEBUG(jail->ctx, "Executing jail...\n");
@@ -1406,29 +1697,66 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail,
                        goto ERROR;
                }
 
-       // Do nothing if we have a callback set
-       } else if (stdout_callback) {
-               // Perform line-buffering on the callback
-               r = pakfire_pty_log_stream(ctx.pty, stdout_callback, stdout_data);
-               if (r < 0) {
-                       ERROR(jail->ctx, "Failed to setup log stream: %s\n", strerror(-r));
+       // Otherwise we are running a non-interactive session
+       } else {
+               // Create a pipe for the standard input only when a callback is set
+               if (stdin_callback) {
+                       r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdin, O_NONBLOCK);
+                       if (r < 0)
+                               goto ERROR;
+               }
+
+               // Create a pipe for standard output
+               r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stdout, O_NONBLOCK);
+               if (r < 0)
+                       goto ERROR;
+
+               // Create a pipe for standard error
+               r = pakfire_jail_setup_pipe(jail, &ctx.pipes.stderr, O_NONBLOCK);
+               if (r < 0)
                        goto ERROR;
+
+               // Setup some buffer if we want to capture the output
+               if (output) {
+                       ctx.streams.stdout = open_memstream(output, output_length);
+                       if (!ctx.streams.stdout) {
+                               ERROR(jail->ctx, "Failed to open memory stream for output: %m\n");
+                               r = -errno;
+                               goto ERROR;
+                       }
+
+               // Otherwise send the output to the log streamer
+               } else {
+                       // Create a new stream
+                       r = pakfire_log_stream_create(&stdout, jail->ctx, stdout_callback, stdout_data);
+                       if (r < 0) {
+                               ERROR(jail->ctx, "Failed to create standard output stream: %s\n", strerror(-r));
+                               goto ERROR;
+                       }
+
+                       // Ask to stream our output into this
+                       ctx.streams.stdout = pakfire_log_stream_as_file(stdout);
+                       if (!ctx.streams.stdout) {
+                               ERROR(jail->ctx, "Failed to open file stream for standard output: %m\n");
+                               r = -errno;
+                               goto ERROR;
+                       }
                }
 
-       // Capture Output?
-       } else if (output) {
-#warning This needs to be moved back internally again
-#if 0
-               r = pakfire_pty_capture_output(ctx.pty, output, output_length);
+               // Send the standard error output to a log streamer, too
+               r = pakfire_log_stream_create(&stderr, jail->ctx, stdout_callback, stdout_data);
                if (r < 0) {
-                       ERROR(jail->ctx, "Failed to set up output capture: %s\n", strerror(-r));
+                       ERROR(jail->ctx, "Failed to create standard error stream: %s\n", strerror(-r));
                        goto ERROR;
                }
-#endif
 
-       // Otherwise we dump everything to the console
-       } else {
-               // XXX Need to find a solution about what to do here...
+               // Ask to stream our output into this
+               ctx.streams.stderr = pakfire_log_stream_as_file(stderr);
+               if (!ctx.streams.stderr) {
+                       ERROR(jail->ctx, "Failed to open file stream for standard output: %m\n");
+                       r = -errno;
+                       goto ERROR;
+               }
        }
 
        /*
@@ -1441,29 +1769,31 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail,
                goto ERROR;
        }
 
+#if 0
        // Configure the callbacks
        if (stdin_callback)
                pakfire_pty_set_stdin_callback(ctx.pty, stdin_callback, stdin_data);
+#endif
 
        // Setup pipes for logging
        // INFO
-       r = pakfire_log_stream_create(&ctx.log.INFO, jail->ctx, pakfire_jail_INFO, jail);
+       r = pakfire_log_stream_create(&ctx.streams.INFO, jail->ctx, pakfire_jail_INFO, jail);
        if (r)
                goto ERROR;
 
        // WARN
-       r = pakfire_log_stream_create(&ctx.log.WARN, jail->ctx, pakfire_jail_WARN, jail);
+       r = pakfire_log_stream_create(&ctx.streams.WARN, jail->ctx, pakfire_jail_WARN, jail);
        if (r)
                goto ERROR;
 
        // ERROR
-       r = pakfire_log_stream_create(&ctx.log.ERROR, jail->ctx, pakfire_jail_ERROR, jail);
+       r = pakfire_log_stream_create(&ctx.streams.ERROR, jail->ctx, pakfire_jail_ERROR, jail);
        if (r)
                goto ERROR;
 
 #ifdef ENABLE_DEBUG
        // DEBUG
-       r = pakfire_log_stream_create(&ctx.log.DEBUG, jail->ctx, pakfire_jail_DEBUG, jail);
+       r = pakfire_log_stream_create(&ctx.streams.DEBUG, jail->ctx, pakfire_jail_DEBUG, jail);
        if (r)
                goto ERROR;
 #endif /* ENABLE_DEBUG */
@@ -1557,6 +1887,11 @@ ERROR:
                pakfire_cgroup_unref(ctx.cgroup);
        }
 
+       // Close any pipes
+       pakfire_jail_close_pipe(jail, ctx.pipes.stdin);
+       pakfire_jail_close_pipe(jail, ctx.pipes.stdout);
+       pakfire_jail_close_pipe(jail, ctx.pipes.stderr);
+
        // Close any file descriptors
        if (ctx.pidfd >= 0)
                close(ctx.pidfd);
@@ -1565,16 +1900,26 @@ ERROR:
        if (ctx.pty)
                pakfire_pty_unref(ctx.pty);
 
+       // Streams
+       if (ctx.streams.stdout)
+               fclose(ctx.streams.stdout);
+       if (ctx.streams.stderr)
+               fclose(ctx.streams.stderr);
+       if (stdout)
+               pakfire_log_stream_unref(stdout);
+       if (stderr)
+               pakfire_log_stream_unref(stderr);
+
        // Logging
-       if (ctx.log.INFO)
-               pakfire_log_stream_unref(ctx.log.INFO);
-       if (ctx.log.WARN)
-               pakfire_log_stream_unref(ctx.log.WARN);
-       if (ctx.log.ERROR)
-               pakfire_log_stream_unref(ctx.log.ERROR);
+       if (ctx.streams.INFO)
+               pakfire_log_stream_unref(ctx.streams.INFO);
+       if (ctx.streams.WARN)
+               pakfire_log_stream_unref(ctx.streams.WARN);
+       if (ctx.streams.ERROR)
+               pakfire_log_stream_unref(ctx.streams.ERROR);
 #ifdef ENABLE_DEBUG
-       if (ctx.log.DEBUG)
-               pakfire_log_stream_unref(ctx.log.DEBUG);
+       if (ctx.streams.DEBUG)
+               pakfire_log_stream_unref(ctx.streams.DEBUG);
 #endif /* ENABLE_DEBUG */
 
        if (ctx.timeout)
index b7f6fafffb3337cebc1021cc3fcc4e68b3827e15..f113949b28f3e428ca4b2a264c65719bac513a95 100644 (file)
@@ -405,3 +405,58 @@ int pakfire_log_stream_pty(struct pakfire_ctx* ctx, void* data, const char* buff
        // Return how many bytes we have consumed
        return length;
 }
+
+static ssize_t __pakfire_log_stream_write(void* cookie, const char* buffer, size_t length) {
+       struct pakfire_log_stream* self = cookie;
+       int r;
+
+       // Push the data into the buffer
+       r = pakfire_buffer_push(&self->buffer, buffer, length);
+       if (r < 0)
+               return r;
+
+       // Drain the buffer
+       r = pakfire_log_stream_drain_buffer(self);
+       if (r < 0)
+               return r;
+
+       // Return how many bytes we have consumed
+       return length;
+}
+
+static int __pakfire_log_stream_close(void* cookie) {
+       struct pakfire_log_stream* self = cookie;
+
+       // Release the reference to the stream
+       if (self)
+               pakfire_log_stream_unref(self);
+
+       return 0;
+}
+
+FILE* pakfire_log_stream_as_file(struct pakfire_log_stream* self) {
+       struct pakfire_log_stream* stream = NULL;
+       static cookie_io_functions_t functions = {
+               .write = __pakfire_log_stream_write,
+               .close = __pakfire_log_stream_close,
+       };
+       FILE* f = NULL;
+
+       // Create a new reference to the stream
+       stream = pakfire_log_stream_ref(self);
+
+       // Create the file handle
+       f = fopencookie(stream, "w+", functions);
+       if (!f) {
+               ERROR(self->ctx, "fopencookie failed: %m\n");
+               goto ERROR;
+       }
+
+       return f;
+
+ERROR:
+       if (stream)
+               pakfire_log_stream_unref(stream);
+
+       return NULL;
+}
index 91b4ce219a82aa5bc330c87fd2728274db35e566..cf3022a39129af877946979b5cd168ccdc692aba 100644 (file)
@@ -51,4 +51,6 @@ int pakfire_log_stream_close(struct pakfire_log_stream* stream);
 
 int pakfire_log_stream_pty(struct pakfire_ctx* ctx, void* data, const char* buffer, size_t length);
 
+FILE* pakfire_log_stream_as_file(struct pakfire_log_stream* self);
+
 #endif /* PAKFIRE_LOG_STREAM_H */
index 8f541032ef93d5cf770e83fbab8485332d3d765c..2196fbe336f554bb8e8324f5d328df79dc44223c 100644 (file)
@@ -68,7 +68,7 @@ static int test_exit_code(const struct test* t) {
        ASSERT_SUCCESS(pakfire_jail_create(&jail, t->pakfire));
 
        // Check if we receive the correct exit code
-       ASSERT(pakfire_jail_exec_command(jail, argv, NULL, 0) == 123);
+       ASSERT_EQUALS(pakfire_jail_exec_command(jail, argv, NULL, 0), 123);
 
        // Success
        r = EXIT_SUCCESS;