mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2026-01-12 12:05:17 +01:00
* fix: Resolved boundary condition issues in string processing Addressed null and length checks during string splitting across multiple model classes to prevent potential null pointer exceptions and array out-of-bounds errors * fix: Throw exceptions instead of silently returning when package manager output formats are invalid Modified the _pacman, _opkg, and _apk parsing methods to throw exceptions when input formats are invalid, rather than silently returning, to prevent potential error handling issues.
175 lines
4.4 KiB
Dart
175 lines
4.4 KiB
Dart
import 'package:fl_lib/fl_lib.dart';
|
|
|
|
import 'package:server_box/data/res/misc.dart';
|
|
|
|
class _ProcValIdxMap {
|
|
final int pid;
|
|
final int? user;
|
|
final int? cpu;
|
|
final int? mem;
|
|
final int? vsz;
|
|
final int? rss;
|
|
final int? tty;
|
|
final int? stat;
|
|
final int? start;
|
|
final int? time;
|
|
final int command;
|
|
|
|
const _ProcValIdxMap({
|
|
required this.pid,
|
|
this.user,
|
|
this.cpu,
|
|
this.mem,
|
|
this.vsz,
|
|
this.rss,
|
|
this.tty,
|
|
this.stat,
|
|
this.start,
|
|
this.time,
|
|
required this.command,
|
|
});
|
|
}
|
|
|
|
/// Some field can be null due to incompatible format on `BSD` and `Alpine`
|
|
class Proc {
|
|
final String? user;
|
|
final int pid;
|
|
final double? cpu;
|
|
final double? mem;
|
|
final String? vsz;
|
|
final String? rss;
|
|
final String? tty;
|
|
final String? stat;
|
|
final String? start;
|
|
final String? time;
|
|
final String command;
|
|
|
|
const Proc({
|
|
this.user,
|
|
required this.pid,
|
|
this.cpu,
|
|
this.mem,
|
|
this.vsz,
|
|
this.rss,
|
|
this.tty,
|
|
this.stat,
|
|
this.start,
|
|
this.time,
|
|
required this.command,
|
|
});
|
|
|
|
factory Proc._parse(String raw, _ProcValIdxMap map) {
|
|
final parts = raw.split(RegExp(r'\s+'));
|
|
return Proc(
|
|
user: map.user == null ? null : parts[map.user!],
|
|
pid: int.parse(parts[map.pid]),
|
|
cpu: map.cpu == null ? null : double.parse(parts[map.cpu!]),
|
|
mem: map.mem == null ? null : double.parse(parts[map.mem!]),
|
|
vsz: map.vsz == null ? null : parts[map.vsz!],
|
|
rss: map.rss == null ? null : parts[map.rss!],
|
|
tty: map.tty == null ? null : parts[map.tty!],
|
|
stat: map.stat == null ? null : parts[map.stat!],
|
|
start: map.start == null ? null : parts[map.start!],
|
|
time: map.time == null ? null : parts[map.time!],
|
|
command: parts.sublist(map.command).join(' '),
|
|
);
|
|
}
|
|
|
|
Map toJson() {
|
|
return {
|
|
'user': user,
|
|
'pid': pid,
|
|
'cpu': cpu,
|
|
'mem': mem,
|
|
'vsz': vsz,
|
|
'rss': rss,
|
|
'tty': tty,
|
|
'stat': stat,
|
|
'start': start,
|
|
'time': time,
|
|
'command': command,
|
|
};
|
|
}
|
|
|
|
@override
|
|
String toString() {
|
|
return Miscs.jsonEncoder.convert(toJson());
|
|
}
|
|
|
|
String get binary {
|
|
final parts = command.trim().split(' ').where((e) => e.isNotEmpty).toList();
|
|
return parts.isNotEmpty ? parts[0] : '';
|
|
}
|
|
}
|
|
|
|
// `ps -aux` result
|
|
class PsResult {
|
|
final List<Proc> procs;
|
|
final String? error;
|
|
|
|
const PsResult({required this.procs, this.error});
|
|
|
|
factory PsResult.parse(String raw, {ProcSortMode sort = ProcSortMode.cpu}) {
|
|
final lines = raw.split('\n').map((e) => e.trim()).toList();
|
|
if (lines.isEmpty) return const PsResult(procs: [], error: null);
|
|
|
|
final header = lines[0];
|
|
final parts = header.split(RegExp(r'\s+'));
|
|
parts.removeWhere((element) => element.isEmpty);
|
|
final map = _ProcValIdxMap(
|
|
pid: parts.indexOfOrNull('PID')!,
|
|
user: parts.indexOfOrNull('USER'),
|
|
cpu: parts.indexOfOrNull('%CPU'),
|
|
mem: parts.indexOfOrNull('%MEM'),
|
|
vsz: parts.indexOfOrNull('VSZ'),
|
|
rss: parts.indexOfOrNull('RSS'),
|
|
tty: parts.indexOfOrNull('TTY'),
|
|
stat: parts.indexOfOrNull('STAT'),
|
|
start: parts.indexOfOrNull('START'),
|
|
time: parts.indexOfOrNull('TIME'),
|
|
command: parts.indexOfOrNull('COMMAND') ?? parts.indexOfOrNull('CMD')!,
|
|
);
|
|
|
|
final procs = <Proc>[];
|
|
final errs = <String>[];
|
|
for (var i = 1; i < lines.length; i++) {
|
|
final line = lines[i];
|
|
if (line.isEmpty) continue;
|
|
try {
|
|
procs.add(Proc._parse(line, map));
|
|
} catch (e, trace) {
|
|
errs.add('$line: $e');
|
|
Loggers.app.warning('Process failed', e, trace);
|
|
}
|
|
}
|
|
|
|
switch (sort) {
|
|
case ProcSortMode.cpu:
|
|
procs.sort((a, b) => b.cpu?.compareTo(a.cpu ?? 0) ?? 0);
|
|
break;
|
|
case ProcSortMode.mem:
|
|
procs.sort((a, b) => b.mem?.compareTo(a.mem ?? 0) ?? 0);
|
|
break;
|
|
case ProcSortMode.pid:
|
|
procs.sort((a, b) => a.pid.compareTo(b.pid));
|
|
break;
|
|
case ProcSortMode.user:
|
|
procs.sort((a, b) => a.user?.compareTo(b.user ?? '') ?? 0);
|
|
break;
|
|
case ProcSortMode.name:
|
|
procs.sort((a, b) => a.binary.compareTo(b.binary));
|
|
break;
|
|
}
|
|
return PsResult(procs: procs, error: errs.isEmpty ? null : errs.join('\n'));
|
|
}
|
|
}
|
|
|
|
enum ProcSortMode { cpu, mem, pid, user, name }
|
|
|
|
extension _StrIndex on List<String> {
|
|
int? indexOfOrNull(String val) {
|
|
final idx = indexOf(val);
|
|
return idx == -1 ? null : idx;
|
|
}
|
|
}
|