kubo/cmd/ipfs2/main.go
2014-11-14 03:17:34 -08:00

303 lines
6.5 KiB
Go

package main
import (
"fmt"
"io"
"os"
"runtime/pprof"
logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging"
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"
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")
const (
heapProfile = "ipfs.mprof"
errorFormat = "ERROR: %v\n\n"
)
func main() {
args := os.Args[1:]
req, root := createRequest(args)
handleOptions(req, root)
res := callCommand(req, root)
outputResponse(res, root)
if u.Debug {
err := writeHeapProfileToFile()
if err != nil {
log.Critical(err)
}
}
}
func createRequest(args []string) (cmds.Request, *cmds.Command) {
req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root)
var options cmds.Request
if req != nil && root != nil {
var err2 error
options, err2 = getOptions(req, root)
if err2 != nil {
fmt.Println(err2)
os.Exit(1)
}
}
// handle parse error (which means the commandline input was wrong,
// e.g. incorrect number of args, or nonexistent subcommand)
if err != nil {
// if the -help flag wasn't specified, show the error message
// or if a path was returned (user specified a valid subcommand), show the error message
// (this means there was an option or argument error)
if options != nil || path != nil && len(path) > 0 {
help := false
if options != nil {
opt, _ := options.Option("help")
help, _ = opt.(bool)
}
if !help {
fmt.Printf(errorFormat, err)
}
}
// 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)
}
os.Exit(1)
}
configPath, err := getConfigRoot(options)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
conf, err := getConfig(configPath)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
ctx := req.Context()
ctx.ConfigRoot = configPath
ctx.Config = conf
if _, found := options.Option("encoding"); !found {
if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
req.SetOption("encoding", cmds.Text)
} else {
req.SetOption("encoding", cmds.JSON)
}
}
return req, root
}
func handleOptions(req cmds.Request, root *cmds.Command) {
options, err := getOptions(req, root)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if help, found := options.Option("help"); found {
if helpBool, ok := help.(bool); helpBool && ok {
helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
if err != nil {
fmt.Println(err.Error())
} else {
fmt.Println(helpText)
}
os.Exit(0)
} else if !ok {
fmt.Println("error: expected 'help' option to be a bool")
os.Exit(1)
}
}
if debug, found := options.Option("debug"); found {
if debugBool, ok := debug.(bool); debugBool && ok {
u.Debug = true
u.SetAllLoggers(logging.DEBUG)
// if debugging, setup profiling.
if u.Debug {
ofi, err := os.Create("cpu.prof")
if err != nil {
fmt.Println(err)
return
}
pprof.StartCPUProfile(ofi)
defer ofi.Close()
defer pprof.StopCPUProfile()
}
} else if !ok {
fmt.Println("error: expected 'debug' option to be a bool")
os.Exit(1)
}
}
}
func callCommand(req cmds.Request, root *cmds.Command) cmds.Response {
var res cmds.Response
if root == Root {
res = root.Call(req)
} else {
options, err := getOptions(req, root)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
var found bool
var local interface{}
localBool := false
if local, found = options.Option("local"); found {
var ok bool
localBool, ok = local.(bool)
if !ok {
fmt.Println("error: expected 'local' option to be a bool")
os.Exit(1)
}
}
if (!found || !localBool) && daemon.Locked(req.Context().ConfigRoot) {
addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
_, host, err := manet.DialArgs(addr)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
client := cmdsHttp.NewClient(host)
res, err = client.Send(req)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
} else {
node, err := core.NewIpfsNode(req.Context().Config, false)
if err != nil {
fmt.Println(err)
os.Exit(1)
}
defer node.Close()
req.Context().Node = node
res = root.Call(req)
}
}
return res
}
func outputResponse(res cmds.Response, root *cmds.Command) {
if res.Error() != nil {
fmt.Printf(errorFormat, res.Error().Error())
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)
}
}
os.Exit(1)
}
out, err := res.Reader()
if err != nil {
fmt.Println(err.Error())
return
}
io.Copy(os.Stdout, out)
}
func getOptions(req cmds.Request, root *cmds.Command) (cmds.Request, error) {
tempReq := cmds.NewRequest(req.Path(), req.Options(), nil, nil)
options, err := root.GetOptions(tempReq.Path())
if err != nil {
return nil, err
}
err = tempReq.ConvertOptions(options)
if err != nil {
return nil, err
}
return tempReq, nil
}
func getConfigRoot(req cmds.Request) (string, error) {
if opt, found := req.Option("config"); found {
if optStr, ok := opt.(string); ok {
return optStr, nil
} else {
return "", fmt.Errorf("Expected 'config' option to be a string")
}
}
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)
}
func writeHeapProfileToFile() error {
mprof, err := os.Create(heapProfile)
if err != nil {
log.Fatal(err)
}
defer mprof.Close()
return pprof.WriteHeapProfile(mprof)
}