main.go 5.63 KB
Newer Older
1 2 3 4 5 6
package main

import (
	"fmt"
	"io"
	"os"
Matt Bell's avatar
Matt Bell committed
7
	"os/signal"
8 9
	"runtime/pprof"

10
	logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging"
11 12 13
	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"

14 15 16 17 18 19 20 21 22 23 24 25 26
	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")

27 28 29 30
const (
	heapProfile = "ipfs.mprof"
	errorFormat = "ERROR: %v\n\n"
)
31

32 33
var ofi io.WriteCloser

34
func main() {
Matt Bell's avatar
Matt Bell committed
35 36
	handleInterrupt()

37
	args := os.Args[1:]
38 39
	req, root := createRequest(args)
	handleOptions(req, root)
40

41
	// if debugging, setup profiling.
42
	if u.Debug {
43 44
		var err error
		ofi, err = os.Create("cpu.prof")
45
		if err != nil {
46 47
			fmt.Println(err)
			return
48
		}
49 50

		pprof.StartCPUProfile(ofi)
51
	}
52 53 54 55 56

	res := callCommand(req, root)
	outputResponse(res, root)

	exit(0)
57
}
58

59
func createRequest(args []string) (cmds.Request, *cmds.Command) {
60 61 62 63
	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)
64
	if err != nil {
65
		// if the -help flag wasn't specified, show the error message
66 67
		// or if a path was returned (user specified a valid subcommand), show the error message
		// (this means there was an option or argument error)
68
		if path != nil && len(path) > 0 {
69
			help, _ := req.Option("help").Bool()
70 71 72
			if !help {
				fmt.Printf(errorFormat, err)
			}
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
		}

		// 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)
		helpText, err := cmdsCli.HelpText("ipfs", root, path)
		if err != nil {
			fmt.Println(err)
		} else {
			fmt.Println(helpText)
		}
89
		exit(1)
90 91
	}

92
	configPath, err := getConfigRoot(req)
93 94
	if err != nil {
		fmt.Println(err)
95
		exit(1)
96 97 98 99 100
	}

	conf, err := getConfig(configPath)
	if err != nil {
		fmt.Println(err)
101
		exit(1)
102 103 104 105 106 107
	}

	ctx := req.Context()
	ctx.ConfigRoot = configPath
	ctx.Config = conf

108
	if !req.Option("encoding").Found() {
109
		if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
110 111 112 113 114 115 116 117 118 119
			req.SetOption("encoding", cmds.Text)
		} else {
			req.SetOption("encoding", cmds.JSON)
		}
	}

	return req, root
}

func handleOptions(req cmds.Request, root *cmds.Command) {
120 121 122 123 124 125
	if help, err := req.Option("help").Bool(); help && err == nil {
		helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
		if err != nil {
			fmt.Println(err.Error())
		} else {
			fmt.Println(helpText)
126
		}
127 128 129 130
		exit(0)
	} else if err != nil {
		fmt.Println(err)
		exit(1)
131 132
	}

133 134 135 136 137 138
	if debug, err := req.Option("debug").Bool(); debug && err == nil {
		u.Debug = true
		u.SetAllLoggers(logging.DEBUG)
	} else if err != nil {
		fmt.Println(err)
		exit(1)
139
	}
140
}
141

142
func callCommand(req cmds.Request, root *cmds.Command) cmds.Response {
143
	var res cmds.Response
144

145 146
	if root == Root {
		res = root.Call(req)
147 148

	} else {
149 150 151 152
		local, err := req.Option("local").Bool()
		if err != nil {
			fmt.Println(err)
			exit(1)
153
		}
154

155
		if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) {
156 157 158
			addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
			if err != nil {
				fmt.Println(err)
159
				exit(1)
160 161 162 163 164
			}

			_, host, err := manet.DialArgs(addr)
			if err != nil {
				fmt.Println(err)
165
				exit(1)
166 167 168 169 170
			}

			client := cmdsHttp.NewClient(host)

			res, err = client.Send(req)
171 172
			if err != nil {
				fmt.Println(err)
173
				exit(1)
174 175 176
			}

		} else {
177
			node, err := core.NewIpfsNode(req.Context().Config, false)
178 179
			if err != nil {
				fmt.Println(err)
180
				exit(1)
181
			}
Brian Tiger Chow's avatar
Brian Tiger Chow committed
182
			defer node.Close()
183
			req.Context().Node = node
184

185
			res = root.Call(req)
186 187 188
		}
	}

189 190 191
	return res
}

192
func outputResponse(res cmds.Response, root *cmds.Command) {
193
	if res.Error() != nil {
194
		fmt.Printf(errorFormat, res.Error().Error())
195

196 197 198 199 200 201 202
		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)
			}
203 204
		}

205
		exit(1)
206 207
	}

208
	out, err := res.Reader()
209 210
	if err != nil {
		fmt.Println(err.Error())
211
		return
212
	}
213 214

	io.Copy(os.Stdout, out)
215 216 217
}

func getConfigRoot(req cmds.Request) (string, error) {
218 219 220 221 222 223
	configOpt, err := req.Option("config").String()
	if err != nil {
		return "", err
	}
	if configOpt != "" {
		return configOpt, nil
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240
	}

	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)
}
241 242 243 244 245 246 247 248 249

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

Matt Bell's avatar
Matt Bell committed
251 252 253 254 255 256 257 258 259 260 261 262 263
// 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...")
			exit(0)
		}
	}()
}

264 265 266 267 268 269 270 271 272 273 274 275 276
func exit(code int) {
	if u.Debug {
		pprof.StopCPUProfile()
		ofi.Close()

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

	os.Exit(code)
}