104 lines
2.0 KiB
Go
104 lines
2.0 KiB
Go
package lsp
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"net"
|
|
"os"
|
|
"os/exec"
|
|
"strconv"
|
|
"syscall"
|
|
"time"
|
|
)
|
|
|
|
// ClientRequest sends a request to the bridge daemon, starting it if needed.
|
|
// Returns the raw JSON result.
|
|
func ClientRequest(req *BridgeRequest) (json.RawMessage, error) {
|
|
if !bridgeRunning() {
|
|
if err := startBridge(); err != nil {
|
|
return nil, fmt.Errorf("start bridge: %w", err)
|
|
}
|
|
}
|
|
|
|
resp, err := sendRequest(req)
|
|
if err != nil {
|
|
// Stale socket — restart
|
|
os.Remove(SockPath())
|
|
if err := startBridge(); err != nil {
|
|
return nil, fmt.Errorf("restart bridge: %w", err)
|
|
}
|
|
resp, err = sendRequest(req)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
if resp.Error != "" {
|
|
return nil, fmt.Errorf("%s", resp.Error)
|
|
}
|
|
return resp.Result, nil
|
|
}
|
|
|
|
func bridgeRunning() bool {
|
|
data, err := os.ReadFile(PidPath())
|
|
if err != nil {
|
|
return false
|
|
}
|
|
pid, err := strconv.Atoi(string(data))
|
|
if err != nil {
|
|
return false
|
|
}
|
|
// Check if process exists
|
|
proc, err := os.FindProcess(pid)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
return proc.Signal(syscall.Signal(0)) == nil
|
|
}
|
|
|
|
func startBridge() error {
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd := exec.Command(exe, "--bridge")
|
|
cmd.Stdin = nil
|
|
cmd.Stdout = nil
|
|
cmd.Stderr = nil
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{Setsid: true}
|
|
if err := cmd.Start(); err != nil {
|
|
return err
|
|
}
|
|
cmd.Process.Release()
|
|
|
|
// Wait for socket
|
|
sock := SockPath()
|
|
for i := 0; i < 50; i++ {
|
|
if _, err := os.Stat(sock); err == nil {
|
|
return nil
|
|
}
|
|
time.Sleep(100 * time.Millisecond)
|
|
}
|
|
return fmt.Errorf("bridge did not start within 5s")
|
|
}
|
|
|
|
func sendRequest(req *BridgeRequest) (*BridgeResponse, error) {
|
|
conn, err := net.DialTimeout("unix", SockPath(), 5*time.Second)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer conn.Close()
|
|
|
|
conn.SetDeadline(time.Now().Add(60 * time.Second))
|
|
|
|
if err := json.NewEncoder(conn).Encode(req); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var resp BridgeResponse
|
|
if err := json.NewDecoder(conn).Decode(&resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|