- Path traversal: CheckPath now requires separator after prefix match - Store namespace: block reserved '_' prefixed groups - StoreGet: distinguish ErrNotFound from real DB errors via sentinel - Store: add rows.Err() checks in GetAll and Render - gRPC leak: cleanupGRPC on all early-return error paths in OnStartup - DenoClient: fix fmt.Sprint(nil) → type assertions - Socket permissions: 0700 dirs, 0600 sockets (owner-only) - Marketplace: persist SignKey, re-verify manifest on Update - io/local: resolve symlinks in New() (macOS /var → /private/var) - Tests: fix sun_path length overflow on macOS Co-Authored-By: Virgil <virgil@lethean.io>
75 lines
1.6 KiB
Go
75 lines
1.6 KiB
Go
package coredeno
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
)
|
|
|
|
// Start launches the Deno sidecar process with the given entrypoint args.
|
|
func (s *Sidecar) Start(ctx context.Context, args ...string) error {
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
|
|
if s.cmd != nil {
|
|
return fmt.Errorf("coredeno: already running")
|
|
}
|
|
|
|
// Ensure socket directory exists with owner-only permissions
|
|
sockDir := filepath.Dir(s.opts.SocketPath)
|
|
if err := os.MkdirAll(sockDir, 0700); err != nil {
|
|
return fmt.Errorf("coredeno: mkdir %s: %w", sockDir, err)
|
|
}
|
|
|
|
// Remove stale Deno socket (the Core socket is managed by ListenGRPC)
|
|
if s.opts.DenoSocketPath != "" {
|
|
os.Remove(s.opts.DenoSocketPath)
|
|
}
|
|
|
|
s.ctx, s.cancel = context.WithCancel(ctx)
|
|
s.cmd = exec.CommandContext(s.ctx, s.opts.DenoPath, args...)
|
|
s.cmd.Env = append(os.Environ(),
|
|
"CORE_SOCKET="+s.opts.SocketPath,
|
|
"DENO_SOCKET="+s.opts.DenoSocketPath,
|
|
)
|
|
s.done = make(chan struct{})
|
|
if err := s.cmd.Start(); err != nil {
|
|
s.cmd = nil
|
|
s.cancel()
|
|
return fmt.Errorf("coredeno: start: %w", err)
|
|
}
|
|
|
|
// Monitor in background — waits for exit, then signals done
|
|
go func() {
|
|
s.cmd.Wait()
|
|
s.mu.Lock()
|
|
s.cmd = nil
|
|
s.mu.Unlock()
|
|
close(s.done)
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
// Stop cancels the context and waits for the process to exit.
|
|
func (s *Sidecar) Stop() error {
|
|
s.mu.RLock()
|
|
if s.cmd == nil {
|
|
s.mu.RUnlock()
|
|
return nil
|
|
}
|
|
done := s.done
|
|
s.mu.RUnlock()
|
|
|
|
s.cancel()
|
|
<-done
|
|
return nil
|
|
}
|
|
|
|
// IsRunning returns true if the sidecar process is alive.
|
|
func (s *Sidecar) IsRunning() bool {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
return s.cmd != nil
|
|
}
|