daemon.go 4.14 KB
Newer Older
1
package main
2 3

import (
4
	"fmt"
5 6
	"net/http"

7
	manners "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/braintree/manners"
8 9
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
10 11
	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
12
	core "github.com/jbenet/go-ipfs/core"
13
	commands "github.com/jbenet/go-ipfs/core/commands2"
14
	daemon "github.com/jbenet/go-ipfs/daemon2"
15
	util "github.com/jbenet/go-ipfs/util"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
16
	"github.com/jbenet/go-ipfs/util/debugerror"
17 18 19
)

const (
20
	initOptionKwd = "init"
21 22 23
	mountKwd      = "mount"
	ipfsMountKwd  = "mount-ipfs"
	ipnsMountKwd  = "mount-ipns"
24 25
)

26
var daemonCmd = &cmds.Command{
27
	Helptext: cmds.HelpText{
Matt Bell's avatar
Matt Bell committed
28 29 30 31 32 33 34 35
		Tagline: "Run a network-connected IPFS node",
		ShortDescription: `
'ipfs daemon' runs a persistent IPFS daemon that can serve commands
over the network. Most applications that use IPFS will do so by
communicating with a daemon over the HTTP API. While the daemon is
running, calls to 'ipfs' commands will be sent over the network to
the daemon.
`,
36 37
	},

38
	Options: []cmds.Option{
39
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
40 41 42
		cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
		cmds.StringOption(ipfsMountKwd, "Path to the mountpoint for IPFS (if using --mount)"),
		cmds.StringOption(ipnsMountKwd, "Path to the mountpoint for IPNS (if using --mount)"),
43
	},
44 45 46 47
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

48
func daemonFunc(req cmds.Request) (interface{}, error) {
49 50 51 52 53 54 55 56 57
	cfg, err := req.Context().GetConfig()
	if err != nil {
		return nil, err
	}

	node, err := core.NewIpfsNode(cfg, true)
	if err != nil {
		return nil, err
	}
58

59
	initialize, _, err := req.Option(initOptionKwd).Bool()
60 61 62
	if err != nil {
		return nil, err
	}
63
	if initialize {
64 65 66 67 68 69 70 71

		// now, FileExists is our best method of detecting whether IPFS is
		// configured. Consider moving this into a config helper method
		// `IsInitialized` where the quality of the signal can be improved over
		// time, and many call-sites can benefit.
		if !util.FileExists(req.Context().ConfigRoot) {
			err := initWithDefaults(req.Context().ConfigRoot)
			if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
72
				return nil, debugerror.Wrap(err)
73 74 75 76
			}
		}
	}

77
	lock, err := daemon.Lock(req.Context().ConfigRoot)
78
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
79
		return nil, debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?")
80
	}
81
	defer lock.Close()
82

83 84 85
	// setup function that constructs the context. we have to do it this way
	// to play along with how the Context works and thus not expose its internals
	req.Context().ConstructNode = func() (*core.IpfsNode, error) {
86
		return node, nil
87
	}
88 89

	addr, err := ma.NewMultiaddr(cfg.Addresses.API)
90
	if err != nil {
91
		return nil, err
92 93
	}

94
	_, host, err := manet.DialArgs(addr)
95
	if err != nil {
96
		return nil, err
97 98
	}

99 100
	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
101
	if err != nil {
102
		return nil, err
103
	}
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
	if mount {
		fsdir, found, err := req.Option(ipfsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found, err := req.Option(ipnsMountKwd).String()
		if err != nil {
			return nil, err
		}
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = commands.Mount(node, fsdir, nsdir)
		if err != nil {
			return nil, err
		}
	}
126

127
	mux := http.NewServeMux()
128
	cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root)
129
	mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)
130 131

	ifpsHandler := &ipfsHandler{node}
132 133
	mux.Handle("/ipfs/", ifpsHandler)

134
	err = listenAndServe(node, mux, host)
135 136 137
	return nil, err
}

138
func listenAndServe(node *core.IpfsNode, mux *http.ServeMux, host string) error {
139 140

	fmt.Printf("API server listening on '%s'\n", host)
141
	s := manners.NewServer()
142

143 144 145 146
	done := make(chan struct{}, 1)
	defer func() {
		done <- struct{}{}
	}()
147

148
	// go wait until the node dies
149 150
	go func() {
		select {
151
		case <-node.Closed():
152
		case <-done:
153
			return
154
		}
155 156 157

		log.Info("terminating daemon at %s...", host)
		s.Shutdown <- true
158 159 160 161
	}()

	if err := s.ListenAndServe(host, mux); err != nil {
		return err
162
	}
163

164
	return nil
165
}