init.go 6.91 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
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:
64 65 66

    export IPFS_PATH=/path/to/ipfsrepo
`,
67
	},
68 69 70
	Arguments: []cmds.Argument{
		cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
	},
Matt Bell's avatar
Matt Bell committed
71
	Options: []cmds.Option{
72 73
		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),
74
		cmds.StringOption("profile", "p", "Apply profile settings to config"),
75 76 77 78

		// 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?
79
		// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs."),
Matt Bell's avatar
Matt Bell committed
80
	},
81
	PreRun: func(req cmds.Request) error {
Jeromy's avatar
Jeromy committed
82
		daemonLocked, err := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot)
83 84 85
		if err != nil {
			return err
		}
86 87 88

		log.Info("checking if daemon is running...")
		if daemonLocked {
89
			log.Debug("ipfs daemon is running")
90 91 92 93 94 95
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

		return nil
	},
96
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
97
		if req.InvocContext().Online {
98 99 100
			res.SetError(errors.New("init must be run offline only!"), cmds.ErrNormal)
			return
		}
101

102
		empty, _, err := req.Option("e").Bool()
103 104 105 106 107
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

108
		nBitsForKeypair, _, err := req.Option("b").Int()
109
		if err != nil {
110 111
			res.SetError(err, cmds.ErrNormal)
			return
112
		}
Henry's avatar
Henry committed
113

114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
		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
			}
		}

131 132 133 134 135 136 137 138 139 140 141 142
		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 {
143 144 145
			res.SetError(err, cmds.ErrNormal)
			return
		}
146 147
	},
}
Matt Bell's avatar
Matt Bell committed
148

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
149
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
150 151 152
Reinitializing would overwrite your keys.
`)

153
func initWithDefaults(out io.Writer, repoRoot string) error {
154
	return doInit(out, repoRoot, false, nBitsForKeypairDefault, nil, nil)
155 156
}

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

162 163 164 165
	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
166
	if fsrepo.IsInitialized(repoRoot) {
167
		return errRepoExists
168
	}
Henry's avatar
Henry committed
169

170 171 172 173 174 175
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
176
	}
Henry's avatar
Henry committed
177

178 179 180 181 182 183 184 185 186 187 188
	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
		}
	}

189
	if err := fsrepo.Init(repoRoot, conf); err != nil {
190
		return err
191
	}
192

193 194 195 196
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
197
	}
198 199

	return initializeIpnsKeyspace(repoRoot)
200 201
}

202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229
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
}

230
func addDefaultAssets(out io.Writer, repoRoot string) error {
231
	ctx, cancel := context.WithCancel(context.Background())
232
	defer cancel()
Henry's avatar
Henry committed
233

234 235
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
236 237
		return err
	}
Henry's avatar
Henry committed
238

239
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
240
	if err != nil {
241
		return err
242 243 244
	}
	defer nd.Close()

Henry's avatar
Henry committed
245
	dkey, err := assets.SeedInitDocs(nd)
246
	if err != nil {
Henry's avatar
Henry committed
247
		return fmt.Errorf("init: seeding init docs failed: %s", err)
248
	}
rht's avatar
rht committed
249
	log.Debugf("init: seeded init docs %s", dkey)
250

Henry's avatar
Henry committed
251 252 253 254 255 256
	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
257
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
258

Jeromy's avatar
Jeromy committed
259 260 261 262
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

263 264
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
265 266 267
		return err
	}

268
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
269 270 271 272 273 274 275 276 277 278
	if err != nil {
		return err
	}
	defer nd.Close()

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

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