Skip to content

Commit a91a4dd

Browse files
michaelsbradleyjriurimatias
authored andcommitted
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).
1 parent 9c37f97 commit a91a4dd

File tree

8 files changed

+53
-33
lines changed

8 files changed

+53
-33
lines changed

src/lib/core/fs.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,19 @@ function diagramPath() {
148148
return anchoredPath(env.DIAGRAM_PATH, ...arguments);
149149
}
150150

151+
function ipcPath(basename, usePipePathOnWindows = false) {
152+
if (!(basename && typeof basename === 'string')) {
153+
throw new TypeError('first argument must be a non-empty string');
154+
}
155+
if (process.platform === 'win32' && usePipePathOnWindows) {
156+
return `\\\\.\\pipe\\${basename}`;
157+
}
158+
return utils.joinPath(
159+
tmpDir(`embark-${utils.sha512(dappPath()).slice(0, 8)}`),
160+
basename
161+
);
162+
}
163+
151164
function pkgPath() {
152165
return anchoredPath(env.PKG_PATH, ...arguments);
153166
}
@@ -200,6 +213,7 @@ module.exports = {
200213
existsSync,
201214
ensureFileSync,
202215
ensureDirSync,
216+
ipcPath,
203217
mkdirp,
204218
mkdirpSync,
205219
move,

src/lib/core/ipc.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
const fs = require('./fs.js');
1+
const fs = require('./fs');
22
const ipc = require('node-ipc');
33
const {parse, stringify} = require('flatted/cjs');
4+
const utils = require('../utils/utils');
45

56
class IPC {
6-
77
constructor(options) {
88
this.logger = options.logger;
9-
this.socketPath = options.socketPath || fs.dappPath(".embark/embark.ipc");
9+
this.socketPath = options.socketPath || fs.ipcPath('embark.ipc');
1010
this.ipcRole = options.ipcRole;
1111
ipc.config.silent = true;
1212
this.connected = false;
@@ -39,7 +39,7 @@ class IPC {
3939
}
4040

4141
serve() {
42-
fs.mkdirpSync(fs.dappPath(".embark"));
42+
fs.mkdirpSync(utils.dirname(this.socketPath));
4343
ipc.serve(this.socketPath, () => {});
4444
ipc.server.start();
4545

src/lib/core/processes/processManager.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
21
const ProcessState = {
32
Unstarted: 'unstarted',
43
Starting: 'starting',

src/lib/modules/blockchain_process/gethClient.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
const async = require('async');
2-
const {spawn, exec} = require('child_process');
2+
const {exec, spawn} = require('child_process');
3+
const fs = require('../../core/fs');
34
const GethMiner = require('./miner');
45
const semver = require('semver');
56
const constants = require('../../constants');
@@ -51,7 +52,7 @@ class GethClient {
5152
needKeepAlive() {
5253
// TODO: check version also (geth version < 1.8.15)
5354
if (this.isDev) {
54-
// Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode.
55+
// Trigger regular txs due to a bug in geth (< 1.8.15) and stuck transactions in --dev mode.
5556
return true;
5657
}
5758
return false;
@@ -79,6 +80,8 @@ class GethClient {
7980
cmd.push("--verbosity=" + config.verbosity);
8081
}
8182

83+
cmd.push(`--ipcpath=${fs.ipcPath('geth.ipc', true)}`);
84+
8285
return cmd;
8386
}
8487

src/lib/modules/blockchain_process/miner.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
const async = require('async');
2+
const fs = require('../../core/fs');
23
const NetcatClient = require('netcat/client');
34

45
//Constants
@@ -43,14 +44,7 @@ class GethMiner {
4344
}
4445
}
4546

46-
const isWin = process.platform === "win32";
47-
48-
let ipcPath;
49-
if (isWin) {
50-
ipcPath = '\\\\.\\pipe\\geth.ipc';
51-
} else {
52-
ipcPath = this.datadir + '/geth.ipc';
53-
}
47+
const ipcPath = fs.ipcPath('geth.ipc', true);
5448

5549
this.client = new NetcatClient();
5650
this.client.unixSocket(ipcPath)

src/lib/utils/host.ts

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,29 @@
1-
const isDocker = (() => {
2-
let isDockerProcess;
3-
4-
const hostname = require("os").hostname();
5-
const pattern = new RegExp(
6-
"[0-9]+\:[a-z_-]+\:\/docker\/" + hostname + "[0-9a-z]+", "i",
7-
);
1+
const {execSync} = require("child_process");
2+
const {hostname} = require("os");
83

4+
const isDocker = (() => {
5+
// assumption: an Embark container is always a Linux Docker container, though
6+
// the Docker host may be Linux, macOS, or Windows
7+
if (process.platform !== "linux") { return false; }
98
try {
10-
isDockerProcess = require("child_process")
11-
.execSync(
12-
"cat /proc/self/cgroup",
13-
{stdio: ["ignore", "pipe", "ignore"]},
14-
)
15-
.toString().match(pattern) !== null;
9+
return (
10+
new RegExp(`[0-9]+\:[a-z_-]+\:\/docker\/${hostname()}[0-9a-z]+`, "i")
11+
).test(
12+
execSync(
13+
"cat /proc/self/cgroup",
14+
{stdio: ["ignore", "pipe", "ignore"]},
15+
).toString(),
16+
);
1617
} catch (e) {
17-
isDockerProcess = false;
18+
return false;
1819
}
19-
20-
return isDockerProcess;
2120
})();
2221

2322
const defaultHost = isDocker ? "0.0.0.0" : "localhost";
2423

25-
// when we"re runing in Docker, we can expect (generally, in a development
24+
// when we're runing in Docker, we can expect (generally, in a development
2625
// scenario) that the user would like to connect to the service in the
27-
// container via the **host"s** loopback address, so this helper can be used to
26+
// container via the **host's** loopback address, so this helper can be used to
2827
// swap 0.0.0.0 for localhost in code/messages that pertain to client-side
2928
function canonicalHost(host: string): string {
3029
return isDocker && host === "0.0.0.0" ? "localhost" : host;

src/lib/utils/utils.js

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,15 @@ function sha3(arg) {
390390
return Web3.utils.sha3(arg);
391391
}
392392

393+
function sha512(arg) {
394+
if (typeof arg !== 'string') {
395+
throw new TypeError('argument must be a string');
396+
}
397+
const crypto = require('crypto');
398+
const hash = crypto.createHash('sha512');
399+
return hash.update(arg).digest('hex');
400+
}
401+
393402
function soliditySha3(arg) {
394403
const Web3 = require('web3');
395404
return Web3.utils.soliditySha3(arg);
@@ -640,6 +649,7 @@ module.exports = {
640649
getExternalContractUrl,
641650
toChecksumAddress,
642651
sha3,
652+
sha512,
643653
soliditySha3,
644654
normalizeInput,
645655
buildUrl,

src/test/fs.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ describe('fs', () => {
3131
'dappPath',
3232
'diagramPath',
3333
'embarkPath',
34+
'ipcPath',
3435
'pkgPath',
3536
'tmpDir'
3637
];

0 commit comments

Comments
 (0)