Skip to content

Commit

Permalink
cmd, common, node, rpc: move IPC into the node itself
Browse files Browse the repository at this point in the history
  • Loading branch information
karalabe committed Feb 4, 2016
1 parent 3274db1 commit 188ab92
Show file tree
Hide file tree
Showing 14 changed files with 292 additions and 156 deletions.
7 changes: 1 addition & 6 deletions cmd/geth/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -502,12 +502,7 @@ func startNode(ctx *cli.Context, stack *node.Node) {
unlockAccount(ctx, accman, trimmed, i, passwords)
}
}
// Start auxiliary services if enabled.
if !ctx.GlobalBool(utils.IPCDisabledFlag.Name) {
if err := utils.StartIPC(stack, ctx); err != nil {
utils.Fatalf("Failed to start IPC: %v", err)
}
}
// Start auxiliary services if enabled
if ctx.GlobalBool(utils.RPCEnabledFlag.Name) {
if err := utils.StartRPC(stack, ctx); err != nil {
utils.Fatalf("Failed to start RPC: %v", err)
Expand Down
4 changes: 2 additions & 2 deletions cmd/geth/monitorcmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,15 @@ import (

"github.com/codegangsta/cli"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/gizak/termui"
)

var (
monitorCommandAttachFlag = cli.StringFlag{
Name: "attach",
Value: "ipc:" + common.DefaultIpcPath(),
Value: "ipc:" + node.DefaultIpcEndpoint(),
Usage: "API endpoint to attach to",
}
monitorCommandRowsFlag = cli.IntFlag{
Expand Down
61 changes: 2 additions & 59 deletions cmd/gethrpctest/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,20 @@
package main

import (
"errors"
"flag"
"io/ioutil"
"log"
"os"
"os/signal"
"path/filepath"
"runtime"

"errors"

"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/eth"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
"github.com/ethereum/go-ethereum/node"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/tests"
"github.com/ethereum/go-ethereum/whisper"
)
Expand Down Expand Up @@ -89,11 +84,6 @@ func main() {
}
log.Println("Initial test suite passed...")

if err := StartIPC(stack); err != nil {
log.Fatalf("Failed to start IPC interface: %v\n", err)
}
log.Println("IPC Interface started, accepting requests...")

// Start the RPC interface and wait until terminated
if err := StartRPC(stack); err != nil {
log.Fatalf("Failed to start RPC interface: %v", err)
Expand All @@ -109,7 +99,7 @@ func main() {
// keystore path and initial pre-state.
func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node.Node, error) {
// Create a networkless protocol stack
stack, err := node.New(&node.Config{NoDiscovery: true})
stack, err := node.New(&node.Config{IpcPath: node.DefaultIpcEndpoint(), NoDiscovery: true})
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -194,50 +184,3 @@ func StartRPC(stack *node.Node) error {
glog.V(logger.Error).Infof("Unable to start RPC-HTTP interface, could not find admin API")
return errors.New("Unable to start RPC-HTTP interface")
}

// StartIPC initializes an IPC interface to the given protocol stack.
func StartIPC(stack *node.Node) error {
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
return err
}

endpoint := `\\.\pipe\geth.ipc`
if runtime.GOOS != "windows" {
endpoint = filepath.Join(common.DefaultDataDir(), "geth.ipc")
}

listener, err := rpc.CreateIPCListener(endpoint)
if err != nil {
return err
}

server := rpc.NewServer()

// register package API's this node provides
offered := stack.APIs()
for _, api := range offered {
server.RegisterName(api.Namespace, api.Service)
glog.V(logger.Debug).Infof("Register %T under namespace '%s' for IPC service\n", api.Service, api.Namespace)
}

//var ethereum *eth.Ethereum
//if err := stack.Service(&ethereum); err != nil {
// return err
//}

go func() {
glog.V(logger.Info).Infof("Start IPC server on %s\n", endpoint)
for {
conn, err := listener.Accept()
if err != nil {
glog.V(logger.Error).Infof("Unable to accept connection - %v\n", err)
}

codec := rpc.NewJSONCodec(conn)
go server.ServeCodec(codec)
}
}()

return nil
}
4 changes: 1 addition & 3 deletions cmd/utils/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,10 +150,8 @@ func NewRemoteRPCClient(ctx *cli.Context) (rpc.Client, error) {
endpoint := ctx.Args().First()
return NewRemoteRPCClientFromString(endpoint)
}

// use IPC by default
endpoint := IPCSocketPath(ctx)
return rpc.NewIPCClient(endpoint)
return rpc.NewIPCClient(node.DefaultIpcEndpoint())
}

// NewRemoteRPCClientFromString returns a RPC client which connects to the given
Expand Down
71 changes: 12 additions & 59 deletions cmd/utils/flags.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,8 +261,8 @@ var (
}
IPCPathFlag = DirectoryFlag{
Name: "ipcpath",
Usage: "Filename for IPC socket/pipe",
Value: DirectoryString{common.DefaultIpcPath()},
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
Value: DirectoryString{common.DefaultIpcSocket()},
}
WSEnabledFlag = cli.BoolFlag{
Name: "ws",
Expand Down Expand Up @@ -394,6 +394,15 @@ func MustMakeDataDir(ctx *cli.Context) string {
return ""
}

// MakeIpcPath creates an IPC path configuration from the set command line flags,
// returning an empty string if IPC was explicitly disabled, or the set path.
func MakeIpcPath(ctx *cli.Context) string {
if ctx.GlobalBool(IPCDisabledFlag.Name) {
return ""
}
return ctx.GlobalString(IPCPathFlag.Name)
}

// MakeNodeKey creates a node key from set command line flags, either loading it
// from a file or as a specified hex value. If neither flags were provided, this
// method returns nil and an emphemeral key is to be generated.
Expand Down Expand Up @@ -582,6 +591,7 @@ func MakeSystemNode(name, version string, extra []byte, ctx *cli.Context) *node.
// Configure the node's service container
stackConf := &node.Config{
DataDir: MustMakeDataDir(ctx),
IpcPath: MakeIpcPath(ctx),
PrivateKey: MakeNodeKey(ctx),
Name: MakeNodeName(name, version, ctx),
NoDiscovery: ctx.GlobalBool(NoDiscoverFlag.Name),
Expand Down Expand Up @@ -734,63 +744,6 @@ func MakeChain(ctx *cli.Context) (chain *core.BlockChain, chainDb ethdb.Database
return chain, chainDb
}

func IPCSocketPath(ctx *cli.Context) (ipcpath string) {
if runtime.GOOS == "windows" {
ipcpath = common.DefaultIpcPath()
if ctx.GlobalIsSet(IPCPathFlag.Name) {
ipcpath = ctx.GlobalString(IPCPathFlag.Name)
}
} else {
ipcpath = common.DefaultIpcPath()
if ctx.GlobalIsSet(DataDirFlag.Name) {
ipcpath = filepath.Join(ctx.GlobalString(DataDirFlag.Name), "geth.ipc")
}
if ctx.GlobalIsSet(IPCPathFlag.Name) {
ipcpath = ctx.GlobalString(IPCPathFlag.Name)
}
}

return
}

func StartIPC(stack *node.Node, ctx *cli.Context) error {
var ethereum *eth.Ethereum
if err := stack.Service(&ethereum); err != nil {
return err
}

endpoint := IPCSocketPath(ctx)
listener, err := rpc.CreateIPCListener(endpoint)
if err != nil {
return err
}

server := rpc.NewServer()

// register package API's this node provides
offered := stack.APIs()
for _, api := range offered {
server.RegisterName(api.Namespace, api.Service)
glog.V(logger.Debug).Infof("Register %T under namespace '%s' for IPC service\n", api.Service, api.Namespace)
}

go func() {
glog.V(logger.Info).Infof("Start IPC server on %s\n", endpoint)
for {
conn, err := listener.Accept()
if err != nil {
glog.V(logger.Error).Infof("Unable to accept connection - %v\n", err)
}

codec := rpc.NewJSONCodec(conn)
go server.ServeCodec(codec)
}
}()

return nil

}

// StartRPC starts a HTTP JSON-RPC API server.
func StartRPC(stack *node.Node, ctx *cli.Context) error {
for _, api := range stack.APIs() {
Expand Down
9 changes: 4 additions & 5 deletions common/path.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,9 +89,8 @@ func DefaultDataDir() string {
return ""
}

func DefaultIpcPath() string {
if runtime.GOOS == "windows" {
return `\\.\pipe\geth.ipc`
}
return filepath.Join(DefaultDataDir(), "geth.ipc")
// DefaultIpcSocket returns the relative name of the default IPC socket. The path
// resolution is done by a node with other contextual infos.
func DefaultIpcSocket() string {
return "geth.ipc"
}
40 changes: 40 additions & 0 deletions node/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ import (
"net"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/logger"
"github.com/ethereum/go-ethereum/logger/glog"
Expand All @@ -49,6 +52,12 @@ type Config struct {
// in memory.
DataDir string

// IpcPath is the requested location to place the IPC endpoint. If the path is
// a simple file name, it is placed inside the data directory (or on the root
// pipe path on Windows), whereas if it's a resolvable path name (absolute or
// relative), then that specific path is enforced. An empty path disables IPC.
IpcPath string

// This field should be a valid secp256k1 private key that will be used for both
// remote peer identification as well as network traffic encryption. If no key
// is configured, the preset one is loaded from the data dir, generating it if
Expand Down Expand Up @@ -90,6 +99,37 @@ type Config struct {
MaxPendingPeers int
}

// IpcEndpoint resolves an IPC endpoint based on a configured value, taking into
// account the set data folders as well as the designated platform we're currently
// running on.
func (c *Config) IpcEndpoint() string {
// Short circuit if IPC has not been enabled
if c.IpcPath == "" {
return ""
}
// On windows we can only use plain top-level pipes
if runtime.GOOS == "windows" {
if strings.HasPrefix(c.IpcPath, `\\.\pipe\`) {
return c.IpcPath
}
return `\\.\pipe\` + c.IpcPath
}
// Resolve names into the data directory full paths otherwise
if filepath.Base(c.IpcPath) == c.IpcPath {
if c.DataDir == "" {
return filepath.Join(os.TempDir(), c.IpcPath)
}
return filepath.Join(c.DataDir, c.IpcPath)
}
return c.IpcPath
}

// DefaultIpcEndpoint returns the IPC path used by default.
func DefaultIpcEndpoint() string {
config := &Config{DataDir: common.DefaultDataDir(), IpcPath: common.DefaultIpcSocket()}
return config.IpcEndpoint()
}

// NodeKey retrieves the currently configured private key of the node, checking
// first any manually set key, falling back to the one found in the configured
// data folder. If no key can be found, a new one is generated.
Expand Down
32 changes: 32 additions & 0 deletions node/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (
"io/ioutil"
"os"
"path/filepath"
"runtime"
"testing"

"github.com/ethereum/go-ethereum/crypto"
Expand Down Expand Up @@ -60,6 +61,37 @@ func TestDatadirCreation(t *testing.T) {
}
}

// Tests that IPC paths are correctly resolved to valid endpoints of different
// platforms.
func TestIpcPathResolution(t *testing.T) {
var tests = []struct {
DataDir string
IpcPath string
Windows bool
Endpoint string
}{
{"", "", false, ""},
{"data", "", false, ""},
{"", "geth.ipc", false, filepath.Join(os.TempDir(), "geth.ipc")},
{"data", "geth.ipc", false, "data/geth.ipc"},
{"data", "./geth.ipc", false, "./geth.ipc"},
{"data", "/geth.ipc", false, "/geth.ipc"},
{"", "", true, ``},
{"data", "", true, ``},
{"", "geth.ipc", true, `\\.\pipe\geth.ipc`},
{"data", "geth.ipc", true, `\\.\pipe\geth.ipc`},
{"data", `\\.\pipe\geth.ipc`, true, `\\.\pipe\geth.ipc`},
}
for i, test := range tests {
// Only run when platform/test match
if (runtime.GOOS == "windows") == test.Windows {
if endpoint := (&Config{DataDir: test.DataDir, IpcPath: test.IpcPath}).IpcEndpoint(); endpoint != test.Endpoint {
t.Errorf("test %d: IPC endpoint mismatch: have %s, want %s", i, endpoint, test.Endpoint)
}
}
}
}

// Tests that node keys can be correctly created, persisted, loaded and/or made
// ephemeral.
func TestNodeKeyPersistency(t *testing.T) {
Expand Down
Loading

0 comments on commit 188ab92

Please sign in to comment.