github.com/zignig/go-ipfs@v0.0.0-20141111235910-c9e5fdf55a52/cmd/ipfs2/main.go (about)

     1  package main
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"os/signal"
     9  	"runtime/pprof"
    10  
    11  	logging "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-logging"
    12  	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
    13  	manet "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr/net"
    14  
    15  	cmds "github.com/jbenet/go-ipfs/commands"
    16  	cmdsCli "github.com/jbenet/go-ipfs/commands/cli"
    17  	cmdsHttp "github.com/jbenet/go-ipfs/commands/http"
    18  	"github.com/jbenet/go-ipfs/config"
    19  	"github.com/jbenet/go-ipfs/core"
    20  	commands "github.com/jbenet/go-ipfs/core/commands2"
    21  	daemon "github.com/jbenet/go-ipfs/daemon2"
    22  	u "github.com/jbenet/go-ipfs/util"
    23  )
    24  
    25  // log is the command logger
    26  var log = u.Logger("cmd/ipfs")
    27  
    28  const (
    29  	cpuProfile  = "ipfs.cpuprof"
    30  	heapProfile = "ipfs.memprof"
    31  	errorFormat = "ERROR: %v\n\n"
    32  )
    33  
    34  func main() {
    35  	err := run()
    36  	if err != nil {
    37  		fmt.Println(err)
    38  		os.Exit(1)
    39  	}
    40  }
    41  
    42  func run() error {
    43  	handleInterrupt()
    44  
    45  	args := os.Args[1:]
    46  	req, root, err := createRequest(args)
    47  	if err != nil {
    48  		return err
    49  	}
    50  
    51  	debug, err := req.Option("debug").Bool()
    52  	if err != nil {
    53  		return err
    54  	}
    55  	if debug {
    56  		u.Debug = true
    57  		u.SetAllLoggers(logging.DEBUG)
    58  	}
    59  
    60  	if u.Debug {
    61  		stopProfilingFunc, err := startProfiling()
    62  		if err != nil {
    63  			return err
    64  		}
    65  		defer stopProfilingFunc() // to be executed as late as possible
    66  	}
    67  
    68  	helpTextDisplayed, err := handleHelpOption(req, root)
    69  	if err != nil {
    70  		return err
    71  	}
    72  	if helpTextDisplayed {
    73  		return nil
    74  	}
    75  
    76  	res, err := callCommand(req, root)
    77  	if err != nil {
    78  		return err
    79  	}
    80  
    81  	err = outputResponse(res, root)
    82  	if err != nil {
    83  		return err
    84  	}
    85  
    86  	return nil
    87  }
    88  
    89  func createRequest(args []string) (cmds.Request, *cmds.Command, error) {
    90  	req, root, cmd, path, err := cmdsCli.Parse(args, Root, commands.Root)
    91  
    92  	// handle parse error (which means the commandline input was wrong,
    93  	// e.g. incorrect number of args, or nonexistent subcommand)
    94  	if err != nil {
    95  		// if the -help flag wasn't specified, show the error message
    96  		// or if a path was returned (user specified a valid subcommand), show the error message
    97  		// (this means there was an option or argument error)
    98  		if path != nil && len(path) > 0 {
    99  			help, _ := req.Option("help").Bool()
   100  			if !help {
   101  				fmt.Printf(errorFormat, err)
   102  			}
   103  		}
   104  
   105  		// when generating help for the root command, we don't want the autogenerated subcommand text
   106  		// (since we have better hand-made subcommand list in the root Help field)
   107  		if cmd == nil {
   108  			root = &*commands.Root
   109  			root.Subcommands = nil
   110  		}
   111  
   112  		// generate the help text for the command the user was trying to call (or root)
   113  		helpText, htErr := cmdsCli.HelpText("ipfs", root, path)
   114  		if htErr != nil {
   115  			fmt.Println(htErr)
   116  		} else {
   117  			fmt.Println(helpText)
   118  		}
   119  		return nil, nil, err
   120  	}
   121  
   122  	configPath, err := getConfigRoot(req)
   123  	if err != nil {
   124  		return nil, nil, err
   125  	}
   126  
   127  	conf, err := getConfig(configPath)
   128  	if err != nil {
   129  		return nil, nil, err
   130  	}
   131  	ctx := req.Context()
   132  	ctx.ConfigRoot = configPath
   133  	ctx.Config = conf
   134  
   135  	if !req.Option("encoding").Found() {
   136  		if req.Command().Marshallers != nil && req.Command().Marshallers[cmds.Text] != nil {
   137  			req.SetOption("encoding", cmds.Text)
   138  		} else {
   139  			req.SetOption("encoding", cmds.JSON)
   140  		}
   141  	}
   142  
   143  	return req, root, nil
   144  }
   145  
   146  func handleHelpOption(req cmds.Request, root *cmds.Command) (helpTextDisplayed bool, err error) {
   147  	help, err := req.Option("help").Bool()
   148  	if err != nil {
   149  		return false, err
   150  	}
   151  	if !help {
   152  		return false, nil
   153  	}
   154  	helpText, err := cmdsCli.HelpText("ipfs", root, req.Path())
   155  	if err != nil {
   156  		return false, err
   157  	}
   158  	fmt.Println(helpText)
   159  
   160  	return true, nil
   161  }
   162  
   163  func callCommand(req cmds.Request, root *cmds.Command) (cmds.Response, error) {
   164  	var res cmds.Response
   165  
   166  	if root == Root { // TODO explain what it means when root == Root
   167  		res = root.Call(req)
   168  
   169  	} else {
   170  		local, err := req.Option("local").Bool()
   171  		if err != nil {
   172  			return nil, err
   173  		}
   174  
   175  		if (!req.Option("local").Found() || !local) && daemon.Locked(req.Context().ConfigRoot) {
   176  			addr, err := ma.NewMultiaddr(req.Context().Config.Addresses.API)
   177  			if err != nil {
   178  				return nil, err
   179  			}
   180  
   181  			_, host, err := manet.DialArgs(addr)
   182  			if err != nil {
   183  				return nil, err
   184  			}
   185  
   186  			client := cmdsHttp.NewClient(host)
   187  
   188  			res, err = client.Send(req)
   189  			if err != nil {
   190  				return nil, err
   191  			}
   192  
   193  		} else {
   194  			node, err := core.NewIpfsNode(req.Context().Config, false)
   195  			if err != nil {
   196  				return nil, err
   197  			}
   198  			defer node.Close()
   199  			req.Context().Node = node
   200  
   201  			res = root.Call(req)
   202  		}
   203  	}
   204  
   205  	return res, nil
   206  }
   207  
   208  func outputResponse(res cmds.Response, root *cmds.Command) error {
   209  	if res.Error() != nil {
   210  		fmt.Printf(errorFormat, res.Error().Error())
   211  
   212  		if res.Error().Code != cmds.ErrClient {
   213  			return res.Error()
   214  		}
   215  
   216  		// if this is a client error, we try to display help text
   217  		if res.Error().Code == cmds.ErrClient {
   218  			helpText, err := cmdsCli.HelpText("ipfs", root, res.Request().Path())
   219  			if err != nil {
   220  				fmt.Println(err.Error())
   221  			} else {
   222  				fmt.Println(helpText)
   223  			}
   224  		}
   225  
   226  		emptyErr := errors.New("") // already displayed error text
   227  		return emptyErr
   228  	}
   229  
   230  	out, err := res.Reader()
   231  	if err != nil {
   232  		return err
   233  	}
   234  
   235  	io.Copy(os.Stdout, out)
   236  	return nil
   237  }
   238  
   239  func getConfigRoot(req cmds.Request) (string, error) {
   240  	configOpt, err := req.Option("config").String()
   241  	if err != nil {
   242  		return "", err
   243  	}
   244  	if configOpt != "" {
   245  		return configOpt, nil
   246  	}
   247  
   248  	configPath, err := config.PathRoot()
   249  	if err != nil {
   250  		return "", err
   251  	}
   252  	return configPath, nil
   253  }
   254  
   255  func getConfig(path string) (*config.Config, error) {
   256  	configFile, err := config.Filename(path)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	return config.Load(configFile)
   262  }
   263  
   264  // startProfiling begins CPU profiling and returns a `stop` function to be
   265  // executed as late as possible. The stop function captures the memprofile.
   266  func startProfiling() (func(), error) {
   267  
   268  	// start CPU profiling as early as possible
   269  	ofi, err := os.Create(cpuProfile)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  	pprof.StartCPUProfile(ofi)
   274  
   275  	stopProfiling := func() {
   276  		pprof.StopCPUProfile()
   277  		defer ofi.Close() // captured by the closure
   278  		err := writeHeapProfileToFile()
   279  		if err != nil {
   280  			log.Critical(err)
   281  		}
   282  	}
   283  	return stopProfiling, nil
   284  }
   285  
   286  func writeHeapProfileToFile() error {
   287  	mprof, err := os.Create(heapProfile)
   288  	if err != nil {
   289  		return err
   290  	}
   291  	defer mprof.Close() // _after_ writing the heap profile
   292  	return pprof.WriteHeapProfile(mprof)
   293  }
   294  
   295  // listen for and handle SIGTERM
   296  func handleInterrupt() {
   297  	c := make(chan os.Signal, 1)
   298  	signal.Notify(c, os.Interrupt)
   299  
   300  	go func() {
   301  		for _ = range c {
   302  			log.Info("Received interrupt signal, terminating...")
   303  			os.Exit(0)
   304  		}
   305  	}()
   306  }