elvish/pkg/daemon/client.go

190 lines
4.9 KiB
Go
Raw Normal View History

package daemon
import (
"errors"
"net"
"sync"
2017-11-11 20:30:27 +08:00
"src.elv.sh/pkg/daemon/daemondefs"
"src.elv.sh/pkg/daemon/internal/api"
"src.elv.sh/pkg/rpc"
"src.elv.sh/pkg/store/storedefs"
)
const retriesOnShutdown = 3
var (
// ErrDaemonUnreachable is returned when the daemon cannot be reached after
// several retries.
ErrDaemonUnreachable = errors.New("daemon offline")
)
2019-12-27 01:23:18 +08:00
// Implementation of the Client interface.
type client struct {
sockPath string
rpcClient *rpc.Client
waits sync.WaitGroup
}
2017-12-23 06:25:07 +08:00
// NewClient creates a new Client instance that talks to the socket. Connection
// creation is deferred to the first request.
func NewClient(sockPath string) daemondefs.Client {
2019-12-27 01:23:18 +08:00
return &client{sockPath, nil, sync.WaitGroup{}}
}
// SockPath returns the socket path that the Client talks to. If the client is
// nil, it returns an empty string.
2019-12-27 01:23:18 +08:00
func (c *client) SockPath() string {
return c.sockPath
}
// ResetConn resets the current connection. A new connection will be established
// the next time a request is made. If the client is nil, it does nothing.
2019-12-27 01:23:18 +08:00
func (c *client) ResetConn() error {
if c.rpcClient == nil {
return nil
}
rc := c.rpcClient
c.rpcClient = nil
return rc.Close()
}
2017-12-23 06:25:07 +08:00
// Close waits for all outstanding requests to finish and close the connection.
// If the client is nil, it does nothing and returns nil.
2019-12-27 01:23:18 +08:00
func (c *client) Close() error {
2017-11-11 20:30:27 +08:00
c.waits.Wait()
return c.ResetConn()
2017-11-11 20:30:27 +08:00
}
func (c *client) call(f string, req, res any) error {
2017-12-23 06:25:07 +08:00
c.waits.Add(1)
defer c.waits.Done()
for attempt := 0; attempt < retriesOnShutdown; attempt++ {
if c.rpcClient == nil {
conn, err := net.Dial("unix", c.sockPath)
if err != nil {
return err
}
c.rpcClient = rpc.NewClient(conn)
}
err := c.rpcClient.Call(api.ServiceName+"."+f, req, res)
if err == rpc.ErrShutdown {
// Clear rpcClient so as to reconnect next time
c.rpcClient = nil
continue
} else {
return err
}
}
return ErrDaemonUnreachable
}
2017-11-11 20:30:27 +08:00
2017-12-23 06:25:07 +08:00
// Convenience methods for RPC methods. These are quite repetitive; when the
// number of RPC calls grow above some threshold, a code generator should be
// written to generate them.
2017-11-11 20:30:27 +08:00
2019-12-27 01:23:18 +08:00
func (c *client) Version() (int, error) {
req := &api.VersionRequest{}
res := &api.VersionResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("Version", req, res)
return res.Version, err
}
2019-12-27 01:23:18 +08:00
func (c *client) Pid() (int, error) {
req := &api.PidRequest{}
res := &api.PidResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("Pid", req, res)
return res.Pid, err
}
2019-12-27 01:23:18 +08:00
func (c *client) NextCmdSeq() (int, error) {
req := &api.NextCmdRequest{}
res := &api.NextCmdSeqResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("NextCmdSeq", req, res)
return res.Seq, err
}
2019-12-27 01:23:18 +08:00
func (c *client) AddCmd(text string) (int, error) {
req := &api.AddCmdRequest{Text: text}
res := &api.AddCmdResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("AddCmd", req, res)
return res.Seq, err
}
2019-12-27 01:23:18 +08:00
func (c *client) DelCmd(seq int) error {
req := &api.DelCmdRequest{Seq: seq}
res := &api.DelCmdResponse{}
err := c.call("DelCmd", req, res)
return err
}
2019-12-27 01:23:18 +08:00
func (c *client) Cmd(seq int) (string, error) {
req := &api.CmdRequest{Seq: seq}
res := &api.CmdResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("Cmd", req, res)
return res.Text, err
}
func (c *client) CmdsWithSeq(from, upto int) ([]storedefs.Cmd, error) {
req := &api.CmdsWithSeqRequest{From: from, Upto: upto}
res := &api.CmdsWithSeqResponse{}
err := c.call("CmdsWithSeq", req, res)
return res.Cmds, err
}
func (c *client) NextCmd(from int, prefix string) (storedefs.Cmd, error) {
req := &api.NextCmdRequest{From: from, Prefix: prefix}
res := &api.NextCmdResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("NextCmd", req, res)
return storedefs.Cmd{Text: res.Text, Seq: res.Seq}, err
2017-11-11 20:30:27 +08:00
}
func (c *client) PrevCmd(upto int, prefix string) (storedefs.Cmd, error) {
req := &api.PrevCmdRequest{Upto: upto, Prefix: prefix}
res := &api.PrevCmdResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("PrevCmd", req, res)
return storedefs.Cmd{Text: res.Text, Seq: res.Seq}, err
2017-11-11 20:30:27 +08:00
}
2019-12-27 01:23:18 +08:00
func (c *client) AddDir(dir string, incFactor float64) error {
req := &api.AddDirRequest{Dir: dir, IncFactor: incFactor}
res := &api.AddDirResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("AddDir", req, res)
return err
}
2019-12-27 01:23:18 +08:00
func (c *client) DelDir(dir string) error {
req := &api.DelDirRequest{Dir: dir}
res := &api.DelDirResponse{}
err := c.call("DelDir", req, res)
return err
}
func (c *client) Dirs(blacklist map[string]struct{}) ([]storedefs.Dir, error) {
req := &api.DirsRequest{Blacklist: blacklist}
res := &api.DirsResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("Dirs", req, res)
return res.Dirs, err
}
2019-12-27 01:23:18 +08:00
func (c *client) SharedVar(name string) (string, error) {
req := &api.SharedVarRequest{Name: name}
res := &api.SharedVarResponse{}
2017-11-11 20:30:27 +08:00
err := c.call("SharedVar", req, res)
return res.Value, err
}
2019-12-27 01:23:18 +08:00
func (c *client) SetSharedVar(name, value string) error {
req := &api.SetSharedVarRequest{Name: name, Value: value}
res := &api.SetSharedVarResponse{}
2017-11-11 20:30:27 +08:00
return c.call("SetSharedVar", req, res)
}
2019-12-27 01:23:18 +08:00
func (c *client) DelSharedVar(name string) error {
req := &api.DelSharedVarRequest{Name: name}
res := &api.DelSharedVarResponse{}
2017-11-11 20:30:27 +08:00
return c.call("DelSharedVar", req, res)
}