]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
ptyfwd: add support for additional out-of-band hotkeys in ptyfwd
authorLennart Poettering <lennart@poettering.net>
Sun, 2 Mar 2025 20:40:50 +0000 (21:40 +0100)
committerLennart Poettering <lennart@poettering.net>
Tue, 4 Mar 2025 22:02:31 +0000 (23:02 +0100)
Let's add the ability that ptyfwd tools can register additional hotkeys
that they then can handle.

So far the only hotkey we support is ^]^]^] to exit the ptyfwd session
abruptly. Staying close to this let's add ^]^]<char> for additional
commands.

src/shared/ptyfwd.c
src/shared/ptyfwd.h

index 8c3ea6b5ae90b816794875ce41daacd42fefdad9..d434f8e850f0b5291dc383e4d143ab919645674a 100644 (file)
@@ -103,6 +103,8 @@ struct PTYForward {
 
         PTYForwardHangupHandler hangup_handler;
         void *hangup_userdata;
+        PTYForwardHotkeyHandler hotkey_handler;
+        void *hotkey_userdata;
 
         char *background_color;
         AnsiColorState ansi_color_state;
@@ -198,17 +200,26 @@ static int pty_forward_done(PTYForward *f, int rcode) {
         return sd_event_exit(e, rcode < 0 ? EXIT_FAILURE : rcode);
 }
 
-static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
-        const char *p;
-
+typedef enum RequestOperation {
+        REQUEST_NOP,
+        REQUEST_EXIT,
+        REQUEST_HOTKEY_BASE,
+        REQUEST_HOTKEY_A = REQUEST_HOTKEY_BASE + 'a',
+        REQUEST_HOTKEY_Z = REQUEST_HOTKEY_BASE + 'z',
+        _REQUEST_OPERATION_MAX,
+        _REQUEST_OPERATION_INVALID = -EINVAL,
+} RequestOperation;
+
+static RequestOperation look_for_escape(PTYForward *f, const char *buffer, size_t n) {
         assert(f);
         assert(buffer);
         assert(n > 0);
 
-        for (p = buffer; p < buffer + n; p++) {
+        for (const char *p = buffer; p < buffer + n; p++) {
+
+                switch (*p) {
 
-                /* Check for ^] */
-                if (*p == 0x1D) {
+                case 0x1D: { /* Check for ^] */
                         usec_t nw = now(CLOCK_MONOTONIC);
 
                         if (f->escape_counter == 0 || nw > f->escape_timestamp + ESCAPE_USEC) {
@@ -218,15 +229,29 @@ static bool look_for_escape(PTYForward *f, const char *buffer, size_t n) {
                                 (f->escape_counter)++;
 
                                 if (f->escape_counter >= 3)
-                                        return true;
+                                        return REQUEST_EXIT;
                         }
-                } else {
+
+                        break;
+                }
+
+                case 'a'...'z':
+                        if (f->escape_counter == 2 &&
+                            now(CLOCK_MONOTONIC) <= f->escape_timestamp + ESCAPE_USEC) {
+                                f->escape_timestamp = 0;
+                                f->escape_counter = 0;
+                                return REQUEST_HOTKEY_BASE + *p;
+                        }
+
+                        _fallthrough_;
+
+                default:
                         f->escape_timestamp = 0;
                         f->escape_counter = 0;
                 }
         }
 
-        return false;
+        return REQUEST_NOP;
 }
 
 static bool ignore_vhangup(PTYForward *f) {
@@ -654,10 +679,17 @@ static int do_shovel(PTYForward *f) {
                         } else {
                                 /* Check if ^] has been pressed three times within one second. If we get this we quite
                                  * immediately. */
-                                if (look_for_escape(f, f->in_buffer + f->in_buffer_full, k))
-                                        return -ECANCELED;
-
+                                RequestOperation q = look_for_escape(f, f->in_buffer + f->in_buffer_full, k);
                                 f->in_buffer_full += (size_t) k;
+                                if (q < 0)
+                                        return q;
+                                if (q == REQUEST_EXIT)
+                                        return -ECANCELED;
+                                if (q >= REQUEST_HOTKEY_A && q <= REQUEST_HOTKEY_Z && f->hotkey_handler) {
+                                        r = f->hotkey_handler(f, q - REQUEST_HOTKEY_BASE, f->hotkey_userdata);
+                                        if (r < 0)
+                                                return r;
+                                }
                         }
 
                         did_something = true;
@@ -1091,6 +1123,13 @@ void pty_forward_set_hangup_handler(PTYForward *f, PTYForwardHangupHandler cb, v
         f->hangup_userdata = userdata;
 }
 
+void pty_forward_set_hotkey_handler(PTYForward *f, PTYForwardHotkeyHandler cb, void *userdata) {
+        assert(f);
+
+        f->hotkey_handler = cb;
+        f->hotkey_userdata = userdata;
+}
+
 bool pty_forward_drain(PTYForward *f) {
         assert(f);
 
index 8f096e42cbad4697001eedb8c1c9307498d29c81..b276cb15c59c0e8c238a05d48beac770c0a6770b 100644 (file)
@@ -24,6 +24,7 @@ typedef enum PTYForwardFlags {
 } PTYForwardFlags;
 
 typedef int (*PTYForwardHangupHandler)(PTYForward *f, int rcode, void *userdata);
+typedef int (*PTYForwardHotkeyHandler)(PTYForward *f, char key, void *userdata);
 
 #define N_PTY_FORWARD_SIGNALS 7
 extern const int pty_forward_signals[N_PTY_FORWARD_SIGNALS];
@@ -35,6 +36,7 @@ int pty_forward_set_ignore_vhangup(PTYForward *f, bool ignore_vhangup);
 bool pty_forward_get_ignore_vhangup(PTYForward *f);
 
 void pty_forward_set_hangup_handler(PTYForward *f, PTYForwardHangupHandler handler, void *userdata);
+void pty_forward_set_hotkey_handler(PTYForward *f, PTYForwardHotkeyHandler handler, void *userdata);
 
 bool pty_forward_drain(PTYForward *f);