From a91a4dd7c01ab57bd113986ab18f870c71669cc6 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Thu, 20 Dec 2018 21:27:59 -0600 Subject: [PATCH] feat(@embark/core): store IPC files in a dir within os.tmpdir() This PR replaces #1202. When embark is running on Linux and macOS, unix socket files are used for geth's and embark's IPC files. A problem can arise if a DApp is in a deeply nested directory structure such that character-length of the path to a socket file exceeds the maximum supported length. See #450. Also, if the DApp context is a Linux container running on a Windows Docker host, and if the DApp is mounted from the host's file system, there is a problem: unix socket files are incompatible with the Windows file system. Solve both problems at once by storing a DApp's `.ipc` files in a directory within `os.tmpdir()`. Introduce `ipcPath()` in `core/fs.js` and use a truncated SHA-512 hash of the DApp's path in the name of the temporary directory created for that purpose so that DApps won't collide (with an acceptably low probability of collision). --- src/lib/core/fs.js | 14 ++++++++ src/lib/core/ipc.js | 8 ++--- src/lib/core/processes/processManager.js | 1 - .../modules/blockchain_process/gethClient.js | 7 ++-- src/lib/modules/blockchain_process/miner.js | 10 ++---- src/lib/utils/host.ts | 35 +++++++++---------- src/lib/utils/utils.js | 10 ++++++ src/test/fs.js | 1 + 8 files changed, 53 insertions(+), 33 deletions(-) diff --git a/src/lib/core/fs.js b/src/lib/core/fs.js index d17161f7c..e23bf319c 100644 --- a/src/lib/core/fs.js +++ b/src/lib/core/fs.js @@ -148,6 +148,19 @@ function diagramPath() { return anchoredPath(env.DIAGRAM_PATH, ...arguments); } +function ipcPath(basename, usePipePathOnWindows = false) { + if (!(basename && typeof basename === 'string')) { + throw new TypeError('first argument must be a non-empty string'); + } + if (process.platform === 'win32' && usePipePathOnWindows) { + return `\\\\.\\pipe\\${basename}`; + } + return utils.joinPath( + tmpDir(`embark-${utils.sha512(dappPath()).slice(0, 8)}`), + basename + ); +} + function pkgPath() { return anchoredPath(env.PKG_PATH, ...arguments); } @@ -200,6 +213,7 @@ module.exports = { existsSync, ensureFileSync, ensureDirSync, + ipcPath, mkdirp, mkdirpSync, move, diff --git a/src/lib/core/ipc.js b/src/lib/core/ipc.js index 8b073935a..6e634d393 100644 --- a/src/lib/core/ipc.js +++ b/src/lib/core/ipc.js @@ -1,12 +1,12 @@ -const fs = require('./fs.js'); +const fs = require('./fs'); const ipc = require('node-ipc'); const {parse, stringify} = require('flatted/cjs'); +const utils = require('../utils/utils'); class IPC { - constructor(options) { this.logger = options.logger; - this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc"); + this.socketPath = options.socketPath || fs.ipcPath('embark.ipc'); this.ipcRole = options.ipcRole; ipc.config.silent = true; this.connected = false; @@ -39,7 +39,7 @@ class IPC { } serve() { - fs.mkdirpSync(fs.dappPath(".embark")); + fs.mkdirpSync(utils.dirname(this.socketPath)); ipc.serve(this.socketPath, () => {}); ipc.server.start(); diff --git a/src/lib/core/processes/processManager.js b/src/lib/core/processes/processManager.js index 4922fa716..4f314598d 100644 --- a/src/lib/core/processes/processManager.js +++ b/src/lib/core/processes/processManager.js @@ -1,4 +1,3 @@ - const ProcessState = { Unstarted: 'unstarted', Starting: 'starting', diff --git a/src/lib/modules/blockchain_process/gethClient.js b/src/lib/modules/blockchain_process/gethClient.js index ccf11fca2..a0bb73e8a 100644 --- a/src/lib/modules/blockchain_process/gethClient.js +++ b/src/lib/modules/blockchain_process/gethClient.js @@ -1,5 +1,6 @@ const async = require('async'); -const {spawn, exec} = require('child_process'); +const {exec, spawn} = require('child_process'); +const fs = require('../../core/fs'); const GethMiner = require('./miner'); const semver = require('semver'); const constants = require('../../constants'); @@ -51,7 +52,7 @@ class GethClient { needKeepAlive() { // TODO: check version also (geth version < 1.8.15) if (this.isDev) { - // Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode. + // Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode. return true; } return false; @@ -79,6 +80,8 @@ class GethClient { cmd.push("--verbosity=" + config.verbosity); } + cmd.push(`--ipcpath=${fs.ipcPath('geth.ipc', true)}`); + return cmd; } diff --git a/src/lib/modules/blockchain_process/miner.js b/src/lib/modules/blockchain_process/miner.js index 79f132b83..bcd7de7c4 100644 --- a/src/lib/modules/blockchain_process/miner.js +++ b/src/lib/modules/blockchain_process/miner.js @@ -1,4 +1,5 @@ const async = require('async'); +const fs = require('../../core/fs'); const NetcatClient = require('netcat/client'); //Constants @@ -43,14 +44,7 @@ class GethMiner { } } - const isWin = process.platform === "win32"; - - let ipcPath; - if (isWin) { - ipcPath = '\\\\.\\pipe\\geth.ipc'; - } else { - ipcPath = this.datadir + '/geth.ipc'; - } + const ipcPath = fs.ipcPath('geth.ipc', true); this.client = new NetcatClient(); this.client.unixSocket(ipcPath) diff --git a/src/lib/utils/host.ts b/src/lib/utils/host.ts index 3e1a1c379..c9b96a23a 100644 --- a/src/lib/utils/host.ts +++ b/src/lib/utils/host.ts @@ -1,30 +1,29 @@ +const {execSync} = require("child_process"); +const {hostname} = require("os"); + const isDocker = (() => { - let isDockerProcess; - - const hostname = require("os").hostname(); - const pattern = new RegExp( - "[0-9]+\:[a-z_-]+\:\/docker\/" + hostname + "[0-9a-z]+", "i", - ); - + // assumption: an Embark container is always a Linux Docker container, though + // the Docker host may be Linux, macOS, or Windows + if (process.platform !== "linux") { return false; } try { - isDockerProcess = require("child_process") - .execSync( - "cat /proc/self/cgroup", - {stdio: ["ignore", "pipe", "ignore"]}, - ) - .toString().match(pattern) !== null; + return ( + new RegExp(`[0-9]+\:[a-z_-]+\:\/docker\/${hostname()}[0-9a-z]+`, "i") + ).test( + execSync( + "cat /proc/self/cgroup", + {stdio: ["ignore", "pipe", "ignore"]}, + ).toString(), + ); } catch (e) { - isDockerProcess = false; + return false; } - - return isDockerProcess; })(); const defaultHost = isDocker ? "0.0.0.0" : "localhost"; -// when we"re runing in Docker, we can expect (generally, in a development +// when we're runing in Docker, we can expect (generally, in a development // scenario) that the user would like to connect to the service in the -// container via the **host"s** loopback address, so this helper can be used to +// container via the **host's** loopback address, so this helper can be used to // swap 0.0.0.0 for localhost in code/messages that pertain to client-side function canonicalHost(host: string): string { return isDocker && host === "0.0.0.0" ? "localhost" : host; diff --git a/src/lib/utils/utils.js b/src/lib/utils/utils.js index c747377b2..7d69aa76e 100644 --- a/src/lib/utils/utils.js +++ b/src/lib/utils/utils.js @@ -390,6 +390,15 @@ function sha3(arg) { return Web3.utils.sha3(arg); } +function sha512(arg) { + if (typeof arg !== 'string') { + throw new TypeError('argument must be a string'); + } + const crypto = require('crypto'); + const hash = crypto.createHash('sha512'); + return hash.update(arg).digest('hex'); +} + function soliditySha3(arg) { const Web3 = require('web3'); return Web3.utils.soliditySha3(arg); @@ -640,6 +649,7 @@ module.exports = { getExternalContractUrl, toChecksumAddress, sha3, + sha512, soliditySha3, normalizeInput, buildUrl, diff --git a/src/test/fs.js b/src/test/fs.js index a68966333..48f50d4eb 100644 --- a/src/test/fs.js +++ b/src/test/fs.js @@ -31,6 +31,7 @@ describe('fs', () => { 'dappPath', 'diagramPath', 'embarkPath', + 'ipcPath', 'pkgPath', 'tmpDir' ];