From 540d7476fb2a3ec812720158f797a3a916eadeba Mon Sep 17 00:00:00 2001 From: Bernhard Guillon Date: Tue, 7 Jul 2026 11:49:16 +0200 Subject: Switch from Unix sockets to TCP for Docker Desktop compatibility Architecture: - Container acts as TCP server (listens on :5151) - Windows binary connects as TCP client - Same protocol: newline-delimited JSON Usage: docker run -p 5151:5151 -v C:\src:/host:ro -v data:/data sourcewatch sourcewatch.exe -dir C:\src -connect localhost:5151 This avoids Docker Desktop's inability to mount Windows Unix sockets into Linux containers (they become directories instead of files). --- cmd_linux.go | 55 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 26 deletions(-) (limited to 'cmd_linux.go') diff --git a/cmd_linux.go b/cmd_linux.go index 65e8644..069881a 100644 --- a/cmd_linux.go +++ b/cmd_linux.go @@ -15,10 +15,10 @@ import ( ) var ( - hostDir = flag.String("host", "/host", "Host source directory (read-only)") - dataDir = flag.String("data", "/data", "Data destination directory") - dryRun = flag.Bool("dry-run", false, "Preview what would be synced without copying") - socketPath = flag.String("socket", "/run/sourcewatch.sock", "Unix socket to receive events from (Windows watcher)") + hostDir = flag.String("host", "/host", "Host source directory (read-only)") + dataDir = flag.String("data", "/data", "Data destination directory") + dryRun = flag.Bool("dry-run", false, "Preview what would be synced without copying") + listen = flag.String("listen", ":5151", "TCP address to listen on for Windows watcher connections") ) func run() { @@ -40,8 +40,8 @@ func run() { stack := NewGitignoreStack(*hostDir) - if *socketPath == "" { - logInfo("no -socket specified, running standalone (full scan)") + if *listen == "" { + logInfo("no -listen specified, running standalone (full scan)") manifest, err := initialSync(*hostDir, *dataDir, stack, *dryRun) if err != nil { logErrorf("initial sync failed: %v", err) @@ -57,29 +57,35 @@ func run() { return } - logInfo("watcher: connecting to %s ...", *socketPath) + ln, err := net.Listen("tcp", *listen) + if err != nil { + logErrorf("listen: %v", err) + os.Exit(1) + } + defer ln.Close() + + logInfo("watcher: listening on %s", *listen) + logInfo("watcher: waiting for Windows watcher to connect ...") - var conn net.Conn for { if ctx.Err() != nil { - logErrorf("cancelled while connecting") - os.Exit(1) - } - conn, err = net.Dial("unix", *socketPath) - if err == nil { - break + return } - logDebug("connect failed: %v, retrying...", err) - select { - case <-ctx.Done(): - logErrorf("cancelled while connecting") - os.Exit(1) - case <-time.After(1 * time.Second): + + conn, err := ln.Accept() + if err != nil { + logErrorf("accept: %v", err) + continue } + + logInfo("watcher: client connected from %s", conn.RemoteAddr()) + handleClient(conn, stack) + logInfo("watcher: client disconnected, waiting for reconnect ...") } - defer conn.Close() +} - logInfo("watcher: connected, receiving manifest ...") +func handleClient(conn net.Conn, stack *GitignoreStack) { + defer conn.Close() scanner := bufio.NewScanner(conn) remoteManifest := &Manifest{Files: make(map[string]FileMeta)} @@ -134,7 +140,7 @@ func run() { } if err := scanner.Err(); err != nil { - logErrorf("socket read: %v", err) + logErrorf("read: %v", err) } } @@ -142,7 +148,6 @@ func syncFromManifest(remote *Manifest, stack *GitignoreStack) { stats := &syncStats{} start := time.Now() - // Scan local /data to get current state (fast, local volume) localManifest := scanLocalDirectory(*dataDir) for relPath, remoteMeta := range remote.Files { @@ -157,7 +162,6 @@ func syncFromManifest(remote *Manifest, stack *GitignoreStack) { destPath := filepath.Join(*dataDir, relPath) srcPath := filepath.Join(*hostDir, relPath) - // Check if file already exists locally with same metadata if localMeta, exists := localManifest.Files[relPath]; exists { if localMeta.Size == remoteMeta.Size && localMeta.Mode == remoteMeta.Mode && @@ -167,7 +171,6 @@ func syncFromManifest(remote *Manifest, stack *GitignoreStack) { } } - // Need to sync info, err := os.Lstat(srcPath) if err != nil { if os.IsNotExist(err) { -- cgit v1.2.3