github.com/Cloud-Foundations/Dominator@v0.3.4/lib/fsutil/watchFile.go (about) 1 //go:build !windows 2 // +build !windows 3 4 package fsutil 5 6 import ( 7 "io" 8 "os" 9 "syscall" 10 "time" 11 12 "github.com/Cloud-Foundations/Dominator/lib/log" 13 "github.com/Cloud-Foundations/Dominator/lib/wsyscall" 14 ) 15 16 var stopChannel = make(chan struct{}) 17 18 func watchFile(pathname string, logger log.Logger) <-chan io.ReadCloser { 19 readCloserChannel := make(chan io.ReadCloser, 1) 20 notifyChannel := watchFileWithFsNotify(pathname, logger) 21 go watchFileForever(pathname, readCloserChannel, notifyChannel, logger) 22 return readCloserChannel 23 } 24 25 func watchFileStop() { 26 watchFileStopWithFsNotify() 27 select { 28 case stopChannel <- struct{}{}: 29 default: 30 } 31 } 32 33 func watchFileForever(pathname string, readCloserChannel chan<- io.ReadCloser, 34 notifyChannel <-chan struct{}, logger log.Logger) { 35 interval := time.Second 36 if notifyChannel != nil { 37 interval = 15 * time.Second 38 } 39 intervalTimer := time.NewTimer(0) 40 var lastStat syscall.Stat_t 41 lastFd := -1 42 for { 43 select { 44 case <-intervalTimer.C: 45 case <-notifyChannel: 46 if !intervalTimer.Stop() { 47 <-intervalTimer.C 48 } 49 if lastFd >= 0 { 50 syscall.Close(lastFd) 51 } 52 lastFd = -1 53 case <-stopChannel: 54 if lastFd >= 0 { 55 syscall.Close(lastFd) 56 } 57 close(readCloserChannel) 58 return 59 } 60 intervalTimer = time.NewTimer(interval) 61 var stat syscall.Stat_t 62 if err := syscall.Stat(pathname, &stat); err != nil { 63 if logger != nil { 64 logger.Printf("Error stating file: %s: %s\n", pathname, err) 65 } 66 continue 67 } 68 if stat.Ino != lastStat.Ino { 69 if file, err := os.Open(pathname); err != nil { 70 if logger != nil { 71 logger.Printf("Error opening file: %s: %s\n", pathname, err) 72 } 73 continue 74 } else { 75 // By holding onto the file, we guarantee that the inode number 76 // for the file we've opened cannot be reused until we've seen 77 // a new inode. 78 if lastFd >= 0 { 79 syscall.Close(lastFd) 80 } 81 lastFd, _ = wsyscall.Dup(int(file.Fd())) 82 readCloserChannel <- file // Must happen after FD is duplicated. 83 lastStat = stat 84 } 85 } 86 } 87 }