Skip to content

Instantly share code, notes, and snippets.

@vodrazka
Last active May 30, 2017 08:30
Show Gist options
  • Save vodrazka/b6fbf0f6dfc51e5a15e972bef0e2507b to your computer and use it in GitHub Desktop.
Save vodrazka/b6fbf0f6dfc51e5a15e972bef0e2507b to your computer and use it in GitHub Desktop.
Watch for changes in local directory, if file is created start watching it. If file is not busy by other pids (except this programs process and fuser child process) fire given command with that new file as first argument.
package main
import (
"log"
"github.com/howeyc/fsnotify"
"path/filepath"
"os"
"flag"
"os/exec"
"fmt"
"bytes"
"strconv"
"strings"
"time"
)
var fireCommand string
var intervalDuration time.Duration
func main() {
initFlags()
watcher, err := fsnotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
done := make(chan bool)
dir, _ := filepath.Abs(filepath.Dir(os.Args[0]))
_, err = exec.LookPath("fuser")
if err != nil {
log.Fatal(err, "fuser cannot be found. Export? Install? Access rights?")
}
// Process events
go func() {
for {
select {
case ev := <-watcher.Event:
if ev.IsCreate() {
if !strings.HasSuffix(ev.Name, ".ignore") {
log.Println("event:", ev)
fullFilePath := dir + "/" + ev.Name
log.Println("Started to watching file:", fullFilePath)
go watchFile(fullFilePath)
}
}
case err := <-watcher.Error:
log.Println("error:", err)
}
}
}()
err = watcher.Watch(".")
if err != nil {
log.Fatal(err)
}
// Hang so program doesn't exit
<-done
watcher.Close()
}
func watchFile(file string) {
if isFileFree(file) {
log.Printf("File is free: %s\n", file)
fmt.Printf("Running: %s %s\n", fireCommand, file)
cmd := exec.Command(fireCommand, file)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
fmt.Print(out.String())
if err != nil {
log.Fatal(err)
}
}
}
func isFileFree(file string) bool {
pid := os.Getpid()
ownPid := strconv.Itoa(pid)
cmd := exec.Command("fuser", file)
//cmd.Stdin = strings.NewReader("some input")
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
log.Print("fuser err:", err)
}
//log.Printf("Own pid: %s\n", ownPid)
fuserPid := strconv.Itoa(cmd.Process.Pid)
//log.Printf("Child fuser pid: %s\n", fuserPid)
output := out.String()
//log.Printf("fuser output original:(%s)", output)
output = strings.Replace(output, ownPid, "", 1)
output = strings.Replace(output, fuserPid, "", 1)
output = strings.Trim(output, " ")
if len(output) == 0 {
return true
} else {
log.Printf("File %s locked by pid(s) %s\n", file, output)
time.Sleep(intervalDuration)
return isFileFree(file)
}
}
func initFlags() {
flag.StringVar(&fireCommand, "c", "", "Command which will be fired when a new file gets created "+
"(file checked with fuser until free). The new file will be passed as 1st arg to that command.")
flag.DurationVar(&intervalDuration, "n", 5*time.Second, "Interval for checking on a locked file.")
flag.Parse()
if fireCommand == "" {
log.Fatal("Fire command not provided")
} else {
fmt.Printf("Fire command: %v \n", fireCommand)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment