init.go 6.15 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/QmPEpj17FDRpc7K1aArKZp3RsHtzRMKykeK9GVgn4WQGPR/go-ipfs-config"
20
	"gx/ipfs/QmSXUokcP4TJpFfqozT69AVAYRtzXVMUjzQVkYX41R9Svs/go-ipfs-cmds"
21
	"gx/ipfs/Qmde5VP1qUkyQXKCfmEUA7bP64V2HAptbJ7phuPp7jXWwg/go-ipfs-cmdkit"
Matt Bell's avatar
Matt Bell committed
22 23
)

24 25 26
const (
	nBitsForKeypairDefault = 2048
)
27

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

34 35 36
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
37
For the list of available profiles see 'ipfs config profile --help'
38

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

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

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

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

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

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

84 85
		var conf *config.Config

fyrchik's avatar
fyrchik committed
86
		f := req.Files
87 88 89
		if f != nil {
			confFile, err := f.NextFile()
			if err != nil {
keks's avatar
keks committed
90
				return err
91 92 93 94
			}

			conf = &config.Config{}
			if err := json.NewDecoder(confFile).Decode(conf); err != nil {
keks's avatar
keks committed
95
				return err
96 97 98
			}
		}

fyrchik's avatar
fyrchik committed
99
		profile, _ := req.Options["profile"].(string)
100 101 102 103 104 105

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

keks's avatar
keks committed
106
		return doInit(os.Stdout, cctx.ConfigRoot, empty, nBitsForKeypair, profiles, conf)
107 108
	},
}
Matt Bell's avatar
Matt Bell committed
109

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

114 115 116 117 118 119 120
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)
121 122
}

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

Łukasz Magiera's avatar
Łukasz Magiera committed
128
	if err := checkWritable(repoRoot); err != nil {
129 130 131
		return err
	}

Jeromy's avatar
Jeromy committed
132
	if fsrepo.IsInitialized(repoRoot) {
133
		return errRepoExists
134
	}
Henry's avatar
Henry committed
135

136 137 138 139 140 141
	if conf == nil {
		var err error
		conf, err = config.Init(out, nBitsForKeypair)
		if err != nil {
			return err
		}
142
	}
Henry's avatar
Henry committed
143

144
	for _, profile := range confProfiles {
145
		transformer, ok := config.Profiles[profile]
146
		if !ok {
147 148 149
			return fmt.Errorf("invalid configuration profile: %s", profile)
		}

Łukasz Magiera's avatar
Łukasz Magiera committed
150
		if err := transformer.Transform(conf); err != nil {
151 152 153 154
			return err
		}
	}

155
	if err := fsrepo.Init(repoRoot, conf); err != nil {
156
		return err
157
	}
158

159 160 161 162
	if !empty {
		if err := addDefaultAssets(out, repoRoot); err != nil {
			return err
		}
Jeromy's avatar
Jeromy committed
163
	}
164 165

	return initializeIpnsKeyspace(repoRoot)
166 167
}

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

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

	return err
}

196
func addDefaultAssets(out io.Writer, repoRoot string) error {
197
	ctx, cancel := context.WithCancel(context.Background())
198
	defer cancel()
Henry's avatar
Henry committed
199

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

205
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
206
	if err != nil {
207
		return err
208 209 210
	}
	defer nd.Close()

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

Henry's avatar
Henry committed
217 218 219 220 221 222
	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
223
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
224

Jeromy's avatar
Jeromy committed
225 226 227 228
func initializeIpnsKeyspace(repoRoot string) error {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

229 230
	r, err := fsrepo.Open(repoRoot)
	if err != nil { // NB: repo is owned by the node
Jeromy's avatar
Jeromy committed
231 232 233
		return err
	}

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

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

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