527 lines
14 KiB
Dart
527 lines
14 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
import 'dart:convert';
|
|
import 'dart:math';
|
|
import 'dart:typed_data';
|
|
|
|
import 'package:dart_framework/charset/euc_kr.dart';
|
|
import 'package:dart_framework/cp949.dart' as cp949;
|
|
|
|
class ProcessData {
|
|
late Process process;
|
|
late File file;
|
|
late bool isRunning;
|
|
late int exitCode;
|
|
Function(ProcessData)? _endListener;
|
|
late StringBuffer stdout;
|
|
late StringBuffer stderr;
|
|
|
|
Function(ProcessData)? get endListener {
|
|
return _endListener;
|
|
}
|
|
|
|
bool get isSuccess {
|
|
return ProcessExecutor.getSuccess(exitCode);
|
|
}
|
|
|
|
List<String> get stdoutArr {
|
|
return getBufferToArray(stdout);
|
|
}
|
|
|
|
List<String> get stderrArr {
|
|
return getBufferToArray(stderr);
|
|
}
|
|
|
|
List<String> getBufferToArray(StringBuffer buffer) {
|
|
var list = <String>[];
|
|
var arr = buffer.toString().split('\n');
|
|
var length = arr.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
if (arr[i].isNotEmpty) {
|
|
list.add(arr[i]);
|
|
}
|
|
}
|
|
return list;
|
|
}
|
|
|
|
set processValue(Process value) {
|
|
process = value;
|
|
stdout = StringBuffer();
|
|
stderr = StringBuffer();
|
|
process.stdout.transform(utf8.decoder).listen((event) {
|
|
stdout.writeln(event);
|
|
print(event);
|
|
});
|
|
process.stderr.transform(utf8.decoder).listen((event) {
|
|
stderr.writeln(event);
|
|
print(event);
|
|
});
|
|
isRunning = true;
|
|
checkExit();
|
|
}
|
|
|
|
void addEndListener(Function(ProcessData) listener) {
|
|
_endListener = listener;
|
|
}
|
|
|
|
void removeEndListener() {
|
|
_endListener = null;
|
|
}
|
|
|
|
void checkExit() async {
|
|
exitCode = await process.exitCode;
|
|
print('#### Exit Code: $exitCode');
|
|
deleteFile();
|
|
isRunning = false;
|
|
if (_endListener != null) _endListener!(this);
|
|
}
|
|
|
|
void terminate() {
|
|
process.kill();
|
|
}
|
|
|
|
void deleteFile() async {
|
|
if (await file.exists()) {
|
|
await file.delete();
|
|
}
|
|
}
|
|
|
|
void dispose() async {
|
|
deleteFile();
|
|
}
|
|
}
|
|
|
|
class ProcessExecutor {
|
|
static Future<ProcessData> startExe(String exe, List<String> args,
|
|
{String? workspace}) async {
|
|
var c = Completer<ProcessData>();
|
|
var arg = '$exe ${args.join(' ')}';
|
|
var processData = ProcessData();
|
|
if (Platform.isMacOS || Platform.isWindows) {
|
|
workspace = workspace ?? Directory.systemTemp.path;
|
|
print('Execute Shell: $arg');
|
|
try {
|
|
processData.processValue =
|
|
await Process.start(exe, args, workingDirectory: workspace);
|
|
} on Exception catch (e) {
|
|
print(e);
|
|
}
|
|
} else {
|
|
print('This platform not support process executor.');
|
|
}
|
|
c.complete(processData);
|
|
return c.future;
|
|
}
|
|
|
|
static Future<ProcessData> start(StringBuffer shell,
|
|
{String? workspace}) async {
|
|
var c = Completer<ProcessData>();
|
|
|
|
try {
|
|
var processData = ProcessData();
|
|
if (Platform.isMacOS || Platform.isLinux) {
|
|
workspace = workspace ?? Directory.systemTemp.path;
|
|
var file = File('$workspace/$tempFileName');
|
|
processData.file = file;
|
|
print('Execute Shell: ${file.path} \n ${shell.toString()}');
|
|
if (await file.exists()) {
|
|
await file.delete();
|
|
}
|
|
file.createSync();
|
|
file.writeAsStringSync(shell.toString());
|
|
try {
|
|
processData.processValue = await Process.start('zsh', [file.path],
|
|
workingDirectory: workspace, runInShell: false);
|
|
} on Exception catch (e) {
|
|
print(e);
|
|
}
|
|
} else if (Platform.isWindows) {
|
|
workspace = workspace ?? Directory.systemTemp.path;
|
|
var file = File('$workspace/$tempFileName');
|
|
processData.file = file;
|
|
print('Execute Shell: ${file.path} \n ${shell.toString()}');
|
|
if (await file.exists()) {
|
|
await file.delete();
|
|
}
|
|
file.createSync();
|
|
file.writeAsStringSync(shell.toString());
|
|
try {
|
|
processData.processValue = await Process.start(
|
|
'cmd', ['/C', file.path],
|
|
workingDirectory: workspace, runInShell: false);
|
|
} on Exception catch (e) {
|
|
print(e);
|
|
}
|
|
} else {
|
|
print('This platform not support process executor.');
|
|
}
|
|
c.complete(processData);
|
|
} on Exception catch (e) {
|
|
print('General Exception in ProcessExecutor.start: $e');
|
|
}
|
|
return c.future;
|
|
}
|
|
|
|
static Future<ProcessResult> run(StringBuffer shell,
|
|
{String? workspace, bool printStdout = true}) async {
|
|
var c = Completer<ProcessResult>();
|
|
ProcessResult? processResult;
|
|
if (Platform.isMacOS || Platform.isLinux) {
|
|
workspace = workspace ?? Directory.systemTemp.path;
|
|
var file = File('$workspace/$tempFileName');
|
|
print('Execute Shell: ${file.path} \n ${shell.toString()}');
|
|
file.createSync();
|
|
file.writeAsStringSync(shell.toString());
|
|
try {
|
|
await Process.run('zsh', [file.path], workingDirectory: workspace)
|
|
.then((ProcessResult result) {
|
|
processResult = result;
|
|
});
|
|
if (printStdout) {
|
|
print(
|
|
// ignore: prefer_interpolation_to_compose_strings
|
|
'[ProcessExecutor] Exit Code: ${processResult!.exitCode}\nResult: ${processResult!.stdout}\nError: ${processResult!.stderr}');
|
|
}
|
|
} on Exception catch (e) {
|
|
print(e);
|
|
} finally {
|
|
file.deleteSync();
|
|
}
|
|
c.complete(processResult);
|
|
} else if (Platform.isWindows) {
|
|
workspace = workspace ?? Directory.systemTemp.path;
|
|
var file = File('$workspace/$tempFileName');
|
|
print('Execute Bat: ${file.path} \n ${shell.toString()}');
|
|
file.createSync();
|
|
file.writeAsStringSync(shell.toString());
|
|
await Process.run('cmd', ['/C', file.path], workingDirectory: workspace)
|
|
.then((ProcessResult result) {
|
|
processResult = result;
|
|
});
|
|
if (printStdout) {
|
|
print(
|
|
// ignore: prefer_interpolation_to_compose_strings
|
|
'[ProcessExecutor] Exit Code: ${processResult!.exitCode}, Result: ${cp949.decodeString(processResult!.stdout)}\nError: ${cp949.decodeString(processResult!.stderr)}');
|
|
}
|
|
file.deleteSync();
|
|
c.complete(processResult);
|
|
} else {
|
|
print('This platform not support process executor.');
|
|
c.complete(null);
|
|
}
|
|
return c.future;
|
|
}
|
|
|
|
static String get tempFileName {
|
|
if (Platform.isMacOS || Platform.isLinux) {
|
|
return 'temp_${DateTime.now().microsecondsSinceEpoch}.sh';
|
|
} else {
|
|
return 'temp_${DateTime.now().microsecondsSinceEpoch}.bat';
|
|
}
|
|
}
|
|
|
|
static bool getSuccess(int exitCode) {
|
|
if (Platform.isMacOS || Platform.isLinux) return exitCode == 0;
|
|
if (Platform.isWindows) return exitCode == 1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
class NetStatData {
|
|
late String type;
|
|
late String from;
|
|
late String to;
|
|
late String state;
|
|
late int pid;
|
|
|
|
NetStatData(List<String> categories) {
|
|
var count = 0;
|
|
var length = categories.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var category = categories[i].replaceAll(' ', '');
|
|
if (category.isNotEmpty) {
|
|
switch (count) {
|
|
case 0:
|
|
type = category;
|
|
break;
|
|
case 1:
|
|
from = category;
|
|
break;
|
|
case 2:
|
|
to = category;
|
|
break;
|
|
case 3:
|
|
state = category;
|
|
break;
|
|
case 4:
|
|
var value = int.tryParse(category);
|
|
value ??= -1;
|
|
pid = value;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool _removedBat = false;
|
|
Future removeTempBat() async {
|
|
var c = Completer();
|
|
if (!_removedBat) {
|
|
_removedBat = true;
|
|
var reg = RegExp(r'(temp_bat_)([0-9_]+)(.bat)');
|
|
var list = Directory.current.listSync();
|
|
for (var item in list) {
|
|
var match = reg.stringMatch(item.path);
|
|
if (match != null) {
|
|
await item.delete();
|
|
}
|
|
}
|
|
await Future.delayed(Duration(milliseconds: 500));
|
|
}
|
|
c.complete();
|
|
return c.future;
|
|
}
|
|
|
|
Future<List<NetStatData>> findNetStat(String searchWord) async {
|
|
await removeTempBat();
|
|
var c = Completer<List<NetStatData>>();
|
|
var shell = StringBuffer('netstat -ano | findstr $searchWord');
|
|
var stdout = await ProcessSilentExecutor.run(shell);
|
|
var netList = <NetStatData>[];
|
|
var returnPattern = RegExp('\n|\r');
|
|
var list = stdout.split(returnPattern);
|
|
for (var item in list) {
|
|
item = item.replaceAll(returnPattern, '');
|
|
if (item.isNotEmpty) {
|
|
var categories = item.split(' ');
|
|
if (categories.isNotEmpty) {
|
|
netList.add(NetStatData(categories));
|
|
}
|
|
}
|
|
}
|
|
c.complete(netList);
|
|
return c.future;
|
|
}
|
|
|
|
Future<List<ProcessInfo>> findProcess(String searchWord) async {
|
|
await removeTempBat();
|
|
var c = Completer<List<ProcessInfo>>();
|
|
var shell = StringBuffer('tasklist /fo "csv" | findstr $searchWord');
|
|
var stdout = await ProcessSilentExecutor.run(shell);
|
|
var pList = parseProcessStd(stdout);
|
|
c.complete(pList);
|
|
return c.future;
|
|
}
|
|
|
|
List<ProcessInfo> parseProcessStd(String stdout) {
|
|
var pList = <ProcessInfo>[];
|
|
var returnPattern = RegExp('\n|\r');
|
|
var list = stdout.split(returnPattern);
|
|
for (var item in list) {
|
|
item = item.replaceAll(returnPattern, '');
|
|
if (item.isNotEmpty) {
|
|
var categories = item.split(',');
|
|
if (categories.length > 4) {
|
|
pList.add(ProcessInfo(categories));
|
|
}
|
|
}
|
|
}
|
|
return pList;
|
|
}
|
|
|
|
Future<List<ProcessInfo>> findProcessByPid(int pid) async {
|
|
// await removeTempBat();
|
|
var c = Completer<List<ProcessInfo>>();
|
|
var batch = getTempBat(fixedName: pid.toString());
|
|
if (!batch.existsSync()) {
|
|
batch.writeAsStringSync(
|
|
'tasklist /fi "pid eq ${pid.toString()}" /nh /fo "csv"');
|
|
}
|
|
String stdout = await ProcessSilentExecutor.runBat(batch.path);
|
|
var pList = parseProcessStd(stdout);
|
|
c.complete(pList);
|
|
return c.future;
|
|
}
|
|
|
|
class ProcessSilentExecutor {
|
|
static bool _initialized = false;
|
|
static Future initialize() async {
|
|
if (!_initialized) {
|
|
_initialized = true;
|
|
//remove before files
|
|
var regBat = RegExp(r'(silent_bat_)([0-9_]+)(.bat)');
|
|
var regLog = RegExp(r'(silent_log_)([0-9_]+)(.txt)');
|
|
var list = Directory.current.listSync();
|
|
for (var item in list) {
|
|
var matchBat = regBat.stringMatch(item.path);
|
|
var matchLog = regLog.stringMatch(item.path);
|
|
if (matchBat != null || matchLog != null) {
|
|
item.deleteSync();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static Future<String> run(StringBuffer shell) async {
|
|
initialize();
|
|
var c = Completer<String>();
|
|
var batch =
|
|
File('${Directory.current.path}/silent_bat_${getRnadomPostfix()}.bat');
|
|
batch.writeAsStringSync(shell.toString());
|
|
var result = await runBat(batch.path);
|
|
await batch.delete();
|
|
c.complete(result);
|
|
return c.future;
|
|
}
|
|
|
|
static Future<String> runBat(String batchPath) async {
|
|
initialize();
|
|
var c = Completer<String>();
|
|
var current = Directory.current;
|
|
String result;
|
|
var log = File('${current.path}/silent_log_${getRnadomPostfix()}.txt');
|
|
await Process.run('${current.path}/silentbatch.exe', [batchPath, log.path])
|
|
.then((resultData) {
|
|
Process.killPid(resultData.pid);
|
|
// print('"$batchPath" => exitCode: ${resultData.exitCode}');
|
|
});
|
|
try {
|
|
result = await log.readAsString();
|
|
} on Exception catch (e) {
|
|
print(e);
|
|
result = '';
|
|
}
|
|
await log.delete();
|
|
c.complete(result);
|
|
return c.future;
|
|
}
|
|
}
|
|
|
|
String getRnadomPostfix() {
|
|
return '${DateTime.now().microsecondsSinceEpoch}_${Random().nextInt(100)}';
|
|
}
|
|
|
|
File getTempBat({String? fixedName}) {
|
|
fixedName ??= getRnadomPostfix();
|
|
File batch = File('${Directory.current.path}/temp_bat_$fixedName.bat');
|
|
return batch;
|
|
}
|
|
|
|
class ProcessInfoListener {
|
|
late ProcessInfo? _process;
|
|
bool isRunning = false;
|
|
late Timer? _timer;
|
|
late final int _checkInterval;
|
|
late Function(ProcessInfoListener, bool)? _connectionListener;
|
|
set _isRunning(bool value) {
|
|
if (isRunning != value) {
|
|
isRunning = value;
|
|
changedConnection();
|
|
}
|
|
}
|
|
|
|
ProcessInfoListener(this._process, this._checkInterval) {
|
|
checkLive();
|
|
}
|
|
|
|
void checkLive() async {
|
|
stopChecker();
|
|
_isRunning = true;
|
|
_timer = Timer.periodic(Duration(seconds: _checkInterval), (time) async {
|
|
var enable = false;
|
|
if (_process != null) {
|
|
var list = await findProcessByPid(_process!.pid);
|
|
if (list.isNotEmpty) {
|
|
print('process live');
|
|
enable = true;
|
|
_isRunning = true;
|
|
}
|
|
}
|
|
|
|
if (!enable) {
|
|
_isRunning = false;
|
|
stopChecker();
|
|
}
|
|
});
|
|
}
|
|
|
|
void stopChecker() {
|
|
if (_timer != null) {
|
|
_timer!.cancel();
|
|
_timer = null;
|
|
}
|
|
}
|
|
|
|
void changedConnection() {
|
|
if (_connectionListener != null) {
|
|
_connectionListener!(this, isRunning);
|
|
}
|
|
}
|
|
|
|
Function(ProcessInfoListener, bool)? get endListener {
|
|
return _connectionListener;
|
|
}
|
|
|
|
void addEndListener(Function(ProcessInfoListener, bool)? listener) {
|
|
_connectionListener = listener;
|
|
}
|
|
|
|
void removeConnectionListener() {
|
|
_connectionListener = null;
|
|
}
|
|
|
|
void dispose() {
|
|
stopChecker();
|
|
removeConnectionListener();
|
|
_isRunning = false;
|
|
_process = null;
|
|
}
|
|
|
|
void terminate() {
|
|
if (isRunning && _process != null) {
|
|
Process.killPid(_process?.pid as int);
|
|
}
|
|
}
|
|
}
|
|
|
|
class ProcessInfo {
|
|
late String name;
|
|
late int pid;
|
|
late String sessionName;
|
|
late int session;
|
|
late String memory;
|
|
|
|
ProcessInfo(List<String> categories) {
|
|
var count = 0;
|
|
var length = categories.length;
|
|
for (var i = 0; i < length; ++i) {
|
|
var category = categories[i].replaceAll('"', '');
|
|
if (category.isNotEmpty) {
|
|
switch (count) {
|
|
case 0:
|
|
name = category;
|
|
break;
|
|
case 1:
|
|
var value = int.tryParse(category);
|
|
value ??= -1;
|
|
pid = value;
|
|
break;
|
|
case 2:
|
|
sessionName = category;
|
|
break;
|
|
case 3:
|
|
var value = int.tryParse(category);
|
|
value ??= -1;
|
|
session = value;
|
|
break;
|
|
case 4:
|
|
memory = category;
|
|
break;
|
|
}
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
}
|