[gtk-vnc] src: add support for VNC power control actions



commit e9526038756bfd2e0cce07a8412282613f51d0a7
Author: Daniel P. Berrangé <dan berrange com>
Date:   Fri Dec 11 11:42:00 2020 +0000

    src: add support for VNC power control actions
    
    This allows a client to request shutdown/reboot/reset of remote virtual
    machines when appropriate.
    
    Signed-off-by: Daniel P. Berrangé <berrange redhat com>

 src/libgvnc_sym.version |   3 ++
 src/vncconnection.c     | 132 ++++++++++++++++++++++++++++++++++++++++++++++--
 src/vncconnection.h     |  15 +++++-
 src/vncdisplay.c        |  45 +++++++++++++++++
 4 files changed, 190 insertions(+), 5 deletions(-)
---
diff --git a/src/libgvnc_sym.version b/src/libgvnc_sym.version
index d029a81..85b90ee 100644
--- a/src/libgvnc_sym.version
+++ b/src/libgvnc_sym.version
@@ -82,12 +82,15 @@
        vnc_connection_auth_get_type;
        vnc_connection_auth_vencrypt_get_type;
        vnc_connection_credential_get_type;
+       vnc_connection_power_action_get_type;
        vnc_connection_audio_enable;
        vnc_connection_audio_disable;
        vnc_connection_set_audio_format;
        vnc_connection_get_audio_format;
        vnc_connection_set_audio;
        vnc_connection_get_ledstate;
+       vnc_connection_get_power_control;
+       vnc_connection_power_control;
 
        vnc_util_set_debug;
        vnc_util_get_debug;
diff --git a/src/vncconnection.c b/src/vncconnection.c
index 13900b0..d335700 100644
--- a/src/vncconnection.c
+++ b/src/vncconnection.c
@@ -77,6 +77,7 @@ typedef enum {
     VNC_CONNECTION_SERVER_MESSAGE_SET_COLOR_MAP_ENTRIES = 1,
     VNC_CONNECTION_SERVER_MESSAGE_BELL = 2,
     VNC_CONNECTION_SERVER_MESSAGE_SERVER_CUT_TEXT = 3,
+    VNC_CONNECTION_SERVER_MESSAGE_XVP = 250,
     VNC_CONNECTION_SERVER_MESSAGE_QEMU = 255,
 } VncConnectionServerMessage;
 
@@ -98,6 +99,7 @@ typedef enum {
     VNC_CONNECTION_CLIENT_MESSAGE_KEY = 4,
     VNC_CONNECTION_CLIENT_MESSAGE_POINTER = 5,
     VNC_CONNECTION_CLIENT_MESSAGE_CUT_TEXT = 6,
+    VNC_CONNECTION_CLIENT_MESSAGE_XVP = 250,
     VNC_CONNECTION_CLIENT_MESSAGE_QEMU = 255,
 } VncConnectionClientMessage;
 
@@ -112,6 +114,10 @@ typedef enum {
     VNC_CONNECTION_CLIENT_MESSAGE_QEMU_AUDIO_SET_FORMAT = 2,
 } VncConnectionClientMessageQEMUAudio;
 
+typedef enum {
+    VNC_CONNECTION_XVP_FAIL = 0,
+    VNC_CONNECTION_XVP_INIT = 1,
+} VncConnectionXVPCode;
 
 typedef void vnc_connection_rich_cursor_blt_func(VncConnection *conn, guint8 *, guint8 *,
                                                  guint8 *, int, guint16, guint16);
@@ -201,6 +207,7 @@ struct _VncConnectionPrivate
     VncCursor *cursor;
     gboolean absPointer;
     gboolean sharedFlag;
+    gboolean powerControl;
 
     vnc_connection_rich_cursor_blt_func *rich_cursor_blt;
     vnc_connection_tight_compute_predicted_func *tight_compute_predicted;
@@ -259,6 +266,8 @@ enum {
     VNC_DESKTOP_RESIZE,
     VNC_PIXEL_FORMAT_CHANGED,
     VNC_LED_STATE,
+    VNC_POWER_CONTROL_INITIALIZED,
+    VNC_POWER_CONTROL_FAILED,
 
     VNC_AUTH_FAILURE,
     VNC_AUTH_UNSUPPORTED,
@@ -274,10 +283,7 @@ enum {
     VNC_LAST_SIGNAL,
 };
 
-static guint signals[VNC_LAST_SIGNAL] = { 0, 0, 0, 0,
-                                          0, 0, 0, 0,
-                                          0, 0, 0, 0,
-                                          0, 0, 0 };
+static guint signals[VNC_LAST_SIGNAL] = { 0 };
 
 #define nibhi(a) (((a) >> 4) & 0x0F)
 #define niblo(a) ((a) & 0x0F)
@@ -491,6 +497,7 @@ struct signal_data
         GValueArray *authCred;
         GValueArray *authTypes;
         const char *message;
+        int powerStatus;
     } params;
 };
 
@@ -559,6 +566,18 @@ static gboolean do_vnc_connection_emit_main_context(gpointer opaque)
                       data->params.ledstate);
         break;
 
+    case VNC_POWER_CONTROL_INITIALIZED:
+        g_signal_emit(G_OBJECT(data->conn),
+                      signals[data->signum],
+                      0);
+        break;
+
+    case VNC_POWER_CONTROL_FAILED:
+        g_signal_emit(G_OBJECT(data->conn),
+                      signals[data->signum],
+                      0);
+        break;
+
     case VNC_AUTH_FAILURE:
         g_signal_emit(G_OBJECT(data->conn),
                       signals[data->signum],
@@ -3538,6 +3557,35 @@ static gboolean vnc_connection_server_message(VncConnection *conn)
         vnc_connection_server_cut_text(conn, data, n_text);
         g_free(data);
     }        break;
+    case VNC_CONNECTION_SERVER_MESSAGE_XVP: {
+        guint8 pad[1];
+        guint8 version;
+        guint8 code;
+        struct signal_data s;
+        vnc_connection_read(conn, pad, 1);
+        version = vnc_connection_read_u8(conn);
+        code = vnc_connection_read_u8(conn);
+
+        if (version < 1) {
+            vnc_connection_set_error(conn, "Invalid XVP version %d", version);
+            break;
+        }
+
+        switch (code) {
+        case VNC_CONNECTION_XVP_FAIL:
+            VNC_DEBUG("XVP power control action failed");
+            vnc_connection_emit_main_context(conn, VNC_POWER_CONTROL_FAILED, &s);
+            break;
+        case VNC_CONNECTION_XVP_INIT:
+            VNC_DEBUG("XVP power control available");
+            priv->powerControl = TRUE;
+            vnc_connection_emit_main_context(conn, VNC_POWER_CONTROL_INITIALIZED, &s);
+            break;
+        default:
+            vnc_connection_set_error(conn, "Invalid XVP code %d", code);
+            break;
+        }
+    }   break;
     case VNC_CONNECTION_SERVER_MESSAGE_QEMU: {
         guint8  n_type;
 
@@ -5166,6 +5214,26 @@ static void vnc_connection_class_init(VncConnectionClass *klass)
                       G_TYPE_NONE,
                       0);
 
+    signals[VNC_POWER_CONTROL_INITIALIZED] =
+        g_signal_new ("vnc-power-control-initialized",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (VncConnectionClass, vnc_power_control_initialized),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
+    signals[VNC_POWER_CONTROL_FAILED] =
+        g_signal_new ("vnc-power-control-failed",
+                      G_OBJECT_CLASS_TYPE (object_class),
+                      G_SIGNAL_RUN_FIRST,
+                      G_STRUCT_OFFSET (VncConnectionClass, vnc_power_control_failed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE,
+                      0);
+
     signals[VNC_AUTH_FAILURE] =
         g_signal_new ("vnc-auth-failure",
                       G_OBJECT_CLASS_TYPE (object_class),
@@ -6336,6 +6404,62 @@ int vnc_connection_get_ledstate(VncConnection *conn)
     return priv->ledstate;
 }
 
+/**
+ * vnc_connection_get_power_control:
+ * @conn: (transfer none): the connection object
+ *
+ * Determine if the remote server supports power control.
+ * This will only be valid once the "vnc-initialized"
+ * signal has been emitted.
+ *
+ * Returns: TRUE if the server supports power control
+ */
+gboolean vnc_connection_get_power_control(VncConnection *conn)
+{
+    VncConnectionPrivate *priv = conn->priv;
+
+    return priv->powerControl;
+}
+
+
+/**
+ * vnc_connection_power_control:
+ * @conn: (transfer none): the connection object
+ *
+ * Perform a power control action on the remote server.
+ *
+ * This is only valid if the "vnc-power-control" signal
+ * has been emitted with a VNC_CONNECTION_POWER_STATUS_INIT
+ * code.
+ *
+ * The action should be assumed to be accepted unless
+ * "vnc-power-control" signal is emitted with a
+ * VNC_CONNECTION_POWER_STATUS_FAIL code.
+ *
+ * Returns: TRUE if the action was sent, FALSE if
+ * power control is not supported
+ */
+gboolean vnc_connection_power_control(VncConnection *conn,
+                                      VncConnectionPowerAction action)
+{
+    VncConnectionPrivate *priv = conn->priv;
+
+    if (!priv->powerControl)
+        return FALSE;
+
+    VNC_DEBUG("Sendng power control action %d", action);
+
+    vnc_connection_buffered_write_u8(conn, VNC_CONNECTION_CLIENT_MESSAGE_XVP);
+    vnc_connection_buffered_write_u8(conn, 0); /* pad */
+    vnc_connection_buffered_write_u8(conn, 1); /* version */
+    vnc_connection_buffered_write_u8(conn, action);
+    vnc_connection_buffered_flush(conn);
+
+    return !vnc_connection_has_error(conn);
+}
+
+
+
 /*
  * Local variables:
  *  c-indent-level: 4
diff --git a/src/vncconnection.h b/src/vncconnection.h
index f203caf..aac2a6b 100644
--- a/src/vncconnection.h
+++ b/src/vncconnection.h
@@ -80,12 +80,14 @@ struct _VncConnectionClass
     void (*vnc_disconnected)(VncConnection *conn);
     void (*vnc_led_state)(VncConnection *conn);
     void (*vnc_error)(VncConnection *conn, const char *message);
+    void (*vnc_power_control_initialized)(VncConnection *conn);
+    void (*vnc_power_control_failed)(VncConnection *conn);
 
     /*
      * If adding fields to this struct, remove corresponding
      * amount of padding to avoid changing overall struct size
      */
-    gpointer _vnc_reserved[VNC_PADDING_LARGE - 2];
+    gpointer _vnc_reserved[VNC_PADDING_LARGE - 4];
 };
 
 
@@ -123,6 +125,7 @@ typedef enum {
     VNC_CONNECTION_ENCODING_AUDIO = -259,
     VNC_CONNECTION_ENCODING_LED_STATE = -261,
 
+    VNC_CONNECTION_ENCODING_XVP = -309,
     VNC_CONNECTION_ENCODING_ALPHA_CURSOR = -314,
 } VncConnectionEncoding;
 
@@ -160,6 +163,12 @@ typedef enum {
     VNC_CONNECTION_CREDENTIAL_CLIENTNAME,
 } VncConnectionCredential;
 
+typedef enum {
+    VNC_CONNECTION_POWER_ACTION_SHUTDOWN = 2,
+    VNC_CONNECTION_POWER_ACTION_REBOOT = 3,
+    VNC_CONNECTION_POWER_ACTION_RESET = 4,
+} VncConnectionPowerAction;
+
 GType vnc_connection_get_type(void) G_GNUC_CONST;
 
 VncConnection *vnc_connection_new(void);
@@ -214,6 +223,7 @@ VncCursor *vnc_connection_get_cursor(VncConnection *conn);
 
 gboolean vnc_connection_get_abs_pointer(VncConnection *conn);
 gboolean vnc_connection_get_ext_key_event(VncConnection *conn);
+gboolean vnc_connection_get_power_control(VncConnection *conn);
 int vnc_connection_get_ledstate(VncConnection *conn);
 
 gboolean vnc_connection_set_audio(VncConnection *conn,
@@ -226,6 +236,9 @@ const VncAudioFormat *vnc_connection_get_audio_format(VncConnection *conn);
 gboolean vnc_connection_audio_enable(VncConnection *conn);
 gboolean vnc_connection_audio_disable(VncConnection *conn);
 
+gboolean vnc_connection_power_control(VncConnection *conn,
+                                      VncConnectionPowerAction action);
+
 
 G_END_DECLS
 
diff --git a/src/vncdisplay.c b/src/vncdisplay.c
index 1d14932..ae9a1bd 100644
--- a/src/vncdisplay.c
+++ b/src/vncdisplay.c
@@ -139,6 +139,8 @@ typedef enum
         VNC_SERVER_CUT_TEXT,
         VNC_BELL,
         VNC_ERROR,
+        VNC_POWER_CONTROL_INITIALIZED,
+        VNC_POWER_CONTROL_FAILED,
 
         LAST_SIGNAL
     } vnc_display_signals;
@@ -1601,6 +1603,21 @@ static void on_error(VncConnection *conn G_GNUC_UNUSED,
     VNC_DEBUG("VNC server error");
 }
 
+static void on_power_control_init(VncConnection *conn G_GNUC_UNUSED,
+                                  gpointer opaque)
+{
+    VncDisplay *obj = VNC_DISPLAY(opaque);
+
+    g_signal_emit(G_OBJECT(obj), signals[VNC_POWER_CONTROL_INITIALIZED], 0);
+}
+
+static void on_power_control_fail(VncConnection *conn G_GNUC_UNUSED,
+                                  gpointer opaque)
+{
+    VncDisplay *obj = VNC_DISPLAY(opaque);
+
+    g_signal_emit(G_OBJECT(obj), signals[VNC_POWER_CONTROL_FAILED], 0);
+}
 
 static void on_initialized(VncConnection *conn G_GNUC_UNUSED,
                            gpointer opaque)
@@ -1613,6 +1630,7 @@ static void on_initialized(VncConnection *conn G_GNUC_UNUSED,
      * server prefers when it has a choice to use */
     gint32 encodings[] = {  VNC_CONNECTION_ENCODING_TIGHT_JPEG5,
                             VNC_CONNECTION_ENCODING_TIGHT,
+                            VNC_CONNECTION_ENCODING_XVP,
                             VNC_CONNECTION_ENCODING_EXT_KEY_EVENT,
                             VNC_CONNECTION_ENCODING_LED_STATE,
                             VNC_CONNECTION_ENCODING_DESKTOP_RESIZE,
@@ -2421,6 +2439,29 @@ static void vnc_display_class_init(VncDisplayClass *klass)
                      g_cclosure_marshal_VOID__VOID,
                      G_TYPE_NONE,
                      0);
+
+    signals[VNC_POWER_CONTROL_INITIALIZED] =
+        g_signal_new("vnc-power-control-initialized",
+                     G_OBJECT_CLASS_TYPE (object_class),
+                     G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+                     0,
+                     NULL,
+                     NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE,
+                     0);
+
+    signals[VNC_POWER_CONTROL_FAILED] =
+        g_signal_new("vnc-power-control-failed",
+                     G_OBJECT_CLASS_TYPE (object_class),
+                     G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS,
+                     0,
+                     NULL,
+                     NULL,
+                     g_cclosure_marshal_VOID__VOID,
+                     G_TYPE_NONE,
+                     0);
+
 }
 
 static void vnc_display_init(VncDisplay *display)
@@ -2546,6 +2587,10 @@ static void vnc_display_init(VncDisplay *display)
                      G_CALLBACK(on_disconnected), display);
     g_signal_connect(G_OBJECT(priv->conn), "vnc-error",
                      G_CALLBACK(on_error), display);
+    g_signal_connect(G_OBJECT(priv->conn), "vnc-power-control-initialized",
+                     G_CALLBACK(on_power_control_init), display);
+    g_signal_connect(G_OBJECT(priv->conn), "vnc-power-control-failed",
+                     G_CALLBACK(on_power_control_fail), display);
 
     priv->keycode_map = vnc_display_keymap_gdk2rfb_table(&priv->keycode_maplen);
 }


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]