158 líneas
4.2 KiB
TypeScript
158 líneas
4.2 KiB
TypeScript
import * as fs from 'fs/promises';
|
|
import * as path from 'path';
|
|
import { exec } from 'child_process';
|
|
import { promisify } from 'util';
|
|
import { SysctlParam } from '../types/procfs';
|
|
|
|
const execAsync = promisify(exec);
|
|
|
|
/**
|
|
* ProcFS Writer - Handles writing to /proc filesystem and sysctl
|
|
*/
|
|
export class ProcFSWriter {
|
|
private procRoot: string;
|
|
|
|
constructor(procRoot = '/proc') {
|
|
this.procRoot = procRoot;
|
|
}
|
|
|
|
/**
|
|
* Write value to a procfs file
|
|
*/
|
|
async writeRaw(filePath: string, value: string | number): Promise<void> {
|
|
const fullPath = path.join(this.procRoot, filePath);
|
|
try {
|
|
await fs.writeFile(fullPath, String(value), 'utf-8');
|
|
} catch (error) {
|
|
throw new Error(`Failed to write to ${fullPath}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Read sysctl parameter
|
|
*/
|
|
async readSysctl(key: string): Promise<SysctlParam> {
|
|
try {
|
|
const { stdout } = await execAsync(`sysctl -n ${key}`);
|
|
const value = stdout.trim();
|
|
|
|
// Try to parse as number
|
|
const numValue = parseFloat(value);
|
|
const finalValue = isNaN(numValue) ? value : numValue;
|
|
|
|
// Check if writable by attempting to read the file
|
|
const sysPath = `/proc/sys/${key.replace(/\./g, '/')}`;
|
|
let writable = false;
|
|
try {
|
|
await fs.access(sysPath, fs.constants.W_OK);
|
|
writable = true;
|
|
} catch {
|
|
writable = false;
|
|
}
|
|
|
|
return {
|
|
key,
|
|
value: finalValue,
|
|
writable,
|
|
};
|
|
} catch (error) {
|
|
throw new Error(`Failed to read sysctl ${key}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Write sysctl parameter
|
|
*/
|
|
async writeSysctl(key: string, value: string | number): Promise<SysctlParam> {
|
|
try {
|
|
await execAsync(`sysctl -w ${key}=${value}`);
|
|
return await this.readSysctl(key);
|
|
} catch (error) {
|
|
throw new Error(`Failed to write sysctl ${key}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* List all sysctl parameters
|
|
*/
|
|
async listSysctl(): Promise<SysctlParam[]> {
|
|
try {
|
|
const { stdout } = await execAsync('sysctl -a');
|
|
const lines = stdout.split('\n');
|
|
const params: SysctlParam[] = [];
|
|
|
|
for (const line of lines) {
|
|
if (!line.trim()) continue;
|
|
const match = line.match(/^([^\s=]+)\s*=\s*(.+)$/);
|
|
if (match) {
|
|
const key = match[1].trim();
|
|
const value = match[2].trim();
|
|
const numValue = parseFloat(value);
|
|
|
|
params.push({
|
|
key,
|
|
value: isNaN(numValue) ? value : numValue,
|
|
writable: false, // Would need to check each individually
|
|
});
|
|
}
|
|
}
|
|
|
|
return params;
|
|
} catch (error) {
|
|
throw new Error(`Failed to list sysctl parameters: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set process priority (nice value)
|
|
*/
|
|
async setProcessPriority(pid: number, priority: number): Promise<void> {
|
|
if (priority < -20 || priority > 19) {
|
|
throw new Error('Priority must be between -20 and 19');
|
|
}
|
|
|
|
try {
|
|
await execAsync(`renice ${priority} -p ${pid}`);
|
|
} catch (error) {
|
|
throw new Error(`Failed to set priority for PID ${pid}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set CPU affinity for a process
|
|
*/
|
|
async setProcessAffinity(pid: number, cpuList: string): Promise<void> {
|
|
try {
|
|
await execAsync(`taskset -cp ${cpuList} ${pid}`);
|
|
} catch (error) {
|
|
throw new Error(`Failed to set CPU affinity for PID ${pid}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Send signal to a process
|
|
*/
|
|
async sendSignal(pid: number, signal: string | number = 'TERM'): Promise<void> {
|
|
try {
|
|
await execAsync(`kill -${signal} ${pid}`);
|
|
} catch (error) {
|
|
throw new Error(`Failed to send signal ${signal} to PID ${pid}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Set OOM score adjustment for a process
|
|
*/
|
|
async setOOMScore(pid: number, score: number): Promise<void> {
|
|
if (score < -1000 || score > 1000) {
|
|
throw new Error('OOM score must be between -1000 and 1000');
|
|
}
|
|
|
|
try {
|
|
await this.writeRaw(`${pid}/oom_score_adj`, score);
|
|
} catch (error) {
|
|
throw new Error(`Failed to set OOM score for PID ${pid}: ${(error as Error).message}`);
|
|
}
|
|
}
|
|
}
|