daemon.go 3.55 KB
Newer Older
1 2 3 4
package daemon

import (
	"encoding/json"
5
	"fmt"
6
	"io"
7
	"net"
8
	"os"
9
	"path"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
	"sync"
11

12
	core "github.com/jbenet/go-ipfs/core"
13
	"github.com/jbenet/go-ipfs/core/commands"
14
	u "github.com/jbenet/go-ipfs/util"
15

16
	lock "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/camlistore/lock"
17
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
18
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
19 20
)

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
21
var log = u.Logger("daemon")
22

23 24 25
// LockFile is the filename of the daemon lock, relative to config dir
const LockFile = "daemon.lock"

26 27
// DaemonListener listens to an initialized IPFS node and can send it commands instead of
// starting up a new set of connections
28
type DaemonListener struct {
29 30 31
	node   *core.IpfsNode
	list   net.Listener
	closed bool
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
32
	wg     sync.WaitGroup
33
	lk     io.Closer
34 35
}

Siraj Ravel's avatar
Siraj Ravel committed
36
//Command accepts user input and can be sent to the running IPFS node
Siraj Ravel's avatar
Siraj Ravel committed
37 38 39 40 41 42
type Command struct {
	Command string
	Args    []string
	Opts    map[string]interface{}
}

43
func NewDaemonListener(ipfsnode *core.IpfsNode, addr ma.Multiaddr, confdir string) (*DaemonListener, error) {
44 45 46 47 48 49
	var err error
	confdir, err = u.TildeExpansion(confdir)
	if err != nil {
		return nil, err
	}

50
	lk, err := daemonLock(confdir)
51 52 53 54
	if err != nil {
		return nil, err
	}

55
	network, host, err := manet.DialArgs(addr)
56 57 58 59
	if err != nil {
		return nil, err
	}

60 61 62 63 64 65
	ofi, err := os.Create(confdir + "/rpcaddress")
	if err != nil {
		log.Warning("Could not create rpcaddress file: %s", err)
		return nil, err
	}

66
	_, err = ofi.Write([]byte(addr.String()))
67 68 69 70 71 72
	if err != nil {
		log.Warning("Could not write to rpcaddress file: %s", err)
		return nil, err
	}
	ofi.Close()

73
	list, err := net.Listen(network, host)
74 75 76
	if err != nil {
		return nil, err
	}
77
	log.Info("New daemon listener initialized.")
78 79

	return &DaemonListener{
Siraj Ravel's avatar
Siraj Ravel committed
80
		node: ipfsnode,
81
		list: list,
82
		lk:   lk,
83 84 85
	}, nil
}

86 87 88 89
func NewCommand() *Command {
	return &Command{
		Opts: make(map[string]interface{}),
	}
90 91 92
}

func (dl *DaemonListener) Listen() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93 94 95 96
	if dl.closed {
		panic("attempting to listen on a closed daemon Listener")
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97
	// add ourselves to workgroup. and remove ourselves when done.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
98
	dl.wg.Add(1)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
99 100
	defer dl.wg.Done()

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101
	log.Info("daemon listening")
102
	for {
Siraj Ravel's avatar
Siraj Ravel committed
103
		conn, err := dl.list.Accept()
104 105
		if err != nil {
			if !dl.closed {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106
				log.Warning("DaemonListener Accept: %v", err)
107 108 109
			}
			return
		}
Siraj Ravel's avatar
Siraj Ravel committed
110
		go dl.handleConnection(conn)
111 112 113
	}
}

Siraj Ravel's avatar
Siraj Ravel committed
114 115
func (dl *DaemonListener) handleConnection(conn net.Conn) {
	defer conn.Close()
116

Siraj Ravel's avatar
Siraj Ravel committed
117
	dec := json.NewDecoder(conn)
118

Siraj Ravel's avatar
Siraj Ravel committed
119 120
	var command Command
	err := dec.Decode(&command)
121
	if err != nil {
Siraj Ravel's avatar
Siraj Ravel committed
122
		fmt.Fprintln(conn, err)
123 124
		return
	}
125

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
	log.Debug("Got command: %v", command)
127
	switch command.Command {
128
	case "add":
129
		err = commands.Add(dl.node, command.Args, command.Opts, conn)
130
	case "cat":
131
		err = commands.Cat(dl.node, command.Args, command.Opts, conn)
132
	case "ls":
133
		err = commands.Ls(dl.node, command.Args, command.Opts, conn)
Jeromy's avatar
Jeromy committed
134
	case "pin":
135
		err = commands.Pin(dl.node, command.Args, command.Opts, conn)
136 137
	case "publish":
		err = commands.Publish(dl.node, command.Args, command.Opts, conn)
138 139
	case "resolve":
		err = commands.Resolve(dl.node, command.Args, command.Opts, conn)
Jeromy's avatar
Jeromy committed
140
	case "diag":
Jeromy's avatar
Jeromy committed
141
		err = commands.Diag(dl.node, command.Args, command.Opts, conn)
142
	default:
143 144 145 146
		err = fmt.Errorf("Invalid Command: '%s'", command.Command)
	}
	if err != nil {
		fmt.Fprintln(conn, err)
147 148 149 150 151
	}
}

func (dl *DaemonListener) Close() error {
	dl.closed = true
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
152 153 154 155
	err := dl.list.Close()
	dl.wg.Wait() // wait till done before releasing lock.
	dl.lk.Close()
	return err
156
}
157 158 159 160

func daemonLock(confdir string) (io.Closer, error) {
	return lock.Lock(path.Join(confdir, LockFile))
}