core_base.dart 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. import 'dart:async';
  2. import 'dart:convert';
  3. import 'dart:io';
  4. import 'package:speed_safe/app/server/server_base.dart';
  5. import 'package:speed_safe/app/util/log.dart';
  6. import 'package:path/path.dart' as p;
  7. import 'package:speed_safe/app/util/system.dart';
  8. abstract class CoreBase {
  9. String coreName;
  10. List<String> coreArgs;
  11. late String configFileName;
  12. late File configFile = File(p.join(tempPath, configFileName));
  13. Process? coreProcess;
  14. bool isPreLog = true;
  15. final List<String> preLogList = [];
  16. final logStreamController = StreamController<String>.broadcast();
  17. late final String runningServer;
  18. Stream<String> get logStream => logStreamController.stream;
  19. CoreBase(this.coreName, this.coreArgs, this.configFileName);
  20. Future<void> start(String server) async {
  21. late final ServerBase serverBase;
  22. runningServer = server;
  23. serverBase = ServerBase.fromJson(jsonDecode(server));
  24. await configure(serverBase);
  25. logger.i('Starting core: $coreName');
  26. try {
  27. coreProcess = await Process.start(
  28. p.join(binPath, SystemUtil.getCoreFileName(coreName)),
  29. coreArgs,
  30. runInShell: true,
  31. );
  32. } on ProcessException catch (e) {
  33. logger.e('Failed to start $coreName: ${e.message}');
  34. throw Exception('Failed to start $coreName: ${e.message}');
  35. }
  36. if (coreProcess == null) {
  37. throw Exception('Core Process is null');
  38. }
  39. listenToProcessStream(coreProcess!.stdout);
  40. listenToProcessStream(coreProcess!.stderr);
  41. try {
  42. if (await coreProcess?.exitCode
  43. .timeout(const Duration(milliseconds: 500)) !=
  44. 0) {
  45. throw Exception('\n${preLogList.join('\n')}');
  46. }
  47. } on TimeoutException catch (_) {
  48. isPreLog = false;
  49. }
  50. }
  51. Future<void> stop() async {
  52. if (coreProcess != null) {
  53. logger.i('Stopping core: $coreName');
  54. coreProcess?.kill(ProcessSignal.sigterm);
  55. await coreProcess?.exitCode.timeout(const Duration(milliseconds: 500),
  56. onTimeout: () {
  57. coreProcess?.kill(ProcessSignal.sigkill);
  58. return Future.error(
  59. 'Failed to stop $coreName, force killed the process.');
  60. });
  61. coreProcess = null;
  62. }
  63. SystemUtil.deleteFileIfExists(
  64. configFile.path, 'Deleting config file: $configFileName');
  65. if (coreName == 'sing-box') {
  66. SystemUtil.deleteFileIfExists(
  67. p.join(tempPath, 'cache.db'), 'Deleting cache file: cache.db');
  68. }
  69. if (!logStreamController.isClosed) {
  70. await logStreamController.close();
  71. }
  72. }
  73. void listenToProcessStream(Stream<List<int>> stream) {
  74. stream.transform(utf8.decoder).listen((data) {
  75. if (data.trim().isNotEmpty) {
  76. logStreamController.add(data);
  77. if (isPreLog) {
  78. preLogList.add(data);
  79. }
  80. }
  81. });
  82. }
  83. Future<void> configure(ServerBase server);
  84. Future<String> generateConfig(ServerBase server);
  85. Future<void> writeConfig(String jsonString) async {
  86. SystemUtil.deleteFileIfExists(
  87. configFile.path, 'Deleting config file: $configFileName');
  88. await configFile.writeAsString(jsonString);
  89. }
  90. }