diff --git a/internal/p9/server/server.go b/internal/p9/server/server.go index f923f3e..4579d36 100644 --- a/internal/p9/server/server.go +++ b/internal/p9/server/server.go @@ -34,6 +34,7 @@ import ( "denote/internal/disk" "denote/pkg/encoding/results" "denote/pkg/metadata" + "errors" "fmt" "io" "net" @@ -41,6 +42,7 @@ import ( "regexp" "strings" "sync" + "syscall" "time" "9fans.net/go/plan9" @@ -165,14 +167,36 @@ func StartServer(initialData metadata.Results, denoteDir string, callbacks Callb sockPath := ns + "/denote" - // Remove old socket if it exists - os.Remove(sockPath) + // Try to create Unix domain socket listener + // Use stale socket detection like acme (see /usr/local/plan9/src/lib9/announce.c) + var listener net.Listener + var err error + for attempts := 0; attempts < 2; attempts++ { + listener, err = net.Listen("unix", sockPath) + if err == nil { + break // Successfully bound to socket + } - // Create Unix domain socket listener - listener, err := net.Listen("unix", sockPath) - if err != nil { + // Check if error is "address already in use" + if errors.Is(err, syscall.EADDRINUSE) { + // Try to connect to see if socket is live or stale + conn, dialErr := net.Dial("unix", sockPath) + if dialErr == nil { + // Connection succeeded - another instance is running + conn.Close() + return fmt.Errorf("Denote already running in this namespace") + } + // Connection failed - socket is stale, remove it and retry + os.Remove(sockPath) + continue + } + + // Some other error return fmt.Errorf("failed to listen on socket: %w", err) } + if err != nil { + return fmt.Errorf("failed to listen on socket after cleanup: %w", err) + } // Start accepting connections in background go srv.acceptLoop(listener) diff --git a/main.go b/main.go index 188b53d..91ac423 100644 --- a/main.go +++ b/main.go @@ -243,14 +243,25 @@ func main() { // 9p server startup with pre-loaded data and callbacks if err := p9server.StartServer(notes, denoteDir, callbacks); err != nil { - fmt.Fprintf(os.Stderr, "warning: failed to start fileserver: %v\n", err) + log.Fatal(err) } // start acme log watcher go disk.WatchAcmeLog() - // open window - if w = acme.Show(wname); w == nil { + // open window - look for existing /Denote/ window across all processes + if wins, err := acme.Windows(); err == nil { + for _, winInfo := range wins { + if winInfo.Name == wname { + // Found existing window, open it + if w, err = acme.Open(winInfo.ID, nil); err == nil { + break + } + } + } + } + // If no existing window found, create new one + if w == nil { if w, err = acme.New(); err != nil { log.Fatal(err) }