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
|
#include "log_tail.h"
#include "client.h"
#include "unit.h"
#include "log.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/epoll.h>
#include <sys/inotify.h>
#define MAX_LOG_WATCHES (MAX_UNITS * 4)
typedef struct {
int inotify_fd;
int watch_wd;
int log_fd;
Unit *unit;
char path[MAX_PATH];
} LogWatch;
static LogWatch watches[MAX_LOG_WATCHES];
static int watch_count = 0;
void log_tail_init(void) {
watch_count = 0;
log_info("log_tail_init: checking %d unit(s) for log paths", g.unit_count);
for (int i = 0; i < g.unit_count; i++) {
Unit *u = &g.units[i];
log_info("log_tail_init: unit '%s' has %d log path(s)",
u->name, u->log_count);
for (int j = 0; j < u->log_count; j++) {
const char *path = u->log_paths[j];
if (!path[0]) continue;
int log_fd = open(path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (log_fd < 0) {
log_warn("log_tail: cannot open %s: %s", path, strerror(errno));
continue;
}
lseek(log_fd, 0, SEEK_END);
int ifd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
if (ifd < 0) {
log_error("inotify_init1: %s", strerror(errno));
close(log_fd);
continue;
}
int wd = inotify_add_watch(ifd, path, IN_MODIFY);
if (wd < 0) {
log_warn("log_tail: inotify_add_watch %s: %s",
path, strerror(errno));
close(ifd);
close(log_fd);
continue;
}
if (watch_count >= MAX_LOG_WATCHES) {
log_warn("log_tail: max watches reached, skipping %s", path);
close(ifd);
close(log_fd);
continue;
}
watches[watch_count].inotify_fd = ifd;
watches[watch_count].watch_wd = wd;
watches[watch_count].log_fd = log_fd;
watches[watch_count].unit = u;
strncpy(watches[watch_count].path, path, MAX_PATH - 1);
watch_count++;
struct epoll_event ev;
ev.events = EPOLLIN;
ev.data.fd = ifd;
epoll_ctl(g.epoll_fd, EPOLL_CTL_ADD, ifd, &ev);
log_info("log_tail: watching %s for unit %s", path, u->name);
}
}
}
Unit *log_tail_fd_to_unit(int fd) {
for (int i = 0; i < watch_count; i++) {
if (watches[i].inotify_fd == fd)
return watches[i].unit;
}
return NULL;
}
static LogWatch *watch_for_inotify_fd(int fd) {
for (int i = 0; i < watch_count; i++) {
if (watches[i].inotify_fd == fd)
return &watches[i];
}
return NULL;
}
void log_tail_handle(int inotify_fd) {
LogWatch *w = watch_for_inotify_fd(inotify_fd);
if (!w) return;
char evbuf[4096];
while (read(inotify_fd, evbuf, sizeof(evbuf)) > 0)
;
char buf[RING_BUF_LINE_MAX];
ssize_t n;
while ((n = read(w->log_fd, buf, sizeof(buf) - 1)) > 0) {
buf[n] = '\0';
ring_push(w->unit->output, buf, (int)n);
client_broadcast_output(w->unit->name, buf, 0);
}
}
void log_tail_cleanup(void) {
for (int i = 0; i < watch_count; i++) {
epoll_ctl(g.epoll_fd, EPOLL_CTL_DEL, watches[i].inotify_fd, NULL);
inotify_rm_watch(watches[i].inotify_fd, watches[i].watch_wd);
close(watches[i].inotify_fd);
close(watches[i].log_fd);
}
watch_count = 0;
}
|