init.go 6.18 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"
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
		if f != nil {
88
			it := req.Files.Entries()
89 90 91 92 93
			if !it.Next() && it.Err() != nil {
				return it.Err()
			}
			if it.File() == nil {
				return fmt.Errorf("expected a regular file")
94 95 96
			}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	return initializeIpnsKeyspace(repoRoot)
169 170
}

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

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

	return err
}

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

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

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

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

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

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

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

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

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