api(job): Switch process cleanup to sync
The system used to use async. This would result in execution is handed off to other processes. In the case of a forkbomb was used, it could circumvent the janitor as it consumed more CPU time which would prevent the process janitor from reading the process information in.
This commit is contained in:
parent
d8b430654b
commit
c7efa5372a
|
@ -5,6 +5,7 @@ const path = require('path');
|
||||||
const config = require('./config');
|
const config = require('./config');
|
||||||
const globals = require('./globals');
|
const globals = require('./globals');
|
||||||
const fs = require('fs/promises');
|
const fs = require('fs/promises');
|
||||||
|
const fss = require('fs');
|
||||||
const wait_pid = require('waitpid');
|
const wait_pid = require('waitpid');
|
||||||
|
|
||||||
const job_states = {
|
const job_states = {
|
||||||
|
@ -184,24 +185,24 @@ class Job {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const exit_cleanup = async () => {
|
const exit_cleanup = () => {
|
||||||
clear_timeout(kill_timeout);
|
clear_timeout(kill_timeout);
|
||||||
|
|
||||||
proc.stderr.destroy();
|
proc.stderr.destroy();
|
||||||
proc.stdout.destroy();
|
proc.stdout.destroy();
|
||||||
|
|
||||||
await this.cleanup_processes();
|
this.cleanup_processes();
|
||||||
logger.debug(`Finished exit cleanup uuid=${this.uuid}`);
|
logger.debug(`Finished exit cleanup uuid=${this.uuid}`);
|
||||||
};
|
};
|
||||||
|
|
||||||
proc.on('exit', async (code, signal) => {
|
proc.on('exit', (code, signal) => {
|
||||||
await exit_cleanup();
|
exit_cleanup();
|
||||||
|
|
||||||
resolve({ stdout, stderr, code, signal, output });
|
resolve({ stdout, stderr, code, signal, output });
|
||||||
});
|
});
|
||||||
|
|
||||||
proc.on('error', async err => {
|
proc.on('error', err => {
|
||||||
await exit_cleanup();
|
exit_cleanup();
|
||||||
|
|
||||||
reject({ error: err, stdout, stderr, output });
|
reject({ error: err, stdout, stderr, output });
|
||||||
});
|
});
|
||||||
|
@ -304,38 +305,46 @@ class Job {
|
||||||
this.state = job_states.EXECUTED;
|
this.state = job_states.EXECUTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
async cleanup_processes(dont_wait = []) {
|
cleanup_processes(dont_wait = []) {
|
||||||
let processes = [1];
|
let processes = [1];
|
||||||
|
const to_wait = [];
|
||||||
logger.debug(`Cleaning up processes uuid=${this.uuid}`);
|
logger.debug(`Cleaning up processes uuid=${this.uuid}`);
|
||||||
|
|
||||||
while (processes.length > 0) {
|
while (processes.length > 0) {
|
||||||
processes = [];
|
processes = [];
|
||||||
|
|
||||||
const proc_ids = await fs.readdir('/proc');
|
const proc_ids = fss.readdir_sync('/proc');
|
||||||
|
|
||||||
processes = await Promise.all(
|
processes = proc_ids.map(proc_id => {
|
||||||
proc_ids.map(async proc_id => {
|
if (isNaN(proc_id)) return -1;
|
||||||
if (isNaN(proc_id)) return -1;
|
try {
|
||||||
try {
|
const proc_status = fss.read_file_sync(
|
||||||
const proc_status = await fs.read_file(
|
path.join('/proc', proc_id, 'status')
|
||||||
path.join('/proc', proc_id, 'status')
|
);
|
||||||
);
|
const proc_lines = proc_status.to_string().split('\n');
|
||||||
const proc_lines = proc_status.to_string().split('\n');
|
const state_line = proc_lines.find(line =>
|
||||||
const uid_line = proc_lines.find(line =>
|
line.starts_with('State:')
|
||||||
line.starts_with('Uid:')
|
);
|
||||||
);
|
const uid_line = proc_lines.find(line =>
|
||||||
const [_, ruid, euid, suid, fuid] =
|
line.starts_with('Uid:')
|
||||||
uid_line.split(/\s+/);
|
);
|
||||||
|
const [_, ruid, euid, suid, fuid] = uid_line.split(/\s+/);
|
||||||
|
|
||||||
if (ruid == this.uid || euid == this.uid)
|
const [_1, state, user_friendly] = state_line.split(/\s+/);
|
||||||
return parse_int(proc_id);
|
|
||||||
} catch {
|
if (state == 'Z')
|
||||||
|
// Zombie process, just needs to be waited
|
||||||
return -1;
|
return -1;
|
||||||
}
|
// We should kill in all other state (Sleep, Stopped & Running)
|
||||||
|
|
||||||
|
if (ruid == this.uid || euid == this.uid)
|
||||||
|
return parse_int(proc_id);
|
||||||
|
} catch {
|
||||||
return -1;
|
return -1;
|
||||||
})
|
}
|
||||||
);
|
|
||||||
|
return -1;
|
||||||
|
});
|
||||||
|
|
||||||
processes = processes.filter(p => p > 0);
|
processes = processes.filter(p => p > 0);
|
||||||
|
|
||||||
|
@ -348,8 +357,12 @@ class Job {
|
||||||
// First stop the processes, but keep their resources allocated so they cant re-fork
|
// First stop the processes, but keep their resources allocated so they cant re-fork
|
||||||
try {
|
try {
|
||||||
process.kill(proc, 'SIGSTOP');
|
process.kill(proc, 'SIGSTOP');
|
||||||
} catch {
|
} catch (e) {
|
||||||
// Could already be dead
|
// Could already be dead
|
||||||
|
logger.debug(
|
||||||
|
`Got error while SIGSTOPping process ${proc}:`,
|
||||||
|
e
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,12 +372,26 @@ class Job {
|
||||||
process.kill(proc, 'SIGKILL');
|
process.kill(proc, 'SIGKILL');
|
||||||
} catch {
|
} catch {
|
||||||
// Could already be dead and just needs to be waited on
|
// Could already be dead and just needs to be waited on
|
||||||
|
logger.debug(
|
||||||
|
`Got error while SIGKILLing process ${proc}:`,
|
||||||
|
e
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!dont_wait.includes(proc)) wait_pid(proc);
|
to_wait.push(proc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
logger.debug(
|
||||||
|
`Finished kill-loop, calling wait_pid to end any zombie processes uuid=${this.uuid}`
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const proc of to_wait) {
|
||||||
|
if (dont_wait.includes(proc)) continue;
|
||||||
|
|
||||||
|
wait_pid(proc);
|
||||||
|
}
|
||||||
|
|
||||||
logger.debug(`Cleaned up processes uuid=${this.uuid}`);
|
logger.debug(`Cleaned up processes uuid=${this.uuid}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -397,6 +424,7 @@ class Job {
|
||||||
async cleanup() {
|
async cleanup() {
|
||||||
logger.info(`Cleaning up job uuid=${this.uuid}`);
|
logger.info(`Cleaning up job uuid=${this.uuid}`);
|
||||||
|
|
||||||
|
this.cleanup_processes(); // Run process janitor, just incase there are any residual processes somehow
|
||||||
await this.cleanup_filesystem();
|
await this.cleanup_filesystem();
|
||||||
|
|
||||||
remaining_job_spaces++;
|
remaining_job_spaces++;
|
||||||
|
|
Loading…
Reference in New Issue