Skip to content

Commit adfa25d

Browse files
authored
Add PersistentShell fallback to avoid getting stuck at PersistentShell (#1170)
* Add PersistentShell fallback to avoid getting stuck at PersistentShell
1 parent 6d4873d commit adfa25d

1 file changed

Lines changed: 56 additions & 21 deletions

File tree

lib/data/provider/server/single.dart

Lines changed: 56 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ abstract class ServerState with _$ServerState {
4343
@Riverpod(keepAlive: true)
4444
class ServerNotifier extends _$ServerNotifier {
4545
PersistentShell? _persistentShell;
46+
bool _usePersistentShellForStatus = true;
4647

4748
@override
4849
ServerState build(String serverId) {
@@ -102,6 +103,7 @@ class ServerNotifier extends _$ServerNotifier {
102103
void updateClient(SSHClient? client) {
103104
if (!identical(state.client, client)) {
104105
unawaited(_disposePersistentShell());
106+
_usePersistentShellForStatus = true;
105107
}
106108
state = state.copyWith(client: client);
107109
}
@@ -392,27 +394,7 @@ class ServerNotifier extends _$ServerNotifier {
392394
customDir: spi.custom?.scriptDir,
393395
);
394396
// Loggers.app.info('Running status command for ${spi.name} (${state.status.system.name}): $statusCmd');
395-
if (state.status.system == SystemType.windows) {
396-
final execResult = await state.client?.run(statusCmd);
397-
if (execResult != null) {
398-
raw = SSHDecoder.decode(
399-
execResult,
400-
isWindows: true,
401-
context: 'GetStatus<${spi.name}>',
402-
);
403-
} else {
404-
raw = '';
405-
Loggers.app.warning('No status result from ${spi.name}');
406-
}
407-
} else {
408-
final shell = await _getPersistentShell();
409-
final statusTimeoutSeconds = Stores.setting.timeout.fetch();
410-
final statusTimeout = Duration(
411-
seconds: statusTimeoutSeconds <= 0 ? 5 : statusTimeoutSeconds,
412-
);
413-
final result = await shell.run(statusCmd, timeout: statusTimeout);
414-
raw = result.output;
415-
}
397+
raw = await _runStatusCommand(statusCmd);
416398

417399
if (raw.isEmpty) {
418400
TryLimiter.inc(sid);
@@ -540,6 +522,59 @@ class ServerNotifier extends _$ServerNotifier {
540522
TryLimiter.reset(sid);
541523
}
542524

525+
Future<String> _runStatusCommand(String statusCmd) async {
526+
final client = state.client;
527+
final spi = state.spi;
528+
529+
if (client == null) {
530+
Loggers.app.warning(
531+
'Client for ${spi.name} is null, skipping status fetch',
532+
);
533+
return '';
534+
}
535+
536+
if (state.status.system == SystemType.windows) {
537+
return _runStatusCommandWithExec(client, statusCmd, isWindows: true);
538+
}
539+
540+
if (!_usePersistentShellForStatus) {
541+
return _runStatusCommandWithExec(client, statusCmd);
542+
}
543+
544+
try {
545+
final shell = await _getPersistentShell();
546+
final statusTimeoutSeconds = Stores.setting.timeout.fetch();
547+
final statusTimeout = Duration(
548+
seconds: statusTimeoutSeconds <= 0 ? 5 : statusTimeoutSeconds,
549+
);
550+
final result = await shell.run(statusCmd, timeout: statusTimeout);
551+
return result.output;
552+
} on TimeoutException catch (e, s) {
553+
_usePersistentShellForStatus = false;
554+
await _disposePersistentShell();
555+
Loggers.app.warning(
556+
'Persistent shell status command timed out for ${spi.name}; fallback to exec for this connection',
557+
e,
558+
s,
559+
);
560+
return _runStatusCommandWithExec(client, statusCmd);
561+
}
562+
}
563+
564+
Future<String> _runStatusCommandWithExec(
565+
SSHClient client,
566+
String statusCmd, {
567+
bool isWindows = false,
568+
}) async {
569+
final spi = state.spi;
570+
final execResult = await client.run(statusCmd);
571+
return SSHDecoder.decode(
572+
execResult,
573+
isWindows: isWindows,
574+
context: 'GetStatus<${spi.name}>',
575+
);
576+
}
577+
543578
Future<PersistentShell> _getPersistentShell() async {
544579
final client = state.client;
545580
if (client == null) {

0 commit comments

Comments
 (0)