92 lines
2.2 KiB
Dart
92 lines
2.2 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:quasar_jet/services/singbox_runtime.dart';
|
|
|
|
final class SingBoxService {
|
|
SingBoxService({SingBoxRuntime? runtime}) : _runtime = runtime ?? SingBoxRuntime();
|
|
|
|
final SingBoxRuntime _runtime;
|
|
final StreamController<String> _logController =
|
|
StreamController<String>.broadcast();
|
|
|
|
Process? _process;
|
|
|
|
Stream<String> get logs => _logController.stream;
|
|
bool get isRunning => _process != null;
|
|
|
|
Future<void> prepare() async {
|
|
await _runtime.ensureBinary();
|
|
}
|
|
|
|
Future<bool> start(String configJson) async {
|
|
if (_process != null) {
|
|
return true;
|
|
}
|
|
|
|
if (!Platform.isLinux) {
|
|
throw UnsupportedError('SingBoxService supports Linux only');
|
|
}
|
|
|
|
try {
|
|
jsonDecode(configJson);
|
|
} catch (_) {
|
|
_logController.add('Config is not valid JSON for sing-box');
|
|
return false;
|
|
}
|
|
|
|
final String binaryPath = await _runtime.ensureBinary();
|
|
final String configPath = await _runtime.configPath();
|
|
await File(configPath).writeAsString(configJson, flush: true);
|
|
|
|
final List<String> command = <String>[
|
|
'pkexec',
|
|
binaryPath,
|
|
'run',
|
|
'-c',
|
|
configPath,
|
|
];
|
|
|
|
try {
|
|
_process = await Process.start(command.first, command.sublist(1));
|
|
|
|
_process!.stdout
|
|
.transform(utf8.decoder)
|
|
.listen((String data) => _logController.add(data));
|
|
_process!.stderr
|
|
.transform(utf8.decoder)
|
|
.listen((String data) => _logController.add('ERROR: $data'));
|
|
|
|
_process!.exitCode.then((int code) {
|
|
_logController.add('sing-box exited with code $code');
|
|
_process = null;
|
|
});
|
|
return true;
|
|
} catch (error) {
|
|
_logController.add('Start failed: $error');
|
|
_process = null;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Future<void> stop() async {
|
|
final Process? process = _process;
|
|
if (process == null) {
|
|
return;
|
|
}
|
|
|
|
process.kill(ProcessSignal.sigterm);
|
|
await process.exitCode.timeout(const Duration(seconds: 3), onTimeout: () {
|
|
process.kill(ProcessSignal.sigkill);
|
|
return -1;
|
|
});
|
|
_process = null;
|
|
}
|
|
|
|
Future<void> dispose() async {
|
|
await stop();
|
|
await _logController.close();
|
|
}
|
|
}
|