Fix spawn() failure on Windows when paths contain spaces
This commit is contained in:
@@ -3,6 +3,7 @@
|
|||||||
* CentOS 8 support
|
* CentOS 8 support
|
||||||
* Support remote send and receive timeouts
|
* Support remote send and receive timeouts
|
||||||
* Support WinFSP mount manager
|
* Support WinFSP mount manager
|
||||||
|
* Fix `spawn()` failure on Windows when paths contain spaces
|
||||||
|
|
||||||
## 1.1.2
|
## 1.1.2
|
||||||
* Style changes
|
* Style changes
|
||||||
|
|||||||
11
package.json
11
package.json
@@ -5,14 +5,15 @@
|
|||||||
"author": "scott.e.graves@protonmail.com",
|
"author": "scott.e.graves@protonmail.com",
|
||||||
"description": "GUI for Repertory - Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",
|
"description": "GUI for Repertory - Repertory allows you to mount Sia and/or ScPrime blockchain storage solutions via FUSE on Linux/OS X or via WinFSP on Windows.",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@fortawesome/fontawesome-svg-core": "^1.2.26",
|
"@fortawesome/fontawesome-svg-core": "^1.2.27",
|
||||||
"@fortawesome/free-solid-svg-icons": "^5.12.0",
|
"@fortawesome/free-solid-svg-icons": "^5.12.1",
|
||||||
"@fortawesome/react-fontawesome": "^0.1.8",
|
"@fortawesome/react-fontawesome": "^0.1.8",
|
||||||
"@reduxjs/toolkit": "^1.2.3",
|
"@reduxjs/toolkit": "^1.2.4",
|
||||||
"auto-launch": "^5.0.5",
|
"auto-launch": "^5.0.5",
|
||||||
"axios": "^0.19.2",
|
"axios": "^0.19.2",
|
||||||
"devtron": "^1.4.0",
|
"devtron": "^1.4.0",
|
||||||
"electron-debug": "^3.0.1",
|
"electron-debug": "^3.0.1",
|
||||||
|
"electron-log": "^4.0.6",
|
||||||
"font-awesome": "^4.7.0",
|
"font-awesome": "^4.7.0",
|
||||||
"node-schedule": "^1.3.2",
|
"node-schedule": "^1.3.2",
|
||||||
"randomstring": "^1.1.5",
|
"randomstring": "^1.1.5",
|
||||||
@@ -21,10 +22,10 @@
|
|||||||
"react-loader-spinner": "^3.1.5",
|
"react-loader-spinner": "^3.1.5",
|
||||||
"react-redux": "^7.1.3",
|
"react-redux": "^7.1.3",
|
||||||
"react-scripts": "3.3.1",
|
"react-scripts": "3.3.1",
|
||||||
"react-tooltip": "^3.11.4",
|
"react-tooltip": "^4.0.3",
|
||||||
"redux": "^4.0.5",
|
"redux": "^4.0.5",
|
||||||
"redux-thunk": "^2.3.0",
|
"redux-thunk": "^2.3.0",
|
||||||
"unzipper": "^0.10.7",
|
"unzipper": "^0.10.8",
|
||||||
"winreg": "^1.2.4"
|
"winreg": "^1.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
@@ -8,12 +8,15 @@ const spawn = require('child_process').spawn;
|
|||||||
const Constants = require('./constants');
|
const Constants = require('./constants');
|
||||||
const RandomString = require('randomstring');
|
const RandomString = require('randomstring');
|
||||||
|
|
||||||
const _executeProcess = (command, args=[]) => {
|
const _executeProcess = (command, working, args=[]) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const processOptions = {
|
let processOptions = {
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
};
|
};
|
||||||
|
if (working) {
|
||||||
|
processOptions.cwd = working;
|
||||||
|
}
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(command, args, processOptions);
|
||||||
const pid = process.pid;
|
const pid = process.pid;
|
||||||
@@ -30,12 +33,16 @@ const _executeProcess = (command, args=[]) => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const _execProcessGetOutput = (cmd, args) => {
|
const _execProcessGetOutput = (cmd, working, args) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const proc = spawn(cmd, args, {
|
let processOptions = {
|
||||||
env: process.env,
|
env: process.env,
|
||||||
stdio: ['ignore', 'pipe', 'pipe']
|
stdio: ['ignore', 'pipe', 'pipe']
|
||||||
});
|
};
|
||||||
|
if (working) {
|
||||||
|
processOptions.cwd = working;
|
||||||
|
}
|
||||||
|
const proc = spawn(cmd, args, processOptions);
|
||||||
|
|
||||||
let output;
|
let output;
|
||||||
proc.stdout.on('data', data => {
|
proc.stdout.on('data', data => {
|
||||||
@@ -74,7 +81,10 @@ const _getDefaultRepertoryArgs = (provider, remote) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const _getRepertoryExec = version => {
|
const _getRepertoryExec = version => {
|
||||||
return path.join(_getDataDirectory(), version, (os.platform() === 'win32') ? 'repertory.exe' : 'repertory');
|
return {
|
||||||
|
cmd: (os.platform() === 'win32') ? 'repertory.exe' : 'repertory',
|
||||||
|
working: path.join(_getDataDirectory(), version),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
const _resolvePath = str => {
|
const _resolvePath = str => {
|
||||||
@@ -97,17 +107,17 @@ const _tryParse = (j, def) => {
|
|||||||
|
|
||||||
module.exports.checkDaemonVersion = (version, provider) => {
|
module.exports.checkDaemonVersion = (version, provider) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, false);
|
const args = _getDefaultRepertoryArgs(provider, false);
|
||||||
args.push('-cv');
|
args.push('-cv');
|
||||||
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
const process = new spawn(command, args, processOptions);
|
|
||||||
|
|
||||||
process.on('error', err => {
|
process.on('error', err => {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -159,17 +169,18 @@ module.exports.detectRepertoryMounts = (version, providerList) => {
|
|||||||
resolve(mountState);
|
resolve(mountState);
|
||||||
} else {
|
} else {
|
||||||
const provider = providerList[index];
|
const provider = providerList[index];
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, !Constants.PROVIDER_LIST.includes(provider));
|
const args = _getDefaultRepertoryArgs(provider, !Constants.PROVIDER_LIST.includes(provider));
|
||||||
args.push('-status');
|
args.push('-status');
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
process.on('error', (err) => {
|
process.on('error', (err) => {
|
||||||
@@ -277,10 +288,15 @@ module.exports.executeAndWait = (command, ignoreResult) => {
|
|||||||
module.exports.executeAsync = (command, args=[]) => {
|
module.exports.executeAsync = (command, args=[]) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const launchProcess = (count, timeout) => {
|
const launchProcess = (count, timeout) => {
|
||||||
const processOptions = {
|
const working = path.dirname(command);
|
||||||
|
command = path.basename(command);
|
||||||
|
let processOptions = {
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
};
|
};
|
||||||
|
if (working) {
|
||||||
|
processOptions.cwd = working;
|
||||||
|
}
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(command, args, processOptions);
|
||||||
const pid = process.pid;
|
const pid = process.pid;
|
||||||
@@ -345,13 +361,14 @@ module.exports.executeScript = script => {
|
|||||||
|
|
||||||
module.exports.executeMount = (version, provider, remote, location, noConsoleSupported, exitCallback) => {
|
module.exports.executeMount = (version, provider, remote, location, noConsoleSupported, exitCallback) => {
|
||||||
return new Promise((resolve) => {
|
return new Promise((resolve) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: false,
|
detached: false,
|
||||||
shell: os.platform() !== 'darwin',
|
shell: os.platform() !== 'darwin',
|
||||||
stdio: 'ignore',
|
stdio: 'ignore',
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
|
|
||||||
if ((os.platform() === 'linux') || (os.platform() === 'darwin')) {
|
if ((os.platform() === 'linux') || (os.platform() === 'darwin')) {
|
||||||
@@ -366,7 +383,7 @@ module.exports.executeMount = (version, provider, remote, location, noConsoleSup
|
|||||||
}
|
}
|
||||||
args.push(location);
|
args.push(location);
|
||||||
|
|
||||||
let process = new spawn(command, args, processOptions);
|
let process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
const pid = process.pid;
|
const pid = process.pid;
|
||||||
|
|
||||||
const timeout = setTimeout(() => {
|
const timeout = setTimeout(() => {
|
||||||
@@ -387,17 +404,18 @@ module.exports.executeMount = (version, provider, remote, location, noConsoleSup
|
|||||||
|
|
||||||
module.exports.getConfig = (version, provider, remote) => {
|
module.exports.getConfig = (version, provider, remote) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
args.push('-dc');
|
args.push('-dc');
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
process.on('error', (err) => {
|
process.on('error', (err) => {
|
||||||
@@ -430,17 +448,18 @@ module.exports.getConfig = (version, provider, remote) => {
|
|||||||
|
|
||||||
module.exports.getConfigTemplate = (version, provider, remote) => {
|
module.exports.getConfigTemplate = (version, provider, remote) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
args.push('-gt');
|
args.push('-gt');
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
let result = '';
|
let result = '';
|
||||||
|
|
||||||
process.on('error', (err) => {
|
process.on('error', (err) => {
|
||||||
@@ -579,7 +598,7 @@ module.exports.performWindowsUninstall = names => {
|
|||||||
} else {
|
} else {
|
||||||
const cmd = path.join(process.env.windir, 'system32', 'reg.exe');
|
const cmd = path.join(process.env.windir, 'system32', 'reg.exe');
|
||||||
const args = ["QUERY", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"];
|
const args = ["QUERY", "HKLM\\SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall"];
|
||||||
_execProcessGetOutput(cmd, args)
|
_execProcessGetOutput(cmd, null, args)
|
||||||
.then(lines => {
|
.then(lines => {
|
||||||
const parseLine = index => {
|
const parseLine = index => {
|
||||||
if (index < lines.length) {
|
if (index < lines.length) {
|
||||||
@@ -591,13 +610,13 @@ module.exports.performWindowsUninstall = names => {
|
|||||||
args2.push('DisplayName');
|
args2.push('DisplayName');
|
||||||
args2.push('/t');
|
args2.push('/t');
|
||||||
args2.push('REG_SZ');
|
args2.push('REG_SZ');
|
||||||
_execProcessGetOutput(cmd, args2)
|
_execProcessGetOutput(cmd, null, args2)
|
||||||
.then(lines => {
|
.then(lines => {
|
||||||
const value = lines[2].trim().substr(args2[3].length).trim().substr(6).trim();
|
const value = lines[2].trim().substr(args2[3].length).trim().substr(6).trim();
|
||||||
if (names.includes(value)) {
|
if (names.includes(value)) {
|
||||||
const items = line.split('\\');
|
const items = line.split('\\');
|
||||||
const productCode = items[items.length - 1];
|
const productCode = items[items.length - 1];
|
||||||
_executeProcess('msiexec.exe', ['/x', productCode, '/norestart'])
|
_executeProcess('msiexec.exe', null,['/x', productCode, '/norestart'])
|
||||||
.then(code => {
|
.then(code => {
|
||||||
if ((code === 0) || (code === 3010) || (code === 1641)) {
|
if ((code === 0) || (code === 3010) || (code === 1641)) {
|
||||||
resolve(true);
|
resolve(true);
|
||||||
@@ -651,19 +670,20 @@ module.exports.resolvePath = _resolvePath;
|
|||||||
|
|
||||||
module.exports.setConfigValue = (name, value, provider, remote, version) => {
|
module.exports.setConfigValue = (name, value, provider, remote, version) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: false,
|
shell: false,
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
args.push('-set');
|
args.push('-set');
|
||||||
args.push(name);
|
args.push(name);
|
||||||
args.push(value);
|
args.push(value);
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
|
|
||||||
process.on('error', (err) => {
|
process.on('error', (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -679,17 +699,18 @@ module.exports.setConfigValue = (name, value, provider, remote, version) => {
|
|||||||
|
|
||||||
module.exports.stopMountProcess = (version, provider, remote) => {
|
module.exports.stopMountProcess = (version, provider, remote) => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: os.platform() === 'darwin',
|
detached: os.platform() === 'darwin',
|
||||||
shell: os.platform() !== 'darwin',
|
shell: os.platform() !== 'darwin',
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
args.push('-unmount');
|
args.push('-unmount');
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
const pid = process.pid;
|
const pid = process.pid;
|
||||||
process.on('error', (err) => {
|
process.on('error', (err) => {
|
||||||
reject(err);
|
reject(err);
|
||||||
@@ -708,24 +729,25 @@ module.exports.stopMountProcess = (version, provider, remote) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
module.exports.stopMountProcessSync = (version, provider, remote) => {
|
module.exports.stopMountProcessSync = (version, provider, remote) => {
|
||||||
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
const processOptions = {
|
const processOptions = {
|
||||||
|
cwd: repertoryExec.working,
|
||||||
detached: true,
|
detached: true,
|
||||||
shell: os.platform() !== 'darwin',
|
shell: os.platform() !== 'darwin',
|
||||||
windowsHide: true,
|
windowsHide: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const command = _getRepertoryExec(version);
|
|
||||||
const args = _getDefaultRepertoryArgs(provider, remote);
|
const args = _getDefaultRepertoryArgs(provider, remote);
|
||||||
args.push('-unmount');
|
args.push('-unmount');
|
||||||
|
|
||||||
const process = new spawn(command, args, processOptions);
|
const process = new spawn(repertoryExec.cmd, args, processOptions);
|
||||||
process.unref();
|
process.unref();
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports.testRepertoryBinary = version => {
|
module.exports.testRepertoryBinary = version => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const command = _getRepertoryExec(version);
|
const repertoryExec = _getRepertoryExec(version);
|
||||||
_executeProcess(command, ['-dc'])
|
_executeProcess(repertoryExec.cmd, repertoryExec.working, ['-dc'])
|
||||||
.then(code => {
|
.then(code => {
|
||||||
if (code === 0) {
|
if (code === 0) {
|
||||||
resolve();
|
resolve();
|
||||||
@@ -809,4 +831,4 @@ module.exports.verifySignature = (file, signatureFile, publicKeyFile) => {
|
|||||||
reject(Error('Platform not supported: ' + os.platform()))
|
reject(Error('Platform not supported: ' + os.platform()))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user