@@ -43,6 +43,7 @@ abstract class ServerState with _$ServerState {
4343@Riverpod (keepAlive: true )
4444class 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