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

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

13
	assets "github.com/ipfs/go-ipfs/assets"
fyrchik's avatar
fyrchik committed
14
	oldcmds "github.com/ipfs/go-ipfs/commands"
15 16 17
	core "github.com/ipfs/go-ipfs/core"
	namesys "github.com/ipfs/go-ipfs/namesys"
	fsrepo "github.com/ipfs/go-ipfs/repo/fsrepo"
Jan Winkelmann's avatar
Jan Winkelmann committed
18

Łukasz Magiera's avatar
Łukasz Magiera committed
19
	"gx/ipfs/QmXWZCd8jfaHmt4UDSnjKmGcrQMw95bDGWqEeVLVJjoANX/go-ipfs-files"
Steven Allen's avatar
Steven Allen committed
20
	"gx/ipfs/QmYyzmMnhNTtoXx5ttgUaRdHHckYnQWjPL98hgLAR2QLDD/go-ipfs-config"
21
	"gx/ipfs/QmaAP56JAwdjwisPTu4yx17whcjTr6y5JCSCF77Y1rahWV/go-ipfs-cmds"
22
	"gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Matt Bell's avatar
Matt Bell committed
23 24
)

25 26 27
const (
	nBitsForKeypairDefault = 2048
)
28

Matt Bell's avatar
Matt Bell committed
29
var initCmd = &cmds.Command{
Jan Winkelmann's avatar
Jan Winkelmann committed
30
	Helptext: cmdkit.HelpText{
31
		Tagline: "Initializes ipfs config file.",
32
		ShortDescription: `
33
Initializes ipfs configuration files and generates a new keypair.
34

35 36 37
If you are going to run IPFS in server environment, you may want to
initialize it using 'server' profile.

Łukasz Magiera's avatar
Łukasz Magiera committed
38
For the list of available profiles see 'ipfs config profile --help'
39

40 41 42
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:
43 44 45

    export IPFS_PATH=/path/to/ipfsrepo
`,
46
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
47 48
	Arguments: []cmdkit.Argument{
		cmdkit.FileArg("default-config", false, false, "Initialize with the given configuration.").EnableStdin(),
49
	},
Jan Winkelmann's avatar
Jan Winkelmann committed
50
	Options: []cmdkit.Option{
51
		cmdkit.IntOption("bits", "b", "Number of bits to use in the generated RSA private key.").WithDefault(nBitsForKeypairDefault),
52
		cmdkit.BoolOption("empty-repo", "e", "Don't add and pin help files to the local storage."),
Jan Winkelmann's avatar
Jan Winkelmann committed
53
		cmdkit.StringOption("profile", "p", "Apply profile settings to config. Multiple profiles can be separated by ','"),
54 55 56 57

		// 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?
Jan Winkelmann's avatar
Jan Winkelmann committed
58
		// TODO cmdkit.StringOption("event-logs", "l", "Location for machine-readable event logs."),
Matt Bell's avatar
Matt Bell committed
59
	},
fyrchik's avatar
fyrchik committed
60 61 62
	PreRun: func(req *cmds.Request, env cmds.Environment) error {
		cctx := env.(*oldcmds.Context)
		daemonLocked, err := fsrepo.LockedByOtherProcess(cctx.ConfigRoot)
63 64 65
		if err != nil {
			return err
		}
66 67 68

		log.Info("checking if daemon is running...")
		if daemonLocked {
69
			log.Debug("ipfs daemon is running")
70 71 72 73 74 75
			e := "ipfs daemon is running. please stop it to run this command"
			return cmds.ClientError(e)
		}

		return nil
	},
keks's avatar
keks committed
76
	Run: func(req *cmds.Request, res cmds.ResponseEmitter, env cmds.Environment) error {
fyrchik's avatar
fyrchik committed
77 78
		cctx := env.(*oldcmds.Context)
		if cctx.Online {
keks's avatar
keks committed
79
			return cmdkit.Error{Message: "init must be run offline only"}
80
		}
81

fyrchik's avatar
fyrchik committed
82 83
		empty, _ := req.Options["empty-repo"].(bool)
		nBitsForKeypair, _ := req.Options["bits"].(int)
Henry's avatar
Henry committed
84

85 86
		var conf *config.Config

fyrchik's avatar
fyrchik committed
87
		f := req.Files
88
		if f != nil {
89
			it := req.Files.Entries()
Łukasz Magiera's avatar
Łukasz Magiera committed
90 91 92 93 94
			if !it.Next() {
				if it.Err() != nil {
					return it.Err()
				}
				return fmt.Errorf("file argument was nil")
95
			}
Łukasz Magiera's avatar
Łukasz Magiera committed
96 97
			file := files.FileFromEntry(it)
			if file == nil {
98
				return fmt.Errorf("expected a regular file")
99 100 101
			}

			conf = &config.Config{}
Łukasz Magiera's avatar
Łukasz Magiera committed
102
			if err := json.NewDecoder(file).Decode(conf); err != nil {
keks's avatar
keks committed
103
				return err
104 105 106
			}
		}

fyrchik's avatar
fyrchik committed
107
		profile, _ := req.Options["profile"].(string)
108 109 110 111 112 113

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

keks's avatar
keks committed
114
		return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profiles, conf)
115 116
	},
}
Matt Bell's avatar
Matt Bell committed
117

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
119 120 121
Reinitializing would overwrite your keys.
`)

122 123 124 125 126 127 128
func initWithDefaults(out io.Writer, repoRoot string, profile string) error {
	var profiles []string
	if profile != "" {
		profiles = strings.Split(profile, ",")
	}

	return doInit(out, repoRoot, false, nBitsForKeypairDefault, profiles, nil)
129 130
}

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

Łukasz Magiera's avatar
Łukasz Magiera committed
136
	if err := checkWritable(repoRoot); err != nil {
137 138 139
		return err
	}

Jeromy's avatar
Jeromy committed
140
	if fsrepo.IsInitialized(repoRoot) {
141
		return errRepoExists
142
	}
Henry's avatar
Henry committed
143

144 145 146 147 148 149
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
150
	}
Henry's avatar
Henry committed
151

152
	for _, profile := range confProfiles {
153
		transformer, ok := config.Profiles[profile]
154
		if !ok {
155 156 157
			return fmt.Errorf("invalid configuration profile: %s", profile)
		}

Łukasz Magiera's avatar
Łukasz Magiera committed
158
		if err := transformer.Transform(conf); err != nil {
159 160 161 162
			return err
		}
	}

163
	if err := fsrepo.Init(repoRoot, conf); err != nil {
164
		return err
165
	}
166

167 168 169 170
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
171
	}
172 173

	return initializeIpnsKeyspace(repoRoot)
174 175
}

Łukasz Magiera's avatar
Łukasz Magiera committed
176
func checkWritable(dir string) error {
177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192
	_, 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) {
Łukasz Magiera's avatar
Łukasz Magiera committed
193
		// dir doesn't exist, check that we can create it
194 195 196 197 198 199 200 201 202 203
		return os.Mkdir(dir, 0775)
	}

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

	return err
}

204
func addDefaultAssets(out io.Writer, repoRoot string) error {
205
	ctx, cancel := context.WithCancel(context.Background())
206
	defer cancel()
Henry's avatar
Henry committed
207

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

213
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
214
	if err != nil {
215
		return err
216 217 218
	}
	defer nd.Close()

Henry's avatar
Henry committed
219
	dkey, err := assets.SeedInitDocs(nd)
220
	if err != nil {
Henry's avatar
Henry committed
221
		return fmt.Errorf("init: seeding init docs failed: %s", err)
222
	}
rht's avatar
rht committed
223
	log.Debugf("init: seeded init docs %s", dkey)
224

Henry's avatar
Henry committed
225 226 227 228 229 230
	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
231
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
232

Jeromy's avatar
Jeromy committed
233 234 235 236
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

237 238
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
239 240 241
		return err
	}

242
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
243 244 245 246 247
	if err != nil {
		return err
	}
	defer nd.Close()

Steven Allen's avatar
Steven Allen committed
248
	return namesys.InitializeKeyspace(ctx, nd.Namesys, nd.Pinning, nd.PrivateKey)
Jeromy's avatar
Jeromy committed
249
}