package cli import ( "fmt" "os" "path/filepath" "strings" "github.com/ethereum/go-ethereum/internal/cli/flagset" "github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/cmd/utils" "github.com/ethereum/go-ethereum/console" "github.com/ethereum/go-ethereum/node" "github.com/mitchellh/cli" ) // AttachCommand is the command to Connect to remote Bor IPC console type AttachCommand struct { UI cli.Ui Meta *Meta Meta2 *Meta2 ExecCMD string Endpoint string PreloadJSFlag string JSpathFlag string } // MarkDown implements cli.MarkDown interface func (c *AttachCommand) MarkDown() string { items := []string{ "# Attach", "Connect to remote Bor IPC console.", c.Flags().MarkDown(), } return strings.Join(items, "\n\n") } // Help implements the cli.Command interface func (c *AttachCommand) Help() string { return `Usage: bor attach Connect to remote Bor IPC console.` } // Synopsis implements the cli.Command interface func (c *AttachCommand) Synopsis() string { return "Connect to Bor via IPC" } func (c *AttachCommand) Flags() *flagset.Flagset { f := flagset.NewFlagSet("attach") f.StringFlag(&flagset.StringFlag{ Name: "exec", Usage: "Command to run in remote console", Value: &c.ExecCMD, }) f.StringFlag(&flagset.StringFlag{ Name: "preload", Usage: "Comma separated list of JavaScript files to preload into the console", Value: &c.PreloadJSFlag, }) f.StringFlag(&flagset.StringFlag{ Name: "jspath", Usage: "JavaScript root path for `loadScript`", Value: &c.JSpathFlag, }) return f } // Run implements the cli.Command interface func (c *AttachCommand) Run(args []string) int { flags := c.Flags() //check if first arg is flag or IPC location if len(args) == 0 { args = append(args, "") } if args[0] != "" && strings.HasPrefix(args[0], "--") { if err := flags.Parse(args); err != nil { c.UI.Error(err.Error()) return 1 } } else { c.Endpoint = args[0] if err := flags.Parse(args[1:]); err != nil { c.UI.Error(err.Error()) return 1 } } if err := c.remoteConsole(); err != nil { c.UI.Error(err.Error()) return 1 } return 0 } // remoteConsole will connect to a remote bor instance, attaching a JavaScript // console to it. func (c *AttachCommand) remoteConsole() error { // Attach to a remotely running geth instance and start the JavaScript console path := node.DefaultDataDir() if c.Endpoint == "" { if c.Meta.dataDir != "" { path = c.Meta.dataDir } if path != "" { homeDir, _ := os.UserHomeDir() path = filepath.Join(homeDir, "/.bor/data") } c.Endpoint = fmt.Sprintf("%s/bor.ipc", path) } client, err := dialRPC(c.Endpoint) if err != nil { utils.Fatalf("Unable to attach to remote bor: %v", err) } config := console.Config{ DataDir: path, DocRoot: c.JSpathFlag, Client: client, Preload: c.makeConsolePreloads(), } console, err := console.New(config) if err != nil { utils.Fatalf("Failed to start the JavaScript console: %v", err) } defer console.Stop(false) if c.ExecCMD != "" { console.Evaluate(c.ExecCMD) return nil } // Otherwise print the welcome screen and enter interactive mode console.Welcome() console.Interactive() return nil } // dialRPC returns a RPC client which connects to the given endpoint. // The check for empty endpoint implements the defaulting logic // for "geth attach" with no argument. func dialRPC(endpoint string) (*rpc.Client, error) { if endpoint == "" { endpoint = node.DefaultIPCEndpoint("bor") } 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) } // MakeConsolePreloads retrieves the absolute paths for the console JavaScript // scripts to preload before starting. func (c *AttachCommand) makeConsolePreloads() []string { // Skip preloading if there's nothing to preload if c.PreloadJSFlag == "" { return nil } // Otherwise resolve absolute paths and return them var preloads []string for _, file := range strings.Split(c.PreloadJSFlag, ",") { preloads = append(preloads, strings.TrimSpace(file)) } return preloads }