daemon.go 4.62 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) (interface{}, error) {
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 56 57
	if err != nil {
		return nil, err
	}
58
	if initialize {
59 60 61 62 63 64 65 66

		// 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
67
				return nil, debugerror.Wrap(err)
68 69 70 71
			}
		}
	}

72 73 74 75 76 77 78 79 80 81 82
	// 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
	}

83
	// acquire the repo lock _before_ constructing a node. we need to make
84
	// sure we are permitted to access the resources (datastore, etc.)
85 86
	repo := fsrepo.At(req.Context().ConfigRoot)
	if err := repo.Open(); err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
87
		return nil, debugerror.Errorf("Couldn't obtain lock. Is another daemon already running?")
88
	}
89
	defer repo.Close()
90

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

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

105 106 107 108 109 110 111 112
	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)
		}
113 114
	}

115 116
	// mount if the user provided the --mount flag
	mount, _, err := req.Option(mountKwd).Bool()
117
	if err != nil {
118
		return nil, err
119
	}
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140
	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
		}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
141 142
		fmt.Printf("IPFS mounted at: %s\n", fsdir)
		fmt.Printf("IPNS mounted at: %s\n", nsdir)
143
	}
144

145
	if gatewayMaddr != nil {
146
		go func() {
147
			err := corehttp.ListenAndServe(node, gatewayMaddr, corehttp.GatewayOption)
148 149 150 151
			if err != nil {
				log.Error(err)
			}
		}()
152 153
	}

154 155 156 157
	var opts = []corehttp.ServeOption{
		corehttp.CommandsOption(*req.Context()),
		corehttp.WebUIOption,
		corehttp.GatewayOption,
158
	}
159
	return nil, corehttp.ListenAndServe(node, apiMaddr, opts...)
160
}