summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorauric <auric@japegames.com>2026-02-22 18:44:30 -0600
committerauric <auric@japegames.com>2026-02-22 18:44:30 -0600
commit3768dd71501e3485b9c2b7557e48a9438b18cb3c (patch)
treefb2963c0fbd18c516337b28757e49dd7c3166e47 /src
parent9d113e9bb8b7d57b4a088d4991674ff068ac56bf (diff)
Resolve symlinks in log_tail_init so inotify watches real file
inotify watches the symlink inode itself, not the target, so IN_MODIFY never fires when the target file is written. Use realpath() to resolve the configured path before open() and inotify_add_watch(), allowing a stable symlink (e.g. current.log) in the unit YAML that gets re-resolved to the actual log file on each init/SIGHUP. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Diffstat (limited to 'src')
-rw-r--r--src/log_tail.c19
1 files changed, 12 insertions, 7 deletions
diff --git a/src/log_tail.c b/src/log_tail.c
index 973cc0b..c238911 100644
--- a/src/log_tail.c
+++ b/src/log_tail.c
@@ -35,9 +35,14 @@ void log_tail_init(void) {
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);
+ /* Resolve symlinks so inotify watches the real file, not the link */
+ char resolved[MAX_PATH];
+ const char *open_path = path;
+ if (realpath(path, resolved) != NULL)
+ open_path = resolved;
+ int log_fd = open(open_path, O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (log_fd < 0) {
- log_warn("log_tail: cannot open %s: %s", path, strerror(errno));
+ log_warn("log_tail: cannot open %s: %s", open_path, strerror(errno));
continue;
}
lseek(log_fd, 0, SEEK_END);
@@ -47,17 +52,17 @@ void log_tail_init(void) {
close(log_fd);
continue;
}
- int wd = inotify_add_watch(ifd, path,
+ int wd = inotify_add_watch(ifd, open_path,
IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF);
if (wd < 0) {
log_warn("log_tail: inotify_add_watch %s: %s",
- path, strerror(errno));
+ open_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);
+ log_warn("log_tail: max watches reached, skipping %s", open_path);
close(ifd);
close(log_fd);
continue;
@@ -66,13 +71,13 @@ void log_tail_init(void) {
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);
+ strncpy(watches[watch_count].path, open_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);
+ log_info("log_tail: watching %s for unit %s", open_path, u->name);
}
}
}