diff options
Diffstat (limited to 'src/filter.c')
| -rw-r--r-- | src/filter.c | 122 |
1 files changed, 122 insertions, 0 deletions
diff --git a/src/filter.c b/src/filter.c new file mode 100644 index 0000000..7bc2a68 --- /dev/null +++ b/src/filter.c @@ -0,0 +1,122 @@ +#include "filter.h" +#include "log.h" + +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> + +#define FILTER_TIMEOUT_MS 250 + +/* ── filter_start ────────────────────────────────────────────────────────── */ + +void filter_start(Unit *u) { + assert(u != NULL); + assert(u->filter_in_fd == -1); + + if (!u->log_filter[0]) return; + + int to_filter[2], from_filter[2]; + if (pipe(to_filter) < 0) { + log_warn("filter: pipe() failed for %s: %s", u->name, strerror(errno)); + return; + } + if (pipe(from_filter) < 0) { + log_warn("filter: pipe() failed for %s: %s", u->name, strerror(errno)); + close(to_filter[0]); close(to_filter[1]); + return; + } + + pid_t pid = fork(); + if (pid < 0) { + log_warn("filter: fork() failed for %s: %s", u->name, strerror(errno)); + close(to_filter[0]); close(to_filter[1]); + close(from_filter[0]); close(from_filter[1]); + return; + } + + if (pid == 0) { + /* Child: wire pipes and exec the filter */ + if (dup2(to_filter[0], STDIN_FILENO) < 0) _exit(1); + if (dup2(from_filter[1], STDOUT_FILENO) < 0) _exit(1); + close(to_filter[0]); close(to_filter[1]); + close(from_filter[0]); close(from_filter[1]); + execl(u->log_filter, u->log_filter, (char *)NULL); + _exit(1); + } + + /* Parent: keep write end of to_filter, read end of from_filter */ + close(to_filter[0]); + close(from_filter[1]); + if (fcntl(from_filter[0], F_SETFL, O_NONBLOCK) < 0) + log_warn("filter: fcntl O_NONBLOCK failed for %s", u->name); + + u->filter_pid = pid; + u->filter_in_fd = to_filter[1]; + u->filter_out_fd = from_filter[0]; + log_info("filter: started '%s' (pid %d) for unit %s", + u->log_filter, (int)pid, u->name); +} + +/* ── filter_apply ────────────────────────────────────────────────────────── */ + +void filter_apply(Unit *u, char *buf, int bufsize, int *len) { + assert(u != NULL); + assert(buf != NULL); + assert(len != NULL); + + if (u->filter_in_fd < 0) return; + + ssize_t w = write(u->filter_in_fd, buf, (size_t)*len); + if (w < 0) { + log_warn("filter: write failed for %s: %s — stopping filter", + u->name, strerror(errno)); + filter_stop(u); + return; + } + + struct pollfd pfd = { u->filter_out_fd, POLLIN, 0 }; + int r = poll(&pfd, 1, FILTER_TIMEOUT_MS); + if (r < 0) { + log_warn("filter: poll failed for %s: %s", u->name, strerror(errno)); + return; /* pass through unchanged */ + } + if (r == 0) return; /* timeout — pass through unchanged */ + + ssize_t n = read(u->filter_out_fd, buf, bufsize - 1); + if (n < 0 && errno != EAGAIN) + log_warn("filter: read failed for %s: %s", u->name, strerror(errno)); + if (n > 0) { + buf[n] = '\0'; + *len = (int)n; + } else { + *len = 0; /* filter suppressed all output */ + } +} + +/* ── filter_stop / filter_stop_all ───────────────────────────────────────── */ + +void filter_stop(Unit *u) { + assert(u != NULL); + + if (u->filter_pid <= 0) return; + + close(u->filter_in_fd); + close(u->filter_out_fd); + kill(u->filter_pid, SIGTERM); + + u->filter_in_fd = -1; + u->filter_out_fd = -1; + u->filter_pid = 0; + log_info("filter: stopped filter for unit %s", u->name); +} + +void filter_stop_all(void) { + assert(g.unit_count >= 0); + + for (int i = 0; i < g.unit_count; i++) + filter_stop(&g.units[i]); +} |
