consolecmd.go 6.06 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// Copyright 2016 The go-ethereum Authors
// This file is part of go-ethereum.
//
// go-ethereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// go-ethereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with go-ethereum. If not, see <http://www.gnu.org/licenses/>.

package main

import (
	"os"
	"os/signal"
22
	"strings"
23 24 25

	"github.com/ethereum/go-ethereum/cmd/utils"
	"github.com/ethereum/go-ethereum/console"
26 27
	"github.com/ethereum/go-ethereum/node"
	"github.com/ethereum/go-ethereum/rpc"
28
	"gopkg.in/urfave/cli.v1"
29 30 31 32
)

var (
	consoleCommand = cli.Command{
33 34 35 36 37
		Action:    localConsole,
		Name:      "console",
		Usage:     "Start an interactive JavaScript environment",
		ArgsUsage: "", // TODO: Write this!
		Category:  "CONSOLE COMMANDS",
38 39 40 41 42 43 44
		Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
	}
	attachCommand = cli.Command{
45 46 47 48 49
		Action:    remoteConsole,
		Name:      "attach",
		Usage:     "Start an interactive JavaScript environment (connect to node)",
		ArgsUsage: "", // TODO: Write this!
		Category:  "CONSOLE COMMANDS",
50 51 52 53 54
		Description: `
The Geth console is an interactive shell for the JavaScript runtime environment
which exposes a node admin interface as well as the Ðapp JavaScript API.
See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console.
This command allows to open a console on a running geth node.
55
`,
56 57
	}
	javascriptCommand = cli.Command{
58 59 60 61 62
		Action:    ephemeralConsole,
		Name:      "js",
		Usage:     "Execute the specified JavaScript files",
		ArgsUsage: "", // TODO: Write this!
		Category:  "CONSOLE COMMANDS",
63 64 65 66 67 68 69 70 71
		Description: `
The JavaScript VM exposes a node admin interface as well as the Ðapp
JavaScript API. See https://github.com/ethereum/go-ethereum/wiki/Javascipt-Console
`,
	}
)

// localConsole starts a new geth node, attaching a JavaScript console to it at the
// same time.
72
func localConsole(ctx *cli.Context) error {
73
	// Create and start the node based on the CLI flags
74
	node := makeFullNode(ctx)
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
	startNode(ctx, node)
	defer node.Stop()

	// Attach to the newly started node and start the JavaScript console
	client, err := node.Attach()
	if err != nil {
		utils.Fatalf("Failed to attach to the inproc geth: %v", err)
	}
	config := console.Config{
		DataDir: node.DataDir(),
		DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
		Client:  client,
		Preload: utils.MakeConsolePreloads(ctx),
	}
	console, err := console.New(config)
	if err != nil {
		utils.Fatalf("Failed to start the JavaScript console: %v", err)
	}
	defer console.Stop(false)

	// If only a short execution was requested, evaluate and return
	if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
		console.Evaluate(script)
98
		return nil
99 100 101 102
	}
	// Otherwise print the welcome screen and enter interactive mode
	console.Welcome()
	console.Interactive()
103 104

	return nil
105 106 107 108
}

// remoteConsole will connect to a remote geth instance, attaching a JavaScript
// console to it.
109
func remoteConsole(ctx *cli.Context) error {
110
	// Attach to a remotely running geth instance and start the JavaScript console
111
	client, err := dialRPC(ctx.Args().First())
112 113 114 115
	if err != nil {
		utils.Fatalf("Unable to attach to remote geth: %v", err)
	}
	config := console.Config{
116
		DataDir: utils.MakeDataDir(ctx),
117 118 119 120 121 122 123 124 125 126 127 128 129
		DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
		Client:  client,
		Preload: utils.MakeConsolePreloads(ctx),
	}
	console, err := console.New(config)
	if err != nil {
		utils.Fatalf("Failed to start the JavaScript console: %v", err)
	}
	defer console.Stop(false)

	// If only a short execution was requested, evaluate and return
	if script := ctx.GlobalString(utils.ExecFlag.Name); script != "" {
		console.Evaluate(script)
130
		return nil
131 132 133 134
	}
	// Otherwise print the welcome screen and enter interactive mode
	console.Welcome()
	console.Interactive()
135 136

	return nil
137 138
}

139 140 141 142 143
// dialRPC returns a RPC client which connects to the given endpoint.
// The check for empty endpoint implements the defaulting logic
// for "geth attach" and "geth monitor" with no argument.
func dialRPC(endpoint string) (*rpc.Client, error) {
	if endpoint == "" {
144
		endpoint = node.DefaultIPCEndpoint(clientIdentifier)
145 146 147 148 149 150 151 152
	} else if strings.HasPrefix(endpoint, "rpc:") || strings.HasPrefix(endpoint, "ipc:") {
		// Backwards compatibility with geth < 1.5 which required
		// these prefixes.
		endpoint = endpoint[4:]
	}
	return rpc.Dial(endpoint)
}

153 154 155
// ephemeralConsole starts a new geth node, attaches an ephemeral JavaScript
// console to it, and each of the files specified as arguments and tears the
// everything down.
156
func ephemeralConsole(ctx *cli.Context) error {
157
	// Create and start the node based on the CLI flags
158
	node := makeFullNode(ctx)
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193
	startNode(ctx, node)
	defer node.Stop()

	// Attach to the newly started node and start the JavaScript console
	client, err := node.Attach()
	if err != nil {
		utils.Fatalf("Failed to attach to the inproc geth: %v", err)
	}
	config := console.Config{
		DataDir: node.DataDir(),
		DocRoot: ctx.GlobalString(utils.JSpathFlag.Name),
		Client:  client,
		Preload: utils.MakeConsolePreloads(ctx),
	}
	console, err := console.New(config)
	if err != nil {
		utils.Fatalf("Failed to start the JavaScript console: %v", err)
	}
	defer console.Stop(false)

	// Evaluate each of the specified JavaScript files
	for _, file := range ctx.Args() {
		if err = console.Execute(file); err != nil {
			utils.Fatalf("Failed to execute %s: %v", file, err)
		}
	}
	// Wait for pending callbacks, but stop for Ctrl-C.
	abort := make(chan os.Signal, 1)
	signal.Notify(abort, os.Interrupt)

	go func() {
		<-abort
		os.Exit(0)
	}()
	console.Stop(true)
194 195

	return nil
196
}