daemon.go 4.89 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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14
	commands "github.com/jbenet/go-ipfs/core/commands"
	daemon "github.com/jbenet/go-ipfs/core/daemon"
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"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
24 25
	// apiAddrKwd    = "address-api"
	// swarmAddrKwd  = "address-swarm"
26 27
)

28
var daemonCmd = &cmds.Command{
29
	Helptext: cmds.HelpText{
Matt Bell's avatar
Matt Bell committed
30 31 32 33 34 35 36 37
		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.
`,
38 39
	},

40
	Options: []cmds.Option{
41
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
42 43 44
		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)"),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
45 46 47 48

		// TODO: add way to override addresses. tricky part: updating the config if also --init.
		// cmds.StringOption(apiAddrKwd, "Address for the daemon rpc API (overrides config)"),
		// cmds.StringOption(swarmAddrKwd, "Address for the swarm socket (overrides config)"),
49
	},
50 51 52 53
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

54
func daemonFunc(req cmds.Request) (interface{}, error) {
55

56 57
	// first, whether user has provided the initialization flag. we may be
	// running in an uninitialized state.
58
	initialize, _, err := req.Option(initOptionKwd).Bool()
59 60 61
	if err != nil {
		return nil, err
	}
62
	if initialize {
63 64 65 66 67 68 69 70

		// 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
71
				return nil, debugerror.Wrap(err)
72 73 74 75
			}
		}
	}

76 77 78 79 80 81 82 83 84 85 86 87 88
	// To ensure that IPFS has been initialized, fetch the config. Do this
	// _before_ acquiring the daemon lock so the user gets an appropriate error
	// message.
	// NB: It's safe to read the config without the daemon lock, but not safe
	// to write.
	ctx := req.Context()
	cfg, err := ctx.GetConfig()
	if err != nil {
		return nil, err
	}

	// acquire the daemon lock _before_ constructing a node. we need to make
	// sure we are permitted to access the resources (datastore, etc.)
89
	lock, err := daemon.Lock(req.Context().ConfigRoot)
90
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
91
		return nil, debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?")
92
	}
93
	defer lock.Close()
94

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95 96
	// OK!!! Now we're ready to construct the node.
	// make sure we construct an online node.
97 98 99 100 101 102
	ctx.Online = true
	node, err := ctx.GetNode()
	if err != nil {
		return nil, err
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103 104
	// verify api address is valid multiaddr
	apiMaddr, err := ma.NewMultiaddr(cfg.Addresses.API)
105
	if err != nil {
106
		return nil, err
107 108
	}

109 110
	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
111
	if err != nil {
112
		return nil, err
113
	}
114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
	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
		}
	}
136

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
137 138 139 140 141 142 143 144 145 146 147
	return nil, listenAndServeAPI(node, req, apiMaddr)
}

func listenAndServeAPI(node *core.IpfsNode, req cmds.Request, addr ma.Multiaddr) error {

	_, host, err := manet.DialArgs(addr)
	if err != nil {
		return err
	}

	server := manners.NewServer()
148
	mux := http.NewServeMux()
149
	cmdHandler := cmdsHttp.NewHandler(*req.Context(), commands.Root)
150
	mux.Handle(cmdsHttp.ApiPath+"/", cmdHandler)
151 152

	ifpsHandler := &ipfsHandler{node}
153 154 155 156 157 158
	mux.Handle("/ipfs/", ifpsHandler)

	done := make(chan struct{}, 1)
	defer func() {
		done <- struct{}{}
	}()
159

160
	// go wait until the node dies
161 162
	go func() {
		select {
163
		case <-node.Closed():
164
		case <-done:
165
			return
166
		}
167

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
168 169
		log.Info("terminating daemon at %s...", addr)
		server.Shutdown <- true
170 171
	}()

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
172 173
	fmt.Printf("daemon listening on %s\n", addr)
	if err := server.ListenAndServe(host, mux); err != nil {
174
		return err
175
	}
176

177
	return nil
178
}