The channel itself is just a pipe pushing bytes. The client on top of
the channel is responsible for all the message handling.
---
server/Makefile.am | 2 +
server/meson.build | 2 +
server/power-channel-client.cpp | 80 +++++++++++++++++++++++++++++++++
server/power-channel-client.h | 50 +++++++++++++++++++++
server/power-channel.cpp | 15 +++++++
server/power-channel.h | 6 +++
server/spice-power.h | 2 +
7 files changed, 157 insertions(+)
diff --git a/server/Makefile.am b/server/Makefile.am
index 63ef40dc..a35d041c 100644
--- a/server/Makefile.am
+++ b/server/Makefile.am
@@ -144,6 +144,8 @@ libserver_la_SOURCES = \
pop-visibility.h \
power-channel.cpp \
power-channel.h \
+ power-channel-client.cpp \
+ power-channel-client.h \
push-visibility.h \
red-channel.cpp \
red-channel-capabilities.c \
diff --git a/server/meson.build b/server/meson.build
index 568b00cd..e590aadc 100644
--- a/server/meson.build
+++ b/server/meson.build
@@ -124,6 +124,8 @@ spice_server_sources = [
'pixmap-cache.h',
'power-channel.cpp',
'power-channel.h',
+ 'power-channel-client.cpp',
+ 'power-channel-client.h',
'red-channel.cpp',
'red-channel-capabilities.c',
'red-channel-capabilities.h',
diff --git a/server/power-channel-client.cpp b/server/power-channel-client.cpp
new file mode 100644
index 00000000..ee0b3a2d
--- /dev/null
+++ b/server/power-channel-client.cpp
@@ -0,0 +1,80 @@
+/*
+ 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-client.h"
+#include "red-client.h"
+
+PowerChannelClient* power_channel_client_create(RedChannel *channel,
+ RedClient *client,
+ RedStream *stream,
+ RedChannelCapabilities *caps)
+{
+ spice_debug("power_channel_client_create() called");
+ auto rcc = red::make_shared<PowerChannelClient>(channel, client, stream,
caps);
+ if (!rcc->init()) {
+ return nullptr;
+ }
+ return rcc.get();
+}
+
+uint8_t *PowerChannelClient::alloc_recv_buf(uint16_t type, uint32_t size)
+{
+ spice_debug("PowerChannelClient alloc_recv_buf() called");
+ if (size > sizeof(recv_buf)) {
+ red_channel_warning(get_channel(), "error: too large incoming
message");
+ return nullptr;
+ }
+
+ return recv_buf;
+}
+
+void PowerChannelClient::release_recv_buf(uint16_t type, uint32_t size,
uint8_t *msg)
+{
+ spice_debug("PowerChannelClient release_recv_buf() called");
+}
+
+bool PowerChannelClient::handle_message(uint16_t type, uint32_t size, void
*message)
+{
+ spice_debug("PowerChannelClient handle_message() called");
+ PowerChannel *power_channel = get_channel();
+ SpicePowerInstance *sin = power_channel->sin;
+ SpicePowerInterface *sif;
+
+ if (!sin) {
+ spice_debug("PowerChannelClient handle_message() no interface");
+ return FALSE;
+ }
+ sif = SPICE_UPCAST(SpicePowerInterface, sin->base.sif);
+
+ switch (type) {
+ case SPICE_MSGC_POWER_RESET: {
+ spice_debug("Got a SPICE_MSGC_POWER_RESET message");
+ if (!sif->do_reset) {
+ spice_debug("PowerChannelClient handle_message() no reset
impl");
+ this->get_client()->get_main()->push_notify("Server does not
support power reset action.");
+ return TRUE;
+ }
+ sif->do_reset(sin);
+ break;
+ }
+ default:
+ return RedChannelClient::handle_message(type, size, message);
+ }
+
+ return TRUE;
+}
diff --git a/server/power-channel-client.h b/server/power-channel-client.h
new file mode 100644
index 00000000..fcc8f878
--- /dev/null
+++ b/server/power-channel-client.h
@@ -0,0 +1,50 @@
+/*
+ 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_CLIENT_H_
+#define POWER_CHANNEL_CLIENT_H_
+
+#include "red-channel-client.h"
+#include "power-channel.h"
+
+#include "push-visibility.h"
+
+class PowerChannelClient final: public RedChannelClient
+{
+private:
+ using RedChannelClient::RedChannelClient;
+
+ virtual uint8_t *alloc_recv_buf(uint16_t type, uint32_t size) override;
+ virtual void release_recv_buf(uint16_t type, uint32_t size, uint8_t *msg)
override;
+ virtual bool handle_message(uint16_t type, uint32_t size, void *message)
override;
+
+ PowerChannel* get_channel()
+ {
+ return static_cast<PowerChannel*>(RedChannelClient::get_channel());
+ }
+
+ uint8_t recv_buf[1024];
+};
+
+PowerChannelClient* power_channel_client_create(RedChannel *channel,
+ RedClient *client,
+ RedStream *stream,
+ RedChannelCapabilities *caps);
+
+#include "pop-visibility.h"
+
+#endif /* POWER_CHANNEL_CLIENT_H_ */
diff --git a/server/power-channel.cpp b/server/power-channel.cpp
index bdf390c6..57f8951f 100644
--- a/server/power-channel.cpp
+++ b/server/power-channel.cpp
@@ -17,6 +17,7 @@
#include <config.h>
#include "power-channel.h"
+#include "power-channel-client.h"
#include "reds.h"
#include "red-client.h"
@@ -27,6 +28,8 @@ void PowerChannel::on_connect(RedClient *client, RedStream
*stream, int migratio
if (!red_stream_is_ssl(stream) && !client->during_migrate_at_target()) {
client->get_main()->push_notify("power channel is insecure");
}
+
+ power_channel_client_create(this, client, stream, caps);
}
red::shared_ptr<PowerChannel> power_channel_new(RedsState *reds)
@@ -41,9 +44,21 @@ PowerChannel::PowerChannel(RedsState *reds):
reds_register_channel(reds, this);
}
+int PowerChannel::set_instance(SpicePowerInstance *new_sin)
+{
+ if (sin) {
+ red_channel_warning(this, "already have interface");
+ return -1;
+ }
+
+ sin = new_sin;
+ return 0;
+}
+
void power_attach(RedsState *reds, SpicePowerInstance *sin)
{
sin->st = new PowerChannel(reds);
+ sin->st->set_instance(sin);
}
void power_detach(SpicePowerInstance *sin)
diff --git a/server/power-channel.h b/server/power-channel.h
index 252a300f..a6fbf3ab 100644
--- a/server/power-channel.h
+++ b/server/power-channel.h
@@ -25,14 +25,20 @@
#include "push-visibility.h"
+class PowerChannelClient;
+
class PowerChannel final: public RedChannel
{
+ friend class PowerChannelClient;
public:
PowerChannel(RedsState *reds);
+ int set_instance(SpicePowerInstance *sin);
private:
void on_connect(RedClient *client, RedStream *stream, int migration,
RedChannelCapabilities *caps) override;
+
+ SpicePowerInstance *sin;
};
red::shared_ptr<PowerChannel> power_channel_new(RedsState *reds);
diff --git a/server/spice-power.h b/server/spice-power.h
index 48d42364..27d26399 100644
--- a/server/spice-power.h
+++ b/server/spice-power.h
@@ -37,6 +37,8 @@ typedef struct SpicePowerState SpicePowerState;
struct SpicePowerInterface {
SpiceBaseInterface base;
+
+ void (*do_reset)(SpicePowerInstance *sin);
};
struct SpicePowerInstance {
--
2.52.0