diff --git a/daemon/daemon.go b/daemon/daemon.go new file mode 100644 index 000000000..3e1cdee33 --- /dev/null +++ b/daemon/daemon.go @@ -0,0 +1,107 @@ +package daemon + +import ( + "encoding/json" + "errors" + "net" + "strings" + + u "github.com/jbenet/go-ipfs/util" +) + +var ErrInvalidCommand = errors.New("invalid command") + +type DaemonListener struct { + list net.Listener + CommChan chan *Command + closed bool +} + +func NewDaemonListener(addr string) (*DaemonListener, error) { + list, err := net.Listen("tcp", addr) + if err != nil { + return nil, err + } + + return &DaemonListener{ + list: list, + CommChan: make(chan *Command), + }, nil +} + +type Command struct { + command string + args []string + resp chan string +} + +func (dl *DaemonListener) Listen() { + for { + c, err := dl.list.Accept() + if err != nil { + if !dl.closed { + u.PErr("DaemonListener Accept: %v\n", err) + } + return + } + go dl.handleConnection(c) + } +} + +func (dl *DaemonListener) handleConnection(c net.Conn) { + dec := json.NewDecoder(c) + enc := json.NewEncoder(c) + var com string + err := dec.Decode(&com) + if err != nil { + err := enc.Encode(err.Error()) + if err != nil { + u.PErr("DaemonListener decode: %v\n", err) + } + return + } + u.DOut("Got command: %v\n", com) + + cmd, err := parseCommand(com) + if err != nil { + err := enc.Encode(err.Error()) + if err != nil { + u.PErr("DaemonListener parse: %v\n", err) + } + return + } + + select { + case dl.CommChan <- cmd: + default: + u.PErr("Recieved command after closing...") + return + } + + resp := <-cmd.resp + err = enc.Encode(resp) + if err != nil { + u.PErr("handleConnection: %v\n", err) + } +} + +func parseCommand(cmdi string) (*Command, error) { + params := strings.Split(cmdi, " ") + if len(params) == 0 { + return nil, ErrInvalidCommand + } + + //TODO: some sort of validation here + + return &Command{ + command: params[0], + args: params[1:], + resp: make(chan string), + }, nil +} + +func (dl *DaemonListener) Close() error { + dl.closed = true + close(dl.CommChan) + return dl.list.Close() +} diff --git a/daemon/daemon_client.go b/daemon/daemon_client.go new file mode 100644 index 000000000..5660fb9b2 --- /dev/null +++ b/daemon/daemon_client.go @@ -0,0 +1,29 @@ +package daemon + +import ( + "encoding/json" + "net" +) + +func SendCommand(command, server string) (string, error) { + con, err := net.Dial("tcp", server) + if err != nil { + return "", err + } + + enc := json.NewEncoder(con) + err = enc.Encode(command) + if err != nil { + return "", err + } + + dec := json.NewDecoder(con) + + var resp string + err = dec.Decode(&resp) + if err != nil { + return "", err + } + + return resp, nil +} diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go new file mode 100644 index 000000000..eb6b37522 --- /dev/null +++ b/daemon/daemon_test.go @@ -0,0 +1,70 @@ +package daemon + +import ( + "fmt" + "testing" +) + +func TestCommandCall(t *testing.T) { + dl, err := NewDaemonListener("localhost:12345") + if err != nil { + t.Fatal(err) + } + + go dl.Listen() + defer dl.Close() + + go func() { + _, err := SendCommand("test command for fun", "localhost:12345") + if err != nil { + t.Fatal(err) + } + }() + + cmd := <-dl.CommChan + if cmd.command != "test" { + t.Fatal("command parsing failed.") + } + + if cmd.args[0] != "command" || + cmd.args[1] != "for" || + cmd.args[2] != "fun" { + t.Fatal("Args parsed incorrectly.") + } +} + +func TestFailures(t *testing.T) { + dl, err := NewDaemonListener("localhost:12345") + if err != nil { + t.Fatal(err) + } + + go dl.Listen() + defer dl.Close() + + go func() { + _, err := SendCommand("test", "localhost:12345") + if err != nil { + t.Fatal(err) + } + }() + + cmd := <-dl.CommChan + if cmd.command != "test" || len(cmd.args) > 0 { + t.Fatal("Parsing Failed.") + } + + go func() { + _, err := SendCommand("", "localhost:12345") + if err != nil { + t.Fatal(err) + } + }() + + cmd = <-dl.CommChan + if cmd.command != "" || len(cmd.args) > 0 { + fmt.Println(cmd) + t.Fatal("Parsing Failed.") + } + +}