init.go 5.38 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"
Matt Bell's avatar
Matt Bell committed
10

11 12 13 14 15 16
	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"
17
	context "context"
Matt Bell's avatar
Matt Bell committed
18 19
)

20 21 22
const (
	nBitsForKeypairDefault = 2048
)
23

Matt Bell's avatar
Matt Bell committed
24
var initCmd = &cmds.Command{
25
	Helptext: cmds.HelpText{
rht's avatar
rht committed
26
		Tagline: "Initializes IPFS config file.",
27 28 29
		ShortDescription: `
Initializes IPFS configuration files and generates a new keypair.

30 31 32
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:
33 34 35

    export IPFS_PATH=/path/to/ipfsrepo
`,
36
	},
37 38 39
	Arguments: []cmds.Argument{
		cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
	},
Matt Bell's avatar
Matt Bell committed
40
	Options: []cmds.Option{
41 42
		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),
43 44 45 46

		// 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?
47
		// TODO cmds.StringOption("event-logs", "l", "Location for machine-readable event logs."),
Matt Bell's avatar
Matt Bell committed
48
	},
49
	PreRun: func(req cmds.Request) error {
Jeromy's avatar
Jeromy committed
50
		daemonLocked, err := fsrepo.LockedByOtherProcess(req.InvocContext().ConfigRoot)
51 52 53
		if err != nil {
			return err
		}
54 55 56

		log.Info("checking if daemon is running...")
		if daemonLocked {
57
			log.Debug("ipfs daemon is running")
58 59 60 61 62 63
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

		return nil
	},
64
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
65
		if req.InvocContext().Online {
66 67 68
			res.SetError(errors.New("init must be run offline only!"), cmds.ErrNormal)
			return
		}
69

70
		empty, _, err := req.Option("e").Bool()
71 72 73 74 75
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

76
		nBitsForKeypair, _, err := req.Option("b").Int()
77
		if err != nil {
78 79
			res.SetError(err, cmds.ErrNormal)
			return
80
		}
Henry's avatar
Henry committed
81

82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
		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
			}
		}

		if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, empty, nBitsForKeypair, conf); err != nil {
100 101 102
			res.SetError(err, cmds.ErrNormal)
			return
		}
103 104
	},
}
Matt Bell's avatar
Matt Bell committed
105

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
106
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107 108 109
Reinitializing would overwrite your keys.
`)

110
func initWithDefaults(out io.Writer, repoRoot string) error {
111
	return doInit(out, repoRoot, false, nBitsForKeypairDefault, nil)
112 113
}

114
func doInit(out io.Writer, repoRoot string, empty bool, nBitsForKeypair int, conf *config.Config) error {
Henry's avatar
Henry committed
115
	if _, err := fmt.Fprintf(out, "initializing ipfs node at %s\n", repoRoot); err != nil {
116 117
		return err
	}
118

119 120 121 122
	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
123
	if fsrepo.IsInitialized(repoRoot) {
124
		return errRepoExists
125
	}
Henry's avatar
Henry committed
126

127 128 129 130 131 132
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
133
	}
Henry's avatar
Henry committed
134

135
	if err := fsrepo.Init(repoRoot, conf); err != nil {
136
		return err
137
	}
138

139 140 141 142
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
143
	}
144 145

	return initializeIpnsKeyspace(repoRoot)
146 147
}

148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
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
}

176
func addDefaultAssets(out io.Writer, repoRoot string) error {
177
	ctx, cancel := context.WithCancel(context.Background())
178
	defer cancel()
Henry's avatar
Henry committed
179

180 181
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
182 183
		return err
	}
Henry's avatar
Henry committed
184

185
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
186
	if err != nil {
187
		return err
188 189 190
	}
	defer nd.Close()

Henry's avatar
Henry committed
191
	dkey, err := assets.SeedInitDocs(nd)
192
	if err != nil {
Henry's avatar
Henry committed
193
		return fmt.Errorf("init: seeding init docs failed: %s", err)
194
	}
rht's avatar
rht committed
195
	log.Debugf("init: seeded init docs %s", dkey)
196

Henry's avatar
Henry committed
197 198 199 200 201 202
	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
203
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
204

Jeromy's avatar
Jeromy committed
205 206 207 208
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

209 210
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
211 212 213
		return err
	}

214
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
215 216 217 218 219 220 221 222 223 224
	if err != nil {
		return err
	}
	defer nd.Close()

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

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