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

import (
4
	"fmt"
5

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

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

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

36
	Options: []cmds.Option{
37
		cmds.BoolOption(initOptionKwd, "Initialize IPFS with default settings if not already initialized"),
38 39 40
		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
41 42 43 44

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

50
func daemonFunc(req cmds.Request, res cmds.Response) {
51

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

		// 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 {
68 69
				res.SetError(debugerror.Wrap(err), cmds.ErrNormal)
				return
70 71 72 73
			}
		}
	}

74 75 76 77 78 79 80 81
	// 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 {
82 83
		res.SetError(err, cmds.ErrNormal)
		return
84 85
	}

86
	// acquire the repo lock _before_ constructing a node. we need to make
87
	// sure we are permitted to access the resources (datastore, etc.)
88 89
	repo := fsrepo.At(req.Context().ConfigRoot)
	if err := repo.Open(); err != nil {
90 91
		res.SetError(debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?"), cmds.ErrNormal)
		return
92
	}
93
	defer repo.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
	ctx.Online = true
	node, err := ctx.GetNode()
	if err != nil {
100 101
		res.SetError(err, cmds.ErrNormal)
		return
102 103
	}

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

111 112 113 114 115 116 117 118
	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)
		}
119 120
	}

121 122
	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
123
	if err != nil {
124 125
		res.SetError(err, cmds.ErrNormal)
		return
126
	}
127 128 129
	if mount {
		fsdir, found, err := req.Option(ipfsMountKwd).String()
		if err != nil {
130 131
			res.SetError(err, cmds.ErrNormal)
			return
132 133 134 135 136 137 138
		}
		if !found {
			fsdir = cfg.Mounts.IPFS
		}

		nsdir, found, err := req.Option(ipnsMountKwd).String()
		if err != nil {
139 140
			res.SetError(err, cmds.ErrNormal)
			return
141 142 143 144 145 146 147
		}
		if !found {
			nsdir = cfg.Mounts.IPNS
		}

		err = commands.Mount(node, fsdir, nsdir)
		if err != nil {
148 149
			res.SetError(err, cmds.ErrNormal)
			return
150
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
151 152
		fmt.Printf("IPFS mounted at: %s\n", fsdir)
		fmt.Printf("IPNS mounted at: %s\n", nsdir)
153
	}
154

155
	if gatewayMaddr != nil {
156
		go func() {
157
			err := corehttp.ListenAndServe(node, gatewayMaddr.String(), corehttp.GatewayOption)
158 159 160 161
			if err != nil {
				log.Error(err)
			}
		}()
162 163
	}

164 165 166 167
	var opts = []corehttp.ServeOption{
		corehttp.CommandsOption(*req.Context()),
		corehttp.WebUIOption,
		corehttp.GatewayOption,
168
	}
169
	if err := corehttp.ListenAndServe(node, apiMaddr.String(), opts...); err != nil {
170 171 172
		res.SetError(err, cmds.ErrNormal)
		return
	}
173
}