daemon.go 1.82 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 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()
}