ipfs.go 6.1 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3
package main

import (
4
	"errors"
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
5
	"fmt"
6
	"os"
7
	"runtime/pprof"
8
	"time"
9

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
10
	flag "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/gonuts/flag"
11
	check "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/inconshreveable/go-update/check"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
	commander "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/commander"
13
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
14 15

	config "github.com/jbenet/go-ipfs/config"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16
	core "github.com/jbenet/go-ipfs/core"
17
	daemon "github.com/jbenet/go-ipfs/daemon"
18
	updates "github.com/jbenet/go-ipfs/updates"
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
19
	u "github.com/jbenet/go-ipfs/util"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
20 21
)

Juan Batiz-Benet's avatar
go lint  
Juan Batiz-Benet committed
22
// The IPFS command tree. It is an instance of `commander.Command`.
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
23 24 25 26 27 28 29
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:

30
    init          Initialize ipfs local configuration.
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
31 32 33 34 35 36 37
    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:

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
38 39 40
    config        Manage configuration.
    version       Show ipfs version information.
    commands      List all available commands.
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
41

Henry's avatar
Henry committed
42 43 44 45
Plumbing commands:
    block         Interact with raw blocks in the datastore
    object        Interact with raw dag nodes

Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
46 47
Advanced Commands:

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
48
    mount         Mount an ipfs read-only mountpoint.
49
    serve         Serve an interface to ipfs.
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
50
    net-diag      Print network diagnostic
Jeromy's avatar
Jeromy committed
51

Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
52 53 54 55
Use "ipfs help <command>" for more information about a command.
`,
	Run: ipfsCmd,
	Subcommands: []*commander.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
56
		cmdIpfsAdd,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57
		cmdIpfsCat,
58
		cmdIpfsLs,
Juan Batiz-Benet's avatar
refs  
Juan Batiz-Benet committed
59
		cmdIpfsRefs,
60
		cmdIpfsConfig,
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
61 62
		cmdIpfsVersion,
		cmdIpfsCommands,
63
		cmdIpfsMount,
64
		cmdIpfsInit,
verokarhu's avatar
verokarhu committed
65
		cmdIpfsServe,
Jeromy's avatar
Jeromy committed
66
		cmdIpfsRun,
67
		cmdIpfsName,
68
		cmdIpfsBootstrap,
69
		cmdIpfsDiag,
70
		cmdIpfsBlock,
Henry's avatar
Henry committed
71
		cmdIpfsObject,
Henry's avatar
Henry committed
72
		cmdIpfsUpdate,
Henry's avatar
Henry committed
73
		cmdIpfsLog,
Jeromy's avatar
Jeromy committed
74
		cmdIpfsPin,
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
75 76 77 78
	},
	Flag: *flag.NewFlagSet("ipfs", flag.ExitOnError),
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79
// log is the command logger
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
80
var log = u.Logger("cmd/ipfs")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
81

Jeromy's avatar
Jeromy committed
82
func init() {
Shanti Bouchez-Mongardé's avatar
Shanti Bouchez-Mongardé committed
83
	config, err := config.PathRoot()
84
	if err != nil {
85
		fmt.Fprintln(os.Stderr, "Failure initializing the default Config Directory: ", err)
86
		os.Exit(1)
87 88
	}
	CmdIpfs.Flag.String("c", config, "specify config directory")
Jeromy's avatar
Jeromy committed
89 90
}

Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
91 92 93 94 95
func ipfsCmd(c *commander.Command, args []string) error {
	u.POut(c.Long)
	return nil
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
96
func main() {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
97 98 99 100 101 102 103 104 105 106
	// 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()
107
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
108 109

	err := CmdIpfs.Dispatch(os.Args[1:])
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
110 111
	if err != nil {
		if len(err.Error()) > 0 {
112
			fmt.Fprintf(os.Stderr, "ipfs %s: %v\n", os.Args[1], err)
Juan Batiz-Benet's avatar
refmt  
Juan Batiz-Benet committed
113 114 115 116
		}
		os.Exit(1)
	}
	return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
117
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118

119
// localNode constructs a node
120
func localNode(confdir string, online bool) (*core.IpfsNode, error) {
Shanti Bouchez-Mongardé's avatar
Shanti Bouchez-Mongardé committed
121
	filename, err := config.Filename(confdir)
122 123 124 125 126
	if err != nil {
		return nil, err
	}

	cfg, err := config.Load(filename)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
127 128 129 130
	if err != nil {
		return nil, err
	}

131
	if cfg.Version.ShouldCheckForUpdate() {
Henry's avatar
Henry committed
132 133
		log.Info("checking for update")
		u, err := updates.CheckForUpdate()
134 135 136 137 138
		if err != nil {
			if err != check.NoUpdateAvailable {
				log.Error("Error while checking for update: %v\n", err)
				return nil, err
			}
139
			log.Notice("No update available, checked on %s", time.Now())
140
		}
141 142

		config.RecordUpdateCheck(cfg, filename)
Henry's avatar
Henry committed
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165

		if u != nil && cfg.Version.AutoUpdate != config.UpdateNever {
			if updates.ShouldAutoUpdate(cfg.Version.AutoUpdate, u.Version) {
				log.Notice("Applying update %s", u.Version)

				if err = updates.AbleToApply(); err != nil {
					log.Error("Can't apply update: %v", err)
					return nil, err
				}

				if err, errRecover := u.Update(); err != nil {
					err = fmt.Errorf("Update failed: %v\n", err)
					if errRecover != nil {
						err = fmt.Errorf("%s\nRecovery failed! Cause: %v\nYou may need to recover manually", err, errRecover)
					}
					log.Error(err.Error())
					return nil, err
				}
				// BUG(cryptix): no good way to restart yet. - tracking https://github.com/inconshreveable/go-update/issues/5
				fmt.Println("update %v applied. please restart.", u.Version)
				os.Exit(0)
			}
		}
166 167
	}

168
	return core.NewIpfsNode(cfg, online)
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
169
}
Jeromy's avatar
Jeromy committed
170 171

// Gets the config "-c" flag from the command, or returns
172
// the default configuration root directory
173
func getConfigDir(c *commander.Command) (string, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
174 175 176

	// use the root cmd (that's where config is specified)
	for ; c.Parent != nil; c = c.Parent {
177
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
178 179 180 181 182

	// flag should be defined on root.
	param := c.Flag.Lookup("c").Value.Get().(string)
	if param != "" {
		return u.TildeExpansion(param)
183
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
184 185 186 187 188 189 190 191

	return config.PathRoot()
}

func getConfig(c *commander.Command) (*config.Config, error) {
	confdir, err := getConfigDir(c)
	if err != nil {
		return nil, err
Jeromy's avatar
Jeromy committed
192
	}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
193 194 195 196

	filename, err := config.Filename(confdir)
	if err != nil {
		return nil, err
197
	}
198

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
199
	return config.Load(filename)
Jeromy's avatar
Jeromy committed
200
}
201 202 203 204 205 206 207 208 209 210 211

// 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
}

// setupCmdContext initializes a cmdContext structure from a given command.
func setupCmdContext(c *commander.Command, online bool) (cc cmdContext, err error) {
212
	rootCmd := c
213
	for ; rootCmd.Parent != nil; rootCmd = rootCmd.Parent {
214 215 216
	}

	cc.configDir, err = getConfigDir(rootCmd)
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251
	if err != nil {
		return
	}

	cc.node, err = localNode(cc.configDir, online)
	if err != nil {
		return
	}

	cc.daemon, err = setupDaemon(cc.configDir, cc.node)
	if err != nil {
		return
	}

	return
}

// 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")
	}

	maddr, err := ma.NewMultiaddr(node.Config.Addresses.API)
	if err != nil {
		return nil, err
	}

	dl, err := daemon.NewDaemonListener(node, maddr, confdir)
	if err != nil {
		return nil, err
	}
	go dl.Listen()
	return dl, nil
}