main.go 7 KB
Newer Older
1 2 3
package main

import (
4
	"errors"
5 6 7
	"fmt"
	"io"
	"os"
Matt Bell's avatar
Matt Bell committed
8
	"os/signal"
9 10
	"runtime/pprof"

11
	logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging"
12 13 14
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"

15 16 17 18 19 20 21 22 23 24 25 26 27
	cmds "github.com/jbenet/go-ipfs/commands"
	cmdsCli "github.com/jbenet/go-ipfs/commands/cli"
	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
	"github.com/jbenet/go-ipfs/config"
	"github.com/jbenet/go-ipfs/core"
	commands "github.com/jbenet/go-ipfs/core/commands2"
	daemon "github.com/jbenet/go-ipfs/daemon2"
	u "github.com/jbenet/go-ipfs/util"
)

// log is the command logger
var log = u.Logger("cmd/ipfs")

28
const (
29 30
	cpuProfile  = "ipfs.cpuprof"
	heapProfile = "ipfs.memprof"
31 32
	errorFormat = "ERROR: %v\n\n"
)
33

34
func main() {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
35 36 37 38 39 40 41 42
	err := run()
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func run() error {
Matt Bell's avatar
Matt Bell committed
43 44
	handleInterrupt()

45
	args := os.Args[1:]
46 47
	req, root, err := createRequest(args)
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
48 49 50
		return err
	}

51
	debug, _, err := req.Option("debug").Bool()
Brian Tiger Chow's avatar
Brian Tiger Chow committed
52 53 54 55 56 57
	if err != nil {
		return err
	}
	if debug {
		u.Debug = true
		u.SetAllLoggers(logging.DEBUG)
58
	}
59 60

	if u.Debug {
61
		stopProfilingFunc, err := startProfiling()
62
		if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
63
			return err
64
		}
65
		defer stopProfilingFunc() // to be executed as late as possible
66
	}
67

Brian Tiger Chow's avatar
Brian Tiger Chow committed
68 69 70 71 72 73 74 75
	helpTextDisplayed, err := handleHelpOption(req, root)
	if err != nil {
		return err
	}
	if helpTextDisplayed {
		return nil
	}

76 77 78 79
	res, err := callCommand(req, root)
	if err != nil {
		return err
	}
80 81 82 83 84

	err = outputResponse(res, root)
	if err != nil {
		return err
	}
85

Brian Tiger Chow's avatar
Brian Tiger Chow committed
86
	return nil
87
}
88

89
func createRequest(args []string) (cmds.Request, *cmds.Command, error) {
90 91 92 93
	req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root)

	// handle parse error (which means the commandline input was wrong,
	// e.g. incorrect number of args, or nonexistent subcommand)
94
	if err != nil {
95 96 97
		var longHelp, shortHelp bool

		if req != nil {
98 99 100 101 102 103 104 105 106
			// help and h are defined in the root. We expect them to be bool.
			longHelp, _, err = req.Option("help").Bool()
			if err != nil {
				return nil, nil, err
			}
			shortHelp, _, err = req.Option("h").Bool()
			if err != nil {
				return nil, nil, err
			}
107
		}
108

109
		// if the -help flag wasn't specified, show the error message
110 111
		// or if a path was returned (user specified a valid subcommand), show the error message
		// (this means there was an option or argument error)
112
		if path != nil && len(path) > 0 {
113
			if !longHelp && !shortHelp {
114 115
				fmt.Printf(errorFormat, err)
			}
116 117 118
		}

		if cmd == nil {
Matt Bell's avatar
Matt Bell committed
119
			root = commands.Root
120 121
		}

122 123
		// show the long help text if the -help flag was specified or we are at the root command
		// otherwise, show short help text
124
		helpFunc := cmdsCli.ShortHelp
125
		if longHelp || len(path) == 0 {
126 127 128 129
			helpFunc = cmdsCli.LongHelp
		}

		htErr := helpFunc("ipfs", root, path, os.Stdout)
130 131
		if htErr != nil {
			fmt.Println(htErr)
132
		}
133
		return nil, nil, err
134 135
	}

136
	configPath, err := getConfigRoot(req)
137
	if err != nil {
138
		return nil, nil, err
139 140 141 142
	}

	conf, err := getConfig(configPath)
	if err != nil {
143
		return nil, nil, err
144 145 146 147 148
	}
	ctx := req.Context()
	ctx.ConfigRoot = configPath
	ctx.Config = conf

149 150
	// if no encoding was specified by user, default to plaintext encoding
	// (if command doesn't support plaintext, use JSON instead)
151
	if !req.Option("encoding").Found() {
152
		if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
153 154 155 156 157 158
			req.SetOption("encoding", cmds.Text)
		} else {
			req.SetOption("encoding", cmds.JSON)
		}
	}

159
	return req, root, nil
160 161
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
162
func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) {
163
	longHelp, _, err := req.Option("help").Bool()
164
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
165
		return false, err
166
	}
167
	shortHelp, _, err := req.Option("h").Bool()
168 169 170 171
	if err != nil {
		return false, err
	}
	if !longHelp && !shortHelp {
172 173
		return false, nil
	}
174 175 176 177 178 179
	helpFunc := cmdsCli.ShortHelp
	if longHelp || len(req.Path()) == 0 {
		helpFunc = cmdsCli.LongHelp
	}

	err = helpFunc("ipfs", root, req.Path(), os.Stdout)
180 181
	if err != nil {
		return false, err
182
	}
183
	return true, nil
184
}
185

186
func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) {
187
	var res cmds.Response
188

189
	if root == Root { // TODO explain what it means when root == Root
190
		res = root.Call(req)
191 192

	} else {
193
		local, found, err := req.Option("local").Bool()
194
		if err != nil {
195
			return nil, err
196
		}
197

198 199 200
		remote := !found || !local

		if remote && daemon.Locked(req.Context().ConfigRoot) {
201 202
			addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
			if err != nil {
203
				return nil, err
204 205 206 207
			}

			_, host, err := manet.DialArgs(addr)
			if err != nil {
208
				return nil, err
209 210 211 212 213
			}

			client := cmdsHttp.NewClient(host)

			res, err = client.Send(req)
214
			if err != nil {
215
				return nil, err
216 217 218
			}

		} else {
219
			node, err := core.NewIpfsNode(req.Context().Config, false)
220
			if err != nil {
221
				return nil, err
222
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
223
			defer node.Close()
224
			req.Context().Node = node
225

226
			res = root.Call(req)
227 228 229
		}
	}

230
	return res, nil
231 232
}

233
func outputResponse(res cmds.Response, root *cmds.Command) error {
234
	if res.Error() != nil {
235
		fmt.Printf(errorFormat, res.Error().Error())
236

237 238 239 240 241
		if res.Error().Code != cmds.ErrClient {
			return res.Error()
		}

		// if this is a client error, we try to display help text
242
		if res.Error().Code == cmds.ErrClient {
243
			err := cmdsCli.ShortHelp("ipfs", root, res.Request().Path(), os.Stdout)
244
			if err != nil {
245
				fmt.Println(err)
246
			}
247 248
		}

249
		emptyErr := errors.New("") // already displayed error text
250
		return emptyErr
251 252
	}

253
	out, err := res.Reader()
254
	if err != nil {
255
		return err
256
	}
257 258

	io.Copy(os.Stdout, out)
259
	return nil
260 261 262
}

func getConfigRoot(req cmds.Request) (string, error) {
263
	configOpt, found, err := req.Option("config").String()
264 265 266
	if err != nil {
		return "", err
	}
267
	if found && configOpt != "" {
268
		return configOpt, nil
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285
	}

	configPath, err := config.PathRoot()
	if err != nil {
		return "", err
	}
	return configPath, nil
}

func getConfig(path string) (*config.Config, error) {
	configFile, err := config.Filename(path)
	if err != nil {
		return nil, err
	}

	return config.Load(configFile)
}
286

287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
// startProfiling begins CPU profiling and returns a `stop` function to be
// executed as late as possible. The stop function captures the memprofile.
func startProfiling() (func(), error) {

	// start CPU profiling as early as possible
	ofi, err := os.Create(cpuProfile)
	if err != nil {
		return nil, err
	}
	pprof.StartCPUProfile(ofi)

	stopProfiling := func() {
		pprof.StopCPUProfile()
		defer ofi.Close() // captured by the closure
		err := writeHeapProfileToFile()
		if err != nil {
			log.Critical(err)
		}
	}
	return stopProfiling, nil
}

309 310 311
func writeHeapProfileToFile() error {
	mprof, err := os.Create(heapProfile)
	if err != nil {
312
		return err
313
	}
314
	defer mprof.Close() // _after_ writing the heap profile
315 316
	return pprof.WriteHeapProfile(mprof)
}
317

Matt Bell's avatar
Matt Bell committed
318 319 320 321 322 323 324 325
// listen for and handle SIGTERM
func handleInterrupt() {
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)

	go func() {
		for _ = range c {
			log.Info("Received interrupt signal, terminating...")
326
			os.Exit(0)
Matt Bell's avatar
Matt Bell committed
327 328 329
		}
	}()
}