init.go 5.56 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"
Jeromy's avatar
Jeromy committed
17
	context "gx/ipfs/QmZy2y8t9zQH2a1b8q2ZSLKp17ATuJoCNxxyMFG5qFExpt/go-net/context"
Matt Bell's avatar
Matt Bell committed
18 19
)

20
const nBitsForKeypairDefault = 2048
21

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

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:

    export IPFS_PATH=/path/to/ipfsrepo
`,
33
	},
34 35 36
	Arguments: []cmds.Argument{
		cmds.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
	},
Matt Bell's avatar
Matt Bell committed
37
	Options: []cmds.Option{
38
		cmds.IntOption("bits", "b", fmt.Sprintf("Number of bits to use in the generated RSA private key (defaults to %d)", nBitsForKeypairDefault)),
39
		cmds.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage."),
40 41 42 43

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

		log.Info("checking if daemon is running...")
		if daemonLocked {
michael's avatar
michael committed
54
			log.Debug("Ipfs daemon is running.")
55 56 57 58 59 60
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

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

67 68 69 70 71 72
		empty, _, err := req.Option("e").Bool() // if !empty, it's okay empty == false
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

73
		nBitsForKeypair, bitsOptFound, err := req.Option("b").Int()
74
		if err != nil {
75 76
			res.SetError(err, cmds.ErrNormal)
			return
77
		}
Henry's avatar
Henry committed
78

79
		if !bitsOptFound {
80
			nBitsForKeypair = nBitsForKeypairDefault
81 82
		}

83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
		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 {
101 102 103
			res.SetError(err, cmds.ErrNormal)
			return
		}
104 105
	},
}
Matt Bell's avatar
Matt Bell committed
106

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

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

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

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

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

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

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

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

	return initializeIpnsKeyspace(repoRoot)
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 176
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
}

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

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

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

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

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

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

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

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

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

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