#include "filter.h" #include "log.h" #include #include #include #include #include #include #include #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]); }