Skip to content

Commit

Permalink
Improve pid handling (#33)
Browse files Browse the repository at this point in the history
* Using files to store pid and refactor processes termination

* Using files to store pid and refactor processes termination

* Refactor process termination

* Update logging message during IPFS daemon termination

* Refactor process termination and returned getPid async

* Deleting a file before processes are terminated

* Fix review comments

* Add to fs.readFile to catch errors handler

* Cleanup old PIDs on start

* Fix installation

---------

Co-authored-by: Noisekit <[email protected]>
  • Loading branch information
vderunov and noisekit authored Feb 22, 2024
1 parent ba13753 commit ebce230
Show file tree
Hide file tree
Showing 5 changed files with 77 additions and 166 deletions.
83 changes: 39 additions & 44 deletions src/main/follower.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { exec, spawn } from 'child_process';
import https from 'https';
import { createReadStream, createWriteStream, promises as fs } from 'fs';
import { createReadStream, createWriteStream, promises as fs, readFileSync, rmSync } from 'fs';
import { pipeline } from 'stream/promises';
import os from 'os';
import zlib from 'zlib';
Expand All @@ -10,7 +10,6 @@ import type { IpcMainInvokeEvent } from 'electron';
import logger from 'electron-log';
import { SYNTHETIX_IPNS } from '../const';
import { ROOT } from './settings';
import { getPid, getPidsSync } from './pid';
import unzipper from 'unzipper';
import { getPlatformDetails } from './util';

Expand All @@ -20,27 +19,17 @@ const IPFS_FOLLOW_PATH = path.join(HOME, '.ipfs-cluster-follow');

export function followerKill() {
try {
getPidsSync(
process.platform === 'win32'
? 'ipfs-cluster-follow.exe'
: '.synthetix/ipfs-cluster-follow/ipfs-cluster-follow synthetix run'
).forEach((pid) => {
logger.log('Killing ipfs-cluster-follow', pid);
process.kill(pid);
});
const pid = readFileSync(path.join(ROOT, 'ipfs-cluster-follow.pid'), 'utf8');
if (pid) {
process.kill(Number(pid));
rmSync(path.join(ROOT, 'ipfs-cluster-follow.pid'));
logger.log(`follower kill: PID ${pid} killed and PID file removed`);
}
} catch (_e) {
// whatever
}
}

export async function followerPid() {
return await getPid(
process.platform === 'win32'
? 'ipfs-cluster-follow.exe'
: '.synthetix/ipfs-cluster-follow/ipfs-cluster-follow synthetix run'
);
}

export async function followerIsInstalled() {
try {
await fs.access(
Expand All @@ -64,36 +53,42 @@ export async function followerDaemon() {
return;
}

const pid = await getPid(
process.platform === 'win32'
? 'ipfs-cluster-follow.exe'
: '.synthetix/ipfs-cluster-follow/ipfs-cluster-follow synthetix run'
);
const pid = await fs
.readFile(path.join(ROOT, 'ipfs-cluster-follow.pid'), 'utf8')
.catch(() => null);

if (!pid) {
await configureFollower();

try {
// Cleanup locks in case of a previous crash
await Promise.all([
fs.rm(path.join(IPFS_FOLLOW_PATH, 'synthetix/badger'), {
recursive: true,
force: true,
}),
fs.rm(path.join(IPFS_FOLLOW_PATH, 'synthetix/api-socket'), {
recursive: true,
force: true,
}),
]);
} catch (e) {
logger.error(e);
// whatever
}
if (pid) {
return;
}
await configureFollower();

spawn(path.join(ROOT, 'ipfs-cluster-follow/ipfs-cluster-follow'), ['synthetix', 'run'], {
try {
// Cleanup locks in case of a previous crash
await Promise.all([
fs.rm(path.join(IPFS_FOLLOW_PATH, 'synthetix/badger'), {
recursive: true,
force: true,
}),
fs.rm(path.join(IPFS_FOLLOW_PATH, 'synthetix/api-socket'), {
recursive: true,
force: true,
}),
]);
} catch (e) {
logger.error(e);
// whatever
}

const { pid: followerPid } = spawn(
path.join(ROOT, 'ipfs-cluster-follow/ipfs-cluster-follow'),
['synthetix', 'run'],
{
stdio: 'inherit',
env: { HOME },
});
}
);
if (followerPid) {
await fs.writeFile(path.join(ROOT, 'ipfs-cluster-follow.pid'), followerPid.toString(), 'utf8');
}
}

Expand Down
60 changes: 26 additions & 34 deletions src/main/ipfs.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { exec, spawn } from 'child_process';
import { exec, spawn, execSync } from 'child_process';
import https from 'https';
import { createReadStream, createWriteStream, promises as fs, rmSync } from 'fs';
import { pipeline } from 'stream/promises';
Expand All @@ -8,7 +8,6 @@ import tar from 'tar';
import http from 'http';
import path from 'path';
import type { IpcMainInvokeEvent } from 'electron';
import { getPid, getPidsSync } from './pid';
import { ROOT } from './settings';
import logger from 'electron-log';
import unzipper from 'unzipper';
Expand All @@ -17,6 +16,10 @@ import { getPlatformDetails } from './util';
const HOME = os.homedir();
// Change if we ever want IPFS to store its data in non-standart path
const IPFS_PATH = path.join(HOME, '.ipfs');
const IPFS_CLI = path.join(
ROOT,
process.platform === 'win32' ? 'go-ipfs/ipfs.exe' : 'go-ipfs/ipfs'
);

const BASE_URL = new URL('http://127.0.0.1:5001/api/v0/');
export async function rpcRequest(
Expand All @@ -41,31 +44,20 @@ export async function rpcRequest(
}
}

export function ipfsKill() {
export function ipfsTeardown() {
try {
getPidsSync(
process.platform === 'win32' ? 'ipfs.exe' : '.synthetix/go-ipfs/ipfs daemon'
).forEach((pid) => {
logger.log('Killing ipfs', pid);
process.kill(pid);
});
logger.log('Removing .ipfs/repo.lock');
execSync(`${IPFS_CLI} shutdown`);
rmSync(path.join(ROOT, 'ipfs.pid'), { recursive: true });
rmSync(path.join(IPFS_PATH, 'repo.lock'), { recursive: true });
} catch (_e) {
// whatever
logger.log('IPFS teardown: PID file removed, daemon shutdown, and repo.lock removed');
} catch (e) {
logger.log('IPFS teardown error:', e);
}
}

export async function ipfsPid() {
return await getPid(process.platform === 'win32' ? 'ipfs.exe' : '.synthetix/go-ipfs/ipfs daemon');
}

export async function ipfsIsInstalled() {
try {
await fs.access(
path.join(ROOT, process.platform === 'win32' ? 'go-ipfs/ipfs.exe' : 'go-ipfs/ipfs'),
fs.constants.F_OK
);
await fs.access(IPFS_CLI, fs.constants.F_OK);
return true;
} catch (_e) {
return false;
Expand All @@ -78,23 +70,23 @@ export async function ipfsDaemon() {
return;
}

const pid = await getPid(
process.platform === 'win32' ? 'ipfs.exe' : '.synthetix/go-ipfs/ipfs daemon'
);
const pid = await fs.readFile(path.join(ROOT, 'ipfs.pid'), 'utf8').catch(() => null);

if (!pid) {
await configureIpfs();
spawn(path.join(ROOT, 'go-ipfs/ipfs'), ['daemon'], {
stdio: 'inherit',
env: { IPFS_PATH },
});
if (pid) {
return;
}

await configureIpfs();
const { pid: ipfsPid } = spawn(IPFS_CLI, ['daemon'], { stdio: 'inherit', env: { IPFS_PATH } });
if (ipfsPid) {
await fs.writeFile(path.join(ROOT, 'ipfs.pid'), ipfsPid.toString(), 'utf8');
}
}

export async function ipfs(arg: string): Promise<string> {
return new Promise((resolve, reject) => {
exec(
`${path.join(ROOT, 'go-ipfs/ipfs')} ${arg}`,
`${IPFS_CLI} ${arg}`,
{ encoding: 'utf8', env: { IPFS_PATH } },
(error, stdout, stderr) => {
if (error) {
Expand Down Expand Up @@ -173,14 +165,14 @@ export async function downloadIpfs(_e?: IpcMainInvokeEvent, { log = logger.log }
.on('end', resolve);
});

const installedVersionCheck = await getInstalledVersion();
if (installedVersionCheck) {
log(`ipfs version ${installedVersionCheck} installed successfully.`);
const isInstalled = await ipfsIsInstalled();
if (isInstalled) {
log(`IPFS installed successfully`);
} else {
throw new Error('IPFS installation failed.');
}

return installedVersionCheck;
return isInstalled;
}

export async function configureIpfs({ log = logger.log } = {}) {
Expand Down
14 changes: 10 additions & 4 deletions src/main/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* `./src/main.js` using webpack. This gives us some performance wins.
*/
import path from 'path';
import fs from 'fs';
import { app, BrowserWindow, ipcMain, Menu, shell, Tray } from 'electron';
// import { autoUpdater } from 'electron-updater';
import logger from 'electron-log';
Expand All @@ -17,7 +18,7 @@ import {
ipfsDaemon,
ipfsIsInstalled,
ipfsIsRunning,
ipfsKill,
ipfsTeardown,
waitForIpfs,
rpcRequest,
} from './ipfs';
Expand All @@ -29,19 +30,22 @@ import {
followerId,
followerIsInstalled,
followerKill,
followerPid,
} from './follower';
import { DAPPS, resolveDapp } from './dapps';
import { fetchPeers } from './peers';
import { SYNTHETIX_NODE_APP_CONFIG } from '../const';
import * as settings from './settings';
import http from 'http';
import fetch from 'node-fetch';
import { ROOT } from './settings';

logger.transports.file.level = 'info';

const isDebug = process.env.NODE_ENV === 'development' || process.env.DEBUG_PROD === 'true';

fs.rmSync(path.join(ROOT, 'ipfs.pid'), { force: true });
fs.rmSync(path.join(ROOT, 'ipfs-cluster-follow.pid'), { force: true });

// class AppUpdater {
// constructor() {
// log.transports.file.level = 'info';
Expand Down Expand Up @@ -290,7 +294,9 @@ ipcMain.handle('install-follower', downloadFollower);
ipcMain.handle('ipfs-isInstalled', ipfsIsInstalled);
ipcMain.handle('follower-isInstalled', followerIsInstalled);
ipcMain.handle('ipfs-isRunning', ipfsIsRunning);
ipcMain.handle('follower-isRunning', followerPid);
ipcMain.handle('follower-isRunning', () =>
fs.promises.readFile(path.join(ROOT, 'ipfs-cluster-follow.pid'), 'utf8').catch(() => null)
);

ipcMain.handle('run-ipfs', async () => {
await configureIpfs();
Expand All @@ -308,7 +314,7 @@ ipcMain.handle('ipfs-repo-stat', () => rpcRequest('repo/stat'));
ipcMain.handle('ipfs-stats-bw', () => rpcRequest('stats/bw'));
ipcMain.handle('ipfs-follower-info', () => follower('synthetix info'));

app.on('will-quit', ipfsKill);
app.on('will-quit', ipfsTeardown);
app.on('will-quit', followerKill);

downloadIpfs();
Expand Down
82 changes: 0 additions & 82 deletions src/main/pid.ts

This file was deleted.

4 changes: 2 additions & 2 deletions src/scripts/ipfs-install.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { downloadIpfs, ipfsDaemon, ipfsKill } from '../main/ipfs';
import { downloadIpfs, ipfsDaemon, ipfsTeardown } from '../main/ipfs';

async function main() {
await downloadIpfs();
await ipfsDaemon();
}

process.on('beforeExit', ipfsKill);
process.on('beforeExit', ipfsTeardown);
main();

0 comments on commit ebce230

Please sign in to comment.