]> git.ipfire.org Git - pakfire.git/blobdiff - src/libpakfire/jail.c
jail: Refactor how to drain logging buffers with callbacks
[pakfire.git] / src / libpakfire / jail.c
index 5f3884445c04117f8e3365b15983f6d536e393ed..29b0198a8a09c0bfc8846b1a2699d3f03c13709d 100644 (file)
@@ -595,66 +595,70 @@ static int pakfire_jail_log_buffer_is_full(const struct pakfire_log_buffer* buff
        return (sizeof(buffer->data) == buffer->used);
 }
 
-/*
-       This function reads as much data as it can from the file descriptor.
-       If it finds a whole line in it, it will send it to the logger and repeat the process.
-       If not newline character is found, it will try to read more data until it finds one.
-*/
-static int pakfire_jail_handle_log(struct pakfire_jail* jail,
-               struct pakfire_jail_exec* ctx, int priority, int fd,
-               struct pakfire_log_buffer* buffer, pakfire_jail_communicate_out callback, void* data) {
-       char line[BUFFER_SIZE + 1];
+static int pakfire_jail_fill_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
+       int r;
 
-       // Fill up buffer from fd
-       if (buffer->used < sizeof(buffer->data)) {
-               ssize_t bytes_read = read(fd, buffer->data + buffer->used,
-                               sizeof(buffer->data) - buffer->used);
-
-               // Handle errors
-               if (bytes_read < 0) {
-                       ERROR(jail->pakfire, "Could not read from fd %d: %m\n", fd);
-                       return -1;
+       // Skip this if there is not space left in the buffer
+       if (buffer->used >= sizeof(buffer->data))
+               return 0;
+
+       // Fill the buffer
+       r = read(fd, buffer->data + buffer->used, sizeof(buffer->data) - buffer->used);
+
+       // Handle errors
+       if (r < 0) {
+               switch (errno) {
+                       case EAGAIN:
+                       case EIO:
+                               break;
+
+                       default:
+                               return -errno;
                }
 
-               // Update buffer size
-               buffer->used += bytes_read;
+       // EOF
+       } else if (r == 0) {
+               // XXX What to do here?
+
+       // Successful read
+       } else {
+               buffer->used += r;
        }
 
-       // See if we have any lines that we can write
+       return 0;
+}
+
+static int pakfire_jail_drain_buffer_with_callback(struct pakfire_jail* jail,
+               struct pakfire_log_buffer* buffer, int priority, pakfire_jail_communicate_out callback, void* data) {
+       const char* eol = NULL;
+       int r;
+
        while (buffer->used) {
                // Search for the end of the first line
-               char* eol = memchr(buffer->data, '\n', buffer->used);
+               eol = memchr(buffer->data, '\n', buffer->used);
 
                // No newline found
                if (!eol) {
-                       // If the buffer is full, we send the content to the logger and try again
-                       // This should not happen in practise
+                       // If the buffer is full, we send the entire content to make space.
                        if (pakfire_jail_log_buffer_is_full(buffer)) {
-                               DEBUG(jail->pakfire, "Logging buffer is full. Sending all content\n");
+                               CTX_DEBUG(jail->ctx, "Buffer is full. Sending all content\n");
 
-                               eol = buffer->data + sizeof(buffer->data) - 1;
+                               eol = buffer->data + buffer->used - 1;
 
-                       // Otherwise we might have only read parts of the output
-                       } else
+                       // Otherwise we might have only read parts of the output...
+                       } else {
                                break;
+                       }
                }
 
                // Find the length of the string
-               size_t length = eol - buffer->data + 1;
-
-               // Copy the line into the buffer
-               memcpy(line, buffer->data, length);
-
-               // Terminate the string
-               line[length] = '\0';
+               const size_t length = eol - buffer->data + 1;
 
-               // Log the line
-               if (callback) {
-                       int r = callback(jail->pakfire, data, priority, line, length);
-                       if (r) {
-                               ERROR(jail->pakfire, "The logging callback returned an error: %d\n", r);
-                               return r;
-                       }
+               // Call the callback
+               r = callback(jail->pakfire, data, priority, buffer->data, length);
+               if (r) {
+                       CTX_ERROR(jail->ctx, "The logging callback returned an error: %d\n", r);
+                       return r;
                }
 
                // Remove line from buffer
@@ -665,6 +669,60 @@ static int pakfire_jail_handle_log(struct pakfire_jail* jail,
        return 0;
 }
 
+static int pakfire_jail_drain_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
+       int r;
+
+       // Nothing to do if the buffer is empty
+       if (!buffer->used)
+               return 0;
+
+       // Drain the buffer
+       r = write(fd, buffer->data, buffer->used);
+
+       // Handle errors
+       if (r < 0) {
+               switch (errno) {
+                       case EAGAIN:
+                       case EIO:
+                               break;
+
+                       default:
+                               return -errno;
+               }
+
+       // Successful write
+       } else {
+               memmove(buffer->data, buffer->data + r, buffer->used - r);
+
+               buffer->used -= r;
+       }
+
+       return 0;
+}
+
+/*
+       This function reads as much data as it can from the file descriptor.
+       If it finds a whole line in it, it will send it to the logger and repeat the process.
+       If not newline character is found, it will try to read more data until it finds one.
+*/
+static int pakfire_jail_handle_log(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx,
+               int priority, int fd, struct pakfire_log_buffer* buffer,
+               pakfire_jail_communicate_out callback, void* data) {
+       int r;
+
+       // Fill up buffer from fd
+       r = pakfire_jail_fill_buffer(jail, fd, buffer);
+       if (r)
+               return r;
+
+       // Drain the buffer
+       r = pakfire_jail_drain_buffer_with_callback(jail, buffer, priority, callback, data);
+       if (r)
+               return r;
+
+       return 0;
+}
+
 #if 0
 static int pakfire_jail_stream_stdin(struct pakfire_jail* jail,
                struct pakfire_jail_exec* ctx, const int fd) {
@@ -991,70 +1049,6 @@ static int pakfire_jail_setup_pty_forwarding(struct pakfire_jail* jail,
        return 0;
 }
 
-static int pakfire_jail_fill_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
-       int r;
-
-       // Skip this if there is not space left in the buffer
-       if (buffer->used >= sizeof(buffer->data))
-               return 0;
-
-       // Fill the buffer
-       r = read(fd, buffer->data + buffer->used, sizeof(buffer->data) - buffer->used);
-
-       // Handle errors
-       if (r < 0) {
-               switch (errno) {
-                       case EAGAIN:
-                       case EIO:
-                               break;
-
-                       default:
-                               return -errno;
-               }
-
-       // EOF
-       } else if (r == 0) {
-               // XXX What to do here?
-
-       // Successful read
-       } else {
-               buffer->used += r;
-       }
-
-       return 0;
-}
-
-static int pakfire_jail_drain_buffer(struct pakfire_jail* jail, int fd, struct pakfire_log_buffer* buffer) {
-       int r;
-
-       // Nothing to do if the buffer is empty
-       if (!buffer->used)
-               return 0;
-
-       // Drain the buffer
-       r = write(fd, buffer->data, buffer->used);
-
-       // Handle errors
-       if (r < 0) {
-               switch (errno) {
-                       case EAGAIN:
-                       case EIO:
-                               break;
-
-                       default:
-                               return -errno;
-               }
-
-       // Successful write
-       } else {
-               memmove(buffer->data, buffer->data + r, buffer->used - r);
-
-               buffer->used -= r;
-       }
-
-       return 0;
-}
-
 static int pakfire_jail_forward_pty(struct pakfire_jail* jail, struct pakfire_jail_exec* ctx) {
        int r;
 
@@ -1204,11 +1198,6 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                        int e  = events[i].events;
                        int fd = events[i].data.fd;
 
-                       struct pakfire_log_buffer* buffer = NULL;
-                       pakfire_jail_communicate_out callback = NULL;
-                       void* data = NULL;
-                       int priority;
-
                        // Handle PTY forwarding events
                        if (ctx->pty.master.fd == fd) {
                                if (e & (EPOLLIN|EPOLLHUP))
@@ -1247,12 +1236,10 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                        CTX_ERROR(jail->ctx, "Failed forwarding the PTY: %s\n", strerror(-r));
                                        goto ERROR;
                                }
-                       }
 
-                       // Check if there is any data to be read
-                       if (e & EPOLLIN) {
-                               // Handle any changes to the PIDFD
-                               if (fd == pidfd) {
+                       // Handle any changes to the PIDFD
+                       } else if (pidfd == fd) {
+                               if (e & EPOLLIN) {
                                        // Call waidid() and store the result
                                        r = waitid(P_PIDFD, ctx->pidfd, &ctx->status, WEXITED);
                                        if (r) {
@@ -1263,10 +1250,11 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                        // Mark that we have ended so that we will process the remaining
                                        // events from epoll() now, but won't restart the outer loop.
                                        ended = 1;
-                                       continue;
+                               }
 
-                               // Handle timer events
-                               } else if (fd == timerfd) {
+                       // Handle timer events
+                       } else if (timerfd == fd) {
+                               if (e & EPOLLIN) {
                                        DEBUG(jail->pakfire, "Timer event received\n");
 
                                        // Disarm the timer
@@ -1288,33 +1276,11 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                                        goto ERROR;
                                                }
                                        }
+                               }
 
-                                       // Don't fall through to log processing
-                                       continue;
-
-                               // Handle logging messages
-                               } else if (fd == log_INFO) {
-                                       buffer = &ctx->buffers.log_INFO;
-                                       priority = LOG_INFO;
-
-                                       callback = pakfire_jail_log;
-
-                               } else if (fd == log_ERROR) {
-                                       buffer = &ctx->buffers.log_ERROR;
-                                       priority = LOG_ERR;
-
-                                       callback = pakfire_jail_log;
-
-#ifdef ENABLE_DEBUG
-                               } else if (fd == log_DEBUG) {
-                                       buffer = &ctx->buffers.log_DEBUG;
-                                       priority = LOG_DEBUG;
-
-                                       callback = pakfire_jail_log;
-#endif /* ENABLE_DEBUG */
-
-                               // Handle socket messages
-                               } else if (fd == socket_recv) {
+                       // Handle socket messages
+                       } else if (socket_recv == fd) {
+                               if (e & EPOLLIN) {
                                        // Receive the passed FD
                                        r = pakfire_jail_recv_fd(jail, socket_recv, &fd);
                                        if (r)
@@ -1328,19 +1294,41 @@ static int pakfire_jail_wait(struct pakfire_jail* jail, struct pakfire_jail_exec
                                                        goto ERROR;
                                                }
                                        }
+                               }
 
-                                       // Don't fall through to log processing
-                                       continue;
+                       // Handle log INFO messages
+                       } else if (log_INFO == fd) {
+                               if (e & EPOLLIN) {
+                                       r = pakfire_jail_handle_log(jail, ctx, LOG_INFO, fd,
+                                               &ctx->buffers.log_INFO, pakfire_jail_log, NULL);
+                                       if (r)
+                                               goto ERROR;
+                               }
 
-                               } else {
-                                       DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
-                                       continue;
+                       // Handle log ERROR messages
+                       } else if (log_ERROR == fd) {
+                               if (e & EPOLLIN) {
+                                       r = pakfire_jail_handle_log(jail, ctx, LOG_ERR, fd,
+                                               &ctx->buffers.log_ERROR, pakfire_jail_log, NULL);
+                                       if (r)
+                                               goto ERROR;
                                }
 
-                               // Handle log event
-                               r = pakfire_jail_handle_log(jail, ctx, priority, fd, buffer, callback, data);
-                               if (r)
-                                       goto ERROR;
+#ifdef ENABLE_DEBUG
+                       // Handle log DEBUG messages
+                       } else if (log_DEBUG == fd) {
+                               if (e & EPOLLIN) {
+                                       r = pakfire_jail_handle_log(jail, ctx, LOG_DEBUG, fd,
+                                               &ctx->buffers.log_DEBUG, pakfire_jail_log, NULL);
+                                       if (r)
+                                               goto ERROR;
+                               }
+#endif /* ENABLE_DEBUG */
+
+                       // Log a message for anything else
+                       } else {
+                               DEBUG(jail->pakfire, "Received invalid file descriptor %d\n", fd);
+                               continue;
                        }
 
                        // Check if any file descriptors have been closed
@@ -2263,7 +2251,6 @@ static int pakfire_jail_child(struct pakfire_jail* jail, struct pakfire_jail_exe
 
 // Run a command in the jail
 static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
-               const int interactive,
                pakfire_jail_communicate_in  communicate_in,
                pakfire_jail_communicate_out communicate_out,
                void* data, int flags) {
@@ -2315,7 +2302,7 @@ static int __pakfire_jail_exec(struct pakfire_jail* jail, const char* argv[],
        DEBUG(jail->pakfire, "Executing jail...\n");
 
        // Enable networking in interactive mode
-       if (interactive)
+       if (ctx.flags & PAKFIRE_JAIL_PTY_FORWARDING)
                ctx.flags |= PAKFIRE_JAIL_HAS_NETWORKING;
 
        /*
@@ -2472,19 +2459,21 @@ PAKFIRE_EXPORT int pakfire_jail_exec(
                pakfire_jail_communicate_in  callback_in,
                pakfire_jail_communicate_out callback_out,
                void* data, int flags) {
-       return __pakfire_jail_exec(jail, argv, 0, callback_in, callback_out, data, flags);
+       return __pakfire_jail_exec(jail, argv, callback_in, callback_out, data, flags);
 }
 
 static int pakfire_jail_exec_interactive(
                struct pakfire_jail* jail, const char* argv[], int flags) {
        int r;
 
+       flags |= PAKFIRE_JAIL_PTY_FORWARDING;
+
        // Setup interactive stuff
        r = pakfire_jail_setup_interactive_env(jail);
        if (r)
                return r;
 
-       return __pakfire_jail_exec(jail, argv, 1, NULL, NULL, NULL, flags);
+       return __pakfire_jail_exec(jail, argv, NULL, NULL, NULL, flags);
 }
 
 int pakfire_jail_exec_script(struct pakfire_jail* jail,