This commit is contained in:
bas-vk 2016-07-22 12:59:13 +00:00 committed by GitHub
commit 320ab1e875
9 changed files with 175 additions and 94 deletions

View file

@ -59,6 +59,7 @@ Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}/{{gover}}
coinbase: {{.Etherbase}}
network: mainnet
at block: 0 ({{niltime}})
datadir: {{.Datadir}}
modules:{{range apis}} {{.}}:1.0{{end}}
@ -153,6 +154,7 @@ Welcome to the Geth JavaScript console!
instance: Geth/v{{gethver}}/{{goos}}/{{gover}}
coinbase: {{etherbase}}
network: mainnet
at block: 0 ({{niltime}}){{if ipc}}
datadir: {{datadir}}{{end}}
modules:{{range apis}} {{.}}:1.0{{end}}

View file

@ -95,7 +95,7 @@ func main() {
func MakeSystemNode(keydir string, privkey string, test *tests.BlockTest) (*node.Node, error) {
// Create a networkless protocol stack
stack, err := node.New(&node.Config{
IPCPath: node.DefaultIPCEndpoint(),
IPCPath: []string{node.DefaultIPCEndpoint()},
HTTPHost: common.DefaultHTTPHost,
HTTPPort: common.DefaultHTTPPort,
HTTPModules: []string{"admin", "db", "eth", "debug", "miner", "net", "shh", "txpool", "personal", "web3"},

View file

@ -18,6 +18,7 @@ package utils
import (
"fmt"
"runtime"
"strings"
"github.com/ethereum/go-ethereum/node"
@ -40,6 +41,11 @@ func NewRemoteRPCClient(ctx *cli.Context) (rpc.Client, error) {
// endpoint. It must start with either `ipc:` or `rpc:` (HTTP).
func NewRemoteRPCClientFromString(endpoint string) (rpc.Client, error) {
if strings.HasPrefix(endpoint, "ipc:") {
if runtime.GOOS == "windows" && !strings.HasPrefix(endpoint, `ipc:\\.\pipe\`) {
// user supplied named pipe name, e.g: attach "namedpipename" instead
// of "attach ipc:\\.\pipe\namedpipename".
endpoint = `ipc:\\.\pipe\` + endpoint[4:]
}
return rpc.NewIPCClient(endpoint[4:])
}
if strings.HasPrefix(endpoint, "rpc:") {

View file

@ -276,8 +276,8 @@ var (
}
IPCPathFlag = DirectoryFlag{
Name: "ipcpath",
Usage: "Filename for IPC socket/pipe within the datadir (explicit paths escape it)",
Value: DirectoryString{common.DefaultIPCSocket},
Usage: "IPC endpoint location (explicit paths escape it)",
Value: DirectoryString{common.DefaultIPCEndpoint()},
}
WSEnabledFlag = cli.BoolFlag{
Name: "ws",
@ -423,13 +423,27 @@ func MakeKeyStoreDir(datadir string, ctx *cli.Context) string {
return filepath.Join(datadir, "keystore")
}
// 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 {
// MakeIPCPaths creates an IPC path configuration from the set command line flags,
// returning an empty slice if IPC was explicitly disabled, or the paths to open
// the IPC endpoint(s).
func MakeIPCPaths(ctx *cli.Context) []string {
if ctx.GlobalBool(IPCDisabledFlag.Name) {
return ""
return []string{}
}
return ctx.GlobalString(IPCPathFlag.Name)
// user overruled default location
if ctx.GlobalIsSet(IPCPathFlag.Name) {
return []string{ctx.GlobalString(IPCPathFlag.Name)}
}
// return the old geth specific path and new more general default path.
// This is a temporary situation, the old path will be removed after a couple
// of weeks. This gives clients time to update and users the ability to use
// the latest geth version with default IPC setting as they are used to.
if runtime.GOOS == "windows" {
return []string{`\\.\pipe\geth.ipc`, ctx.GlobalString(IPCPathFlag.Name)}
}
return []string{"geth.ipc", ctx.GlobalString(IPCPathFlag.Name)}
}
// MakeNodeKey creates a node key from set command line flags, either loading it
@ -655,7 +669,7 @@ func MakeSystemNode(name, version string, relconf release.Config, extra []byte,
NAT: MakeNAT(ctx),
MaxPeers: ctx.GlobalInt(MaxPeersFlag.Name),
MaxPendingPeers: ctx.GlobalInt(MaxPendingPeersFlag.Name),
IPCPath: MakeIPCPath(ctx),
IPCPath: MakeIPCPaths(ctx),
HTTPHost: MakeHTTPRpcHost(ctx),
HTTPPort: ctx.GlobalInt(RPCPortFlag.Name),
HTTPCors: ctx.GlobalString(RPCCORSDomainFlag.Name),

View file

@ -19,10 +19,12 @@ package common
import (
"path/filepath"
"runtime"
"os"
)
const (
DefaultIPCSocket = "geth.ipc" // Default (relative) name of the IPC RPC socket
DefaultIPCSocket = "ipc.sock" // Default (relative) name of the IPC RPC unix socket
DefaultIPCNamedPipe = `\\.\pipe\ethereum` // Default name of the IPC RPC named pipe
DefaultHTTPHost = "localhost" // Default host interface for the HTTP RPC server
DefaultHTTPPort = 8545 // Default TCP port for the HTTP RPC server
DefaultWSHost = "localhost" // Default host interface for the websocket RPC server
@ -46,3 +48,27 @@ func DefaultDataDir() string {
// As we cannot guess a stable location, return empty and handle later
return ""
}
// DefaultIPCEndpoint returns the default location where to open the IPC endpoint.
//
// On Windows this is DefaultIPCNamedPipe.
// On OSX this is DefaultDataDir/DefaultIPCSocket.
// Else this is $ENV{XDG_RUNTIME_DIR}/ethereum/DefaultIPCSocket, if $ENV{XDG_RUNTIME_DIR} is not set,
// or the ethereum sub directory could not be created this is DefaultDataDir/DefaultIPCSocket.
func DefaultIPCEndpoint() string {
if runtime.GOOS == "windows" {
return DefaultIPCNamedPipe
}
if runtime.GOOS == "darwin" {
return filepath.Join(DefaultDataDir(), DefaultIPCSocket)
}
if dir := os.Getenv("XDG_RUNTIME_DIR"); dir != "" {
dir = filepath.Join(dir, "ethereum")
// create "ethereum" subdirectory if it doesn't exist
if err := os.MkdirAll(dir, 0700); err == nil {
return filepath.Join(dir, DefaultIPCSocket)
}
}
return filepath.Join(DefaultDataDir(), DefaultIPCSocket)
}

View file

@ -247,8 +247,17 @@ func (c *Console) Welcome() {
// Print some generic Geth metadata
fmt.Fprintf(c.printer, "Welcome to the Geth JavaScript console!\n\n")
c.jsre.Run(`
var _network = 'private';
switch (eth.getBlock(0).hash.toLowerCase()) {
case '0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3':
_network = 'mainnet'; break
case '0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303':
_network = 'testnet'; break
}
console.log("instance: " + web3.version.node);
console.log("coinbase: " + eth.coinbase);
console.log(" network: " + _network);
console.log("at block: " + eth.blockNumber + " (" + new Date(1000 * eth.getBlock(eth.blockNumber).timestamp) + ")");
console.log(" datadir: " + admin.datadir);
`)

View file

@ -53,11 +53,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
// IPCPath is the requested location to place the IPC endpoint(s). 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
// relative), then that specific path is enforced. A nil or empty slice
// 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
@ -138,35 +139,39 @@ type Config struct {
WSModules []string
}
// IPCEndpoint resolves an IPC endpoint based on a configured value, taking into
// IPCEndpoints resolves the IPC endpoints 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 ""
}
func (c *Config) IPCEndpoints() []string {
var endpoints []string
for _, endpoint := range c.IPCPath {
if endpoint == "" {
endpoints = append(endpoints, "")
} else if runtime.GOOS == "windows" {
// On windows we can only use plain top-level pipes
if runtime.GOOS == "windows" {
if strings.HasPrefix(c.IPCPath, `\\.\pipe\`) {
return c.IPCPath
if strings.HasPrefix(endpoint, `\\.\pipe\`) {
endpoints = append(endpoints, endpoint)
} else {
endpoints = append(endpoints, `\\.\pipe\`+endpoint)
}
return `\\.\pipe\` + c.IPCPath
}
// Resolve names into the data directory full paths otherwise
if filepath.Base(c.IPCPath) == c.IPCPath {
} else if filepath.Base(endpoint) == endpoint { // Resolve names into the data directory full paths otherwise
if c.DataDir == "" {
return filepath.Join(os.TempDir(), c.IPCPath)
endpoints = append(endpoints, filepath.Join(os.TempDir(), endpoint))
} else {
endpoints = append(endpoints, filepath.Join(c.DataDir, endpoint))
}
return filepath.Join(c.DataDir, c.IPCPath)
} else {
endpoints = append(endpoints, endpoint)
}
return c.IPCPath
}
return endpoints
}
// DefaultIPCEndpoint returns the IPC path used by default.
func DefaultIPCEndpoint() string {
config := &Config{DataDir: common.DefaultDataDir(), IPCPath: common.DefaultIPCSocket}
return config.IPCEndpoint()
config := &Config{DataDir: common.DefaultDataDir(), IPCPath: []string{common.DefaultIPCEndpoint()}}
return config.IPCEndpoints()[0]
}
// HTTPEndpoint resolves an HTTP endpoint based on the configured host interface

View file

@ -85,7 +85,7 @@ func TestIPCPathResolution(t *testing.T) {
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 {
if endpoint := (&Config{DataDir: test.DataDir, IPCPath: []string{test.IPCPath}}).IPCEndpoints()[0]; endpoint != test.Endpoint {
t.Errorf("test %d: IPC endpoint mismatch: have %s, want %s", i, endpoint, test.Endpoint)
}
}

View file

@ -43,6 +43,17 @@ var (
datadirInUseErrnos = map[uint]bool{11: true, 32: true, 35: true}
)
type ipcService struct {
endpoint string // endpoints to listen at (empty = IPC disabled)
listener net.Listener // RPC listener sockets to serve API requests
handler *rpc.Server // RPC request handlers to process the API requests
}
func (s *ipcService) stop() {
s.listener.Close()
s.handler.Stop()
}
// Node represents a P2P node into which arbitrary (uniquely typed) services might
// be registered.
type Node struct {
@ -58,9 +69,7 @@ type Node struct {
rpcAPIs []rpc.API // List of APIs currently provided by the node
inprocHandler *rpc.Server // In-process RPC request handler to process the API requests
ipcEndpoint string // IPC endpoint to listen at (empty = IPC disabled)
ipcListener net.Listener // IPC RPC listener socket to serve API requests
ipcHandler *rpc.Server // IPC RPC request handler to process the API requests
ipcServices []*ipcService // IPC services
httpHost string // HTTP hostname
httpPort int // HTTP post
@ -95,6 +104,12 @@ func New(conf *Config) (*Node, error) {
if conf.DataDir != "" {
nodeDbPath = filepath.Join(conf.DataDir, datadirNodeDatabase)
}
ipcServices := make([]*ipcService, len(conf.IPCEndpoints()))
for i, endpoint := range conf.IPCEndpoints() {
ipcServices[i] = &ipcService{endpoint: endpoint}
}
return &Node{
datadir: conf.DataDir,
serverConfig: p2p.Config{
@ -113,7 +128,7 @@ func New(conf *Config) (*Node, error) {
MaxPendingPeers: conf.MaxPendingPeers,
},
serviceFuncs: []ServiceConstructor{},
ipcEndpoint: conf.IPCEndpoint(),
ipcServices: ipcServices,
httpHost: conf.HTTPHost,
httpPort: conf.HTTPPort,
httpEndpoint: conf.HTTPEndpoint(),
@ -272,35 +287,38 @@ func (n *Node) stopInProc() {
// startIPC initializes and starts the IPC RPC endpoint.
func (n *Node) startIPC(apis []rpc.API) error {
// Short circuit if the IPC endpoint isn't being exposed
if n.ipcEndpoint == "" {
return nil
}
for i, _ := range n.ipcServices {
service := n.ipcServices[i]
// Register all the APIs exposed by the services
handler := rpc.NewServer()
for _, api := range apis {
if err := handler.RegisterName(api.Namespace, api.Service); err != nil {
return err
}
glog.V(logger.Debug).Infof("IPC registered %T under '%s'", api.Service, api.Namespace)
glog.V(logger.Debug).Infof("IPC registered %T under '%s' on '%s'", api.Service, api.Namespace, service.endpoint)
}
// All APIs registered, start the IPC listener
var (
listener net.Listener
err error
)
if listener, err = rpc.CreateIPCListener(n.ipcEndpoint); err != nil {
if listener, err = rpc.CreateIPCListener(service.endpoint); err != nil {
return err
}
go func() {
glog.V(logger.Info).Infof("IPC endpoint opened: %s", n.ipcEndpoint)
glog.V(logger.Info).Infof("IPC endpoint opened: %s", service.endpoint)
for {
conn, err := listener.Accept()
if err != nil {
// Terminate if the listener was closed
n.lock.RLock()
closed := n.ipcListener == nil
closed := service.listener == nil
n.lock.RUnlock()
if closed {
return
@ -312,24 +330,22 @@ func (n *Node) startIPC(apis []rpc.API) error {
go handler.ServeCodec(rpc.NewJSONCodec(conn), rpc.OptionMethodInvocation|rpc.OptionSubscriptions)
}
}()
// All listeners booted successfully
n.ipcListener = listener
n.ipcHandler = handler
service.listener = listener
service.handler = handler
}
return nil
}
// stopIPC terminates the IPC RPC endpoint.
func (n *Node) stopIPC() {
if n.ipcListener != nil {
n.ipcListener.Close()
n.ipcListener = nil
glog.V(logger.Info).Infof("IPC endpoint closed: %s", n.ipcEndpoint)
}
if n.ipcHandler != nil {
n.ipcHandler.Stop()
n.ipcHandler = nil
for _, service := range n.ipcServices {
service.listener.Close()
service.listener = nil
service.handler.Stop()
service.handler = nil
glog.V(logger.Info).Infof("IPC endpoint closed: %s", service.endpoint)
}
}
@ -550,9 +566,12 @@ func (n *Node) DataDir() string {
return n.datadir
}
// IPCEndpoint retrieves the current IPC endpoint used by the protocol stack.
func (n *Node) IPCEndpoint() string {
return n.ipcEndpoint
// IPCEndpoint retrieves the current IPC endpoints used by the protocol stack.
func (n *Node) IPCEndpoint() (endpoints []string) {
for _, service := range n.ipcServices {
endpoints = append(endpoints, service.endpoint)
}
return
}
// HTTPEndpoint retrieves the current HTTP endpoint used by the protocol stack.