Skip to content

Instantly share code, notes, and snippets.

@kundank78
Last active May 18, 2024 19:56
Show Gist options
  • Save kundank78/18969e373282a04d786b520de2197ccc to your computer and use it in GitHub Desktop.
Save kundank78/18969e373282a04d786b520de2197ccc to your computer and use it in GitHub Desktop.
Toy Tail - Mimic linux `tail -f` functionality using inotify sys call
package main
import (
"fmt"
"github.com/fsnotify/fsnotify"
"io"
"log"
"os"
)
type Tail struct {
fileName string
fileSize int64
events chan bool
f *os.File
watcher *fsnotify.Watcher
}
// Monitors `f.txt` file for any changes to tail
func main() {
fileName := "f.txt"
ws, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer ws.Close()
fileInfo, err := os.Stat(fileName)
if err != nil {
panic(err)
}
f, err := os.Open(fileName)
t := &Tail{
fileName: fileName,
fileSize: fileInfo.Size(),
events: make(chan bool),
f: f,
watcher: ws,
}
go t.ProcessEvents()
watcher(t.watcher, t.events, fileName)
}
// Monitor for file write events. If file size grows, read increased bytes from end & print;
// otherwise, print the entire file.
func (t *Tail) ProcessEvents() {
for {
select {
case <-t.events:
fileInfo, err := os.Stat(t.fileName)
if err != nil {
log.Println("Error getting new file size", err)
}
newFileSize := fileInfo.Size()
if t.fileSize < newFileSize {
byteChange := newFileSize - t.fileSize
t.f.Seek(-1*byteChange, io.SeekEnd)
t.fileSize = newFileSize
readByte := make([]byte, byteChange)
_, _ = t.f.Read(readByte)
fmt.Println(string(readByte))
} else {
t.fileSize = newFileSize
t.f.Seek(0, io.SeekStart)
readByte := make([]byte, newFileSize)
_, _ = t.f.Read(readByte)
fmt.Println(string(readByte))
}
}
}
}
func watcher(ws *fsnotify.Watcher, events chan bool, fileName string) {
go watch(ws, events)
err := ws.Add(fileName)
if err != nil {
log.Fatal(err)
}
<-make(chan struct{})
}
// looks for any write modification event in file using inotify sys call & send a event in channel
func watch(watcher *fsnotify.Watcher, events chan bool) {
for {
select {
case event, ok := <-watcher.Events:
if !ok {
return
}
if event.Has(fsnotify.Write) {
//log.Println("modified file:", event.Name)
events <- true
}
case err, ok := <-watcher.Errors:
if !ok {
return
}
log.Println("error:", err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment