From 0d706ae72ceefd74053ad6cb0900ecce6cf1f085 Mon Sep 17 00:00:00 2001 From: auric Date: Sat, 21 Feb 2026 11:08:36 -0600 Subject: Add Umbrella 0.1.5 --- src/log_tail.c | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 src/log_tail.c (limited to 'src/log_tail.c') diff --git a/src/log_tail.c b/src/log_tail.c new file mode 100644 index 0000000..d352f8f --- /dev/null +++ b/src/log_tail.c @@ -0,0 +1,118 @@ +#include "log_tail.h" +#include "client.h" +#include "unit.h" +#include "log.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#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; +} -- cgit v1.2.3