prevent multiple instances in same namespace

This commit is contained in:
Levi Neely 2025-11-30 12:10:44 +01:00
parent c2f17beff5
commit 77821a7d01
2 changed files with 43 additions and 8 deletions

View File

@ -34,6 +34,7 @@ import (
"denote/internal/disk" "denote/internal/disk"
"denote/pkg/encoding/results" "denote/pkg/encoding/results"
"denote/pkg/metadata" "denote/pkg/metadata"
"errors"
"fmt" "fmt"
"io" "io"
"net" "net"
@ -41,6 +42,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"sync" "sync"
"syscall"
"time" "time"
"9fans.net/go/plan9" "9fans.net/go/plan9"
@ -165,14 +167,36 @@ func StartServer(initialData metadata.Results, denoteDir string, callbacks Callb
sockPath := ns + "/denote" sockPath := ns + "/denote"
// Remove old socket if it exists // Try to create Unix domain socket listener
os.Remove(sockPath) // 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 // Check if error is "address already in use"
listener, err := net.Listen("unix", sockPath) if errors.Is(err, syscall.EADDRINUSE) {
if err != nil { // 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) 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 // Start accepting connections in background
go srv.acceptLoop(listener) go srv.acceptLoop(listener)

17
main.go
View File

@ -243,14 +243,25 @@ func main() {
// 9p server startup with pre-loaded data and callbacks // 9p server startup with pre-loaded data and callbacks
if err := p9server.StartServer(notes, denoteDir, callbacks); err != nil { 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 // start acme log watcher
go disk.WatchAcmeLog() go disk.WatchAcmeLog()
// open window // open window - look for existing /Denote/ window across all processes
if w = acme.Show(wname); w == nil { 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 { if w, err = acme.New(); err != nil {
log.Fatal(err) log.Fatal(err)
} }