init.go 7.16 KB
Newer Older
1
package main
Matt Bell's avatar
Matt Bell committed
2 3

import (
4
	"encoding/json"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
5
	"errors"
Brian Tiger Chow's avatar
Brian Tiger Chow committed
6
	"fmt"
7
	"io"
8 9
	"os"
	"path"
10
	"strings"
Matt Bell's avatar
Matt Bell committed
11

Jeromy's avatar
Jeromy committed
12
	context "context"
13 14 15 16 17 18
	assets "github.com/ipfs/go-ipfs/assets"
	cmds "github.com/ipfs/go-ipfs/commands"
	core "github.com/ipfs/go-ipfs/core"
	namesys "github.com/ipfs/go-ipfs/namesys"
	config "github.com/ipfs/go-ipfs/repo/config"
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
Matt Bell's avatar
Matt Bell committed
19 20
)

21 22 23
const (
	nBitsForKeypairDefault = 2048
)
24

25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54
// TODO: move this out(where?)
// ConfigProfiles is a map holding configuration transformers
var ConfigProfiles = map[string]func(*config.Config) error{
	"server": func(c *config.Config) error {

		// defaultServerFilters has a list of non-routable IPv4 prefixes
		// according to http://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
		defaultServerFilters := []string{
			"/ip4/10.0.0.0/ipcidr/8",
			"/ip4/100.64.0.0/ipcidr/10",
			"/ip4/169.254.0.0/ipcidr/16",
			"/ip4/172.16.0.0/ipcidr/12",
			"/ip4/192.0.0.0/ipcidr/24",
			"/ip4/192.0.0.0/ipcidr/29",
			"/ip4/192.0.0.8/ipcidr/32",
			"/ip4/192.0.0.170/ipcidr/32",
			"/ip4/192.0.0.171/ipcidr/32",
			"/ip4/192.0.2.0/ipcidr/24",
			"/ip4/192.168.0.0/ipcidr/16",
			"/ip4/198.18.0.0/ipcidr/15",
			"/ip4/198.51.100.0/ipcidr/24",
			"/ip4/203.0.113.0/ipcidr/24",
			"/ip4/240.0.0.0/ipcidr/4",
		}

		c.Swarm.AddrFilters = append(c.Swarm.AddrFilters, defaultServerFilters...)
		return nil
	},
}

Matt Bell's avatar
Matt Bell committed
55
var initCmd = &cmds.Command{
56
	Helptext: cmds.HelpText{
57
		Tagline: "Initializes ipfs config file.",
58
		ShortDescription: `
59
Initializes ipfs configuration files and generates a new keypair.
60

61 62 63 64 65 66 67
If you are going to run IPFS in server environment, you may want to
initialize it using 'server' profile.

Available profiles:
    'server' - Disables local host discovery, recommended when
        running IPFS on machines with public IPv4 addresses.

68 69 70
ipfs uses a repository in the local file system. By default, the repo is
located at ~/.ipfs. To change the repo location, set the $IPFS_PATH
environment variable:
71 72 73

    export IPFS_PATH=/path/to/ipfsrepo
`,
74
	},
75 76 77
	Arguments: []cmds.Argument{
		cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
	},
Matt Bell's avatar
Matt Bell committed
78
	Options: []cmds.Option{
79 80
		cmds.IntOption("bits", "b", "Number of bits to use in the generated RSA private key.").Default(nBitsForKeypairDefault),
		cmds.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage.").Default(false),
81
		cmds.StringOption("profile", "p", "Apply profile settings to config"),
82 83 84 85

		// TODO need to decide whether to expose the override as a file or a
		// directory. That is: should we allow the user to also specify the
		// name of the file?
86
		// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs."),
Matt Bell's avatar
Matt Bell committed
87
	},
88
	PreRun: func(req cmds.Request) error {
Jeromy's avatar
Jeromy committed
89
		daemonLocked, err := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot)
90 91 92
		if err != nil {
			return err
		}
93 94 95

		log.Info("checking if daemon is running...")
		if daemonLocked {
96
			log.Debug("ipfs daemon is running")
97 98 99 100 101 102
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

		return nil
	},
103
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
104
		if req.InvocContext().Online {
105 106 107
			res.SetError(errors.New("init must be run offline only!"), cmds.ErrNormal)
			return
		}
108

109
		empty, _, err := req.Option("e").Bool()
110 111 112 113 114
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

115
		nBitsForKeypair, _, err := req.Option("b").Int()
116
		if err != nil {
117 118
			res.SetError(err, cmds.ErrNormal)
			return
119
		}
Henry's avatar
Henry committed
120

121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137
		var conf *config.Config

		f := req.Files()
		if f != nil {
			confFile, err := f.NextFile()
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			conf = &config.Config{}
			if err := json.NewDecoder(confFile).Decode(conf); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
		}

138 139 140 141 142 143 144 145 146 147 148 149
		profile, _, err := req.Option("profile").String()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

		var profiles []string
		if profile != "" {
			profiles = strings.Split(profile, ",")
		}

		if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, empty, nBitsForKeypair, profiles, conf); err != nil {
150 151 152
			res.SetError(err, cmds.ErrNormal)
			return
		}
153 154
	},
}
Matt Bell's avatar
Matt Bell committed
155

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
156
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
157 158 159
Reinitializing would overwrite your keys.
`)

160
func initWithDefaults(out io.Writer, repoRoot string) error {
161
	return doInit(out, repoRoot, false, nBitsForKeypairDefault, nil, nil)
162 163
}

164
func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, confProfiles []string, conf *config.Config) error {
165
	if _, err := fmt.Fprintf(out, "initializing IPFS node at %s\n", repoRoot); err != nil {
166 167
		return err
	}
168

169 170 171 172
	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
173
	if fsrepo.IsInitialized(repoRoot) {
174
		return errRepoExists
175
	}
Henry's avatar
Henry committed
176

177 178 179 180 181 182
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
183
	}
Henry's avatar
Henry committed
184

185 186 187 188 189 190 191 192 193 194 195
	for _, profile := range confProfiles {
		transformer := ConfigProfiles[profile]
		if transformer == nil {
			return fmt.Errorf("invalid configuration profile: %s", profile)
		}

		if err := transformer(conf); err != nil {
			return err
		}
	}

196
	if err := fsrepo.Init(repoRoot, conf); err != nil {
197
		return err
198
	}
199

200 201 202 203
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
204
	}
205 206

	return initializeIpnsKeyspace(repoRoot)
207 208
}

209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236
func checkWriteable(dir string) error {
	_, err := os.Stat(dir)
	if err == nil {
		// dir exists, make sure we can write to it
		testfile := path.Join(dir, "test")
		fi, err := os.Create(testfile)
		if err != nil {
			if os.IsPermission(err) {
				return fmt.Errorf("%s is not writeable by the current user", dir)
			}
			return fmt.Errorf("unexpected error while checking writeablility of repo root: %s", err)
		}
		fi.Close()
		return os.Remove(testfile)
	}

	if os.IsNotExist(err) {
		// dir doesnt exist, check that we can create it
		return os.Mkdir(dir, 0775)
	}

	if os.IsPermission(err) {
		return fmt.Errorf("cannot write to %s, incorrect permissions", err)
	}

	return err
}

237
func addDefaultAssets(out io.Writer, repoRoot string) error {
238
	ctx, cancel := context.WithCancel(context.Background())
239
	defer cancel()
Henry's avatar
Henry committed
240

241 242
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
243 244
		return err
	}
Henry's avatar
Henry committed
245

246
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
247
	if err != nil {
248
		return err
249 250 251
	}
	defer nd.Close()

Henry's avatar
Henry committed
252
	dkey, err := assets.SeedInitDocs(nd)
253
	if err != nil {
Henry's avatar
Henry committed
254
		return fmt.Errorf("init: seeding init docs failed: %s", err)
255
	}
rht's avatar
rht committed
256
	log.Debugf("init: seeded init docs %s", dkey)
257

Henry's avatar
Henry committed
258 259 260 261 262 263
	if _, err = fmt.Fprintf(out, "to get started, enter:\n"); err != nil {
		return err
	}

	_, err = fmt.Fprintf(out, "\n\tipfs cat /ipfs/%s/readme\n\n", dkey)
	return err
Matt Bell's avatar
Matt Bell committed
264
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
265

Jeromy's avatar
Jeromy committed
266 267 268 269
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

270 271
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
272 273 274
		return err
	}

275
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
276 277 278 279 280 281 282 283 284 285
	if err != nil {
		return err
	}
	defer nd.Close()

	err = nd.SetupOfflineRouting()
	if err != nil {
		return err
	}

286
	return namesys.InitializeKeyspace(ctx, nd.DAG, nd.Namesys, nd.Pinning, nd.PrivateKey)
Jeromy's avatar
Jeromy committed
287
}