diff options
| author | auric <auric7@protonmail.com> | 2026-02-22 18:44:30 -0600 |
|---|---|---|
| committer | auric <auric7@protonmail.com> | 2026-02-22 18:44:30 -0600 |
| commit | 82cf12f471e5e7768a6e8d2a412a0bb750a21bf9 (patch) | |
| tree | fb2963c0fbd18c516337b28757e49dd7c3166e47 | |
| parent | b2dd175b900556fb6d7bb2928cad53e9d73fc6a7 (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>
| -rw-r--r-- | src/log_tail.c | 19 |
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); } } } |
