From: Michael Tremer Date: Fri, 15 Jan 2021 22:33:55 +0000 (+0000) Subject: libpakfire: execute: Read content from stdout/stderr and sent it to the logger X-Git-Tag: 0.9.28~1285^2~853 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=31f64b2c6a3e65eac46d42412442bf2b9ef016e9;p=pakfire.git libpakfire: execute: Read content from stdout/stderr and sent it to the logger Signed-off-by: Michael Tremer --- diff --git a/configure.ac b/configure.ac index c2c5e30e5..32cc047fc 100644 --- a/configure.ac +++ b/configure.ac @@ -120,6 +120,7 @@ AC_CHECK_LIB([m], [round]) AC_CHECK_HEADERS([ \ assert.h \ ctypes.h \ + fcntl.h \ math.h \ regex.h \ sched.h \ @@ -127,6 +128,7 @@ AC_CHECK_HEADERS([ \ stdlib.h \ string.h \ syslog.h \ + sys/epoll.h \ sys/personality.h \ sys/stat.h \ unistd.h @@ -134,6 +136,10 @@ AC_CHECK_HEADERS([ \ AC_CHECK_FUNCS([ \ assert \ + epoll_ctl \ + epoll_create1 \ + epoll_wait \ + fnctl \ personality \ secure_getenv \ strcmp \ diff --git a/src/_pakfire/pakfire.c b/src/_pakfire/pakfire.c index 253f99724..f5dea94a5 100644 --- a/src/_pakfire/pakfire.c +++ b/src/_pakfire/pakfire.c @@ -351,13 +351,15 @@ static Py_ssize_t Pakfire_len(PakfireObject* self) { } static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* kwds) { - char* kwlist[] = {"command", "environ", "enable_network", NULL}; + char* kwlist[] = {"command", "environ", "enable_network", "log_output", NULL}; PyObject* command = NULL; PyObject* environ = NULL; int enable_network = 0; + int log_output = 1; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Op", kwlist, &command, &environ, &enable_network)) + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O|Opp", kwlist, &command, &environ, + &enable_network, &log_output)) return NULL; // Check if command is a list @@ -447,6 +449,10 @@ static PyObject* Pakfire_execute(PakfireObject* self, PyObject* args, PyObject* if (enable_network) flags |= PAKFIRE_EXECUTE_ENABLE_NETWORK; + // Log output? + if (log_output) + flags |= PAKFIRE_EXECUTE_LOG_OUTPUT; + // Execute command int r = pakfire_execute(self->pakfire, argv, envp, flags); diff --git a/src/libpakfire/execute.c b/src/libpakfire/execute.c index 35a5434a9..3fdd167e6 100644 --- a/src/libpakfire/execute.c +++ b/src/libpakfire/execute.c @@ -19,11 +19,14 @@ #############################################################################*/ #include +#include #include #include #include +#include #include #include +#include #include #include @@ -33,6 +36,8 @@ #include #include +#define EPOLL_MAX_EVENTS 2 + static char* envp_empty[1] = { NULL }; struct pakfire_execute { @@ -45,6 +50,103 @@ struct pakfire_execute { int stderr[2]; }; +struct pakfire_execute_line_buffer { + char data[PAGE_SIZE]; + size_t used; +}; + +static int pakfire_execute_logger(Pakfire pakfire, pid_t pid, int stdout, int stderr, int* status) { + struct epoll_event ev; + struct epoll_event events[EPOLL_MAX_EVENTS]; + int r = 0; + + int fds[2] = { + stdout, stderr, + }; + + struct buffers { + struct pakfire_execute_line_buffer stdout; + struct pakfire_execute_line_buffer stderr; + } buffers; + + // Setup epoll + int epollfd = epoll_create1(0); + if (epollfd < 0) { + ERROR(pakfire, "Could not initialize epoll(): %s\n", strerror(errno)); + r = -errno; + goto OUT; + } + + ev.events = EPOLLIN; + + // Turn file descriptors into non-blocking mode and add them to epoll() + for (unsigned int i = 0; i < 2; i++) { + int fd = fds[i]; + + // Read flags + int flags = fcntl(fd, F_GETFL, 0); + + // Set modified flags + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + ERROR(pakfire, "Could not set file descriptor %d into non-blocking mode: %s\n", + fd, strerror(errno)); + r = -errno; + goto OUT; + } + + ev.data.fd = fd; + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, fd, &ev) < 0) { + ERROR(pakfire, "Could not add file descriptor %d to epoll(): %s\n", + fd, strerror(errno)); + r = -errno; + goto OUT; + } + } + + // Loop for as long as the process is alive + while (waitpid(pid, status, WNOHANG) == 0) { + int fds = epoll_wait(epollfd, events, EPOLL_MAX_EVENTS, -1); + if (fds < 1) { + ERROR(pakfire, "epoll_wait() failed: %s\n", strerror(errno)); + r = -errno; + + goto OUT; + } + + struct pakfire_execute_line_buffer* buffer; + + for (int i = 0; i < fds; i++) { + int fd = events[i].data.fd; + + if (fd == stdout) + buffer = &buffers.stdout; + + else if (fd == stderr) + buffer = &buffers.stderr; + + else { + DEBUG(pakfire, "Received invalid file descriptor %d\n", fd); + continue; + } + + // Simply read everything into the buffer and send it to the logger + size_t bytes_read = read(fd, buffer->data, PAGE_SIZE); + if (bytes_read) { + // Terminate the buffer + buffer->data[bytes_read] = '\0'; + + INFO(pakfire, "%s\n", buffer->data); + } + } + } + +OUT: + close(epollfd); + + return r; +} + static int pakfire_execute_fork(void* data) { struct pakfire_execute* env = (struct pakfire_execute*)data; @@ -166,19 +268,26 @@ PAKFIRE_EXPORT int pakfire_execute(Pakfire pakfire, const char* argv[], char* en return -errno; } - // Close any unused file descriptors - if (env.stdout[1]) - close(env.stdout[1]); - if (env.stderr[1]) - close(env.stderr[1]); + // Set some useful error code + int r = -ESRCH; + int status; DEBUG(pakfire, "Waiting for PID %d to finish its work\n", pid); - int status; - waitpid(pid, &status, 0); + if (flags & PAKFIRE_EXECUTE_LOG_OUTPUT) { + // Close any unused file descriptors + if (env.stdout[1]) + close(env.stdout[1]); + if (env.stderr[1]) + close(env.stderr[1]); + + if (pakfire_execute_logger(pakfire, pid, env.stdout[0], env.stderr[0], &status) < 0) { + ERROR(pakfire, "Log reading aborted\n"); + } + } - // Set some useful error code - int r = -ESRCH; + if (!status) + waitpid(pid, &status, 0); if (WIFEXITED(status)) { r = WEXITSTATUS(status); diff --git a/src/pakfire/builder.py b/src/pakfire/builder.py index f83ac943a..1b5c3e157 100644 --- a/src/pakfire/builder.py +++ b/src/pakfire/builder.py @@ -513,4 +513,4 @@ class BuilderContext(object): # Enter the shell self.pakfire.execute(["/usr/bin/bash", "--login"], - environ=self.environ, enable_network=True) + environ=self.environ, enable_network=True, log_output=False) diff --git a/tests/python/execute.py b/tests/python/execute.py index eb25534bd..5bdd033ea 100755 --- a/tests/python/execute.py +++ b/tests/python/execute.py @@ -58,6 +58,9 @@ class Test(unittest.TestCase): with self.assertRaises(pakfire.CommandExecutionError): self.pakfire.execute(["/usr/bin/does-not-exist"]) + def test_execute_output(self): + self.pakfire.execute(["/bin/bash", "--help"], log_output=True) + # This is an interactive test which cannot be performed automatically #def test_shell(self): # self.pakfire.execute(["/bin/bash", "-i"])