1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
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]);
}
|