node-os-utils
Version:
Advanced cross-platform operating system monitoring utilities with TypeScript support
1,144 lines • 41.2 kB
JavaScript
"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.MacOSAdapter = void 0;
const os_1 = __importDefault(require("os"));
const platform_adapter_1 = require("../core/platform-adapter");
const command_executor_1 = require("../utils/command-executor");
const errors_1 = require("../types/errors");
/**
* macOS 平台适配器
*
* 实现 macOS 系统的监控功能,主要通过 sysctl、vm_stat、system_profiler 等命令
*/
class MacOSAdapter extends platform_adapter_1.BasePlatformAdapter {
constructor() {
super('darwin');
this.executor = new command_executor_1.CommandExecutor('darwin');
}
/**
* 执行系统命令
*/
async executeCommand(command, options) {
return this.executor.execute(command, options);
}
/**
* 读取文件内容,捕获并转换常见的权限/不存在错误
*/
async readFile(path) {
try {
const result = await this.executeCommand(`cat "${path}"`);
this.validateCommandResult(result, `cat ${path}`);
return result.stdout;
}
catch (error) {
const err = error;
const errorCode = err?.code === 'EACCES'
? errors_1.ErrorCode.PERMISSION_DENIED
: err?.code === 'ENOENT'
? errors_1.ErrorCode.FILE_NOT_FOUND
: errors_1.ErrorCode.COMMAND_FAILED;
throw new errors_1.MonitorError(`Failed to read file: ${path}`, errorCode, this.platformName, { path, error: err?.message, code: err?.code });
}
}
/**
* 检查文件是否存在
*/
async fileExists(path) {
try {
const result = await this.executeCommand(`test -f "${path}"`);
return result.exitCode === 0;
}
catch {
return false;
}
}
/**
* 通过 sysctl 汇总 CPU 基本信息
*/
async getCPUInfo() {
try {
const [brand, cores, threads, freq] = await Promise.allSettled([
this.executeCommand('sysctl -n machdep.cpu.brand_string'),
this.executeCommand('sysctl -n hw.physicalcpu'),
this.executeCommand('sysctl -n hw.logicalcpu'),
this.executeCommand('sysctl -n hw.cpufrequency_max')
]).then(results => [
results[0].status === 'fulfilled' ? results[0].value : null,
results[1].status === 'fulfilled' ? results[1].value : null,
results[2].status === 'fulfilled' ? results[2].value : null,
results[3].status === 'fulfilled' ? results[3].value : null
]);
return this.parseCPUInfo(brand?.stdout || '', cores?.stdout || '', threads?.stdout || '', freq?.stdout || '');
}
catch (error) {
throw this.createCommandError('getCPUInfo', error);
}
}
/**
* 调用 top/iostat 获取 CPU 使用率,失败时链式回退
*/
async getCPUUsage() {
try {
// 使用 top 命令获取 CPU 使用率
const result = await this.executeCommand('top -l 1 -n 0');
this.validateCommandResult(result, 'top command');
return this.parseCPUUsageFromTop(result.stdout);
}
catch (error) {
// 回退到 iostat
try {
const result = await this.executeCommand('iostat -c 1');
this.validateCommandResult(result, 'iostat command');
return this.parseCPUUsageFromIostat(result.stdout);
}
catch {
throw this.createCommandError('getCPUUsage', error);
}
}
}
/**
* 使用 powermetrics 读取温度,需要 sudo 权限
*/
async getCPUTemperature() {
try {
// macOS 温度监控需要第三方工具或私有 API
// 尝试使用 powermetrics(需要 sudo)
const result = await this.executeCommand('powermetrics -n 1 -i 1000 --samplers smc');
return this.parseTemperatureFromPowermetrics(result.stdout);
}
catch (error) {
// 温度监控在 macOS 上可能需要额外权限
throw this.createUnsupportedError('cpu.temperature');
}
}
/**
* 读取 vm_stat 解析内存占用
*/
async getMemoryInfo() {
try {
const [vmStat, totalMem, pressure] = await Promise.allSettled([
this.executeCommand('vm_stat'),
this.executeCommand('sysctl -n hw.memsize'),
this.executeCommand('memory_pressure')
]).then(results => [
results[0].status === 'fulfilled' ? results[0].value : null,
results[1].status === 'fulfilled' ? results[1].value : null,
results[2].status === 'fulfilled' ? results[2].value : null
]);
if (vmStat)
this.validateCommandResult(vmStat, 'vm_stat');
if (totalMem)
this.validateCommandResult(totalMem, 'sysctl hw.memsize');
return this.parseMemoryInfo(vmStat?.stdout || '', totalMem?.stdout || '', pressure?.stdout || '');
}
catch (error) {
throw this.createCommandError('getMemoryInfo', error);
}
}
/**
* 读取 vm_stat 解析内存占用
*/
async getMemoryUsage() {
// macOS 上内存信息和使用情况来自相同来源
return this.getMemoryInfo();
}
/**
* 读取 df -h 解析磁盘占用
*/
async getDiskInfo() {
try {
const result = await this.executeCommand('df -h');
this.validateCommandResult(result, 'df -h');
return this.parseDiskInfo(result.stdout);
}
catch (error) {
throw this.createCommandError('getDiskInfo', error);
}
}
/**
* 读取 iostat -d 解析磁盘 I/O 统计
*/
async getDiskIO() {
try {
const result = await this.executeCommand('iostat -d');
this.validateCommandResult(result, 'iostat -d');
return this.parseDiskIO(result.stdout);
}
catch (error) {
throw this.createCommandError('getDiskIO', error);
}
}
/**
* 读取 ifconfig 解析网络接口列表
*/
async getNetworkInterfaces() {
try {
const result = await this.executeCommand('ifconfig');
this.validateCommandResult(result, 'ifconfig');
return this.parseNetworkInterfaces(result.stdout);
}
catch (error) {
throw this.createCommandError('getNetworkInterfaces', error);
}
}
/**
* 读取 netstat -ib 解析网络统计信息
*/
async getNetworkStats() {
try {
const result = await this.executeCommand('netstat -ib');
this.validateCommandResult(result, 'netstat -ib');
return this.parseNetworkStats(result.stdout);
}
catch (error) {
throw this.createCommandError('getNetworkStats', error);
}
}
/**
* 读取 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 解析进程列表
*/
async getProcesses() {
try {
const result = await this.executeCommand('ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args=');
this.validateCommandResult(result, 'ps command');
return this.parseProcessList(result.stdout);
}
catch (error) {
throw this.createCommandError('getProcesses', error);
}
}
/**
* 读取 ps -p pid -o pid,ppid,command,pcpu,pmem,state,user,lstart 解析特定进程信息
*/
async getProcessInfo(pid) {
try {
const [summaryResult, commandResult, startResult] = await Promise.all([
this.executeCommand(`ps -p ${pid} -o pid=,ppid=,rss=,pcpu=,pmem=,state=,user=`),
this.executeCommand(`ps -p ${pid} -o command=`),
this.executeCommand(`ps -p ${pid} -o lstart=`)
]);
this.validateCommandResult(summaryResult, `ps summary ${pid}`);
this.validateCommandResult(commandResult, `ps command ${pid}`);
this.validateCommandResult(startResult, `ps lstart ${pid}`);
const summaryLine = summaryResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
const commandLine = commandResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
const lstartLine = startResult.stdout.split('\n').find(line => line.trim().length > 0) || '';
return this.parseProcessInfo({ summary: summaryLine, command: commandLine, start: lstartLine }, pid);
}
catch (error) {
throw this.createCommandError('getProcessInfo', error);
}
}
/**
* 读取 uname -a, uptime, sysctl -n vm.loadavg, sw_vers 解析系统信息
*/
async getSystemInfo() {
try {
const [uname, uptime, loadavg, osVersion] = await Promise.allSettled([
this.executeCommand('uname -a'),
this.executeCommand('uptime'),
this.executeCommand('sysctl -n vm.loadavg'),
this.executeCommand('sw_vers')
]).then(results => [
results[0].status === 'fulfilled' ? results[0].value : null,
results[1].status === 'fulfilled' ? results[1].value : null,
results[2].status === 'fulfilled' ? results[2].value : null,
results[3].status === 'fulfilled' ? results[3].value : null
]);
return this.parseSystemInfo(uname?.stdout || '', uptime?.stdout || '', loadavg?.stdout || '', osVersion?.stdout || null);
}
catch (error) {
throw this.createCommandError('getSystemInfo', error);
}
}
/**
* 读取 sysctl -n vm.loadavg 解析系统负载
*/
async getSystemLoad() {
try {
const result = await this.executeCommand('sysctl -n vm.loadavg');
this.validateCommandResult(result, 'sysctl vm.loadavg');
return this.parseLoadAverage(result.stdout);
}
catch (error) {
throw this.createCommandError('getSystemLoad', error);
}
}
/**
* 读取 sysctl -n vm.loadavg 解析系统负载
*/
initializeSupportedFeatures() {
return {
cpu: {
info: true,
usage: true,
temperature: false, // 需要特殊权限
frequency: true,
cache: false,
perCore: false,
cores: true
},
memory: {
info: true,
usage: true,
swap: true,
pressure: true,
detailed: true,
virtual: true
},
disk: {
info: true,
io: true,
health: false,
smart: false,
filesystem: true,
usage: true,
stats: true,
mounts: true,
filesystems: true
},
network: {
interfaces: true,
stats: true,
connections: true,
bandwidth: false,
gateway: true
},
process: {
list: true,
details: true,
tree: false,
monitor: true,
info: true,
kill: true,
openFiles: true,
environment: true
},
system: {
info: true,
load: true,
uptime: true,
users: true,
services: false
}
};
}
// 私有解析方法,解析命令输出为结构化数据
parseCPUInfo(brand, cores, threads, freq) {
return {
model: brand.trim(),
manufacturer: brand.includes('Intel') ? 'Intel' : brand.includes('Apple') ? 'Apple' : 'Unknown',
architecture: 'Unknown',
cores: this.safeParseInt(cores.trim()),
threads: this.safeParseInt(threads.trim()),
baseFrequency: freq ? this.safeParseInt(freq.trim()) / 1000000 : 0, // 转换为 MHz
maxFrequency: freq ? this.safeParseInt(freq.trim()) / 1000000 : 0,
cache: {},
features: []
};
}
/**
* 解析 top 命令输出为 CPU 使用率
*/
parseCPUUsageFromTop(output) {
const lines = output.split('\n');
const cpuLine = lines.find(line => line.includes('CPU usage:'));
if (!cpuLine) {
throw this.createParseError(output, 'CPU usage line not found in top output');
}
// 解析类似 "CPU usage: 10.81% user, 13.73% sys, 75.45% idle" 的行
const userMatch = cpuLine.match(/([\d.]+)%\s+user/);
const sysMatch = cpuLine.match(/([\d.]+)%\s+sys/);
const idleMatch = cpuLine.match(/([\d.]+)%\s+idle/);
const user = userMatch ? this.safeParseNumber(userMatch[1]) : 0;
const system = sysMatch ? this.safeParseNumber(sysMatch[1]) : 0;
const idle = idleMatch ? this.safeParseNumber(idleMatch[1]) : 0;
return {
overall: 100 - idle,
user,
system,
idle,
cores: []
};
}
/**
* 解析 iostat 命令输出为 CPU 使用率
*/
parseCPUUsageFromIostat(output) {
const lines = output.split('\n');
const dataLine = lines[lines.length - 2]; // iostat 的最后一行数据
if (!dataLine) {
throw this.createParseError(output, 'No data line found in iostat output');
}
const fields = dataLine.trim().split(/\s+/);
if (fields.length >= 3) {
const user = this.safeParseNumber(fields[0]);
const system = this.safeParseNumber(fields[1]);
const idle = this.safeParseNumber(fields[2]);
return {
overall: 100 - idle,
user,
system,
idle,
cores: []
};
}
throw this.createParseError(output, 'Unable to parse iostat CPU data');
}
/**
* 解析 powermetrics 命令输出为温度
*/
parseTemperatureFromPowermetrics(output) {
// powermetrics 输出解析(简化版)
const lines = output.split('\n');
const temperatures = [];
for (const line of lines) {
const tempMatch = line.match(/(\w+)\s+temperature:\s+([\d.]+)/);
if (tempMatch) {
temperatures.push({
sensor: tempMatch[1],
temperature: this.safeParseNumber(tempMatch[2])
});
}
}
return temperatures;
}
/**
* 解析 vm_stat 输出为内存信息
*/
parseMemoryInfo(vmStat, totalMem, pressure) {
const total = this.safeParseInt(totalMem.trim());
// 解析 vm_stat 输出
const vmLines = vmStat.split('\n');
const pageSizeLine = vmLines.find(line => /page size of\s+\d+\s+bytes/i.test(line));
const pageSizeMatch = pageSizeLine ? pageSizeLine.match(/page size of\s+(\d+)\s+bytes/i) : null;
const detectedPageSize = pageSizeMatch && pageSizeMatch[1]
? this.safeParseInt(pageSizeMatch[1])
: 0;
const pageSize = detectedPageSize > 0 ? detectedPageSize : 4096;
let free = 0, active = 0, inactive = 0, wired = 0, compressed = 0;
for (const line of vmLines) {
const match = line.match(/Pages\s+([\w\s]+):\s+(\d+)/);
if (match) {
const [, typeRaw, count] = match;
const type = typeRaw.trim().toLowerCase();
const bytes = this.safeParseInt(count) * pageSize;
switch (type) {
case 'free':
free = bytes;
break;
case 'active':
active = bytes;
break;
case 'inactive':
inactive = bytes;
break;
case 'wired':
case 'wired down':
wired = bytes;
break;
case 'compressed':
case 'occupied':
case 'occupied by compressor':
compressed = bytes;
break;
}
}
}
const used = active + wired + compressed;
const available = total - used;
return {
total,
used,
free,
available,
active,
inactive,
wired,
compressed,
usagePercentage: total > 0 ? (used / total) * 100 : 0,
pressure: this.parseMemoryPressure(pressure)
};
}
/**
* 解析 memory_pressure 输出为内存压力
*/
parseMemoryPressure(pressure) {
if (!pressure) {
return { level: 'normal', score: 0 };
}
// 简化的内存压力解析
if (pressure.includes('critical')) {
return { level: 'critical', score: 90 };
}
else if (pressure.includes('warn')) {
return { level: 'high', score: 70 };
}
else {
return { level: 'normal', score: 10 };
}
}
/**
* 解析 df -h 输出为磁盘信息
*/
parseDiskInfo(output) {
const lines = output.split('\n').filter(line => line.trim());
if (lines.length < 2)
return [];
const disks = [];
for (let i = 1; i < lines.length; i++) {
const fields = lines[i].split(/\s+/);
if (fields.length >= 9) {
const [filesystem, size, used, available, capacity, /* iused */ , /* ifree */ , /* iusedPercent */ , mountpoint] = fields;
disks.push({
filesystem,
mountpoint,
size: this.convertDfSizeToBytes(size),
used: this.convertDfSizeToBytes(used),
available: this.convertDfSizeToBytes(available),
usagePercentage: this.safeParseNumber(capacity.replace('%', ''))
});
}
}
return disks;
}
/**
* 解析 iostat -d 输出为磁盘 I/O 统计
*/
parseDiskIO(output) {
return this.parseIostatDisks(output).map(item => ({
device: item.device,
kbPerTransfer: item.kbPerTransfer,
transfersPerSec: item.transfersPerSec,
mbPerSec: item.mbPerSec,
readSpeed: item.bytePerSec,
iops: item.transfersPerSec
}));
}
/**
* 解析 ifconfig 输出为网络接口列表
*/
parseNetworkInterfaces(output) {
const interfaces = [];
const blocks = output.split(/\n(?=\w)/); // 按接口分割
for (const block of blocks) {
const lines = block.split('\n');
const interfaceLine = lines[0];
if (!interfaceLine)
continue;
const nameMatch = interfaceLine.match(/^(\w+):/);
if (!nameMatch)
continue;
const name = nameMatch[1];
const addresses = [];
let state = 'down';
let mtu = 0;
let macAddress = '';
for (const line of lines) {
// 状态检查
if (line.includes('<UP,')) {
state = 'up';
}
// MTU 检查
const mtuMatch = line.match(/mtu (\d+)/);
if (mtuMatch) {
mtu = this.safeParseInt(mtuMatch[1]);
}
// MAC 地址
const etherMatch = line.match(/ether\s+([0-9a-f:]+)/i);
if (etherMatch) {
macAddress = etherMatch[1].toLowerCase();
}
// IPv4 地址
const inetMatch = line.match(/inet\s+([^\s]+)/);
if (inetMatch) {
addresses.push({
address: inetMatch[1],
family: 'IPv4'
});
}
// IPv6 地址
const inet6Match = line.match(/inet6\s+([^\s]+)/);
if (inet6Match) {
addresses.push({
address: inet6Match[1],
family: 'IPv6'
});
}
}
interfaces.push({
name,
addresses,
state,
mtu,
internal: name === 'lo0',
mac: macAddress
});
}
return interfaces;
}
/**
* 解析 netstat -ib/-i 输出,兼容是否包含字节列两种格式
*/
parseNetworkStats(output) {
const lines = output.split('\n').filter(line => line.trim());
if (lines.length === 0)
return [];
const header = lines[0].toLowerCase();
const stats = [];
for (let i = 1; i < lines.length; i++) {
const fields = lines[i].trim().split(/\s+/);
if (fields.length < 8)
continue;
const name = fields[0];
const mtu = this.safeParseInt(fields[1]);
if (header.includes('ibytes') && fields.length >= 11) {
const tail = fields.slice(-7);
if (tail.length < 7)
continue;
const [ipkts, ierrs, ibytes, opkts, oerrs, obytes, collisions] = tail;
stats.push({
interface: name,
mtu,
rxPackets: this.safeParseInt(ipkts),
rxErrors: this.safeParseInt(ierrs),
rxBytes: this.safeParseInt(ibytes),
txPackets: this.safeParseInt(opkts),
txErrors: this.safeParseInt(oerrs),
txBytes: this.safeParseInt(obytes),
collisions: this.safeParseInt(collisions)
});
continue;
}
const tail = fields.slice(-5);
if (tail.length < 5)
continue;
const [ipkts, ierrs, opkts, oerrs, collisions] = tail;
stats.push({
interface: name,
mtu,
rxPackets: this.safeParseInt(ipkts),
rxErrors: this.safeParseInt(ierrs),
rxBytes: 0,
txPackets: this.safeParseInt(opkts),
txErrors: this.safeParseInt(oerrs),
txBytes: 0,
collisions: this.safeParseInt(collisions)
});
}
return stats;
}
/**
* 解析 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 输出为进程列表
*/
parseProcessList(output) {
const lines = output.split('\n').filter(line => line.trim());
const processes = [];
for (const line of lines) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 8) {
const [pid, ppid, cmd, pcpu, pmem, rss, state, user, ...argsParts] = fields;
const command = argsParts.length > 0 ? argsParts.join(' ').trim() : cmd;
processes.push({
pid: this.safeParseInt(pid),
ppid: this.safeParseInt(ppid),
name: cmd,
comm: cmd,
command,
cpuUsage: this.safeParseNumber(pcpu),
memoryUsage: this.safeParseInt(rss) * 1024,
memoryPercentage: this.safeParseNumber(pmem),
state,
user
});
}
}
return processes;
}
/**
* 解析 ps 输出的进程详细信息
*/
parseProcessInfo(data, pid) {
const summaryParts = data.summary.trim().split(/\s+/);
if (summaryParts.length < 7) {
throw new errors_1.MonitorError(`Process ${pid} not found`, errors_1.ErrorCode.NOT_AVAILABLE, this.platformName, { pid });
}
const [pidStr, ppidStr, rssStr, pcpu, pmem, state, user] = summaryParts;
const command = data.command.trim();
const lstart = data.start.trim();
return {
pid: this.safeParseInt(pidStr) || pid,
ppid: this.safeParseInt(ppidStr),
name: command.split(' ')[0] || command,
command,
cpuUsage: this.safeParseNumber(pcpu),
memoryUsage: this.safeParseInt(rssStr) * 1024,
memoryPercentage: this.safeParseNumber(pmem),
state,
user,
startTime: lstart
};
}
/**
* 解析 uname -a, uptime, sysctl -n vm.loadavg, sw_vers 输出为系统信息
*/
parseSystemInfo(uname, uptime, loadavg, osVersion) {
const unameFields = uname.trim().split(' ');
const uptimeSeconds = os_1.default.uptime();
const bootTime = Date.now() - uptimeSeconds * 1000;
const load = this.parseLoadAverage(loadavg);
const uptimeMs = uptimeSeconds * 1000;
return {
hostname: unameFields[1] || 'Unknown',
platform: 'darwin',
release: unameFields[2] || 'Unknown',
version: osVersion ? osVersion.trim() : unameFields[3] || 'Unknown',
arch: unameFields[4] || 'Unknown',
uptime: uptimeMs,
uptimeSeconds,
bootTime,
loadAverage: load
};
}
/**
* 解析 sysctl -n vm.loadavg 输出为系统负载
*/
parseLoadAverage(output) {
const match = output.trim().match(/\{\s*([\d.]+)\s+([\d.]+)\s+([\d.]+)\s*\}/);
if (match) {
return {
load1: this.safeParseNumber(match[1]),
load5: this.safeParseNumber(match[2]),
load15: this.safeParseNumber(match[3])
};
}
throw this.createParseError(output, 'Unable to parse load average');
}
/**
* 解析 df -h 输出的大小格式转换
*/
convertDfSizeToBytes(sizeStr) {
// df -h 输出的大小格式转换
const match = sizeStr.match(/^([\d.]+)([KMGT]?)i?$/);
if (!match)
return 0;
const value = this.safeParseNumber(match[1]);
const unit = match[2];
const multipliers = {
'': 1024, // df 默认单位是 KB
'K': 1024,
'M': 1024 * 1024,
'G': 1024 * 1024 * 1024,
'T': 1024 * 1024 * 1024 * 1024
};
return value * (multipliers[unit] || 1024);
}
// 实现抽象方法,获取结构化数据
/**
* 获取磁盘使用情况,解析 df -k 输出为磁盘使用情况
*/
async getDiskUsage() {
try {
const result = await this.executeCommand('df -k');
return this.parseDiskUsage(result.stdout);
}
catch (error) {
throw this.createCommandError('getDiskUsage', error);
}
}
/**
* 获取磁盘统计,解析 iostat -d 输出为磁盘统计
*/
async getDiskStats() {
try {
const result = await this.executeCommand('iostat -d');
return this.parseDiskStats(result.stdout);
}
catch (error) {
throw this.createCommandError('getDiskStats', error);
}
}
/**
* 获取挂载点,解析 mount 输出为挂载点
*/
async getMounts() {
try {
const result = await this.executeCommand('mount');
return this.parseMounts(result.stdout);
}
catch (error) {
throw this.createCommandError('getMounts', error);
}
}
/**
* 获取文件系统,解析 diskutil list 输出为文件系统
*/
async getFileSystems() {
try {
const result = await this.executeCommand('diskutil list');
return this.parseFileSystems(result.stdout);
}
catch (error) {
throw this.createCommandError('getFileSystems', error);
}
}
/**
* 获取网络连接,解析 netstat -an 输出为网络连接
*/
async getNetworkConnections() {
try {
const result = await this.executeCommand('netstat -an');
return this.parseNetworkConnections(result.stdout);
}
catch (error) {
throw this.createCommandError('getNetworkConnections', error);
}
}
/**
* 获取默认网关,解析 route -n get default 输出为默认网关
*/
async getDefaultGateway() {
try {
const result = await this.executeCommand('route -n get default');
return this.parseDefaultGateway(result.stdout);
}
catch (error) {
throw this.createCommandError('getDefaultGateway', error);
}
}
/**
* 获取进程列表,解析 ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args= 输出为进程列表
*/
async getProcessList() {
try {
const result = await this.executeCommand('ps -axo pid=,ppid=,comm=,%cpu=,%mem=,rss=,stat=,user=,args=');
return this.parseProcessList(result.stdout);
}
catch (error) {
throw this.createCommandError('getProcessList', error);
}
}
/**
* 杀死进程,解析 kill -${signal} ${pid} 输出为杀死进程
*/
async killProcess(pid, signal = 'TERM') {
try {
const result = await this.executeCommand(`kill -${signal} ${pid}`);
return result.exitCode === 0;
}
catch (error) {
return false;
}
}
/**
* 获取进程打开文件,解析 lsof -p ${pid} +c0 -Fn 输出为进程打开文件
*/
async getProcessOpenFiles(pid) {
try {
const result = await this.executeCommand(`lsof -p ${pid} +c0 -Fn`);
return this.parseOpenFiles(result.stdout);
}
catch (error) {
return [];
}
}
/**
* 获取进程环境变量,解析 ps eww ${pid} 输出为进程环境变量
*/
async getProcessEnvironment(pid) {
try {
const result = await this.executeCommand(`ps eww ${pid}`);
return this.parseEnvironment(result.stdout);
}
catch (error) {
return {};
}
}
/**
* 获取系统运行时间
*/
async getSystemUptime() {
try {
const uptimeSeconds = os_1.default.uptime();
return {
uptime: uptimeSeconds,
bootTime: Date.now() - uptimeSeconds * 1000
};
}
catch (error) {
throw this.createCommandError('getSystemUptime', error);
}
}
/**
* 获取系统用户,解析 who 输出为系统用户
*/
async getSystemUsers() {
try {
const result = await this.executeCommand('who');
return this.parseSystemUsers(result.stdout);
}
catch (error) {
throw this.createCommandError('getSystemUsers', error);
}
}
/**
* 获取系统服务,解析 launchctl list 输出为系统服务
*/
async getSystemServices() {
try {
const result = await this.executeCommand('launchctl list');
return this.parseSystemServices(result.stdout);
}
catch (error) {
throw this.createCommandError('getSystemServices', error);
}
}
// 私有解析方法,解析命令输出为结构化数据
parseDiskUsage(output) {
const lines = output.split('\n').slice(1); // 跳过标题行
const disks = [];
for (const line of lines) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 9) {
const usedPercent = fields[4].replace('%', '');
disks.push({
device: fields[0],
total: this.safeParseInt(fields[1]) * 1024, // KB to bytes
used: this.safeParseInt(fields[2]) * 1024,
available: this.safeParseInt(fields[3]) * 1024,
usagePercentage: this.safeParseNumber(usedPercent),
mountPoint: fields[8]
});
}
}
return disks;
}
/**
* 解析 iostat -d 输出为磁盘统计
*/
parseDiskStats(output) {
return this.parseIostatDisks(output).map(item => ({
device: item.device,
readBytes: item.bytePerSec,
writeBytes: 0,
readCount: item.transfersPerSec,
writeCount: 0,
readTime: 0,
writeTime: 0,
ioTime: 0,
readSpeed: item.bytePerSec,
writeSpeed: 0,
iops: item.transfersPerSec
}));
}
/**
* 解析 mount 输出为挂载点
*/
parseMounts(output) {
const lines = output.split('\n').filter(line => line.trim());
const mounts = [];
for (const line of lines) {
const match = line.match(/^(.+?)\s+on\s+(.+?)\s+\((.+?)\)$/);
if (match) {
const [, device, mountPoint, options] = match;
const optionsArray = options.split(',').map(opt => opt.trim());
mounts.push({
device: device.trim(),
mountPoint: mountPoint.trim(),
filesystem: optionsArray[0] || 'unknown',
options: optionsArray,
dump: 0,
pass: 0
});
}
}
return mounts;
}
/**
* 解析 diskutil list 输出为文件系统
*/
parseFileSystems(output) {
const lines = output.split('\n').filter(line => line.trim());
const filesystems = [];
for (const line of lines) {
if (line.includes('GUID_partition_scheme') || line.includes('Apple_HFS') ||
line.includes('Apple_APFS') || line.includes('Microsoft Basic Data')) {
const parts = line.trim().split(/\s+/);
if (parts.length >= 3) {
filesystems.push({
name: parts[2] || 'unknown',
type: parts[1] || 'unknown',
supported: true
});
}
}
}
return filesystems;
}
/**
* 解析 netstat -an 输出为网络连接
*/
parseNetworkConnections(output) {
const lines = output.split('\n').filter(line => line.trim());
const connections = [];
for (const line of lines) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 6 && (fields[0].includes('tcp') || fields[0].includes('udp'))) {
connections.push({
protocol: fields[0],
localAddress: fields[3] || '*',
foreignAddress: fields[4] || '*',
state: fields[5] || 'unknown'
});
}
}
return connections;
}
/**
* 解析 route -n get default 输出为默认网关
*/
parseDefaultGateway(output) {
const lines = output.split('\n');
let gateway = null;
let interfaceName = null;
for (const line of lines) {
if (line.includes('gateway:')) {
const match = line.match(/gateway:\s*(.+)/);
if (match)
gateway = match[1].trim();
}
if (line.includes('interface:')) {
const match = line.match(/interface:\s*(.+)/);
if (match)
interfaceName = match[1].trim();
}
}
return gateway ? { gateway, interface: interfaceName || 'unknown' } : null;
}
/**
* 解析 lsof -p ${pid} +c0 -Fn 输出为进程打开文件
*/
parseOpenFiles(output) {
const lines = output.split('\n');
const files = [];
for (const line of lines) {
if (line.startsWith('n')) {
files.push(line.substring(1));
}
}
return files;
}
/**
* 解析 ps eww ${pid} 输出为进程环境变量
*/
parseEnvironment(output) {
const env = {};
if (!output) {
return env;
}
const pattern = /(^|\s)([A-Za-z_][A-Za-z0-9_]*)=([\s\S]*?)(?=(\s+[A-Za-z_][A-Za-z0-9_]*=)|$)/g;
let match;
while ((match = pattern.exec(output)) !== null) {
const key = match[2];
if (key === 'PID') {
continue;
}
const value = match[3].replace(/\s+$/u, '');
env[key] = value;
}
return env;
}
parseIostatDisks(output) {
const lines = output.split('\n').map(line => line.trimEnd());
const deviceLineIndex = lines.findIndex(line => /disk\w+/i.test(line));
if (deviceLineIndex === -1) {
return [];
}
const deviceLine = lines[deviceLineIndex];
const deviceNames = (deviceLine.match(/disk[^\s]*/gi) || []).map(name => name.trim());
if (deviceNames.length === 0) {
return [];
}
const dataLines = lines.slice(deviceLineIndex + 1).filter(line => line && /[\d.]/.test(line));
if (dataLines.length === 0) {
return [];
}
const tokens = dataLines[0].trim().split(/\s+/);
const metricsPerDevice = 3;
const result = [];
deviceNames.forEach((device, index) => {
const offset = index * metricsPerDevice;
if (tokens.length >= offset + metricsPerDevice) {
const kbPerTransfer = this.safeParseNumber(tokens[offset]);
const transfersPerSec = this.safeParseNumber(tokens[offset + 1]);
const mbPerSec = this.safeParseNumber(tokens[offset + 2]);
const bytePerSec = mbPerSec * 1024 * 1024;
result.push({
device,
kbPerTransfer,
transfersPerSec,
mbPerSec,
bytePerSec
});
}
});
return result;
}
/**
* 解析 uptime 输出为系统运行时间
*/
parseSystemUptime(output) {
const uptimeMatch = output.match(/up\s+(.+?),/);
const loadMatch = output.match(/load averages:\s+([\d.]+)\s+([\d.]+)\s+([\d.]+)/);
return {
uptime: uptimeMatch ? uptimeMatch[1].trim() : 'unknown',
loadAverage: loadMatch ? {
load1: this.safeParseNumber(loadMatch[1]),
load5: this.safeParseNumber(loadMatch[2]),
load15: this.safeParseNumber(loadMatch[3])
} : { load1: 0, load5: 0, load15: 0 }
};
}
/**
* 解析 who 输出为系统用户
*/
parseSystemUsers(output) {
const lines = output.split('\n').filter(line => line.trim());
const users = [];
for (const line of lines) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 5) {
users.push({
user: fields[0],
terminal: fields[1],
loginTime: fields.slice(2, -1).join(' '),
from: fields[fields.length - 1]
});
}
}
return users;
}
/**
* 解析 launchctl list 输出为系统服务
*/
parseSystemServices(output) {
const lines = output.split('\n').slice(1); // 跳过标题行
const services = [];
for (const line of lines) {
const fields = line.trim().split(/\s+/);
if (fields.length >= 3) {
services.push({
pid: fields[0] === '-' ? null : this.safeParseInt(fields[0]),
status: fields[1],
label: fields[2]
});
}
}
return services;
}
}
exports.MacOSAdapter = MacOSAdapter;
//# sourceMappingURL=macos-adapter.js.map