Skip to content

Commit

Permalink
feature(@embark/core): store IPC files in a dir within os.tmpdir()
Browse files Browse the repository at this point in the history
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 the 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()`. Use a truncated sha512 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).

Remove the `utils/utils.js` dependency from `core/env.js` and implement the
`ipcPath()` helper as a static method on the class exported from `core/ipc.js`.
  • Loading branch information
michaelsbradleyjr committed Dec 20, 2018
1 parent 1a4b0aa commit ba6f578
Show file tree
Hide file tree
Showing 7 changed files with 55 additions and 37 deletions.
11 changes: 5 additions & 6 deletions src/lib/core/env.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
/* global __dirname module process require */

const {execSync} = require('child_process');
const {delimiter} = require('path');
const {joinPath} = require('../utils/utils.js');
const {delimiter, join} = require('path');

function anchoredValue(anchor, value) {
if (!arguments.length) {
Expand Down Expand Up @@ -39,11 +38,11 @@ const DEFAULT_CMD_HISTORY_SIZE = 20;
anchoredValue(CMD_HISTORY_SIZE, DEFAULT_CMD_HISTORY_SIZE);

const DIAGRAM_PATH = 'DIAGRAM_PATH';
const DEFAULT_DIAGRAM_PATH = joinPath(anchoredValue(DAPP_PATH), 'diagram.svg');
const DEFAULT_DIAGRAM_PATH = join(anchoredValue(DAPP_PATH), 'diagram.svg');
anchoredValue(DIAGRAM_PATH, DEFAULT_DIAGRAM_PATH);

const EMBARK_PATH = 'EMBARK_PATH';
const DEFAULT_EMBARK_PATH = joinPath(__dirname, '../../..');
const DEFAULT_EMBARK_PATH = join(__dirname, '../../..');
anchoredValue(EMBARK_PATH, DEFAULT_EMBARK_PATH);

const PKG_PATH = 'PKG_PATH';
Expand All @@ -52,7 +51,7 @@ anchoredValue(PKG_PATH, DEFAULT_PKG_PATH);

let YARN_GLOBAL_PATH;
try {
YARN_GLOBAL_PATH = joinPath(
YARN_GLOBAL_PATH = join(
execSync('yarn global dir', {stdio: ['pipe', 'pipe', 'ignore']})
.toString()
.trim(),
Expand All @@ -65,7 +64,7 @@ try {
const NODE_PATH = 'NODE_PATH';
// NOTE: setting NODE_PATH at runtime won't effect lookup behavior in the
// current process, but will take effect in child processes
process.env[NODE_PATH] = joinPath(anchoredValue(EMBARK_PATH), 'node_modules') +
process.env[NODE_PATH] = join(anchoredValue(EMBARK_PATH), 'node_modules') +
(YARN_GLOBAL_PATH ? delimiter : '') +
(YARN_GLOBAL_PATH || '') +
(process.env[NODE_PATH] ? delimiter : '') +
Expand Down
20 changes: 17 additions & 3 deletions src/lib/core/ipc.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
const fs = require('./fs.js');
const ipc = require('node-ipc');
const os = require('os');
const {parse, stringify} = require('flatted/cjs');
const utils = require('../utils/utils.js');

class IPC {

constructor(options) {
this.logger = options.logger;
this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc");
this.socketPath = options.socketPath || IPC.ipcPath('embark.ipc');
this.ipcRole = options.ipcRole;
ipc.config.silent = true;
this.connected = false;
}

static 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}`;
}
const ipcDir = utils.joinPath(
os.tmpdir(), `embark-${utils.sha512(fs.dappPath()).slice(0, 8)}`
);
return utils.joinPath(ipcDir, basename);
}

connect(done) {
const self = this;
function connecting(_socket) {
Expand Down Expand Up @@ -39,7 +53,7 @@ class IPC {
}

serve() {
fs.mkdirpSync(fs.dappPath(".embark"));
fs.mkdirpSync(utils.dirname(this.socketPath));
ipc.serve(this.socketPath, () => {});
ipc.server.start();

Expand Down
1 change: 0 additions & 1 deletion src/lib/core/processes/processManager.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@

const ProcessState = {
Unstarted: 'unstarted',
Starting: 'starting',
Expand Down
5 changes: 4 additions & 1 deletion src/lib/modules/blockchain_process/gethClient.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
const async = require('async');
const GethMiner = require('./miner');
const IPC = require('../../core/ipc');
const semver = require('semver');
const constants = require('../../constants');

Expand Down Expand Up @@ -50,7 +51,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;
Expand Down Expand Up @@ -78,6 +79,8 @@ class GethClient {
cmd.push("--verbosity=" + config.verbosity);
}

cmd.push(`--ipcpath=${IPC.ipcPath('geth.ipc', true)}`);

return cmd;
}

Expand Down
10 changes: 2 additions & 8 deletions src/lib/modules/blockchain_process/miner.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
const async = require('async');
const IPC = require('../../core/ipc');
const NetcatClient = require('netcat/client');

//Constants
Expand Down Expand Up @@ -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 = IPC.ipcPath('geth.ipc', true);

this.client = new NetcatClient();
this.client.unixSocket(ipcPath)
Expand Down
35 changes: 17 additions & 18 deletions src/lib/utils/host.ts
Original file line number Diff line number Diff line change
@@ -1,30 +1,29 @@
const isDocker = (() => {
let isDockerProcess;

const hostname = require("os").hostname();
const pattern = new RegExp(
"[0-9]+\:[a-z_-]+\:\/docker\/" + hostname + "[0-9a-z]+", "i",
);
const {execSync} = require("child_process");
const {hostname} = require("os");

const isDocker = (() => {
// 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;
Expand Down
10 changes: 10 additions & 0 deletions src/lib/utils/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -640,6 +649,7 @@ module.exports = {
getExternalContractUrl,
toChecksumAddress,
sha3,
sha512,
soliditySha3,
normalizeInput,
buildUrl,
Expand Down

0 comments on commit ba6f578

Please sign in to comment.