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

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

10 11 12 13 14 15 16
	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
	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
17 18
)

19
const nBitsForKeypairDefault = 2048
20

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

IPFS_PATH environment variable

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
`,
34
	},
35

Matt Bell's avatar
Matt Bell committed
36
	Options: []cmds.Option{
37
		cmds.IntOption("bits", "b", fmt.Sprintf("Number of bits to use in the generated RSA private key (defaults to %d)", nBitsForKeypairDefault)),
38
		cmds.BoolOption("force", "f", "Overwrite existing config (if it exists)"),
39
		cmds.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage"),
40 41 42 43 44

		// 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?
		// 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 54 55 56 57 58 59

		log.Info("checking if daemon is running...")
		if daemonLocked {
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

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

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

72 73 74 75 76 77
		empty, _, err := req.Option("e").Bool() // if !empty, it's okay empty == false
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

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

84
		if !bitsOptFound {
85
			nBitsForKeypair = nBitsForKeypairDefault
86 87
		}

88
		if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, force, empty, nBitsForKeypair); err != nil {
89 90 91
			res.SetError(err, cmds.ErrNormal)
			return
		}
92 93
	},
}
Matt Bell's avatar
Matt Bell committed
94

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
95
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96 97 98 99
Reinitializing would overwrite your keys.
(use -f to force overwrite)
`)

100
func initWithDefaults(out io.Writer, repoRoot string) error {
101
	return doInit(out, repoRoot, false, false, nBitsForKeypairDefault)
102 103
}

104
func doInit(out io.Writer, repoRoot string, force bool, empty bool, nBitsForKeypair int) error {
Henry's avatar
Henry committed
105
	if _, err := fmt.Fprintf(out, "initializing ipfs node at %s\n", repoRoot); err != nil {
106 107
		return err
	}
108

109 110 111 112
	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

113
	if fsrepo.IsInitialized(repoRoot) && !force {
114
		return errRepoExists
115
	}
Henry's avatar
Henry committed
116

117
	conf, err := config.Init(out, nBitsForKeypair)
118
	if err != nil {
119
		return err
120
	}
Henry's avatar
Henry committed
121

122
	if fsrepo.IsInitialized(repoRoot) {
123
		if err := fsrepo.Remove(repoRoot); err != nil {
124
			return err
125
		}
126
	}
Henry's avatar
Henry committed
127

128
	if err := fsrepo.Init(repoRoot, conf); err != nil {
129
		return err
130
	}
131

132 133 134 135
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
136
	}
137 138

	return initializeIpnsKeyspace(repoRoot)
139 140
}

141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
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
}

169
func addDefaultAssets(out io.Writer, repoRoot string) error {
170
	ctx, cancel := context.WithCancel(context.Background())
171
	defer cancel()
Henry's avatar
Henry committed
172

173 174
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
175 176
		return err
	}
Henry's avatar
Henry committed
177

178
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
179
	if err != nil {
180
		return err
181 182 183
	}
	defer nd.Close()

Henry's avatar
Henry committed
184
	dkey, err := assets.SeedInitDocs(nd)
185
	if err != nil {
Henry's avatar
Henry committed
186
		return fmt.Errorf("init: seeding init docs failed: %s", err)
187
	}
rht's avatar
rht committed
188
	log.Debugf("init: seeded init docs %s", dkey)
189

Henry's avatar
Henry committed
190 191 192 193 194 195
	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
196
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
197

Jeromy's avatar
Jeromy committed
198 199 200 201
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

202 203
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
204 205 206
		return err
	}

207
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
208 209 210 211 212 213 214 215 216 217
	if err != nil {
		return err
	}
	defer nd.Close()

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

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