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