diff --git a/lib/core/extension/ssh_client.dart b/lib/core/extension/ssh_client.dart index 703cc8b0..c4959fad 100644 --- a/lib/core/extension/ssh_client.dart +++ b/lib/core/extension/ssh_client.dart @@ -1,4 +1,5 @@ import 'dart:async'; +import 'dart:typed_data'; import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/foundation.dart'; @@ -85,4 +86,42 @@ extension SSHClientX on SSHClient { stdin: stdin, ); } + + Future runWithSessionAction( + String command, { + bool runInPty = false, + bool stdout = true, + bool stderr = true, + Map? environment, + Future Function(SSHSession)? action, + }) async { + final session = await execute( + command, + pty: runInPty ? const SSHPtyConfig() : null, + environment: environment, + ); + + final result = BytesBuilder(copy: false); + final stdoutDone = Completer(); + final stderrDone = Completer(); + + session.stdout.listen( + stdout ? result.add : (_) {}, + onDone: stdoutDone.complete, + onError: stderrDone.completeError, + ); + + session.stderr.listen( + stderr ? result.add : (_) {}, + onDone: stderrDone.complete, + onError: stderrDone.completeError, + ); + + if (action != null) await action(session); + + await stdoutDone.future; + await stderrDone.future; + + return result.takeBytes(); + } } diff --git a/lib/data/model/app/shell_func.dart b/lib/data/model/app/shell_func.dart index b4636e1e..297a1f68 100644 --- a/lib/data/model/app/shell_func.dart +++ b/lib/data/model/app/shell_func.dart @@ -33,13 +33,17 @@ enum ShellFunc { /// Issue #168 /// Use `sh` for compatibility - static final installShellCmd = """ -mkdir -p $_homeVar/$_srvBoxDir -cat << 'EOF' > $_installShellPath -${ShellFunc.allScript} -EOF -chmod +x $_installShellPath -"""; + // static final installShellCmd = """ +// mkdir -p $_homeVar/$_srvBoxDir +// cat << 'EOF' > $_installShellPath +// ${ShellFunc.allScript} +// EOF +// chmod +x $_installShellPath +// """; + + static const installerMkdirs = "mkdir -p $_homeVar/$_srvBoxDir"; + static const installerShellWriter = "cat > $_installShellPath"; + static const installerPermissionModifier = "chmod +x $_installShellPath"; String get flag { switch (this) { diff --git a/lib/data/provider/server.dart b/lib/data/provider/server.dart index 8f195304..61edfba0 100644 --- a/lib/data/provider/server.dart +++ b/lib/data/provider/server.dart @@ -1,9 +1,12 @@ import 'dart:async'; import 'dart:io'; +import 'dart:typed_data'; +import 'dart:convert'; import 'package:computer/computer.dart'; import 'package:dartssh2/dartssh2.dart'; import 'package:flutter/material.dart'; +import 'package:toolbox/core/extension/ssh_client.dart'; import 'package:toolbox/core/utils/platform/path.dart'; import 'package:toolbox/data/model/app/shell_func.dart'; import 'package:toolbox/data/model/server/system.dart'; @@ -256,6 +259,29 @@ class ServerProvider extends ChangeNotifier { notifyListeners(); } + Future _writeInstallerScript(Server s) async { + void ensure(String? writeResult) { + if (writeResult == null || writeResult.isNotEmpty) { + throw Exception("Failed to write installer script: $writeResult"); + } + } + + final client = s.client; + if (client == null) { + throw Exception("Invalid state: s.client cannot be null"); + } + + ensure(await client.run(ShellFunc.installerMkdirs).string); + + ensure(await client.runWithSessionAction(ShellFunc.installerShellWriter, + action: (session) async { + session.stdin.add(utf8.encode(ShellFunc.allScript)); + }) + .string); + + ensure(await client.run(ShellFunc.installerPermissionModifier).string); + } + Future _getData(ServerPrivateInfo spi) async { final sid = spi.id; final s = _servers[sid]; @@ -304,11 +330,7 @@ class ServerProvider extends ChangeNotifier { // Write script to server // by ssh try { - final writeResult = - await s.client?.run(ShellFunc.installShellCmd).string; - if (writeResult == null || writeResult.isNotEmpty) { - throw Exception('$writeResult'); - } + await _writeInstallerScript(s); } on SSHAuthAbortError catch (e) { TryLimiter.inc(sid); s.status.err = e.toString();