From 82cf12f471e5e7768a6e8d2a412a0bb750a21bf9 Mon Sep 17 00:00:00 2001 From: auric Date: Sun, 22 Feb 2026 18:44:30 -0600 Subject: 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 --- src/log_tail.c | 19 ++++++++++++------- 1 file 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); } } } -- cgit v1.2.3