From f20256377731097c9478ede750efffd46d83b494 Mon Sep 17 00:00:00 2001 From: Bas van Kervel Date: Thu, 18 Jun 2015 18:23:13 +0200 Subject: added attach over ipc command --- cmd/geth/js.go | 89 ++++++++++++++++++++++++++++++++++++++++---------------- cmd/geth/main.go | 46 +++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 25 deletions(-) (limited to 'cmd/geth') diff --git a/cmd/geth/js.go b/cmd/geth/js.go index bb5143170..38ef59583 100644 --- a/cmd/geth/js.go +++ b/cmd/geth/js.go @@ -26,6 +26,8 @@ import ( "path/filepath" "strings" + "sort" + "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/common" "github.com/ethereum/go-ethereum/common/docserver" @@ -36,7 +38,6 @@ import ( "github.com/ethereum/go-ethereum/rpc/api" "github.com/ethereum/go-ethereum/rpc/codec" "github.com/ethereum/go-ethereum/rpc/comms" - "github.com/ethereum/go-ethereum/rpc/shared" "github.com/ethereum/go-ethereum/xeth" "github.com/peterh/liner" "github.com/robertkrimen/otto" @@ -137,6 +138,40 @@ func apiWordCompleter(line string, pos int) (head string, completions []string, return begin, completionWords, end } +func newLightweightJSRE(libPath string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { + js := &jsre{ps1: "> "} + js.wait = make(chan *big.Int) + js.client = client + + if f == nil { + f = js + } + + // update state in separare forever blocks + js.re = re.New(libPath) + if err := js.apiBindings(f); err != nil { + utils.Fatalf("Unable to initialize console - %v", err) + } + + if !liner.TerminalSupported() || !interactive { + js.prompter = dumbterm{bufio.NewReader(os.Stdin)} + } else { + lr := liner.NewLiner() + //js.withHistory(func(hist *os.File) { lr.ReadHistory(hist) }) + lr.SetCtrlCAborts(true) + js.loadAutoCompletion() + lr.SetWordCompleter(apiWordCompleter) + lr.SetTabCompletionStyle(liner.TabPrints) + js.prompter = lr + js.atexit = func() { + js.withHistory(func(hist *os.File) { hist.Truncate(0); lr.WriteHistory(hist) }) + lr.Close() + close(js.wait) + } + } + return js +} + func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.EthereumClient, interactive bool, f xeth.Frontend) *jsre { js := &jsre{ethereum: ethereum, ps1: "> "} // set default cors domain used by startRpc from CLI flag @@ -177,7 +212,7 @@ func newJSRE(ethereum *eth.Ethereum, libPath, corsDomain string, client comms.Et } func (self *jsre) loadAutoCompletion() { - if modules, err := self.suportedApis(); err == nil { + if modules, err := self.supportedApis(); err == nil { loadedModulesMethods = make(map[string][]string) for module, _ := range modules { loadedModulesMethods[module] = api.AutoCompletion[module] @@ -185,34 +220,33 @@ func (self *jsre) loadAutoCompletion() { } } -func (self *jsre) suportedApis() (map[string]string, error) { - req := shared.Request{ - Id: 1, - Jsonrpc: "2.0", - Method: "modules", - } - - err := self.client.Send(&req) - if err != nil { - return nil, err - } - - res, err := self.client.Recv() - if err != nil { - return nil, err - } - - if sucRes, ok := res.(map[string]string); ok { - if err == nil { - return sucRes, nil +// show summary of current geth instance +func (self *jsre) welcome() { + self.re.Eval(`console.log('instance: ' + web3.version.client);`) + self.re.Eval(`console.log(' datadir: ' + admin.datadir);`) + self.re.Eval(`console.log("coinbase: " + eth.coinbase);`) + self.re.Eval(`var lastBlockTimestamp = 1000 * eth.getBlock(eth.blockNumber).timestamp`) + self.re.Eval(`console.log("at block: " + eth.blockNumber + " (" + new Date(lastBlockTimestamp).toLocaleDateString() + + " " + new Date(lastBlockTimestamp).toLocaleTimeString() + ")");`) + + if modules, err := self.supportedApis(); err == nil { + loadedModules := make([]string, 0) + for api, version := range modules { + loadedModules = append(loadedModules, fmt.Sprintf("%s:%s", api, version)) } + sort.Strings(loadedModules) + + self.re.Eval(fmt.Sprintf("var modules = '%s';", strings.Join(loadedModules, " "))) + self.re.Eval(`console.log(" modules: " + modules);`) } +} - return nil, fmt.Errorf("Unable to determine supported API's") +func (self *jsre) supportedApis() (map[string]string, error) { + return self.client.SupportedModules() } func (js *jsre) apiBindings(f xeth.Frontend) error { - apis, err := js.suportedApis() + apis, err := js.supportedApis() if err != nil { return err } @@ -366,7 +400,12 @@ func (self *jsre) interactive() { } func (self *jsre) withHistory(op func(*os.File)) { - hist, err := os.OpenFile(filepath.Join(self.ethereum.DataDir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) + datadir := common.DefaultDataDir() + if self.ethereum != nil { + datadir = self.ethereum.DataDir + } + + hist, err := os.OpenFile(filepath.Join(datadir, "history"), os.O_RDWR|os.O_CREATE, os.ModePerm) if err != nil { fmt.Printf("unable to open history file: %v\n", err) return diff --git a/cmd/geth/main.go b/cmd/geth/main.go index 5c4c33cea..f1e8ace3d 100644 --- a/cmd/geth/main.go +++ b/cmd/geth/main.go @@ -204,6 +204,16 @@ nodes. 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 +`}, + { + Action: attach, + Name: "attach", + Usage: `Geth Console: interactive JavaScript environment`, + 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. `, }, { @@ -297,6 +307,40 @@ func run(ctx *cli.Context) { ethereum.WaitForShutdown() } +func attach(ctx *cli.Context) { + // Wrap the standard output with a colorified stream (windows) + if isatty.IsTerminal(os.Stdout.Fd()) { + if pr, pw, err := os.Pipe(); err == nil { + go io.Copy(colorable.NewColorableStdout(), pr) + os.Stdout = pw + } + } + + var client comms.EthereumClient + var err error + if ctx.Args().Present() { + client, err = comms.ClientFromEndpoint(ctx.Args().First(), codec.JSON) + } else { + cfg := comms.IpcConfig{ + Endpoint: ctx.GlobalString(utils.IPCPathFlag.Name), + } + client, err = comms.NewIpcClient(cfg, codec.JSON) + } + + if err != nil { + utils.Fatalf("Unable to attach to geth node - %v", err) + } + + repl := newLightweightJSRE( + ctx.String(utils.JSpathFlag.Name), + client, + true, + nil) + + repl.welcome() + repl.interactive() +} + func console(ctx *cli.Context) { // Wrap the standard output with a colorified stream (windows) if isatty.IsTerminal(os.Stdout.Fd()) { @@ -323,6 +367,8 @@ func console(ctx *cli.Context) { true, nil, ) + + repl.welcome() repl.interactive() ethereum.Stop() -- cgit v1.2.3