lxc enter: improve escape key handling
authorWolfgang Bumiller <w.bumiller@proxmox.com>
Mon, 11 Jun 2018 09:30:38 +0000 (11:30 +0200)
committerDietmar Maurer <dietmar@proxmox.com>
Tue, 12 Jun 2018 04:30:39 +0000 (06:30 +0200)
The escape key should not be passed through, this now only
happens when pressing the escape key twice (as it is with
tmux, screen, ssh, etc.).
Also prepared the code to handle a configurable escape key,
and restructured the handling to more easily allow adding
more keys. For instance we could add shortcuts for some
`pct` commands. Most of them are marginally useful, but
snapshot, migrate, shutdown or stop might still be
nice-to-have things.
The idea is also that if we reuse the code for serial
terminals for VMs we can add shortcuts to eg. paste the
current terminal size (probably the most useful one), or
to send sys-rqs or agent commands.

Signed-off-by: Wolfgang Bumiller <w.bumiller@proxmox.com>
PVE/APIClient/Commands/lxc.pm

index c60017b..b30e09f 100644 (file)
@@ -149,6 +149,16 @@ my $full_write = sub {
     return $len;
 };
 
+# Takes an escape character with an optional '^' prefix and returns an escape
+# character code.
+my $escapekey_to_char = sub {
+    my ($def) = @_;
+    if ($def =~ /^\^?([a-zA-Z])$/) {
+       return 1 + ord(lc($1)) - ord('a');
+    }
+    die "bad escape key definition: $def\n";
+};
+
 __PACKAGE__->register_method ({
     name => 'enter',
     path => 'enter',
@@ -171,6 +181,9 @@ __PACKAGE__->register_method ({
        my $config = PVE::APIClient::Config->load();
        my $conn = PVE::APIClient::Config->remote_conn($config, $param->{remote});
 
+       # FIXME: This should come from $config
+       my $escape_char = $escapekey_to_char->('a');
+
        # Get the real node from the resources endpoint
        my $resource_list = $conn->get("api2/json/cluster/resources", { type => 'vm'});
        my ($resource) = grep { $_->{type} eq "lxc" && $_->{vmid} eq $param->{vmid}} @$resource_list;
@@ -268,7 +281,7 @@ __PACKAGE__->register_method ({
 
            my $output_fh = \*STDOUT;
 
-           my $ctrl_a_pressed_before = 0;
+           my $in_escape_sequence;
 
            my $winch_received = 0;
            $SIG{WINCH} = sub { $winch_received = 1; };
@@ -292,7 +305,7 @@ __PACKAGE__->register_method ({
                my $len = length($$buffer_ref);
                my $nr = syswrite($fh, $$buffer_ref);
                if (!defined($nr)) {
-                   next if $! == EINTR || $! == EAGAIN;
+                   return if $! == EINTR || $! == EAGAIN;
                    die "drain buffer - write error - $!\n";
                }
                return $nr if !$nr;
@@ -344,11 +357,27 @@ __PACKAGE__->register_method ({
 
                            my $char = ord($buff);
 
-                           # check for CTRL-a-q
-                           return if $ctrl_a_pressed_before == 1 && $char == hex("0x71");
-
-                           $ctrl_a_pressed_before = ($char == hex("0x01") && $ctrl_a_pressed_before == 0) ? 1 : 0;
+                           # Handle escape sequences:
+                           if ($in_escape_sequence) {
+                               $in_escape_sequence = 0;
+                               if ($char == 0x71) {
+                                   # (escape, 'q')
+                                   return;
+                               } elsif ($char == $escape_char) {
+                                   # (escape, escape)
+                                   # Pass this one through as a single escapekey
+                               } else {
+                                   # Unknown escape sequence
+                                   # We could generate a bell or something...
+                                   # but for now just skip it
+                                   next;
+                               }
+                           } elsif ($char == $escape_char) {
+                               $in_escape_sequence = 1;
+                               next;
+                           }
 
+                           # Pass the key through:
                            $websock_buffer .= $create_websockt_frame->("0:" . $nr . ":" . $buff);
                            $write_select->add($web_socket);
                        }