swarm.go 4.38 KB
Newer Older
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
1 2 3 4 5
package commands

import (
	"bytes"
	"fmt"
6
	"io"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
7
	"path"
8
	"sort"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
9 10

	cmds "github.com/jbenet/go-ipfs/commands"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
11
	peer "github.com/jbenet/go-ipfs/p2p/peer"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
12
	errors "github.com/jbenet/go-ipfs/util/debugerror"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
13 14 15

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	ma "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multiaddr"
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
16 17 18 19 20 21
)

type stringList struct {
	Strings []string
}

22
var SwarmCmd = &cmds.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
23 24 25 26 27 28 29 30 31 32 33 34 35
	Helptext: cmds.HelpText{
		Tagline: "swarm inspection tool",
		Synopsis: `
ipfs swarm peers             - List peers with open connections
ipfs swarm connect <address> - Open connection to a given peer
`,
		ShortDescription: `
ipfs swarm is a tool to manipulate the network swarm. The swarm is the
component that opens, listens for, and maintains connections to other
ipfs peers in the internet.
`,
	},
	Subcommands: map[string]*cmds.Command{
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
36 37
		"peers":   swarmPeersCmd,
		"connect": swarmConnectCmd,
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
38 39 40 41 42 43 44 45 46 47
	},
}

var swarmPeersCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "List peers with open connections",
		ShortDescription: `
ipfs swarm peers lists the set of peers this node is connected to.
`,
	},
48
	Run: func(req cmds.Request, res cmds.Response) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
49 50 51 52

		log.Debug("ipfs swarm peers")
		n, err := req.Context().GetNode()
		if err != nil {
53 54
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
55 56
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
57
		if n.PeerHost == nil {
58 59
			res.SetError(errNotOnline, cmds.ErrClient)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
60 61
		}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
62
		conns := n.PeerHost.Network().Conns()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
63 64
		addrs := make([]string, len(conns))
		for i, c := range conns {
65
			pid := c.RemotePeer()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
66
			addr := c.RemoteMultiaddr()
67
			addrs[i] = fmt.Sprintf("%s/%s", addr, pid.Pretty())
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
68 69
		}

70
		sort.Sort(sort.StringSlice(addrs))
71
		res.SetOutput(&stringList{addrs})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
72 73 74 75
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
76
	Type: stringList{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
77 78
}

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
79 80 81 82 83 84 85 86 87 88 89
var swarmConnectCmd = &cmds.Command{
	Helptext: cmds.HelpText{
		Tagline: "Open connection to a given peer",
		ShortDescription: `
'ipfs swarm connect' opens a connection to a peer address. The address format
is an ipfs multiaddr:

ipfs swarm connect /ip4/104.131.131.82/tcp/4001/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ
`,
	},
	Arguments: []cmds.Argument{
90
		cmds.StringArg("address", true, true, "address of peer to connect to").EnableStdin(),
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
91
	},
92
	Run: func(req cmds.Request, res cmds.Response) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
93 94 95 96 97
		ctx := context.TODO()

		log.Debug("ipfs swarm connect")
		n, err := req.Context().GetNode()
		if err != nil {
98 99
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
100 101
		}

102
		addrs := req.Arguments()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
103

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
104
		if n.PeerHost == nil {
105 106
			res.SetError(errNotOnline, cmds.ErrClient)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
107 108 109 110
		}

		peers, err := peersWithAddresses(n.Peerstore, addrs)
		if err != nil {
111 112
			res.SetError(err, cmds.ErrNormal)
			return
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
113 114 115 116
		}

		output := make([]string, len(peers))
		for i, p := range peers {
117
			output[i] = "connect " + p.Pretty()
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
118

Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
119
			err := n.PeerHost.Connect(ctx, peer.PeerInfo{ID: p})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
120 121 122 123 124 125 126
			if err != nil {
				output[i] += " failure: " + err.Error()
			} else {
				output[i] += " success"
			}
		}

127
		res.SetOutput(&stringList{output})
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
128 129 130 131
	},
	Marshalers: cmds.MarshalerMap{
		cmds.Text: stringListMarshaler,
	},
132
	Type: stringList{},
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
133 134
}

135
func stringListMarshaler(res cmds.Response) (io.Reader, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
136 137 138 139 140 141 142
	list, ok := res.Output().(*stringList)
	if !ok {
		return nil, errors.New("failed to cast []string")
	}

	var buf bytes.Buffer
	for _, s := range list.Strings {
143 144
		buf.WriteString(s)
		buf.WriteString("\n")
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
145
	}
146
	return &buf, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
147
}
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
148 149 150 151 152 153 154 155 156 157 158 159

// splitAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns slices of multiaddrs and peerids.
func splitAddresses(addrs []string) (maddrs []ma.Multiaddr, pids []peer.ID, err error) {

	maddrs = make([]ma.Multiaddr, len(addrs))
	pids = make([]peer.ID, len(addrs))
	for i, addr := range addrs {
		a, err := ma.NewMultiaddr(path.Dir(addr))
		if err != nil {
			return nil, nil, cmds.ClientError("invalid peer address: " + err.Error())
		}
160
		id, err := peer.IDB58Decode(path.Base(addr))
161 162 163 164
		if err != nil {
			return nil, nil, err
		}
		pids[i] = id
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
165 166 167 168 169 170 171
		maddrs[i] = a
	}
	return
}

// peersWithAddresses is a function that takes in a slice of string peer addresses
// (multiaddr + peerid) and returns a slice of properly constructed peers
172
func peersWithAddresses(ps peer.Peerstore, addrs []string) ([]peer.ID, error) {
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
173 174 175 176 177
	maddrs, pids, err := splitAddresses(addrs)
	if err != nil {
		return nil, err
	}

178 179
	for i, p := range pids {
		ps.AddAddress(p, maddrs[i])
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
180
	}
181
	return pids, nil
Juan Batiz-Benet's avatar
Juan Batiz-Benet committed
182
}