Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Changed the start-up file to launch daemon process #2309

Merged
merged 4 commits into from
Mar 22, 2024
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
162 changes: 57 additions & 105 deletions packages/gui/src/util/chiaEnvironment.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@ const path = require('path');

const PY_MAC_DIST_FOLDER = '../../../app.asar.unpacked/daemon';
const PY_WIN_DIST_FOLDER = '../../../app.asar.unpacked/daemon';
const PY_DIST_FILE = 'daemon';
const PY_FOLDER = '../../../chia/daemon';
const PY_MODULE = 'server'; // without .py suffix
const PY_DIST_EXECUTABLE = 'chia';
const PY_DIST_EXEC_ARGS = Object.freeze(['start', 'daemon']);

const PY_DEV_EXECUTABLE = `../../../venv/${process.platform === 'win32' ? 'Scripts/chia.exe' : 'bin/chia'}`;
const PY_DEV_EXEC_ARGS = Object.freeze([PY_DEV_EXECUTABLE, 'start', 'daemon']);

let pyProc = null;
let haveCert = null;
Expand All @@ -34,13 +36,6 @@ const getExecutablePath = (dist_file) => {
return path.join(__dirname, PY_MAC_DIST_FOLDER, dist_file);
};

const getScriptPath = (dist_file) => {
if (!guessPackaged()) {
return path.join(PY_FOLDER, `${PY_MODULE}.py`);
}
return getExecutablePath(dist_file);
};

const getChiaVersion = () => {
let version = null;
const exePath = getExecutablePath('chia');
Expand All @@ -67,120 +62,77 @@ const getChiaVersion = () => {
return version;
};

const spawnChildProcess = (command, args = [], options = undefined) => {
// As of Feb 11 2024, there is a bug in Electron that prevents electron from exiting when a child process is spawned and detached.
// This is a workaround for that bug.
if (process.platform === 'linux') {
// https://github.com/electron/electron/issues/34808#issuecomment-1275530924
return childProcess.spawn(
'/bin/bash',
[
'-c',
'for fd in $(ls /proc/$$/fd); do case "$fd" in 0|1|2|255) ;; *) eval "exec $fd<&-" ;; esac; done; exec "$@"',
'--',
command,
...args,
],
options,
);
}
return childProcess.spawn(command, args, options);
};

const startChiaDaemon = () => {
const script = getScriptPath(PY_DIST_FILE);
const processOptions = {};
if (process.platform === 'win32') {
// We want to detach child daemon process from parent GUI process.
// You may think `detached: true` will do but it shows blank terminal on Windows.
// In order to hide the blank terminal while detaching child process,
// {detached: false, windowsHide: false, shell: true} works which is exact opposite of what we expect
// Please see the comment below for more details.
// https://github.com/nodejs/node/issues/21825#issuecomment-503766781
processOptions.detached = false;
processOptions.stdio = 'ignore';
processOptions.windowsHide = false;
processOptions.shell = true;
} else {
processOptions.detached = true;
processOptions.stdio = 'ignore';
processOptions.windowsHide = true;
}
pyProc = null;

if (guessPackaged()) {
const executablePath = getExecutablePath(PY_DIST_EXECUTABLE);
console.info('Running python executable: ');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is "python executable" accurate? afaict, the file ends with .exe on winows, and there's no python prefix to the command line

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in Fixed in #2311


try {
console.info('Running python executable: ');
if (processOptions.stdio === 'ignore') {
const subProcess = spawnChildProcess(script, ['--wait-for-unlock'], processOptions);
subProcess.unref();
} else {
const Process = childProcess.spawn;
pyProc = new Process(script, ['--wait-for-unlock'], processOptions);
}
const Process = childProcess.spawn;
pyProc = new Process(executablePath, PY_DIST_EXEC_ARGS);
} catch (e) {
console.info('Running python executable: Error: ');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

shouldn't you print e here as well?
I suppose you're not changing this though

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in #2311

console.info(`Script ${script}`);
console.info(`Script: ${executablePath} ${PY_DIST_EXEC_ARGS.join(' ')}`);
}
} else {
console.info('Running python script');
console.info(`Script ${script}`);
console.info(`Script: python ${PY_DEV_EXEC_ARGS.join(' ')}`);

if (processOptions.stdio === 'ignore') {
const subProcess = spawnChildProcess('python', [script, '--wait-for-unlock'], processOptions);
subProcess.unref();
} else {
const Process = childProcess.spawn;
pyProc = new Process('python', [script, '--wait-for-unlock'], processOptions);
}
const Process = childProcess.spawn;
pyProc = new Process('python', PY_DEV_EXEC_ARGS);
}
if (pyProc != null && processOptions.stdio !== 'ignore') {
pyProc.stdout.setEncoding('utf8');

pyProc.stdout.on('data', (data) => {
if (!haveCert) {
process.stdout.write('No cert\n');
// listen for ssl path message
try {
const strArr = data.toString().split('\n');
for (let i = 0; i < strArr.length; i++) {
const str = strArr[i];
try {
const json = JSON.parse(str);
global.cert_path = json.cert;
global.key_path = json.key;
// TODO Zlatko: cert_path and key_path were undefined. Prefixed them with global, which changes functionality.
// Do they even need to be globals?
if (global.cert_path && global.key_path) {
haveCert = true;
process.stdout.write('Have cert\n');
return;
}
} catch (e) {
// Do nothing

if (!pyProc) {
throw new Error('Failed to start chia daemon');
}

pyProc.stdout.setEncoding('utf8');

pyProc.stdout.on('data', (data) => {
if (!haveCert) {
process.stdout.write('No cert\n');
// listen for ssl path message
try {
const strArr = data.toString().split('\n');
for (let i = 0; i < strArr.length; i++) {
const str = strArr[i];
try {
const json = JSON.parse(str);
global.cert_path = json.cert;
global.key_path = json.key;
// TODO Zlatko: cert_path and key_path were undefined. Prefixed them with global, which changes functionality.
// Do they even need to be globals?
if (global.cert_path && global.key_path) {
haveCert = true;
process.stdout.write('Have cert\n');
return;
}
} catch (e) {
// Do nothing
}
} catch (e) {
// Do nothing
}
} catch (e) {
// Do nothing
}
}

process.stdout.write(data.toString());
});
process.stdout.write(data.toString());
});

pyProc.stderr.setEncoding('utf8');
pyProc.stderr.on('data', (data) => {
// Here is where the error output goes
process.stdout.write(`stderr: ${data.toString()}`);
});
pyProc.stderr.setEncoding('utf8');
pyProc.stderr.on('data', (data) => {
// Here is where the error output goes
process.stdout.write(`stderr: ${data.toString()}`);
});

pyProc.on('close', (code) => {
// Here you can get the exit code of the script
console.info(`closing code: ${code}`);
});
pyProc.on('close', (code) => {
// Here you can get the exit code of the script
console.info(`closing code: ${code}`);
});

console.info('child process success');
}
// pyProc.unref();
console.info('child process success');
};

module.exports = {
Expand Down
Loading