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

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

Jeromy's avatar
Jeromy committed
12
	context "context"
13 14 15 16 17 18
	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
19 20
)

21 22 23
const (
	nBitsForKeypairDefault = 2048
)
24

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

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

Available profiles:
    'server' - Disables local host discovery, recommended when
        running IPFS on machines with public IPv4 addresses.

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

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

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

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

		return nil
	},
73
	Run: func(req cmds.Request, res cmds.Response) {
Jeromy's avatar
Jeromy committed
74
		if req.InvocContext().Online {
75 76 77
			res.SetError(errors.New("init must be run offline only!"), cmds.ErrNormal)
			return
		}
78

79
		empty, _, err := req.Option("e").Bool()
80 81 82 83 84
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

85
		nBitsForKeypair, _, err := req.Option("b").Int()
86
		if err != nil {
87 88
			res.SetError(err, cmds.ErrNormal)
			return
89
		}
Henry's avatar
Henry committed
90

91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
		var conf *config.Config

		f := req.Files()
		if f != nil {
			confFile, err := f.NextFile()
			if err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}

			conf = &config.Config{}
			if err := json.NewDecoder(confFile).Decode(conf); err != nil {
				res.SetError(err, cmds.ErrNormal)
				return
			}
		}

108 109 110 111 112 113 114 115 116 117 118 119
		profile, _, err := req.Option("profile").String()
		if err != nil {
			res.SetError(err, cmds.ErrNormal)
			return
		}

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

		if err := doInit(os.Stdout, req.InvocContext().ConfigRoot, empty, nBitsForKeypair, profiles, conf); err != nil {
120 121 122
			res.SetError(err, cmds.ErrNormal)
			return
		}
123 124
	},
}
Matt Bell's avatar
Matt Bell committed
125

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
126
var errRepoExists = errors.New(`ipfs configuration file already exists!
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127 128 129
Reinitializing would overwrite your keys.
`)

130
func initWithDefaults(out io.Writer, repoRoot string) error {
131
	return doInit(out, repoRoot, false, nBitsForKeypairDefault, nil, nil)
132 133
}

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

139 140 141 142
	if err := checkWriteable(repoRoot); err != nil {
		return err
	}

Jeromy's avatar
Jeromy committed
143
	if fsrepo.IsInitialized(repoRoot) {
144
		return errRepoExists
145
	}
Henry's avatar
Henry committed
146

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

155
	for _, profile := range confProfiles {
156 157
		transformer, ok := config.ConfigProfiles[profile]
		if !ok {
158 159 160 161 162 163 164 165
			return fmt.Errorf("invalid configuration profile: %s", profile)
		}

		if err := transformer(conf); err != nil {
			return err
		}
	}

166
	if err := fsrepo.Init(repoRoot, conf); err != nil {
167
		return err
168
	}
169

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

	return initializeIpnsKeyspace(repoRoot)
177 178
}

179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
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
}

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

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

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

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

Henry's avatar
Henry committed
228 229 230 231 232 233
	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
234
}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
235

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

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

245
	nd, err := core.NewNode(ctx, &core.BuildCfg{Repo: r})
Jeromy's avatar
Jeromy committed
246 247 248 249 250 251 252 253 254 255
	if err != nil {
		return err
	}
	defer nd.Close()

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

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