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 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).
  • Loading branch information
michaelsbradleyjr committed Dec 21, 2018
1 parent 9c37f97 commit d1c615a
Show file tree
Hide file tree
Showing 8 changed files with 53 additions and 33 deletions.
14 changes: 14 additions & 0 deletions src/lib/core/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -200,6 +213,7 @@ module.exports = {
existsSync,
ensureFileSync,
ensureDirSync,
ipcPath,
mkdirp,
mkdirpSync,
move,
Expand Down
8 changes: 4 additions & 4 deletions src/lib/core/ipc.js
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -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();

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
7 changes: 5 additions & 2 deletions 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 {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');
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -79,6 +80,8 @@ class GethClient {
cmd.push("--verbosity=" + config.verbosity);
}

cmd.push(`--ipcpath=${fs.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 fs = require('../../core/fs');
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 = fs.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
1 change: 1 addition & 0 deletions src/test/fs.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ describe('fs', () => {
'dappPath',
'diagramPath',
'embarkPath',
'ipcPath',
'pkgPath',
'tmpDir'
];
Expand Down

0 comments on commit d1c615a

Please sign in to comment.