Add button to launch SSH session
This commit is contained in:
parent
f93ae4bfe3
commit
fcf1ab92f0
|
@ -1,4 +1,5 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:convert';
|
||||||
import 'dart:core';
|
import 'dart:core';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:path/path.dart' as path;
|
import 'package:path/path.dart' as path;
|
||||||
|
@ -24,11 +25,14 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
List<String> _currentVms = [];
|
List<String> _currentVms = [];
|
||||||
Map<String, VmInfo> _activeVms = {};
|
Map<String, VmInfo> _activeVms = {};
|
||||||
final List<String> _spicyVms = [];
|
final List<String> _spicyVms = [];
|
||||||
|
final List<String> _sshVms = [];
|
||||||
|
String? _terminalEmulator;
|
||||||
Timer? refreshTimer;
|
Timer? refreshTimer;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
|
_getTerminalEmulator();
|
||||||
getPreference<String>(prefWorkingDirectory).then((pref) {
|
getPreference<String>(prefWorkingDirectory).then((pref) {
|
||||||
setState(() {
|
setState(() {
|
||||||
if (pref == null) {
|
if (pref == null) {
|
||||||
|
@ -38,7 +42,6 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
});
|
});
|
||||||
Future.delayed(Duration.zero, () => _getVms(context)); // Reload VM list when we enter the page.
|
Future.delayed(Duration.zero, () => _getVms(context)); // Reload VM list when we enter the page.
|
||||||
});
|
});
|
||||||
|
|
||||||
refreshTimer = Timer.periodic(const Duration(seconds: 5), (Timer t) {
|
refreshTimer = Timer.periodic(const Duration(seconds: 5), (Timer t) {
|
||||||
_getVms(context);
|
_getVms(context);
|
||||||
}); // Reload VM list every 5 seconds.
|
}); // Reload VM list every 5 seconds.
|
||||||
|
@ -50,6 +53,18 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void _getTerminalEmulator() async {
|
||||||
|
ProcessResult result = Process.runSync('x-terminal-emulator', ['-h']);
|
||||||
|
RegExp pattern = RegExp(r"usage:\s+([^\s]+)", multiLine: true, caseSensitive: false);
|
||||||
|
RegExpMatch? match = pattern.firstMatch(result.stdout);
|
||||||
|
if (match != null) {
|
||||||
|
setState(() {
|
||||||
|
_terminalEmulator = match.group(1);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VmInfo _parseVmInfo(name) {
|
VmInfo _parseVmInfo(name) {
|
||||||
VmInfo info = VmInfo();
|
VmInfo info = VmInfo();
|
||||||
List<String> lines = File(name + '/' + name + '.ports').readAsLinesSync();
|
List<String> lines = File(name + '/' + name + '.ports').readAsLinesSync();
|
||||||
|
@ -107,6 +122,18 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<bool> _detectSsh(int port) async {
|
||||||
|
bool isSSH = false;
|
||||||
|
try {
|
||||||
|
Socket socket = await Socket.connect('localhost', port);
|
||||||
|
isSSH = await socket.any((event) => utf8.decode(event).contains('SSH'));
|
||||||
|
socket.close();
|
||||||
|
return isSSH;
|
||||||
|
} catch (exception) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildVmList() {
|
Widget _buildVmList() {
|
||||||
List<Widget> _widgetList = [];
|
List<Widget> _widgetList = [];
|
||||||
_widgetList.add(
|
_widgetList.add(
|
||||||
|
@ -156,12 +183,24 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
List<Widget> _buildRow(String currentVm) {
|
List<Widget> _buildRow(String currentVm) {
|
||||||
final bool active = _activeVms.containsKey(currentVm);
|
final bool active = _activeVms.containsKey(currentVm);
|
||||||
final bool spicy = _spicyVms.contains(currentVm);
|
final bool spicy = _spicyVms.contains(currentVm);
|
||||||
|
final bool sshy = _sshVms.contains(currentVm);
|
||||||
VmInfo vmInfo = VmInfo();
|
VmInfo vmInfo = VmInfo();
|
||||||
String connectInfo = '';
|
String connectInfo = '';
|
||||||
if (active) {
|
if (active) {
|
||||||
vmInfo = _activeVms[currentVm]!;
|
vmInfo = _activeVms[currentVm]!;
|
||||||
if (vmInfo.sshPort != null) {
|
if (vmInfo.sshPort != null && _terminalEmulator != null) {
|
||||||
connectInfo += context.t('SSH port') + ': ' + vmInfo.sshPort! + ' ';
|
connectInfo += context.t('SSH port') + ': ' + vmInfo.sshPort! + ' ';
|
||||||
|
_detectSsh(int.parse(vmInfo.sshPort!)).then((sshRunning) {
|
||||||
|
if (sshRunning && !sshy) {
|
||||||
|
setState(() {
|
||||||
|
_sshVms.add(currentVm);
|
||||||
|
});
|
||||||
|
} else if (!sshRunning && sshy) {
|
||||||
|
setState(() {
|
||||||
|
_sshVms.remove(currentVm);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
if (vmInfo.spicePort != null) {
|
if (vmInfo.spicePort != null) {
|
||||||
connectInfo += context.t('SPICE port') + ': ' + vmInfo.spicePort! + ' ';
|
connectInfo += context.t('SPICE port') + ': ' + vmInfo.spicePort! + ' ';
|
||||||
|
@ -259,6 +298,56 @@ class _ManagerState extends State<Manager> with PreferencesMixin {
|
||||||
Process.start('spicy', ['-p', vmInfo.spicePort!]);
|
Process.start('spicy', ['-p', vmInfo.spicePort!]);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
TextButton(
|
||||||
|
child: Text('SSH'),
|
||||||
|
onPressed: !sshy ? null : () {
|
||||||
|
TextEditingController _usernameController = TextEditingController();
|
||||||
|
showDialog<bool>(
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) => AlertDialog(
|
||||||
|
title: Text('Launch SSH connection to $currentVm'),
|
||||||
|
content: TextField(
|
||||||
|
controller: _usernameController,
|
||||||
|
decoration: const InputDecoration(hintText: "SSH username"),
|
||||||
|
),
|
||||||
|
actions: <Widget>[
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, false),
|
||||||
|
child: const Text('Cancel'),
|
||||||
|
),
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context, true),
|
||||||
|
child: const Text('Connect'),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
).then((result) {
|
||||||
|
result = result ?? false;
|
||||||
|
if (result) {
|
||||||
|
List<String> sshArgs = ['ssh', '-p', vmInfo.sshPort!, _usernameController.text + '@localhost'];
|
||||||
|
switch(_terminalEmulator) {
|
||||||
|
case 'gnome-terminal':
|
||||||
|
case 'mate-terminal':
|
||||||
|
sshArgs.insert(0, '--');
|
||||||
|
break;
|
||||||
|
case 'xterm':
|
||||||
|
case 'konsole':
|
||||||
|
sshArgs.insert(0, '-e');
|
||||||
|
break;
|
||||||
|
case 'terminator':
|
||||||
|
case 'xfce4-terminal':
|
||||||
|
sshArgs.insert(0, '-x');
|
||||||
|
break;
|
||||||
|
case 'guake':
|
||||||
|
String command = sshArgs.join(' ');
|
||||||
|
sshArgs = ['-e', command];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Process.start(_terminalEmulator!, sshArgs);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
),
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
|
|
Loading…
Reference in New Issue