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

import (
4
	"bytes"
5
	"fmt"
6

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

const (
17
	initOptionKwd = "init"
18
	mountKwd      = "mount"
19
	writableKwd   = "writable"
20 21
	ipfsMountKwd  = "mount-ipfs"
	ipnsMountKwd  = "mount-ipns"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
22 23
	// apiAddrKwd    = "address-api"
	// swarmAddrKwd  = "address-swarm"
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
		cmds.BoolOption(mountKwd, "Mounts IPFS to the filesystem"),
41
		cmds.BoolOption(writableKwd, "Enable writing objects (with POST, PUT and DELETE)"),
42 43
		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
44 45 46 47

		// 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)"),
48
	},
49 50 51 52
	Subcommands: map[string]*cmds.Command{},
	Run:         daemonFunc,
}

53
func daemonFunc(req cmds.Request, res cmds.Response) {
54 55 56
	var out bytes.Buffer
	res.SetOutput(&out)
	writef(&out, "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(&out, 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
	defer repo.Close()
101

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
102 103
	// OK!!! Now we're ready to construct the node.
	// make sure we construct an online node.
104 105 106
	ctx.Online = true
	node, err := ctx.GetNode()
	if err != nil {
107 108
		res.SetError(err, cmds.ErrNormal)
		return
109 110
	}

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

118 119 120 121 122 123 124 125
	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)
		}
126 127
	}

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

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

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

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

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

	if writable {
		fmt.Printf("IPNS gateway mounted read-write\n")
	}

180
	if gatewayMaddr != nil {
181
		go func() {
182
			var opts = []corehttp.ServeOption{corehttp.GatewayOption(writable)}
183 184 185 186
			if rootRedirect != nil {
				opts = append(opts, rootRedirect)
			}
			err := corehttp.ListenAndServe(node, gatewayMaddr.String(), opts...)
187 188 189 190
			if err != nil {
				log.Error(err)
			}
		}()
191 192
	}

193 194 195
	var opts = []corehttp.ServeOption{
		corehttp.CommandsOption(*req.Context()),
		corehttp.WebUIOption,
196
		corehttp.GatewayOption(true),
197
	}
198 199 200
	if rootRedirect != nil {
		opts = append(opts, rootRedirect)
	}
201
	if err := corehttp.ListenAndServe(node, apiMaddr.String(), opts...); err != nil {
202 203 204
		res.SetError(err, cmds.ErrNormal)
		return
	}
205
}