main.go 6.14 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 29 30 31
const (
	heapProfile = "ipfs.mprof"
	errorFormat = "ERROR: %v\n\n"
)
32

33 34
var ofi io.WriteCloser

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

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

46
	args := os.Args[1:]
47 48
	req, root, err := createRequest(args)
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
49 50 51 52 53 54 55 56 57 58
		return err
	}

	debug, err := req.Option("debug").Bool()
	if err != nil {
		return err
	}
	if debug {
		u.Debug = true
		u.SetAllLoggers(logging.DEBUG)
59
	}
60

61
	// if debugging, setup profiling.
62
	if u.Debug {
63 64
		var err error
		ofi, err = os.Create("cpu.prof")
65
		if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
66
			return err
67
		}
68 69

		pprof.StartCPUProfile(ofi)
70
	}
71

Brian Tiger Chow's avatar
Brian Tiger Chow committed
72 73 74 75 76 77 78 79
	helpTextDisplayed, err := handleHelpOption(req, root)
	if err != nil {
		return err
	}
	if helpTextDisplayed {
		return nil
	}

80 81 82 83
	res, err := callCommand(req, root)
	if err != nil {
		return err
	}
84 85 86 87 88

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

Brian Tiger Chow's avatar
Brian Tiger Chow committed
90
	return nil
91
}
92

93
func createRequest(args []string) (cmds.Request, *cmds.Command, error) {
94 95 96 97
	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)
98
	if err != nil {
99
		// if the -help flag wasn't specified, show the error message
100 101
		// or if a path was returned (user specified a valid subcommand), show the error message
		// (this means there was an option or argument error)
102
		if path != nil && len(path) > 0 {
103
			help, _ := req.Option("help").Bool()
104 105 106
			if !help {
				fmt.Printf(errorFormat, err)
			}
107 108 109 110 111 112 113 114 115 116
		}

		// when generating help for the root command, we don't want the autogenerated subcommand text
		// (since we have better hand-made subcommand list in the root Help field)
		if cmd == nil {
			root = &*commands.Root
			root.Subcommands = nil
		}

		// generate the help text for the command the user was trying to call (or root)
117 118 119
		helpText, htErr := cmdsCli.HelpText("ipfs", root, path)
		if htErr != nil {
			fmt.Println(htErr)
120 121 122
		} else {
			fmt.Println(helpText)
		}
123
		return nil, nil, err
124 125
	}

126
	configPath, err := getConfigRoot(req)
127
	if err != nil {
128
		return nil, nil, err
129 130 131 132
	}

	conf, err := getConfig(configPath)
	if err != nil {
133
		return nil, nil, err
134 135 136 137 138
	}
	ctx := req.Context()
	ctx.ConfigRoot = configPath
	ctx.Config = conf

139
	if !req.Option("encoding").Found() {
140
		if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
141 142 143 144 145 146
			req.SetOption("encoding", cmds.Text)
		} else {
			req.SetOption("encoding", cmds.JSON)
		}
	}

147
	return req, root, nil
148 149
}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
150
func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) {
151
	help, err := req.Option("help").Bool()
152
	if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
153
		return false, err
154 155 156
	}

	if help {
157 158
		helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
		if err != nil {
Brian Tiger Chow's avatar
Brian Tiger Chow committed
159
			return false, err
160
		}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
161 162
		fmt.Println(helpText)
		return true, nil
163 164
	}

Brian Tiger Chow's avatar
Brian Tiger Chow committed
165
	return false, nil
166
}
167

168
func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) {
169
	var res cmds.Response
170

171
	if root == Root { // TODO explain what it means when root == Root
172
		res = root.Call(req)
173 174

	} else {
175 176
		local, err := req.Option("local").Bool()
		if err != nil {
177
			return nil, err
178
		}
179

180
		if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) {
181 182
			addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
			if err != nil {
183
				return nil, err
184 185 186 187
			}

			_, host, err := manet.DialArgs(addr)
			if err != nil {
188
				return nil, err
189 190 191 192 193
			}

			client := cmdsHttp.NewClient(host)

			res, err = client.Send(req)
194
			if err != nil {
195
				return nil, err
196 197 198
			}

		} else {
199
			node, err := core.NewIpfsNode(req.Context().Config, false)
200
			if err != nil {
201
				return nil, err
202
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
203
			defer node.Close()
204
			req.Context().Node = node
205

206
			res = root.Call(req)
207 208 209
		}
	}

210
	return res, nil
211 212
}

213
func outputResponse(res cmds.Response, root *cmds.Command) error {
214
	if res.Error() != nil {
215
		fmt.Printf(errorFormat, res.Error().Error())
216

217 218 219 220 221
		if res.Error().Code != cmds.ErrClient {
			return res.Error()
		}

		// if this is a client error, we try to display help text
222 223 224 225 226 227 228
		if res.Error().Code == cmds.ErrClient {
			helpText, err := cmdsCli.HelpText("ipfs", root, res.Request().Path())
			if err != nil {
				fmt.Println(err.Error())
			} else {
				fmt.Println(helpText)
			}
229 230
		}

231 232
		emptyErr := errors.New("") // already displayed error text, but want to exit(1)
		return emptyErr
233 234
	}

235
	out, err := res.Reader()
236
	if err != nil {
237
		return err
238
	}
239 240

	io.Copy(os.Stdout, out)
241
	return nil
242 243 244
}

func getConfigRoot(req cmds.Request) (string, error) {
245 246 247 248 249 250
	configOpt, err := req.Option("config").String()
	if err != nil {
		return "", err
	}
	if configOpt != "" {
		return configOpt, nil
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
	}

	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)
}
268 269 270 271 272 273 274 275 276

func writeHeapProfileToFile() error {
	mprof, err := os.Create(heapProfile)
	if err != nil {
		log.Fatal(err)
	}
	defer mprof.Close()
	return pprof.WriteHeapProfile(mprof)
}
277

Matt Bell's avatar
Matt Bell committed
278 279 280 281 282 283 284 285
// 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...")
286
			os.Exit(0)
Matt Bell's avatar
Matt Bell committed
287 288 289 290
		}
	}()
}

291 292 293 294 295 296 297 298 299 300 301 302 303
func exit(code int) {
	if u.Debug {
		pprof.StopCPUProfile()
		ofi.Close()

		err := writeHeapProfileToFile()
		if err != nil {
			log.Critical(err)
		}
	}

	os.Exit(code)
}