init.go 6.27 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

Steven Allen's avatar
Steven Allen committed
19
	"gx/ipfs/QmYyzmMnhNTtoXx5ttgUaRdHHckYnQWjPL98hgLAR2QLDD/go-ipfs-config"
20
	"gx/ipfs/QmaAP56JAwdjwisPTu4yx17whcjTr6y5JCSCF77Y1rahWV/go-ipfs-cmds"
21
	"gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
22
	"gx/ipfs/QmeaQRmnRog7NxLEWHP9zSTkics4cbgwBVa7q49LmBowDr/go-ipfs-files"
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()
90 91 92
			if !it.Next() && it.Err() != nil {
				return it.Err()
			}
93
			if files.FileFrom(it) == nil {
94
				return fmt.Errorf("expected a regular file")
95 96 97
			}

			conf = &config.Config{}
98
			if err := json.NewDecoder(files.FileFrom(it)).Decode(conf); err != nil {
keks's avatar
keks committed
99
				return err
100 101 102
			}
		}

fyrchik's avatar
fyrchik committed
103
		profile, _ := req.Options["profile"].(string)
104 105 106 107 108 109

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

keks's avatar
keks committed
110
		return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profiles, conf)
111 112
	},
}
Matt Bell's avatar
Matt Bell committed
113

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
114
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
115 116 117
Reinitializing would overwrite your keys.
`)

118 119 120 121 122 123 124
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)
125 126
}

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

Łukasz Magiera's avatar
Łukasz Magiera committed
132
	if err := checkWritable(repoRoot); err != nil {
133 134 135
		return err
	}

Jeromy's avatar
Jeromy committed
136
	if fsrepo.IsInitialized(repoRoot) {
137
		return errRepoExists
138
	}
Henry's avatar
Henry committed
139

140 141 142 143 144 145
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
146
	}
Henry's avatar
Henry committed
147

148
	for _, profile := range confProfiles {
149
		transformer, ok := config.Profiles[profile]
150
		if !ok {
151 152 153
			return fmt.Errorf("invalid configuration profile: %s", profile)
		}

Łukasz Magiera's avatar
Łukasz Magiera committed
154
		if err := transformer.Transform(conf); err != nil {
155 156 157 158
			return err
		}
	}

159
	if err := fsrepo.Init(repoRoot, conf); err != nil {
160
		return err
161
	}
162

163 164 165 166
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
167
	}
168 169

	return initializeIpnsKeyspace(repoRoot)
170 171
}

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

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

	return err
}

200
func addDefaultAssets(out io.Writer, repoRoot string) error {
201
	ctx, cancel := context.WithCancel(context.Background())
202
	defer cancel()
Henry's avatar
Henry committed
203

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

209
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
210
	if err != nil {
211
		return err
212 213 214
	}
	defer nd.Close()

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

Henry's avatar
Henry committed
221 222 223 224 225 226
	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
227
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
228

Jeromy's avatar
Jeromy committed
229 230 231 232
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

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

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

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