//go:build linux package control import ( "context" "log/slog" "net" "os" "time" ) // sockPath lives in the unit's RuntimeDirectory; mode 0666 lets every // local user ask, nothing can reach it from off the machine. const sockPath = "/run/gpu-turnstile/control.sock" // Serve starts the socket listener in the background and returns; only a // setup failure is reported. Each client connection is answered in its own // goroutine. func Serve(ctx context.Context, h Handler, log *slog.Logger) error { os.Remove(sockPath) // stale socket from a previous run ln, err := net.Listen("unix", sockPath) if err != nil { return err } if err := os.Chmod(sockPath, 0o666); err != nil { ln.Close() return err } go func() { <-ctx.Done() ln.Close() }() go func() { for { c, err := ln.Accept() if err != nil { return // shutting down } uc := unixConn{c} if !serve(uc, h) { uc.ForceClose() } } }() return nil } // unixConn adds an abortive ForceClose to net.Conn: a deadline in the // past fails pending and future I/O immediately. type unixConn struct{ net.Conn } func (c unixConn) ForceClose() error { c.SetDeadline(time.Now().Add(-time.Second)) //nolint:errcheck // best effort return c.Conn.Close() } // Ask sends one command to the running service and returns its reply. func Ask(cmd string) (string, error) { c, err := net.DialTimeout("unix", sockPath, 2*time.Second) if err != nil { return "", ErrUnavailable } defer c.Close() return readReply(c, cmd) }