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 { 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 { 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 { 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 { 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 { 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 { 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 { 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 { 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}`); } } }