Commit 3e062f6e authored by Matt Bell's avatar Matt Bell Committed by Juan Batiz-Benet

cmd/ipfs: Rewrote entry point to invoke commands via new command API

parent 0d830e5a
package main
import (
"errors"
"fmt"
"io"
"os"
"runtime/pprof"
flag "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
config "github.com/jbenet/go-ipfs/config"
core "github.com/jbenet/go-ipfs/core"
daemon "github.com/jbenet/go-ipfs/daemon"
updates "github.com/jbenet/go-ipfs/updates"
cmds "github.com/jbenet/go-ipfs/commands"
"github.com/jbenet/go-ipfs/commands/cli"
"github.com/jbenet/go-ipfs/core/commands"
u "github.com/jbenet/go-ipfs/util"
)
// The IPFS command tree. It is an instance of `commander.Command`.
var CmdIpfs = &commander.Command{
UsageLine: "ipfs [<flags>] <command> [<args>]",
Short: "global versioned p2p merkledag file system",
Long: `ipfs - global versioned p2p merkledag file system
Basic commands:
init Initialize ipfs local configuration.
add <path> Add an object to ipfs.
cat <ref> Show ipfs object data.
ls <ref> List links from an object.
refs <ref> List link hashes from an object.
Tool commands:
config Manage configuration.
update Download and apply go-ipfs updates.
version Show ipfs version information.
commands List all available commands.
Advanced Commands:
mount Mount an ipfs read-only mountpoint.
serve Serve an interface to ipfs.
net-diag Print network diagnostic
Plumbing commands:
block Interact with raw blocks in the datastore
object Interact with raw dag nodes
Use "ipfs help <command>" for more information about a command.
`,
Run: ipfsCmd,
Subcommands: []*commander.Command{
cmdIpfsAdd,
cmdIpfsCat,
cmdIpfsLs,
cmdIpfsRefs,
cmdIpfsConfig,
cmdIpfsVersion,
cmdIpfsCommands,
cmdIpfsMount,
cmdIpfsInit,
cmdIpfsServe,
cmdIpfsRun,
cmdIpfsName,
cmdIpfsBootstrap,
cmdIpfsDiag,
cmdIpfsBlock,
cmdIpfsObject,
cmdIpfsUpdate,
cmdIpfsLog,
cmdIpfsPin,
cmdIpfsTour,
},
Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
}
// log is the command logger
var log = u.Logger("cmd/ipfs")
func init() {
config, err := config.PathRoot()
if err != nil {
fmt.Fprintln(os.Stderr, "Failure initializing the default Config Directory: ", err)
os.Exit(1)
}
CmdIpfs.Flag.String("c", config, "specify config directory")
}
func ipfsCmd(c *commander.Command, args []string) error {
u.POut(c.Long)
return nil
}
const API_PATH = "/api/v0"
func main() {
// 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()
}
err := CmdIpfs.Dispatch(os.Args[1:])
req, err := cli.Parse(os.Args[1:], commands.Root)
if err != nil {
if len(err.Error()) > 0 {
fmt.Fprintf(os.Stderr, "ipfs %s: %v\n", os.Args[1], err)
}
fmt.Println(err)
os.Exit(1)
}
return
}
// localNode constructs a node
func localNode(confdir string, online bool) (*core.IpfsNode, error) {
filename, err := config.Filename(confdir)
cmd, err := commands.Root.Get(req.Path())
if err != nil {
return nil, err
}
cfg, err := config.Load(filename)
if err != nil {
return nil, err
}
if err := updates.CliCheckForUpdates(cfg, filename); err != nil {
return nil, err
}
return core.NewIpfsNode(cfg, online)
}
// Gets the config "-c" flag from the command, or returns
// the default configuration root directory
func getConfigDir(c *commander.Command) (string, error) {
// use the root cmd (that's where config is specified)
for ; c.Parent != nil; c = c.Parent {
}
// flag should be defined on root.
param := c.Flag.Lookup("c").Value.Get().(string)
if param != "" {
return u.TildeExpansion(param)
}
return config.PathRoot()
}
func getConfig(c *commander.Command) (*config.Config, error) {
confdir, err := getConfigDir(c)
if err != nil {
return nil, err
}
filename, err := config.Filename(confdir)
if err != nil {
return nil, err
fmt.Println(err)
os.Exit(1)
}
return config.Load(filename)
}
// cmdContext is a wrapper structure that keeps a node, a daemonlistener, and
// a config directory together. These three are needed for most commands.
type cmdContext struct {
node *core.IpfsNode
daemon *daemon.DaemonListener
configDir string
}
// TODO: send request to daemon via HTTP API
// setupCmdContext initializes a cmdContext structure from a given command.
func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) {
rootCmd := c
for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent {
}
if debug, ok := req.Option("debug"); ok && debug.(bool) {
u.Debug = true
cc.configDir, err = getConfigDir(rootCmd)
if err != nil {
return
// 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()
}
}
cc.node, err = localNode(cc.configDir, online)
if err != nil {
return
}
res := commands.Root.Call(req)
cc.daemon, err = setupDaemon(cc.configDir, cc.node)
if err != nil {
return
}
return
}
if res.Error() != nil {
fmt.Println(res.Error().Error())
// setupDaemon sets up the daemon corresponding to given node.
func setupDaemon(confdir string, node *core.IpfsNode) (*daemon.DaemonListener, error) {
if node.Config.Addresses.API == "" {
return nil, errors.New("no config.Addresses.API endpoint supplied")
}
if cmd.Help != "" && res.Error().Code == cmds.ErrClient {
// TODO: convert from markdown to ANSI terminal format?
fmt.Println(cmd.Help)
}
maddr, err := ma.NewMultiaddr(node.Config.Addresses.API)
if err != nil {
return nil, err
os.Exit(1)
}
dl, err := daemon.NewDaemonListener(node, maddr, confdir)
_, err = io.Copy(os.Stdout, res)
if err != nil {
return nil, err
fmt.Println(err.Error())
}
go dl.Listen()
return dl, nil
}
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment