diff options
Diffstat (limited to 'cmd_linux.go')
| -rw-r--r-- | cmd_linux.go | 131 |
1 files changed, 131 insertions, 0 deletions
diff --git a/cmd_linux.go b/cmd_linux.go new file mode 100644 index 0000000..9e7e237 --- /dev/null +++ b/cmd_linux.go @@ -0,0 +1,131 @@ +//go:build !windows + +package main + +import ( + "bufio" + "context" + "encoding/json" + "flag" + "net" + "os" + "os/signal" + "path/filepath" + "syscall" + "time" +) + +var ( + hostDir = flag.String("host", "/host", "Host source directory (read-only)") + dataDir = flag.String("data", "/data", "Data destination directory") + verbose = flag.Bool("verbose", false, "Enable verbose logging (shorthand for -log-level debug)") + logLevel = flag.String("log-level", "info", "Log level: debug, info, warn, error, none") + dryRun = flag.Bool("dry-run", false, "Preview what would be synced without copying") + socketPath = flag.String("socket", "", "Unix socket to receive events from (Windows watcher)") +) + +func run() { + flag.Parse() + + if *verbose { + *logLevel = "debug" + } + + level, err := ParseLogLevel(*logLevel) + if err != nil { + logErrorf("%v", err) + os.Exit(1) + } + SetLogLevel(level) + + stack := NewGitignoreStack(*hostDir) + + manifest, err := initialSync(*hostDir, *dataDir, stack, *dryRun) + if err != nil { + logErrorf("initial sync failed: %v", err) + os.Exit(1) + } + + if *dryRun { + logInfo("dry-run complete, exiting") + return + } + + if *socketPath == "" { + logInfo("no -socket specified, running in standalone mode (no file watching)") + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + <-ctx.Done() + return + } + + logInfo("watcher: connecting to %s ...", *socketPath) + ctx, cancel := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM) + defer cancel() + + if err := listenSocket(ctx, *socketPath, manifest, stack); err != nil { + logErrorf("socket listener failed: %v", err) + os.Exit(1) + } +} + +func listenSocket(ctx context.Context, socketPath string, manifest *Manifest, stack *GitignoreStack) error { + var conn net.Conn + var err error + + for { + if ctx.Err() != nil { + return ctx.Err() + } + conn, err = net.Dial("unix", socketPath) + if err == nil { + break + } + logDebug("connect failed: %v, retrying...", err) + select { + case <-ctx.Done(): + return ctx.Err() + case <-time.After(1 * time.Second): + } + } + defer conn.Close() + + logInfo("watcher: connected, monitoring for changes ...") + + scanner := bufio.NewScanner(conn) + for scanner.Scan() { + var evt FileEvent + if err := json.Unmarshal(scanner.Bytes(), &evt); err != nil { + logDebug("bad event: %v", err) + continue + } + + logDebug("event: %s %s", evt.Op, evt.Path) + + if isGitDir(evt.Path) { + continue + } + + if stack.IsIgnored(evt.Path) { + continue + } + + destPath := filepath.Join(*dataDir, evt.Path) + srcPath := filepath.Join(*hostDir, evt.Path) + + switch evt.Op { + case "create": + syncCreate(srcPath, destPath, evt.Path, manifest, stack, *dryRun, nil) + case "write": + syncWrite(srcPath, destPath, evt.Path, manifest, *dryRun) + case "remove": + syncRemove(destPath, evt.Path, manifest, *dryRun) + } + } + + if err := scanner.Err(); err != nil { + return err + } + + return nil +} |
