84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package toolsrv
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net"
|
|
)
|
|
|
|
// Addr describes how to reach a tool server.
|
|
type Addr struct {
|
|
// Network is the transport: "unix" (pre-existing socket) or "ssh".
|
|
Network string
|
|
// Host is the SSH target (user@host or user@host:port). Empty for local.
|
|
Host string
|
|
// CWD is the working directory on the target.
|
|
CWD string
|
|
// Socket is the Unix socket path to connect to.
|
|
// For local: set by Spawn(). For SSH: set by SpawnRemote().
|
|
Socket string
|
|
}
|
|
|
|
// LocalAddr returns an Addr for connecting to a local socket.
|
|
func LocalAddr(socket string) Addr {
|
|
return Addr{Network: "unix", Socket: socket}
|
|
}
|
|
|
|
// SSHAddr returns an Addr for a remote tool server over SSH.
|
|
func SSHAddr(host, cwd string) Addr {
|
|
return Addr{Network: "ssh", Host: host, CWD: cwd}
|
|
}
|
|
|
|
// Dial connects to a running tool server at the given address.
|
|
// The server must already be running (started via Spawn or SpawnRemote).
|
|
func Dial(ctx context.Context, addr Addr) (*Conn, error) {
|
|
switch addr.Network {
|
|
case "", "unix":
|
|
if addr.Socket == "" {
|
|
return nil, fmt.Errorf("toolsrv.Dial: socket path required for unix network")
|
|
}
|
|
conn, err := net.Dial("unix", addr.Socket)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("toolsrv.Dial: %w", err)
|
|
}
|
|
c := NewConn(conn, func() { conn.Close() })
|
|
if err := c.Ping(); err != nil {
|
|
c.Close()
|
|
return nil, fmt.Errorf("toolsrv.Dial: ping failed: %w", err)
|
|
}
|
|
return c, nil
|
|
|
|
case "ssh":
|
|
// For SSH, the caller should use SpawnRemote first, then Dial the
|
|
// returned local socket. Direct SSH dial is a convenience that does both.
|
|
proc, err := SpawnRemote(ctx, RemoteConfig{
|
|
SSHTarget: addr.Host,
|
|
CWD: addr.CWD,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
conn, err := net.Dial("unix", proc.Socket)
|
|
if err != nil {
|
|
proc.Close()
|
|
return nil, fmt.Errorf("toolsrv.Dial ssh: %w", err)
|
|
}
|
|
// Wrap cleanup: closing the conn + the process.
|
|
c := NewConn(conn, func() {
|
|
conn.Close()
|
|
proc.Close()
|
|
})
|
|
if err := c.Ping(); err != nil {
|
|
c.Close()
|
|
return nil, fmt.Errorf("toolsrv.Dial ssh: ping failed: %w", err)
|
|
}
|
|
if info, err := c.FetchHostInfo(); err == nil {
|
|
c.Info = info
|
|
}
|
|
return c, nil
|
|
|
|
default:
|
|
return nil, fmt.Errorf("toolsrv.Dial: unsupported network %q", addr.Network)
|
|
}
|
|
}
|