Skip to content

Commit 5458a65

Browse files
authored
Merge pull request #6 from pmamatsis/master
Attempt suspend/resume/stop of the BOINC client.
2 parents 769f7b2 + 0d39c15 commit 5458a65

File tree

4 files changed

+93
-24
lines changed

4 files changed

+93
-24
lines changed

package-lock.json

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@
7474
"fs-extra": "^5.0.0",
7575
"jsonfile": "^5.0.0",
7676
"node-machine-id": "^1.1.12",
77+
"ps-list": "^6.3.0",
7778
"sudo-prompt": "^8.2.5",
7879
"xml-to-json-promise": "0.0.3",
7980
"xml2js": "^0.4.19"

src/boinc.js

Lines changed: 71 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ const cfg = require('electron-settings')
1616
const ipc = require('./ipcWrapper')()
1717
var sudo = require('sudo-prompt')
1818
const log = require('electron-log')
19+
const psList = require('ps-list') //<--- Include the nodeJS module for checking if a process exists.
20+
21+
const BOINCPROJECTNAME="http://www.worldcommunitygrid.org/" //<--- That is the name of the project we are participating. We must use this as a reference for the start/suspend/stop tasks.
22+
const BOINCSUSPENDCMD="project " + BOINCPROJECTNAME + " suspend" //<--- That is the BOINCCMD command to suspend temporarily the project.
23+
const BOINCRESUMECMD="project " + BOINCPROJECTNAME + " resume" //<--- That is the BOINCCMD command to resume the project.
1924

2025
var HOMEPATH = path.join(app.getPath('home'), '.Boid')
2126
// if (isDev) var HOMEPATH = path.join(app.getPath('home'), '.BoidDev')
@@ -25,6 +30,7 @@ if (isDev) var RESOURCEDIR = path.join(__dirname, '../')
2530
else var RESOURCEDIR = path.join(__dirname, '../../')
2631

2732
async function sleep(){return new Promise(resolve => setTimeout(resolve,3000))}
33+
2834
function ec(error){
2935
log.error(error)
3036
if (ipc.ipc) boinc.send('error',{date:Date.now(),error})
@@ -48,19 +54,19 @@ async function setupIPC(funcName) {
4854
} catch (error) {
4955
log.error(error)
5056
}
51-
5257
}
5358

54-
5559
var boinc = {
56-
eventsRegistered:false,
57-
initializing:false,
58-
shouldBeRunning:false
60+
eventsRegistered: false,
61+
initializing: false,
62+
shouldBeRunning: false,
63+
thisPlatform: thisPlatform
5964
}
65+
6066
boinc.killExisting = async () => {
6167
try {
6268
await boinc.stop()
63-
if ( boinc.process) {
69+
if (boinc.process) {
6470
process.kill(-boinc.process.pid)
6571
boinc.process.kill()
6672
}
@@ -73,7 +79,9 @@ boinc.killExisting = async () => {
7379
}
7480

7581
boinc.send = (channel,data,data2) => ipc.send(channel,data,data2)
82+
7683
boinc.on = (channel,data) => ipc.on(channel,data)
84+
7785
boinc.init = async (event) => {
7886
try {
7987
ipc.init(event.sender,'boinc')
@@ -105,11 +113,47 @@ boinc.reset = async () => {
105113
app.relaunch()
106114
app.exit()
107115
}
116+
108117
boinc.openDirectory = async () => {
109118
shell.openItem(BOINCPATH)
110119
}
111120

121+
/*
122+
* With this method we detect if BOINC client is running.
123+
*/
124+
boinc.detectIfRunning = async () => {
125+
var boincProcessFound=false
126+
await psList().then(processData => {
127+
for(var i=0, len=processData.length; i<len; i++){
128+
if(processData[i].name==='boinc.exe'){
129+
boincProcessFound=!boincProcessFound
130+
break
131+
}
132+
}
133+
});
134+
135+
return boincProcessFound
136+
}
137+
138+
boinc.spawnProcess = async () =>{
139+
//if (thisPlatform === 'win32') exe = 'boinc.exe'
140+
//else exe = './boinc'
141+
142+
var exe = (thisPlatform === 'win32' ? 'boinc.exe' : './boinc') //<--- Use the more elegant ternary expression.
143+
var params = ['-dir', BOINCPATH, '-no_gpus', '-allow_remote_gui_rpc','-suppress_net_info']
144+
if (thisPlatform === 'win32') params.push('-allow_multiple_clients')
145+
146+
boinc.process = spawn(exe, params, {
147+
silent: false,
148+
cwd: BOINCPATH,
149+
shell: false,
150+
detached: true,
151+
env: null,
152+
})
153+
}
154+
112155
boinc.start = async (data) => {
156+
/* Check for a valid installation of BOINC platform. If there is none we are trying to install it. Recall ourselves after installing. */
113157
try {
114158
const checkInstall = await boinc.checkInstalled()
115159
if(!checkInstall) {
@@ -121,28 +165,30 @@ boinc.start = async (data) => {
121165
ec(error)
122166
boinc.stop()
123167
}
124-
await boinc.killExisting()
168+
169+
/* That is the actual 'start' process part of the source code. */
170+
//await boinc.killExisting() //<--- We won't be killing the existing process if it's running. We are just going to be checking if running and then start it.
171+
//<--- Find of a way to check if the BOINC client is running...if not we must start it 'safely'...
172+
173+
const checkIfRunning = await boinc.detectIfRunning()
174+
125175
boinc.initializing = false
126176
boinc.shouldBeRunning = true
127177
boinc.send('status', 'Starting...')
128178
try {
129179
cfg.set('state.cpu.toggle', true)
130-
if(boinc.process && boinc.process.killed === false) return boinc.process.kill()
131-
var exe
132-
if (thisPlatform === 'win32') exe = 'boinc.exe'
133-
else exe = './boinc'
134-
var params = ['-dir', BOINCPATH, '-no_gpus', '-allow_remote_gui_rpc','-suppress_net_info']
135-
if (thisPlatform === 'win32') params.push('-allow_multiple_clients')
136-
boinc.process = spawn(exe, params, {
137-
silent: false,
138-
cwd: BOINCPATH,
139-
shell: false,
140-
detached: true,
141-
env: null,
142-
})
180+
//if(boinc.process && boinc.process.killed === false) return boinc.process.kill() //<--- We don't need to kill the BOINC process anymore. If running we will be autostarting.
181+
182+
if(!checkIfRunning) //<--- If BOINC client is not running then we need to start it manually.
183+
await boinc.spawnProcess()
184+
143185
setTimeout(()=>boinc.send('started'),1000)
186+
187+
await boinc.cmd(BOINCRESUMECMD)
188+
144189
boinc.process.stdout.on('data', data => ipc.send('log', data.toString()))
145190
boinc.process.stderr.on('data', data => ipc.send('error', data.toString()))
191+
146192
boinc.process.on('exit', (code, signal) => {
147193
log.info('detected close code:', code, signal)
148194
log.info('should be running', boinc.shouldBeRunning)
@@ -162,15 +208,18 @@ boinc.start = async (data) => {
162208
}
163209
})
164210
}catch(error){if(ec)ec(error)}
211+
}
165212

166-
}
167213
boinc.stop = async (data) => {
168214
cfg.set('state.cpu.toggle', false)
169215
if(!boinc.process) return boinc.send('toggle', false)
170216
// boinc.process.kill()
171-
await boinc.cmd('quit')
217+
//await boinc.cmd('quit') //<--- We cannot kill the BOINC client. This line have been substituted by the one that is following.
218+
await boinc.cmd(BOINCSUSPENDCMD) //<--- The project gets suspended only.
172219
await sleep(5000)
173220
boinc.shouldBeRunning = false
221+
boinc.send('status', 'Stopped') //<--- We must update the client with the new status for 'Stopped'.
222+
boinc.send('Stopped')
174223
return sleep(5000)
175224
}
176225

src/registerGlobalListeners.js

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ const isDev = require('electron-is-dev')
77
const log = require('electron-log')
88
const path = require('path')
99
var isQuiting
10+
const { exec } = require('child_process')
1011

1112
function init(appWindow) {
1213
ipcMain.on('gpu.init',gpu.init)
@@ -15,8 +16,8 @@ function init(appWindow) {
1516
app.on('before-quit', async ()=>{
1617
isQuiting = true
1718
if (appWindow) appWindow.close()
18-
if(boinc.process) await boinc.killExisting() //<--- We must check if the 'process' is active prior killing it. If not, we get errors in console.
1919
})
20+
2021
app.on('activate', () => appWindow.show)
2122
appWindow.on('ready-to-show', () => {
2223
if (platform === 'darwin') {
@@ -27,10 +28,16 @@ function init(appWindow) {
2728
appWindow.center()
2829
if (!cfg.get('config.startMinimized')) appWindow.show()
2930
})
31+
3032
setupGlobalIPC()
3133
// appWindow.on('minimize',e=>{e.preventDefault(),appWindow.hide()})
34+
3235
appWindow.on('close', e => {
33-
if (isQuiting) return appWindow = null
36+
if (isQuiting){
37+
boinc.shouldBeRunning=false //<--- Inform the BOID Desktop Application that we the BOINC client module should not be running. (Avoid the auto-restart of the process by the application)
38+
destroyBOINCClient() //<--- We must kill the BOINC process before we quit the BOID Desktop Application.
39+
return appWindow = null //<--- The original 'return' value of this method.
40+
}
3441
e.preventDefault()
3542
appWindow.hide()
3643
if ( platform === 'darwin' ) app.dock.hide()
@@ -39,6 +46,13 @@ function init(appWindow) {
3946
else appWindow.webContents.executeJavaScript("webview.loadURL('https://app.boid.com/desktop2')")
4047
}
4148

49+
function destroyBOINCClient(){
50+
if (boinc.thisPlatform === 'win32')
51+
exec('Taskkill /IM boinc.exe /F')
52+
else
53+
exec('pkill -9 boinc')
54+
}
55+
4256
function setupGlobalIPC(){
4357
ipcMain.on('openURL', (event, url) => {
4458
return shell.openExternal(url)

0 commit comments

Comments
 (0)