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

import (
4
	"fmt"
5
	"os"
6

7
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
8
	cmds "github.com/jbenet/go-ipfs/commands"
9
	"github.com/jbenet/go-ipfs/core"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
	commands "github.com/jbenet/go-ipfs/core/commands"
11
	corehttp "github.com/jbenet/go-ipfs/core/corehttp"
12
	fsrepo "github.com/jbenet/go-ipfs/repo/fsrepo"
13
	util "github.com/jbenet/go-ipfs/util"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
14
	"github.com/jbenet/go-ipfs/util/debugerror"
15 16 17
)

const (
18
	initOptionKwd = "init"
19
	mountKwd      = "mount"
20
	writableKwd   = "writable"
21 22
	ipfsMountKwd  = "mount-ipfs"
	ipnsMountKwd  = "mount-ipns"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23 24
	// apiAddrKwd    = "address-api"
	// swarmAddrKwd  = "address-swarm"
25 26
)

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

39
	Options: []cmds.Option{
40
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
41
		cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
42
		cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
43 44
		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, res cmds.Response) {
55 56
	// let the user know we're going.
	fmt.Printf("Initializing daemon...\n")
57

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

66
	if initialize {
67 68 69 70 71 72

		// 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) {
73
			err := initWithDefaults(os.Stdout, req.Context().ConfigRoot)
74
			if err != nil {
75 76
				res.SetError(debugerror.Wrap(err), cmds.ErrNormal)
				return
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 {
89 90
		res.SetError(err, cmds.ErrNormal)
		return
91 92
	}

93
	// acquire the repo lock _before_ constructing a node. we need to make
94
	// sure we are permitted to access the resources (datastore, etc.)
95 96
	repo := fsrepo.At(req.Context().ConfigRoot)
	if err := repo.Open(); err != nil {
97 98
		res.SetError(debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal)
		return
99 100
	}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
101 102
	// OK!!! Now we're ready to construct the node.
	// make sure we construct an online node.
103
	node, err := core.NewIPFSNode(ctx.Context, core.Online(repo))
104
	if err != nil {
105 106
		res.SetError(err, cmds.ErrNormal)
		return
107
	}
108 109 110 111
	defer node.Close()
	req.Context().ConstructNode = func() (*core.IpfsNode, error) {
		return node, nil
	}
112

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114
	// verify api address is valid multiaddr
	apiMaddr, err := ma.NewMultiaddr(cfg.Addresses.API)
115
	if err != nil {
116 117
		res.SetError(err, cmds.ErrNormal)
		return
118 119
	}

120 121 122 123 124 125 126 127
	var gatewayMaddr ma.Multiaddr
	if len(cfg.Addresses.Gateway) > 0 {
		// ignore error for gateway address
		// if there is an error (invalid address), then don't run the gateway
		gatewayMaddr, _ = ma.NewMultiaddr(cfg.Addresses.Gateway)
		if gatewayMaddr == nil {
			log.Errorf("Invalid gateway address: %s", cfg.Addresses.Gateway)
		}
128 129
	}

130 131
	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
132
	if err != nil {
133 134
		res.SetError(err, cmds.ErrNormal)
		return
135
	}
136 137 138
	if mount {
		fsdir, found, err := req.Option(ipfsMountKwd).String()
		if err != nil {
139 140
			res.SetError(err, cmds.ErrNormal)
			return
141 142 143 144 145 146 147
		}
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found, err := req.Option(ipnsMountKwd).String()
		if err != nil {
148 149
			res.SetError(err, cmds.ErrNormal)
			return
150 151 152 153 154 155 156
		}
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = commands.Mount(node, fsdir, nsdir)
		if err != nil {
157 158
			res.SetError(err, cmds.ErrNormal)
			return
159
		}
160 161
		fmt.Printf("IPFS mounted at: %s\n", fsdir)
		fmt.Printf("IPNS mounted at: %s\n", nsdir)
162
	}
163

164 165 166 167 168
	var rootRedirect corehttp.ServeOption
	if len(cfg.Gateway.RootRedirect) > 0 {
		rootRedirect = corehttp.RedirectOption("", cfg.Gateway.RootRedirect)
	}

169 170 171 172 173 174 175 176 177
	writable, writableOptionFound, err := req.Option(writableKwd).Bool()
	if err != nil {
		res.SetError(err, cmds.ErrNormal)
		return
	}
	if !writableOptionFound {
		writable = cfg.Gateway.Writable
	}

178
	if gatewayMaddr != nil {
179
		go func() {
180
			var opts = []corehttp.ServeOption{corehttp.GatewayOption(writable)}
181 182 183
			if rootRedirect != nil {
				opts = append(opts, rootRedirect)
			}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184
			fmt.Printf("Gateway server listening on %s\n", gatewayMaddr)
185 186 187
			if writable {
				fmt.Printf("Gateway server is writable\n")
			}
188
			err := corehttp.ListenAndServe(node, gatewayMaddr.String(), opts...)
189 190 191 192
			if err != nil {
				log.Error(err)
			}
		}()
193 194
	}

195 196 197
	var opts = []corehttp.ServeOption{
		corehttp.CommandsOption(*req.Context()),
		corehttp.WebUIOption,
198
		corehttp.GatewayOption(true),
199
	}
200 201 202
	if rootRedirect != nil {
		opts = append(opts, rootRedirect)
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
203
	fmt.Printf("API server listening on %s\n", apiMaddr)
204
	if err := corehttp.ListenAndServe(node, apiMaddr.String(), opts...); err != nil {
205 206 207
		res.SetError(err, cmds.ErrNormal)
		return
	}
208
}