summaryrefslogtreecommitdiff
path: root/src/log_tail.c
blob: d352f8fef72c2d4e54736d102ada2c8debaca716 (plain)
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;
}