mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2025-01-21 20:15:17 +00:00
Qemu remote protocol.
Since we are adding a new "per-hypervisor" protocol, we make it so that the qemu remote protocol uses a new PROTOCOL and PROGRAM number. This allows us to easily distinguish it from the normal REMOTE protocol. This necessitates changing the proc in remote_message_header from a "remote_procedure" to an "unsigned", which should be the same size (and thus preserve the on-wire protocol). Changes since v1: - Fixed up a couple of script problems in remote_generate_stubs.pl - Switch an int flag to a bool in dispatch.c Changes since v2: - None Changes since v3: - Change unsigned proc to signed proc, to conform to spec Signed-off-by: Chris Lalancette <clalance@redhat.com>
This commit is contained in:
parent
057e855324
commit
337d201ef2
2
cfg.mk
2
cfg.mk
@ -416,7 +416,7 @@ sc_prohibit_trailing_blank_lines:
|
||||
test $$found = 0
|
||||
|
||||
# Regex for grep -E that exempts generated files from style rules.
|
||||
preprocessor_exempt = (remote_(driver|protocol)\.h)$$
|
||||
preprocessor_exempt = ((qemu|remote)_(driver|protocol)\.h)$$
|
||||
# Enforce recommended preprocessor indentation style.
|
||||
sc_preprocessor_indentation:
|
||||
@if cppi --version >/dev/null 2>&1; then \
|
||||
|
@ -10,7 +10,8 @@ DAEMON_SOURCES = \
|
||||
remote_dispatch_table.h \
|
||||
remote_dispatch_args.h \
|
||||
remote_dispatch_ret.h \
|
||||
../src/remote/remote_protocol.c
|
||||
../src/remote/remote_protocol.c \
|
||||
../src/remote/qemu_protocol.c
|
||||
|
||||
AVAHI_SOURCES = \
|
||||
mdns.c mdns.h
|
||||
@ -96,7 +97,7 @@ libvirtd_LDADD = \
|
||||
$(SASL_LIBS) \
|
||||
$(POLKIT_LIBS)
|
||||
|
||||
libvirtd_LDADD += ../src/libvirt_util.la
|
||||
libvirtd_LDADD += ../src/libvirt_util.la ../src/libvirt-qemu.la
|
||||
|
||||
if WITH_DRIVER_MODULES
|
||||
libvirtd_LDADD += ../src/libvirt_driver.la
|
||||
@ -187,21 +188,38 @@ endif
|
||||
remote.c: remote_dispatch_prototypes.h \
|
||||
remote_dispatch_table.h \
|
||||
remote_dispatch_args.h \
|
||||
remote_dispatch_ret.h
|
||||
remote_dispatch_ret.h \
|
||||
qemu_dispatch_prototypes.h \
|
||||
qemu_dispatch_table.h \
|
||||
qemu_dispatch_args.h \
|
||||
qemu_dispatch_ret.h
|
||||
|
||||
REMOTE_PROTOCOL = $(top_srcdir)/src/remote/remote_protocol.x
|
||||
QEMU_PROTOCOL = $(top_srcdir)/src/remote/qemu_protocol.x
|
||||
|
||||
remote_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p $(REMOTE_PROTOCOL) > $@
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -p remote $(REMOTE_PROTOCOL) > $@
|
||||
|
||||
remote_dispatch_table.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -t $(REMOTE_PROTOCOL) > $@
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -t remote $(REMOTE_PROTOCOL) > $@
|
||||
|
||||
remote_dispatch_args.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -a $(REMOTE_PROTOCOL) > $@
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -a remote $(REMOTE_PROTOCOL) > $@
|
||||
|
||||
remote_dispatch_ret.h: $(srcdir)/remote_generate_stubs.pl $(REMOTE_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -r $(REMOTE_PROTOCOL) > $@
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -c -r remote $(REMOTE_PROTOCOL) > $@
|
||||
|
||||
qemu_dispatch_prototypes.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -p qemu $(QEMU_PROTOCOL) > $@
|
||||
|
||||
qemu_dispatch_table.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -t qemu $(QEMU_PROTOCOL) > $@
|
||||
|
||||
qemu_dispatch_args.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -a qemu $(QEMU_PROTOCOL) > $@
|
||||
|
||||
qemu_dispatch_ret.h: $(srcdir)/remote_generate_stubs.pl $(QEMU_PROTOCOL)
|
||||
$(AM_V_GEN)perl -w $(srcdir)/remote_generate_stubs.pl -r qemu $(QEMU_PROTOCOL) > $@
|
||||
|
||||
LOGROTATE_CONFS = libvirtd.qemu.logrotate libvirtd.lxc.logrotate \
|
||||
libvirtd.uml.logrotate
|
||||
|
@ -25,6 +25,7 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "dispatch.h"
|
||||
#include "remote.h"
|
||||
@ -339,10 +340,11 @@ cleanup:
|
||||
}
|
||||
|
||||
|
||||
int
|
||||
static int
|
||||
remoteDispatchClientCall (struct qemud_server *server,
|
||||
struct qemud_client *client,
|
||||
struct qemud_client_message *msg);
|
||||
struct qemud_client_message *msg,
|
||||
bool qemu_protocol);
|
||||
|
||||
|
||||
/*
|
||||
@ -359,12 +361,13 @@ remoteDispatchClientCall (struct qemud_server *server,
|
||||
* Returns 0 if the message was dispatched, -1 upon fatal error
|
||||
*/
|
||||
int
|
||||
remoteDispatchClientRequest (struct qemud_server *server,
|
||||
struct qemud_client *client,
|
||||
struct qemud_client_message *msg)
|
||||
remoteDispatchClientRequest(struct qemud_server *server,
|
||||
struct qemud_client *client,
|
||||
struct qemud_client_message *msg)
|
||||
{
|
||||
int ret;
|
||||
remote_error rerr;
|
||||
bool qemu_call;
|
||||
|
||||
DEBUG("prog=%d ver=%d type=%d status=%d serial=%d proc=%d",
|
||||
msg->hdr.prog, msg->hdr.vers, msg->hdr.type,
|
||||
@ -373,22 +376,33 @@ remoteDispatchClientRequest (struct qemud_server *server,
|
||||
memset(&rerr, 0, sizeof rerr);
|
||||
|
||||
/* Check version, etc. */
|
||||
if (msg->hdr.prog != REMOTE_PROGRAM) {
|
||||
if (msg->hdr.prog == REMOTE_PROGRAM)
|
||||
qemu_call = false;
|
||||
else if (msg->hdr.prog == QEMU_PROGRAM)
|
||||
qemu_call = true;
|
||||
else {
|
||||
remoteDispatchFormatError (&rerr,
|
||||
_("program mismatch (actual %x, expected %x)"),
|
||||
msg->hdr.prog, REMOTE_PROGRAM);
|
||||
_("program mismatch (actual %x, expected %x or %x)"),
|
||||
msg->hdr.prog, REMOTE_PROGRAM, QEMU_PROGRAM);
|
||||
goto error;
|
||||
}
|
||||
if (msg->hdr.vers != REMOTE_PROTOCOL_VERSION) {
|
||||
|
||||
if (!qemu_call && msg->hdr.vers != REMOTE_PROTOCOL_VERSION) {
|
||||
remoteDispatchFormatError (&rerr,
|
||||
_("version mismatch (actual %x, expected %x)"),
|
||||
msg->hdr.vers, REMOTE_PROTOCOL_VERSION);
|
||||
goto error;
|
||||
}
|
||||
else if (qemu_call && msg->hdr.vers != QEMU_PROTOCOL_VERSION) {
|
||||
remoteDispatchFormatError (&rerr,
|
||||
_("version mismatch (actual %x, expected %x)"),
|
||||
msg->hdr.vers, QEMU_PROTOCOL_VERSION);
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (msg->hdr.type) {
|
||||
case REMOTE_CALL:
|
||||
return remoteDispatchClientCall(server, client, msg);
|
||||
return remoteDispatchClientCall(server, client, msg, qemu_call);
|
||||
|
||||
case REMOTE_STREAM:
|
||||
/* Since stream data is non-acked, async, we may continue to received
|
||||
@ -430,10 +444,11 @@ error:
|
||||
*
|
||||
* Returns 0 if the reply was sent, or -1 upon fatal error
|
||||
*/
|
||||
int
|
||||
static int
|
||||
remoteDispatchClientCall (struct qemud_server *server,
|
||||
struct qemud_client *client,
|
||||
struct qemud_client_message *msg)
|
||||
struct qemud_client_message *msg,
|
||||
bool qemu_protocol)
|
||||
{
|
||||
XDR xdr;
|
||||
remote_error rerr;
|
||||
@ -472,7 +487,10 @@ remoteDispatchClientCall (struct qemud_server *server,
|
||||
}
|
||||
}
|
||||
|
||||
data = remoteGetDispatchData(msg->hdr.proc);
|
||||
if (qemu_protocol)
|
||||
data = qemuGetDispatchData(msg->hdr.proc);
|
||||
else
|
||||
data = remoteGetDispatchData(msg->hdr.proc);
|
||||
|
||||
if (!data) {
|
||||
remoteDispatchFormatError (&rerr, _("unknown procedure: %d"),
|
||||
|
@ -45,6 +45,7 @@
|
||||
# include <rpc/types.h>
|
||||
# include <rpc/xdr.h>
|
||||
# include "remote_protocol.h"
|
||||
# include "qemu_protocol.h"
|
||||
# include "logging.h"
|
||||
# include "threads.h"
|
||||
|
||||
|
5
daemon/qemu_dispatch_args.h
Normal file
5
daemon/qemu_dispatch_args.h
Normal file
@ -0,0 +1,5 @@
|
||||
/* Automatically generated by remote_generate_stubs.pl.
|
||||
* Do not edit this file. Any changes you make will be lost.
|
||||
*/
|
||||
|
||||
qemu_monitor_command_args val_qemu_monitor_command_args;
|
12
daemon/qemu_dispatch_prototypes.h
Normal file
12
daemon/qemu_dispatch_prototypes.h
Normal file
@ -0,0 +1,12 @@
|
||||
/* Automatically generated by remote_generate_stubs.pl.
|
||||
* Do not edit this file. Any changes you make will be lost.
|
||||
*/
|
||||
|
||||
static int qemuDispatchMonitorCommand(
|
||||
struct qemud_server *server,
|
||||
struct qemud_client *client,
|
||||
virConnectPtr conn,
|
||||
remote_message_header *hdr,
|
||||
remote_error *err,
|
||||
qemu_monitor_command_args *args,
|
||||
qemu_monitor_command_ret *ret);
|
5
daemon/qemu_dispatch_ret.h
Normal file
5
daemon/qemu_dispatch_ret.h
Normal file
@ -0,0 +1,5 @@
|
||||
/* Automatically generated by remote_generate_stubs.pl.
|
||||
* Do not edit this file. Any changes you make will be lost.
|
||||
*/
|
||||
|
||||
qemu_monitor_command_ret val_qemu_monitor_command_ret;
|
14
daemon/qemu_dispatch_table.h
Normal file
14
daemon/qemu_dispatch_table.h
Normal file
@ -0,0 +1,14 @@
|
||||
/* Automatically generated by remote_generate_stubs.pl.
|
||||
* Do not edit this file. Any changes you make will be lost.
|
||||
*/
|
||||
|
||||
{ /* (unused) => 0 */
|
||||
.fn = NULL,
|
||||
.args_filter = (xdrproc_t) xdr_void,
|
||||
.ret_filter = (xdrproc_t) xdr_void,
|
||||
},
|
||||
{ /* MonitorCommand => 1 */
|
||||
.fn = (dispatch_fn) qemuDispatchMonitorCommand,
|
||||
.args_filter = (xdrproc_t) xdr_qemu_monitor_command_args,
|
||||
.ret_filter = (xdrproc_t) xdr_qemu_monitor_command_ret,
|
||||
},
|
@ -57,6 +57,7 @@
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
#include "stream.h"
|
||||
#include "libvirt/libvirt-qemu.h"
|
||||
|
||||
#define VIR_FROM_THIS VIR_FROM_REMOTE
|
||||
#define REMOTE_DEBUG(fmt, ...) DEBUG(fmt, __VA_ARGS__)
|
||||
@ -81,11 +82,16 @@ static void make_nonnull_domain_snapshot (remote_nonnull_domain_snapshot *snapsh
|
||||
|
||||
|
||||
#include "remote_dispatch_prototypes.h"
|
||||
#include "qemu_dispatch_prototypes.h"
|
||||
|
||||
static const dispatch_data const dispatch_table[] = {
|
||||
#include "remote_dispatch_table.h"
|
||||
};
|
||||
|
||||
static const dispatch_data const qemu_dispatch_table[] = {
|
||||
#include "qemu_dispatch_table.h"
|
||||
};
|
||||
|
||||
const dispatch_data const *remoteGetDispatchData(int proc)
|
||||
{
|
||||
if (proc >= ARRAY_CARDINALITY(dispatch_table) ||
|
||||
@ -96,6 +102,16 @@ const dispatch_data const *remoteGetDispatchData(int proc)
|
||||
return &(dispatch_table[proc]);
|
||||
}
|
||||
|
||||
const dispatch_data const *qemuGetDispatchData(int proc)
|
||||
{
|
||||
if (proc >= ARRAY_CARDINALITY(qemu_dispatch_table) ||
|
||||
qemu_dispatch_table[proc].fn == NULL) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return &(qemu_dispatch_table[proc]);
|
||||
}
|
||||
|
||||
/* Prototypes */
|
||||
static void
|
||||
remoteDispatchDomainEventSend (struct qemud_client *client,
|
||||
@ -6564,6 +6580,35 @@ remoteDispatchDomainGetBlockInfo (struct qemud_server *server ATTRIBUTE_UNUSED,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qemuDispatchMonitorCommand (struct qemud_server *server ATTRIBUTE_UNUSED,
|
||||
struct qemud_client *client ATTRIBUTE_UNUSED,
|
||||
virConnectPtr conn,
|
||||
remote_message_header *hdr ATTRIBUTE_UNUSED,
|
||||
remote_error *rerr,
|
||||
qemu_monitor_command_args *args,
|
||||
qemu_monitor_command_ret *ret)
|
||||
{
|
||||
virDomainPtr domain;
|
||||
|
||||
domain = get_nonnull_domain(conn, args->domain);
|
||||
if (domain == NULL) {
|
||||
remoteDispatchConnError(rerr, conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (virDomainQemuMonitorCommand(domain, args->cmd, &ret->result,
|
||||
args->flags) == -1) {
|
||||
virDomainFree(domain);
|
||||
remoteDispatchConnError(rerr, conn);
|
||||
return -1;
|
||||
}
|
||||
|
||||
virDomainFree(domain);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
/*----- Helpers. -----*/
|
||||
|
||||
|
@ -35,6 +35,13 @@ typedef union {
|
||||
# include "remote_dispatch_ret.h"
|
||||
} dispatch_ret;
|
||||
|
||||
typedef union {
|
||||
# include "qemu_dispatch_args.h"
|
||||
} qemu_dispatch_args;
|
||||
|
||||
typedef union {
|
||||
# include "qemu_dispatch_ret.h"
|
||||
} qemu_dispatch_ret;
|
||||
|
||||
|
||||
|
||||
@ -67,6 +74,7 @@ typedef struct {
|
||||
|
||||
|
||||
const dispatch_data const *remoteGetDispatchData(int proc);
|
||||
const dispatch_data const *qemuGetDispatchData(int proc);
|
||||
|
||||
|
||||
|
||||
|
@ -1,7 +1,16 @@
|
||||
#!/usr/bin/perl -w
|
||||
#
|
||||
# This script parses remote_protocol.x and produces lots of boilerplate
|
||||
# code for both ends of the remote connection.
|
||||
# This script parses remote_protocol.x or qemu_protocol.x and produces lots of
|
||||
# boilerplate code for both ends of the remote connection.
|
||||
#
|
||||
# The first non-option argument specifies the prefix to be searched for, and
|
||||
# output to, the boilerplate code. The second non-option argument is the
|
||||
# file you want to operate on. For instance, to generate the dispatch table
|
||||
# for both remote_protocol.x and qemu_protocol.x, you would run the
|
||||
# following:
|
||||
#
|
||||
# remote_generate_stubs.pl -c -t remote ../src/remote/remote_protocol.x
|
||||
# remote_generate_stubs.pl -t qemu ../src/remote/qemu_protocol.x
|
||||
#
|
||||
# By Richard Jones <rjones@redhat.com>
|
||||
|
||||
@ -10,8 +19,12 @@ use strict;
|
||||
use Getopt::Std;
|
||||
|
||||
# Command line options.
|
||||
our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d);
|
||||
getopts ('ptard');
|
||||
our ($opt_p, $opt_t, $opt_a, $opt_r, $opt_d, $opt_c);
|
||||
getopts ('ptardc');
|
||||
|
||||
my $structprefix = $ARGV[0];
|
||||
my $procprefix = uc $structprefix;
|
||||
shift;
|
||||
|
||||
# Convert name_of_call to NameOfCall.
|
||||
sub name_to_ProcName {
|
||||
@ -25,47 +38,50 @@ sub name_to_ProcName {
|
||||
# opinion about the name, args and return type of each RPC.
|
||||
my ($name, $ProcName, $id, %calls, @calls);
|
||||
|
||||
# REMOTE_PROC_CLOSE has no args or ret.
|
||||
$calls{close} = {
|
||||
name => "close",
|
||||
ProcName => "Close",
|
||||
UC_NAME => "CLOSE",
|
||||
args => "void",
|
||||
ret => "void",
|
||||
};
|
||||
# only generate a close method if -c was passed
|
||||
if ($opt_c) {
|
||||
# REMOTE_PROC_CLOSE has no args or ret.
|
||||
$calls{close} = {
|
||||
name => "close",
|
||||
ProcName => "Close",
|
||||
UC_NAME => "CLOSE",
|
||||
args => "void",
|
||||
ret => "void",
|
||||
};
|
||||
}
|
||||
|
||||
while (<>) {
|
||||
if (/^struct remote_(.*)_args/) {
|
||||
if (/^struct ${structprefix}_(.*)_args/) {
|
||||
$name = $1;
|
||||
$ProcName = name_to_ProcName ($name);
|
||||
|
||||
die "duplicate definition of remote_${name}_args"
|
||||
die "duplicate definition of ${structprefix}_${name}_args"
|
||||
if exists $calls{$name};
|
||||
|
||||
$calls{$name} = {
|
||||
name => $name,
|
||||
ProcName => $ProcName,
|
||||
UC_NAME => uc $name,
|
||||
args => "remote_${name}_args",
|
||||
args => "${structprefix}_${name}_args",
|
||||
ret => "void",
|
||||
};
|
||||
|
||||
} elsif (/^struct remote_(.*)_ret/) {
|
||||
} elsif (/^struct ${structprefix}_(.*)_ret/) {
|
||||
$name = $1;
|
||||
$ProcName = name_to_ProcName ($name);
|
||||
|
||||
if (exists $calls{$name}) {
|
||||
$calls{$name}->{ret} = "remote_${name}_ret";
|
||||
$calls{$name}->{ret} = "${structprefix}_${name}_ret";
|
||||
} else {
|
||||
$calls{$name} = {
|
||||
name => $name,
|
||||
ProcName => $ProcName,
|
||||
UC_NAME => uc $name,
|
||||
args => "void",
|
||||
ret => "remote_${name}_ret"
|
||||
ret => "${structprefix}_${name}_ret"
|
||||
}
|
||||
}
|
||||
} elsif (/^struct remote_(.*)_msg/) {
|
||||
} elsif (/^struct ${structprefix}_(.*)_msg/) {
|
||||
$name = $1;
|
||||
$ProcName = name_to_ProcName ($name);
|
||||
|
||||
@ -73,9 +89,9 @@ while (<>) {
|
||||
name => $name,
|
||||
ProcName => $ProcName,
|
||||
UC_NAME => uc $name,
|
||||
msg => "remote_${name}_msg"
|
||||
msg => "${structprefix}_${name}_msg"
|
||||
}
|
||||
} elsif (/^\s*REMOTE_PROC_(.*?)\s+=\s+(\d+),?$/) {
|
||||
} elsif (/^\s*${procprefix}_PROC_(.*?)\s+=\s+(\d+),?$/) {
|
||||
$name = lc $1;
|
||||
$id = $2;
|
||||
$ProcName = name_to_ProcName ($name);
|
||||
@ -111,7 +127,7 @@ elsif ($opt_p) {
|
||||
# Skip things which are REMOTE_MESSAGE
|
||||
next if $calls{$_}->{msg};
|
||||
|
||||
print "static int remoteDispatch$calls{$_}->{ProcName}(\n";
|
||||
print "static int ${structprefix}Dispatch$calls{$_}->{ProcName}(\n";
|
||||
print " struct qemud_server *server,\n";
|
||||
print " struct qemud_client *client,\n";
|
||||
print " virConnectPtr conn,\n";
|
||||
@ -152,7 +168,7 @@ elsif ($opt_t) {
|
||||
for ($id = 0 ; $id <= $#calls ; $id++) {
|
||||
if (defined $calls[$id] && !$calls[$id]->{msg}) {
|
||||
print "{ /* $calls[$id]->{ProcName} => $id */\n";
|
||||
print " .fn = (dispatch_fn) remoteDispatch$calls[$id]->{ProcName},\n";
|
||||
print " .fn = (dispatch_fn) ${structprefix}Dispatch$calls[$id]->{ProcName},\n";
|
||||
if ($calls[$id]->{args} ne "void") {
|
||||
print " .args_filter = (xdrproc_t) xdr_$calls[$id]->{args},\n";
|
||||
} else {
|
||||
|
@ -155,7 +155,9 @@ REMOTE_DRIVER_SOURCES = \
|
||||
gnutls_1_0_compat.h \
|
||||
remote/remote_driver.c remote/remote_driver.h \
|
||||
remote/remote_protocol.c \
|
||||
remote/remote_protocol.h
|
||||
remote/remote_protocol.h \
|
||||
remote/qemu_protocol.c \
|
||||
remote/qemu_protocol.h
|
||||
|
||||
EXTRA_DIST += remote/remote_protocol.x remote/rpcgen_fix.pl
|
||||
|
||||
@ -489,7 +491,7 @@ if HAVE_RPCGEN
|
||||
# Support for non-GLIB rpcgen is here as a convenience for
|
||||
# non-Linux people needing to test changes during dev.
|
||||
#
|
||||
rpcgen:
|
||||
rpcgen-normal:
|
||||
rm -f rp.c-t rp.h-t rp.c-t1 rp.c-t2 rp.h-t1
|
||||
$(RPCGEN) -h -o rp.h-t $(srcdir)/remote/remote_protocol.x
|
||||
$(RPCGEN) -c -o rp.c-t $(srcdir)/remote/remote_protocol.x
|
||||
@ -506,6 +508,37 @@ else
|
||||
mv -f rp.h-t $(srcdir)/remote/remote_protocol.h
|
||||
mv -f rp.c-t $(srcdir)/remote/remote_protocol.c
|
||||
endif
|
||||
|
||||
rpcgen-qemu:
|
||||
rm -f rp_qemu.c-t rp_qemu.h-t rp_qemu.c-t1 rp_qemu.c-t2 rp_qemu.h-t1
|
||||
$(RPCGEN) -h -o rp_qemu.h-t $(srcdir)/remote/qemu_protocol.x
|
||||
$(RPCGEN) -c -o rp_qemu.c-t $(srcdir)/remote/qemu_protocol.x
|
||||
if HAVE_GLIBC_RPCGEN
|
||||
perl -w $(srcdir)/remote/rpcgen_fix.pl rp_qemu.h-t > rp_qemu.h-t1
|
||||
perl -w $(srcdir)/remote/rpcgen_fix.pl rp_qemu.c-t > rp_qemu.c-t1
|
||||
(echo '#include <config.h>'; cat rp_qemu.c-t1) > rp_qemu.c-t2
|
||||
chmod 0444 rp_qemu.c-t2 rp_qemu.h-t1
|
||||
mv -f rp_qemu.h-t1 $(srcdir)/remote/qemu_protocol.h
|
||||
mv -f rp_qemu.c-t2 $(srcdir)/remote/qemu_protocol.c
|
||||
rm -f rp_qemu.c-t rp_qemu.h-t rp_qemu.c-t1
|
||||
else
|
||||
chmod 0444 rp_qemu.c-t rp_qemu.h-t
|
||||
mv -f rp_qemu.h-t $(srcdir)/remote/qemu_protocol.h
|
||||
mv -f rp_qemu.c-t $(srcdir)/remote/qemu_protocol.c
|
||||
endif
|
||||
|
||||
#
|
||||
# Maintainer-only target for re-generating the derived .c/.h source
|
||||
# files, which are actually derived from the .x file.
|
||||
#
|
||||
# For committing protocol changes to GIT, the GLIBC rpcgen *must*
|
||||
# be used.
|
||||
#
|
||||
# Support for non-GLIB rpcgen is here as a convenience for
|
||||
# non-Linux people needing to test changes during dev.
|
||||
#
|
||||
rpcgen: rpcgen-normal rpcgen-qemu
|
||||
|
||||
endif
|
||||
|
||||
remote/remote_protocol.c: remote/remote_protocol.h
|
||||
|
41
src/remote/qemu_protocol.c
Normal file
41
src/remote/qemu_protocol.c
Normal file
@ -0,0 +1,41 @@
|
||||
#include <config.h>
|
||||
/*
|
||||
* Please do not edit this file.
|
||||
* It was generated using rpcgen.
|
||||
*/
|
||||
|
||||
#include "./remote/qemu_protocol.h"
|
||||
#include "internal.h"
|
||||
#include "remote_protocol.h"
|
||||
#include <arpa/inet.h>
|
||||
|
||||
bool_t
|
||||
xdr_qemu_monitor_command_args (XDR *xdrs, qemu_monitor_command_args *objp)
|
||||
{
|
||||
|
||||
if (!xdr_remote_nonnull_domain (xdrs, &objp->domain))
|
||||
return FALSE;
|
||||
if (!xdr_remote_nonnull_string (xdrs, &objp->cmd))
|
||||
return FALSE;
|
||||
if (!xdr_int (xdrs, &objp->flags))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool_t
|
||||
xdr_qemu_monitor_command_ret (XDR *xdrs, qemu_monitor_command_ret *objp)
|
||||
{
|
||||
|
||||
if (!xdr_remote_nonnull_string (xdrs, &objp->result))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
bool_t
|
||||
xdr_qemu_procedure (XDR *xdrs, qemu_procedure *objp)
|
||||
{
|
||||
|
||||
if (!xdr_enum (xdrs, (enum_t *) objp))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
57
src/remote/qemu_protocol.h
Normal file
57
src/remote/qemu_protocol.h
Normal file
@ -0,0 +1,57 @@
|
||||
/*
|
||||
* Please do not edit this file.
|
||||
* It was generated using rpcgen.
|
||||
*/
|
||||
|
||||
#ifndef _RP_QEMU_H_RPCGEN
|
||||
#define _RP_QEMU_H_RPCGEN
|
||||
|
||||
#include <rpc/rpc.h>
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "internal.h"
|
||||
#include "remote_protocol.h"
|
||||
#include <arpa/inet.h>
|
||||
|
||||
struct qemu_monitor_command_args {
|
||||
remote_nonnull_domain domain;
|
||||
remote_nonnull_string cmd;
|
||||
int flags;
|
||||
};
|
||||
typedef struct qemu_monitor_command_args qemu_monitor_command_args;
|
||||
|
||||
struct qemu_monitor_command_ret {
|
||||
remote_nonnull_string result;
|
||||
};
|
||||
typedef struct qemu_monitor_command_ret qemu_monitor_command_ret;
|
||||
#define QEMU_PROGRAM 0x20008087
|
||||
#define QEMU_PROTOCOL_VERSION 1
|
||||
|
||||
enum qemu_procedure {
|
||||
QEMU_PROC_MONITOR_COMMAND = 1,
|
||||
};
|
||||
typedef enum qemu_procedure qemu_procedure;
|
||||
|
||||
/* the xdr functions */
|
||||
|
||||
#if defined(__STDC__) || defined(__cplusplus)
|
||||
extern bool_t xdr_qemu_monitor_command_args (XDR *, qemu_monitor_command_args*);
|
||||
extern bool_t xdr_qemu_monitor_command_ret (XDR *, qemu_monitor_command_ret*);
|
||||
extern bool_t xdr_qemu_procedure (XDR *, qemu_procedure*);
|
||||
|
||||
#else /* K&R C */
|
||||
extern bool_t xdr_qemu_monitor_command_args ();
|
||||
extern bool_t xdr_qemu_monitor_command_ret ();
|
||||
extern bool_t xdr_qemu_procedure ();
|
||||
|
||||
#endif /* K&R C */
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* !_RP_QEMU_H_RPCGEN */
|
46
src/remote/qemu_protocol.x
Normal file
46
src/remote/qemu_protocol.x
Normal file
@ -0,0 +1,46 @@
|
||||
/* -*- c -*-
|
||||
* qemu_protocol.x: private protocol for communicating between
|
||||
* remote_internal driver and libvirtd. This protocol is
|
||||
* internal and may change at any time.
|
||||
*
|
||||
* Copyright (C) 2010 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, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* Author: Chris Lalancette <clalance@redhat.com>
|
||||
*/
|
||||
|
||||
%#include "internal.h"
|
||||
%#include "remote_protocol.h"
|
||||
%#include <arpa/inet.h>
|
||||
|
||||
/*----- Protocol. -----*/
|
||||
struct qemu_monitor_command_args {
|
||||
remote_nonnull_domain domain;
|
||||
remote_nonnull_string cmd;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct qemu_monitor_command_ret {
|
||||
remote_nonnull_string result;
|
||||
};
|
||||
|
||||
/* Define the program number, protocol version and procedure numbers here. */
|
||||
const QEMU_PROGRAM = 0x20008087;
|
||||
const QEMU_PROTOCOL_VERSION = 1;
|
||||
|
||||
enum qemu_procedure {
|
||||
QEMU_PROC_MONITOR_COMMAND = 1
|
||||
};
|
@ -77,6 +77,7 @@
|
||||
#include "qparams.h"
|
||||
#include "remote_driver.h"
|
||||
#include "remote_protocol.h"
|
||||
#include "qemu_protocol.h"
|
||||
#include "memory.h"
|
||||
#include "util.h"
|
||||
#include "event.h"
|
||||
@ -199,8 +200,9 @@ struct private_data {
|
||||
};
|
||||
|
||||
enum {
|
||||
REMOTE_CALL_IN_OPEN = 1,
|
||||
REMOTE_CALL_QUIET_MISSING_RPC = 2,
|
||||
REMOTE_CALL_IN_OPEN = (1 << 0),
|
||||
REMOTE_CALL_QUIET_MISSING_RPC = (1 << 1),
|
||||
REMOTE_QEMU_CALL = (1 << 2),
|
||||
};
|
||||
|
||||
|
||||
@ -8859,9 +8861,49 @@ done:
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int
|
||||
remoteQemuDomainMonitorCommand (virDomainPtr domain, const char *cmd,
|
||||
char **result, unsigned int flags)
|
||||
{
|
||||
int rv = -1;
|
||||
qemu_monitor_command_args args;
|
||||
qemu_monitor_command_ret ret;
|
||||
struct private_data *priv = domain->conn->privateData;
|
||||
|
||||
remoteDriverLock(priv);
|
||||
|
||||
make_nonnull_domain(&args.domain, domain);
|
||||
args.cmd = (char *)cmd;
|
||||
args.flags = flags;
|
||||
|
||||
memset (&ret, 0, sizeof ret);
|
||||
if (call (domain->conn, priv, REMOTE_QEMU_CALL, QEMU_PROC_MONITOR_COMMAND,
|
||||
(xdrproc_t) xdr_qemu_monitor_command_args, (char *) &args,
|
||||
(xdrproc_t) xdr_qemu_monitor_command_ret, (char *) &ret) == -1)
|
||||
goto done;
|
||||
|
||||
*result = strdup(ret.result);
|
||||
if (*result == NULL) {
|
||||
|
||||
virReportOOMError();
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rv = 0;
|
||||
|
||||
cleanup:
|
||||
xdr_free ((xdrproc_t) xdr_qemu_monitor_command_ret, (char *) &ret);
|
||||
|
||||
done:
|
||||
remoteDriverUnlock(priv);
|
||||
return rv;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static struct remote_thread_call *
|
||||
prepareCall(struct private_data *priv,
|
||||
int flags,
|
||||
int proc_nr,
|
||||
xdrproc_t args_filter, char *args,
|
||||
xdrproc_t ret_filter, char *ret)
|
||||
@ -8889,8 +8931,14 @@ prepareCall(struct private_data *priv,
|
||||
rv->ret = ret;
|
||||
rv->want_reply = 1;
|
||||
|
||||
hdr.prog = REMOTE_PROGRAM;
|
||||
hdr.vers = REMOTE_PROTOCOL_VERSION;
|
||||
if (flags & REMOTE_QEMU_CALL) {
|
||||
hdr.prog = QEMU_PROGRAM;
|
||||
hdr.vers = QEMU_PROTOCOL_VERSION;
|
||||
}
|
||||
else {
|
||||
hdr.prog = REMOTE_PROGRAM;
|
||||
hdr.vers = REMOTE_PROTOCOL_VERSION;
|
||||
}
|
||||
hdr.proc = proc_nr;
|
||||
hdr.type = REMOTE_CALL;
|
||||
hdr.serial = rv->serial;
|
||||
@ -9237,7 +9285,6 @@ remoteIODecodeMessageLength(struct private_data *priv) {
|
||||
|
||||
static int
|
||||
processCallDispatchReply(virConnectPtr conn, struct private_data *priv,
|
||||
int in_open,
|
||||
remote_message_header *hdr,
|
||||
XDR *xdr);
|
||||
|
||||
@ -9249,18 +9296,19 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv,
|
||||
|
||||
static int
|
||||
processCallDispatchStream(virConnectPtr conn, struct private_data *priv,
|
||||
int in_open,
|
||||
remote_message_header *hdr,
|
||||
XDR *xdr);
|
||||
|
||||
|
||||
static int
|
||||
processCallDispatch(virConnectPtr conn, struct private_data *priv,
|
||||
int in_open) {
|
||||
int flags) {
|
||||
XDR xdr;
|
||||
struct remote_message_header hdr;
|
||||
int len = priv->bufferLength - 4;
|
||||
int rv = -1;
|
||||
int expectedprog;
|
||||
int expectedvers;
|
||||
|
||||
/* Length word has already been read */
|
||||
priv->bufferOffset = 4;
|
||||
@ -9274,35 +9322,40 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv,
|
||||
|
||||
priv->bufferOffset += xdr_getpos(&xdr);
|
||||
|
||||
expectedprog = REMOTE_PROGRAM;
|
||||
expectedvers = REMOTE_PROTOCOL_VERSION;
|
||||
if (flags & REMOTE_QEMU_CALL) {
|
||||
expectedprog = QEMU_PROGRAM;
|
||||
expectedvers = QEMU_PROTOCOL_VERSION;
|
||||
}
|
||||
|
||||
/* Check program, version, etc. are what we expect. */
|
||||
if (hdr.prog != REMOTE_PROGRAM) {
|
||||
if (hdr.prog != expectedprog) {
|
||||
remoteError(VIR_ERR_RPC,
|
||||
_("unknown program (received %x, expected %x)"),
|
||||
hdr.prog, REMOTE_PROGRAM);
|
||||
hdr.prog, expectedprog);
|
||||
return -1;
|
||||
}
|
||||
if (hdr.vers != REMOTE_PROTOCOL_VERSION) {
|
||||
if (hdr.vers != expectedvers) {
|
||||
remoteError(VIR_ERR_RPC,
|
||||
_("unknown protocol version (received %x, expected %x)"),
|
||||
hdr.vers, REMOTE_PROTOCOL_VERSION);
|
||||
hdr.vers, expectedvers);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
switch (hdr.type) {
|
||||
case REMOTE_REPLY: /* Normal RPC replies */
|
||||
rv = processCallDispatchReply(conn, priv, in_open,
|
||||
&hdr, &xdr);
|
||||
rv = processCallDispatchReply(conn, priv, &hdr, &xdr);
|
||||
break;
|
||||
|
||||
case REMOTE_MESSAGE: /* Async notifications */
|
||||
rv = processCallDispatchMessage(conn, priv, in_open,
|
||||
rv = processCallDispatchMessage(conn, priv, flags & REMOTE_CALL_IN_OPEN,
|
||||
&hdr, &xdr);
|
||||
break;
|
||||
|
||||
case REMOTE_STREAM: /* Stream protocol */
|
||||
rv = processCallDispatchStream(conn, priv, in_open,
|
||||
&hdr, &xdr);
|
||||
rv = processCallDispatchStream(conn, priv, &hdr, &xdr);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -9321,7 +9374,6 @@ processCallDispatch(virConnectPtr conn, struct private_data *priv,
|
||||
static int
|
||||
processCallDispatchReply(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
struct private_data *priv,
|
||||
int in_open ATTRIBUTE_UNUSED,
|
||||
remote_message_header *hdr,
|
||||
XDR *xdr) {
|
||||
struct remote_thread_call *thecall;
|
||||
@ -9441,7 +9493,6 @@ processCallDispatchMessage(virConnectPtr conn, struct private_data *priv,
|
||||
static int
|
||||
processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
struct private_data *priv,
|
||||
int in_open ATTRIBUTE_UNUSED,
|
||||
remote_message_header *hdr,
|
||||
XDR *xdr) {
|
||||
struct private_stream_data *privst;
|
||||
@ -9547,7 +9598,7 @@ processCallDispatchStream(virConnectPtr conn ATTRIBUTE_UNUSED,
|
||||
|
||||
static int
|
||||
remoteIOHandleInput(virConnectPtr conn, struct private_data *priv,
|
||||
int in_open)
|
||||
int flags)
|
||||
{
|
||||
/* Read as much data as is available, until we get
|
||||
* EAGAIN
|
||||
@ -9574,7 +9625,7 @@ remoteIOHandleInput(virConnectPtr conn, struct private_data *priv,
|
||||
* next iteration.
|
||||
*/
|
||||
} else {
|
||||
ret = processCallDispatch(conn, priv, in_open);
|
||||
ret = processCallDispatch(conn, priv, flags);
|
||||
priv->bufferOffset = priv->bufferLength = 0;
|
||||
/*
|
||||
* We've completed one call, so return even
|
||||
@ -9597,7 +9648,7 @@ remoteIOHandleInput(virConnectPtr conn, struct private_data *priv,
|
||||
static int
|
||||
remoteIOEventLoop(virConnectPtr conn,
|
||||
struct private_data *priv,
|
||||
int in_open,
|
||||
int flags,
|
||||
struct remote_thread_call *thiscall)
|
||||
{
|
||||
struct pollfd fds[2];
|
||||
@ -9687,7 +9738,7 @@ remoteIOEventLoop(virConnectPtr conn,
|
||||
}
|
||||
|
||||
if (fds[0].revents & POLLIN) {
|
||||
if (remoteIOHandleInput(conn, priv, in_open) < 0)
|
||||
if (remoteIOHandleInput(conn, priv, flags) < 0)
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -9892,9 +9943,7 @@ remoteIO(virConnectPtr conn,
|
||||
if (priv->watch >= 0)
|
||||
virEventUpdateHandle(priv->watch, 0);
|
||||
|
||||
rv = remoteIOEventLoop(conn, priv,
|
||||
flags & REMOTE_CALL_IN_OPEN ? 1 : 0,
|
||||
thiscall);
|
||||
rv = remoteIOEventLoop(conn, priv, flags, thiscall);
|
||||
|
||||
if (priv->watch >= 0)
|
||||
virEventUpdateHandle(priv->watch, VIR_EVENT_HANDLE_READABLE);
|
||||
@ -9966,14 +10015,14 @@ cleanup:
|
||||
*/
|
||||
static int
|
||||
call (virConnectPtr conn, struct private_data *priv,
|
||||
int flags /* if we are in virConnectOpen */,
|
||||
int flags,
|
||||
int proc_nr,
|
||||
xdrproc_t args_filter, char *args,
|
||||
xdrproc_t ret_filter, char *ret)
|
||||
{
|
||||
struct remote_thread_call *thiscall;
|
||||
|
||||
thiscall = prepareCall(priv, proc_nr, args_filter, args,
|
||||
thiscall = prepareCall(priv, flags, proc_nr, args_filter, args,
|
||||
ret_filter, ret);
|
||||
|
||||
if (!thiscall) {
|
||||
@ -10302,7 +10351,7 @@ static virDriver remote_driver = {
|
||||
remoteDomainSnapshotCurrent, /* domainSnapshotCurrent */
|
||||
remoteDomainRevertToSnapshot, /* domainRevertToSnapshot */
|
||||
remoteDomainSnapshotDelete, /* domainSnapshotDelete */
|
||||
NULL, /* qemuDomainMonitorCommand */
|
||||
remoteQemuDomainMonitorCommand, /* qemuDomainMonitorCommand */
|
||||
};
|
||||
|
||||
static virNetworkDriver network_driver = {
|
||||
|
@ -3611,12 +3611,60 @@ xdr_remote_message_status (XDR *xdrs, remote_message_status *objp)
|
||||
bool_t
|
||||
xdr_remote_message_header (XDR *xdrs, remote_message_header *objp)
|
||||
{
|
||||
register int32_t *buf;
|
||||
|
||||
|
||||
if (xdrs->x_op == XDR_ENCODE) {
|
||||
buf = (int32_t*)XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
|
||||
if (buf == NULL) {
|
||||
if (!xdr_u_int (xdrs, &objp->prog))
|
||||
return FALSE;
|
||||
if (!xdr_u_int (xdrs, &objp->vers))
|
||||
return FALSE;
|
||||
if (!xdr_int (xdrs, &objp->proc))
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
(void)IXDR_PUT_U_INT32(buf, objp->prog);
|
||||
(void)IXDR_PUT_U_INT32(buf, objp->vers);
|
||||
(void)IXDR_PUT_INT32(buf, objp->proc);
|
||||
}
|
||||
if (!xdr_remote_message_type (xdrs, &objp->type))
|
||||
return FALSE;
|
||||
if (!xdr_u_int (xdrs, &objp->serial))
|
||||
return FALSE;
|
||||
if (!xdr_remote_message_status (xdrs, &objp->status))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
} else if (xdrs->x_op == XDR_DECODE) {
|
||||
buf = (int32_t*)XDR_INLINE (xdrs, 3 * BYTES_PER_XDR_UNIT);
|
||||
if (buf == NULL) {
|
||||
if (!xdr_u_int (xdrs, &objp->prog))
|
||||
return FALSE;
|
||||
if (!xdr_u_int (xdrs, &objp->vers))
|
||||
return FALSE;
|
||||
if (!xdr_int (xdrs, &objp->proc))
|
||||
return FALSE;
|
||||
|
||||
} else {
|
||||
objp->prog = IXDR_GET_U_LONG(buf);
|
||||
objp->vers = IXDR_GET_U_LONG(buf);
|
||||
objp->proc = IXDR_GET_INT32(buf);
|
||||
}
|
||||
if (!xdr_remote_message_type (xdrs, &objp->type))
|
||||
return FALSE;
|
||||
if (!xdr_u_int (xdrs, &objp->serial))
|
||||
return FALSE;
|
||||
if (!xdr_remote_message_status (xdrs, &objp->status))
|
||||
return FALSE;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (!xdr_u_int (xdrs, &objp->prog))
|
||||
return FALSE;
|
||||
if (!xdr_u_int (xdrs, &objp->vers))
|
||||
return FALSE;
|
||||
if (!xdr_remote_procedure (xdrs, &objp->proc))
|
||||
if (!xdr_int (xdrs, &objp->proc))
|
||||
return FALSE;
|
||||
if (!xdr_remote_message_type (xdrs, &objp->type))
|
||||
return FALSE;
|
||||
|
@ -2249,7 +2249,7 @@ typedef enum remote_message_status remote_message_status;
|
||||
struct remote_message_header {
|
||||
u_int prog;
|
||||
u_int vers;
|
||||
remote_procedure proc;
|
||||
int proc;
|
||||
remote_message_type type;
|
||||
u_int serial;
|
||||
remote_message_status status;
|
||||
|
@ -2122,7 +2122,7 @@ const REMOTE_MESSAGE_HEADER_XDR_LEN = 4;
|
||||
struct remote_message_header {
|
||||
unsigned prog; /* REMOTE_PROGRAM */
|
||||
unsigned vers; /* REMOTE_PROTOCOL_VERSION */
|
||||
remote_procedure proc; /* REMOTE_PROC_x */
|
||||
int proc; /* REMOTE_PROC_x */
|
||||
remote_message_type type;
|
||||
unsigned serial; /* Serial number of message. */
|
||||
remote_message_status status;
|
||||
|
Loading…
x
Reference in New Issue
Block a user