spice-power.h is the API towards the consumer/implementor (e.g. QEMU),
while power-channel.* is the actual library-internal implementation. The
consumer interface is required to start the channel, and currently empty.
---
 server/Makefile.am       |  3 +++
 server/meson.build       |  3 +++
 server/power-channel.cpp | 58 ++++++++++++++++++++++++++++++++++++++++
 server/power-channel.h   | 45 +++++++++++++++++++++++++++++++
 server/reds.cpp          |  9 +++++++
 server/spice-power.h     | 49 +++++++++++++++++++++++++++++++++
 server/spice-wrapped.h   |  1 +
 server/spice.h           |  1 +
 server/utils.c           |  1 +
 9 files changed, 170 insertions(+)

diff --git a/server/Makefile.am b/server/Makefile.am
index 5260051b..63ef40dc 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -74,6 +74,7 @@ libspice_serverinclude_HEADERS =              \
        spice-core.h                            \
        spice-input.h                           \
        spice-migration.h                       \
+       spice-power.h                           \
        spice-qxl.h                             \
        spice-server.h                          \
        spice-version.h                         \
@@ -141,6 +142,8 @@ libserver_la_SOURCES =                              \
        pixmap-cache.cpp                        \
        pixmap-cache.h                          \
        pop-visibility.h                        \
+       power-channel.cpp                       \
+       power-channel.h                         \
        push-visibility.h                       \
        red-channel.cpp                         \
        red-channel-capabilities.c              \
diff --git a/server/meson.build b/server/meson.build
index a8da777f..568b00cd 100644
--- a/server/meson.build
+++ b/server/meson.build
@@ -48,6 +48,7 @@ spice_server_headers = [
   'spice-core.h',
   'spice-input.h',
   'spice-migration.h',
+  'spice-power.h',
   'spice-qxl.h',
   'spice-server.h',
   'spice-replay.h',
@@ -121,6 +122,8 @@ spice_server_sources = [
   'net-utils.h',
   'pixmap-cache.cpp',
   'pixmap-cache.h',
+  'power-channel.cpp',
+  'power-channel.h',
   'red-channel.cpp',
   'red-channel-capabilities.c',
   'red-channel-capabilities.h',
diff --git a/server/power-channel.cpp b/server/power-channel.cpp
new file mode 100644
index 00000000..bdf390c6
--- /dev/null
+++ b/server/power-channel.cpp
@@ -0,0 +1,58 @@
+/*
+   Copyright (C) 2026 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+#include <config.h>
+
+#include "power-channel.h"
+#include "reds.h"
+#include "red-client.h"
+
+void PowerChannel::on_connect(RedClient *client, RedStream *stream, int 
migration,
+                               RedChannelCapabilities *caps)
+{
+    spice_debug("PowerChannel on_connect() called");
+    if (!red_stream_is_ssl(stream) && !client->during_migrate_at_target()) {
+        client->get_main()->push_notify("power channel is insecure");
+    }
+}
+
+red::shared_ptr<PowerChannel> power_channel_new(RedsState *reds)
+{
+    return red::make_shared<PowerChannel>(reds);
+}
+
+PowerChannel::PowerChannel(RedsState *reds):
+    RedChannel(reds, SPICE_CHANNEL_POWER, 0, RedChannel::MigrateAll)
+{
+    spice_debug("PowerChannel() called");
+    reds_register_channel(reds, this);
+}
+
+void power_attach(RedsState *reds, SpicePowerInstance *sin)
+{
+    sin->st = new PowerChannel(reds);
+}
+
+void power_detach(SpicePowerInstance *sin)
+{
+    PowerChannel *channel = sin->st;
+
+    if (!channel) {
+        return;
+    }
+
+    channel->destroy();
+}
diff --git a/server/power-channel.h b/server/power-channel.h
new file mode 100644
index 00000000..252a300f
--- /dev/null
+++ b/server/power-channel.h
@@ -0,0 +1,45 @@
+/*
+   Copyright (C) 2026 Red Hat, Inc.
+
+   This library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
+
+   This library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+   Lesser General Public License for more details.
+
+   You should have received a copy of the GNU Lesser General Public
+   License along with this library; if not, see <http://www.gnu.org/licenses/>.
+*/
+
+#ifndef POWER_CHANNEL_H_
+#define POWER_CHANNEL_H_
+
+// Power channel, dealing with power-off and hard-reset.
+// This include should only be used by reds.cpp and power-channel.cpp
+
+#include "red-channel.h"
+
+#include "push-visibility.h"
+
+class PowerChannel final: public RedChannel
+{
+public:
+    PowerChannel(RedsState *reds);
+
+private:
+    void on_connect(RedClient *client, RedStream *stream, int migration,
+                    RedChannelCapabilities *caps) override;
+};
+
+red::shared_ptr<PowerChannel> power_channel_new(RedsState *reds);
+
+void power_attach(RedsState *reds, SpicePowerInstance *sin);
+void power_detach(SpicePowerInstance *sin);
+
+#include "pop-visibility.h"
+
+#endif /* POWER_CHANNEL_H_ */
diff --git a/server/reds.cpp b/server/reds.cpp
index b0f1be6a..c13dce16 100644
--- a/server/reds.cpp
+++ b/server/reds.cpp
@@ -71,6 +71,7 @@
 #ifdef USE_SMARTCARD
 #include "smartcard.h"
 #endif
+#include "power-channel.h"
 #include "red-stream.h"
 #include "red-client.h"
 
@@ -3347,6 +3348,14 @@ SPICE_GNUC_VISIBLE int 
spice_server_add_interface(SpiceServer *reds,
         reds->migration_interface = SPICE_UPCAST(SpiceMigrateInstance, sin);
         reds->migration_interface->st =
             reinterpret_cast<SpiceMigrateState *>(static_cast<intptr_t>(1)); 
// dummy pointer
+    } else if (strcmp(base_interface->type, SPICE_INTERFACE_POWER) == 0) {
+        spice_debug("SPICE_INTERFACE_POWER");
+        if (base_interface->major_version != SPICE_INTERFACE_POWER_MAJOR ||
+            base_interface->minor_version > SPICE_INTERFACE_POWER_MINOR) {
+            spice_warning("unsupported power interface");
+            return -1;
+        }
+        power_attach(reds, SPICE_UPCAST(SpicePowerInstance, sin));
     }
 
     return 0;
diff --git a/server/spice-power.h b/server/spice-power.h
new file mode 100644
index 00000000..48d42364
--- /dev/null
+++ b/server/spice-power.h
@@ -0,0 +1,49 @@
+/*
+ *  Copyright (C) 2026 Red Hat, Inc.
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2.1 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, see 
<http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SPICE_POWER_H_
+#define SPICE_POWER_H_
+
+#if !defined(SPICE_H_INSIDE) && !defined(SPICE_SERVER_INTERNAL)
+#error "Only spice.h can be included directly."
+#endif
+
+#include "spice-core.h"
+
+SPICE_BEGIN_DECLS
+
+/* power interfaces */
+
+#define SPICE_INTERFACE_POWER "power"
+#define SPICE_INTERFACE_POWER_MAJOR 1
+#define SPICE_INTERFACE_POWER_MINOR 1
+typedef struct SpicePowerInterface SpicePowerInterface;
+typedef struct SpicePowerInstance SpicePowerInstance;
+typedef struct SpicePowerState SpicePowerState;
+
+struct SpicePowerInterface {
+    SpiceBaseInterface base;
+};
+
+struct SpicePowerInstance {
+    SpiceBaseInstance base;
+    SpicePowerState   *st;
+};
+
+SPICE_END_DECLS
+
+#endif /* SPICE_POWER_H_ */
diff --git a/server/spice-wrapped.h b/server/spice-wrapped.h
index 769f652f..5fbd62c4 100644
--- a/server/spice-wrapped.h
+++ b/server/spice-wrapped.h
@@ -26,6 +26,7 @@
 #define SpiceCharDeviceState RedCharDevice
 #define SpicePlaybackState PlaybackChannel
 #define SpiceRecordState RecordChannel
+#define SpicePowerState PowerChannel
 
 #include "push-visibility.h"
 struct RedCharDevice;
diff --git a/server/spice.h b/server/spice.h
index 901addf2..3a5dc798 100644
--- a/server/spice.h
+++ b/server/spice.h
@@ -29,6 +29,7 @@
 #include "spice-char.h"
 #include "spice-migration.h"
 #include "spice-replay.h"
+#include "spice-power.h"
 
 #undef SPICE_H_INSIDE
 
diff --git a/server/utils.c b/server/utils.c
index 6232991f..40f6c430 100644
--- a/server/utils.c
+++ b/server/utils.c
@@ -65,6 +65,7 @@ static const char *const channel_names[] = {
     [ SPICE_CHANNEL_USBREDIR ] = "usbredir",
     [ SPICE_CHANNEL_PORT     ] = "port",
     [ SPICE_CHANNEL_WEBDAV   ] = "webdav",
+    [ SPICE_CHANNEL_POWER    ] = "power",
 };
 
 /* Make sure the last channel in the protocol has a name.
-- 
2.52.0

Reply via email to