提交 4f32c5f7 编写于 作者: Y Yuto KAWAMURA(kawamuray) 提交者: Michal Privoznik

Introduce Libvirt Wireshark dissector

Introduce Wireshark dissector plugin which adds support to Wireshark
for dissecting libvirt RPC protocol.
Added following files to build Wireshark dissector from libvirt source
tree.
* tools/wireshark/*: Source tree of Wireshark dissector plugin.

Added followings to configure.ac or Makefile.am.
configure.ac
* --with-wireshark-dissector: Enable support for building Wireshark
  dissector.
* --with-ws-plugindir: Specify wireshark plugin directory that dissector
  will installed.
* Added tools/wireshark/{Makefile,src/Makefile} to  AC_CONFIG_FILES.
Makefile.am
* Added tools/wireshark/ to SUBDIR.
上级 a1cbe4b5
......@@ -226,6 +226,8 @@
/tools/virsh-*-edit.c
/tools/virt-*-validate
/tools/virt-sanlock-cleanup
/tools/wireshark/src/plugin.c
/tools/wireshark/src/libvirt
/update.log
GPATH
GRTAGS
......
......@@ -22,7 +22,8 @@ GENHTML = genhtml
SUBDIRS = . gnulib/lib include src daemon tools docs gnulib/tests \
tests po examples/object-events examples/hellolibvirt \
examples/dominfo examples/domsuspend examples/apparmor \
examples/xml/nwfilter examples/openauth examples/systemtap
examples/xml/nwfilter examples/openauth examples/systemtap \
tools/wireshark
ACLOCAL_AMFLAGS = -I m4
......
......@@ -977,10 +977,10 @@ exclude_file_name_regexp--sc_prohibit_newline_at_end_of_diagnostic = \
^src/rpc/gendispatch\.pl$$
exclude_file_name_regexp--sc_prohibit_nonreentrant = \
^((po|tests)/|docs/.*(py|html\.in)|run.in$$)
^((po|tests)/|docs/.*(py|html\.in)|run.in$$|tools/wireshark/util/genxdrstub\.pl$$)
exclude_file_name_regexp--sc_prohibit_raw_allocation = \
^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c)$$
^(docs/hacking\.html\.in)|(src/util/viralloc\.[ch]|examples/.*|tests/securityselinuxhelper\.c|tests/vircgroupmock\.c|tools/wireshark/src/packet-libvirt.c)$$
exclude_file_name_regexp--sc_prohibit_readlink = \
^src/(util/virutil|lxc/lxc_container)\.c$$
......@@ -988,7 +988,7 @@ exclude_file_name_regexp--sc_prohibit_readlink = \
exclude_file_name_regexp--sc_prohibit_setuid = ^src/util/virutil\.c$$
exclude_file_name_regexp--sc_prohibit_sprintf = \
^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)$$
^(docs/hacking\.html\.in)|(examples/systemtap/.*stp)|(src/dtrace2systemtap\.pl)|(src/rpc/gensystemtap\.pl)|(tools/wireshark/util/genxdrstub\.pl)$$
exclude_file_name_regexp--sc_prohibit_strncpy = ^src/util/virstring\.c$$
......@@ -1021,7 +1021,7 @@ exclude_file_name_regexp--sc_correct_id_types = \
exclude_file_name_regexp--sc_m4_quote_check = m4/virt-lib.m4
exclude_file_name_regexp--sc_prohibit_include_public_headers_quote = \
^src/internal\.h$$
^(src/internal\.h$$|tools/wireshark/src/packet-libvirt.h$$)
exclude_file_name_regexp--sc_prohibit_include_public_headers_brackets = \
^(tools/|examples/|include/libvirt/(virterror|libvirt-(qemu|lxc))\.h$$)
......
......@@ -2502,6 +2502,70 @@ AM_CONDITIONAL([HAVE_LIBNL], [test "$have_libnl" = "yes"])
AC_SUBST([LIBNL_CFLAGS])
AC_SUBST([LIBNL_LIBS])
dnl wireshark dissector
AC_ARG_WITH([wireshark-dissector],
[AS_HELP_STRING([--with-wireshark-dissector],
[enable wireshark dissector plugin support @<:@default=check@:>@])],
[ with_wireshark_dissector=$withval ],
[ with_wireshark_dissector=check ])
AC_DEFUN([LIBVIRT_WS_HANDLE_ERROR], [
if test "$with_wireshark_dissector" = "yes"; then
AC_MSG_ERROR([$1])
else
with_wireshark_dissector=no
fi
])
if test "$with_wireshark_dissector" != "no"; then
dnl Check for XDR headers existence
AC_CHECK_HEADERS([rpc/types.h])
dnl Check for glib-2.0 existence
PKG_CHECK_MODULES([GLIB], [glib-2.0], [
WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS `$PKG_CONFIG --cflags glib-2.0`"
], [
LIBVIRT_WS_HANDLE_ERROR([pkg-config 'glib-2.0' is required for wireshark-dissector support])
])
dnl Search for wireshark(or tshark) command
AC_PATH_PROG([WIRESHARK], [wireshark])
AC_PATH_PROG([WIRESHARK], [tshark])
if test -z "$WIRESHARK"; then
LIBVIRT_WS_HANDLE_ERROR([command not found wireshark or tshark])
else
dnl Check for wireshark headers
save_CPPFLAGS="$CPPFLAGS"
WS_DISSECTOR_CPPFLAGS="$WS_DISSECTOR_CPPFLAGS -I`dirname $WIRESHARK`/../include/wireshark"
CPPFLAGS="$CPPFLAGS $WS_DISSECTOR_CPPFLAGS"
AC_CHECK_HEADERS([wireshark/config.h],, [
LIBVIRT_WS_HANDLE_ERROR([wireshark/config.h is required for wireshark-dissector support])
])
AC_CHECK_HEADERS([wireshark/epan/packet.h wireshark/epan/dissectors/packet-tcp.h],, [
LIBVIRT_WS_HANDLE_ERROR([wireshark/epan/{packet,packet-tcp}.h are required for wireshark-dissector support])
], [
#include <wireshark/config.h>
])
CPPFLAGS="$save_CPPFLAGS"
fi
if test "$with_wireshark_dissector" != "no"; then
with_wireshark_dissector=yes
fi
fi
AC_SUBST([WS_DISSECTOR_CPPFLAGS])
AM_CONDITIONAL([WITH_WIRESHARK_DISSECTOR], [test "$with_wireshark_dissector" = "yes"])
AC_ARG_WITH([ws-plugindir],
[AS_HELP_STRING([--with-ws-plugindir],
[wireshark plugins directory that plugin will installed])],
[ ws_plugindir=$withval ])
if test "$with_wireshark_dissector" != "no" && test -z "$ws_plugindir"; then
ws_version=`$WIRESHARK -v | head -1 | cut -f 2 -d' '`
ws_plugindir=`dirname $WIRESHARK`/../lib/wireshark/plugins/$ws_version
fi
AC_SUBST([ws_plugindir])
# Check for Linux vs. BSD ifreq members
AC_CHECK_MEMBERS([struct ifreq.ifr_newname,
struct ifreq.ifr_ifindex,
......@@ -2585,7 +2649,9 @@ AC_CONFIG_FILES([\
examples/openauth/Makefile \
examples/hellolibvirt/Makefile \
examples/systemtap/Makefile \
examples/xml/nwfilter/Makefile])
examples/xml/nwfilter/Makefile \
tools/wireshark/Makefile \
tools/wireshark/src/Makefile])
AC_OUTPUT
AC_MSG_NOTICE([])
......@@ -2746,6 +2812,10 @@ AC_MSG_NOTICE([ XML Catalog: $XML_CATALOG_FILE])
AC_MSG_NOTICE([ Init script: $with_init_script])
AC_MSG_NOTICE([Char device locks: $with_chrdev_lock_files])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Developer Tools])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Wireshark dissector: $with_wireshark_dissector])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([Privileges])
AC_MSG_NOTICE([])
AC_MSG_NOTICE([ QEMU: $QEMU_USER:$QEMU_GROUP])
......
## Process this file with automake to produce Makefile.in
# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com>
#
# 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/>.
#
# Author: Yuto KAWAMURA(kawamuray)
if WITH_WIRESHARK_DISSECTOR
SUBDIRS = src
endif WITH_WIRESHARK_DISSECTOR
About
=====
This is the project of Google Summer of Code 2013 accepted by QEMU.org and
libvirt community. The goal of this project is, provide Wireshark dissector for
Libvirt RPC protocol. It will provide Libvirt packet overview/detail analysing
in Wireshark. Furthermore, it will be able to build(generated) from RPC protocol
definition placed in Libvirt source tree to support latest protocol
specification.
See also:
- http://www.google-melange.com/gsoc/project/google/gsoc2013/kawamuray/7001
- http://wiki.qemu.org/Features/LibvirtWiresharkDissector
Installation
=============
Run ./configure with --with-wireshark-dissector option enabled.
Then dissector will compiled with libvirt itself.
Add/Remove protocol from dissector's support
--------------------------------------------
Modify variable WS\_DISSECTOR\_PROTO\_FILES in tools/wireshark/src/Makefile.am.
Changing installation directory
-------------------------------
You can change installation directory of pluggable shared object(libvirt.so) by
specifying --with-ws-plugindir=<path>.
You can install libvirt.so into your local wireshark plugin directory:
./configure --with-wireshark-dissector \
--with-ws-plugindir=$HOME/.wireshark/plugins
## Process this file with automake to produce Makefile.in
# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com>
#
# 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/>.
#
# Author: Yuto KAWAMURA(kawamuray)
ws_plugin_LTLIBRARIES = libvirt.la
libvirt_la_SOURCES = packet-libvirt.c plugin.c
libvirt_la_CPPFLAGS = $(WS_DISSECTOR_CPPFLAGS)
libvirt_la_LDFLAGS = -avoid-version -module
packet-libvirt.c: packet-libvirt.h libvirt/protocol.h
plugin.c: packet-libvirt.c
$(srcdir)/../util/make-dissector-reg . plugin $<
WS_DISSECTOR_PROTO_FILES = \
$(top_srcdir)/src/remote/remote_protocol.x \
$(top_srcdir)/src/remote/qemu_protocol.x \
$(top_srcdir)/src/remote/lxc_protocol.x \
$(top_srcdir)/src/rpc/virkeepaliveprotocol.x
libvirt/protocol.h: $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES)
$(MKDIR_P) libvirt
LIBVIRT_VERSION=$(LIBVIRT_VERSION) \
$(PERL) $(srcdir)/../util/genxdrstub.pl $(WS_DISSECTOR_PROTO_FILES)
clean-local:
-rm -rf libvirt plugin.c
/* packet-libvirt.c --- Libvirt packet dissector routines.
*
* Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com>
*
* 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/>.
*
* Authors:
* Michal Privoznik <mprivozn redhat com>
* Yuto KAWAMURA(kawamuray) <kawamuray.dadada gmail.com>
*/
#include <config.h>
#include <stdlib.h>
#include <wireshark/config.h>
#include <wireshark/epan/proto.h>
#include <wireshark/epan/packet.h>
#include <wireshark/epan/dissectors/packet-tcp.h>
#include <glib.h>
#include <glib/gprintf.h>
#ifdef HAVE_RPC_TYPES_H
# include <rpc/types.h>
#endif
#include <rpc/xdr.h>
#include "packet-libvirt.h"
static int proto_libvirt = -1;
static int hf_libvirt_length = -1;
static int hf_libvirt_program = -1;
static int hf_libvirt_version = -1;
static int hf_libvirt_procedure = -1;
static int hf_libvirt_type = -1;
static int hf_libvirt_serial = -1;
static int hf_libvirt_status = -1;
static int hf_libvirt_stream = -1;
static int hf_libvirt_num_of_fds = -1;
static int hf_libvirt_unknown = -1;
static gint ett_libvirt = -1;
#define XDR_PRIMITIVE_DISSECTOR(xtype, ctype, ftype) \
static gboolean \
dissect_xdr_##xtype(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf) \
{ \
goffset start; \
ctype val; \
start = xdr_getpos(xdrs); \
if (xdr_##xtype(xdrs, &val)) { \
proto_tree_add_##ftype(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val); \
return TRUE; \
} else { \
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA); \
return FALSE; \
} \
}
XDR_PRIMITIVE_DISSECTOR(int, gint32, int)
XDR_PRIMITIVE_DISSECTOR(u_int, guint32, uint)
XDR_PRIMITIVE_DISSECTOR(short, gint16, int)
XDR_PRIMITIVE_DISSECTOR(u_short, guint16, uint)
XDR_PRIMITIVE_DISSECTOR(char, gchar, int)
XDR_PRIMITIVE_DISSECTOR(u_char, guchar, uint)
XDR_PRIMITIVE_DISSECTOR(hyper, gint64, int64)
XDR_PRIMITIVE_DISSECTOR(u_hyper, guint64, uint64)
XDR_PRIMITIVE_DISSECTOR(float, gfloat, float)
XDR_PRIMITIVE_DISSECTOR(double, gdouble, double)
XDR_PRIMITIVE_DISSECTOR(bool, bool_t, boolean)
static gboolean
dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
guint32 maxlen)
{
goffset start;
gchar *val = NULL;
start = xdr_getpos(xdrs);
if (xdr_string(xdrs, &val, maxlen)) {
proto_tree_add_string(tree, hf, tvb, start, xdr_getpos(xdrs) - start, val);
xdr_free((xdrproc_t)xdr_string, (char *)&val);
return TRUE;
} else {
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA);
return FALSE;
}
}
static gchar *
format_xdr_bytes(guint8 *bytes, guint32 length)
{
gchar *buf;
guint32 i;
if (length == 0)
return "";
buf = ep_alloc(length*2 + 1);
for (i = 0; i < length; i++) {
/* We know that buf has enough size to contain
2 * length + '\0' characters. */
g_sprintf(buf, "%02x", bytes[i]);
buf += 2;
}
return buf - length*2;
}
static gboolean
dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
guint32 size)
{
goffset start;
gboolean rc;
guint8 *val;
val = g_malloc(size);
start = xdr_getpos(xdrs);
if ((rc = xdr_opaque(xdrs, (caddr_t)val, size))) {
proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start,
NULL, "%s", format_xdr_bytes(val, size));
} else {
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA);
}
g_free(val);
return rc;
}
static gboolean
dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
guint32 maxlen)
{
goffset start;
guint8 *val = NULL;
guint32 length;
start = xdr_getpos(xdrs);
if (xdr_bytes(xdrs, (char **)&val, &length, maxlen)) {
proto_tree_add_bytes_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start,
NULL, "%s", format_xdr_bytes(val, length));
/* Seems I can't call xdr_free() for this case.
It will raises SEGV by referencing out of bounds call stack */
free(val);
return TRUE;
} else {
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA);
return FALSE;
}
}
static gboolean
dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
vir_xdr_dissector_t dissect)
{
goffset start;
bool_t not_null;
start = xdr_getpos(xdrs);
if (!xdr_bool(xdrs, &not_null)) {
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, start, -1, ENC_NA);
return FALSE;
}
if (not_null) {
return dissect(tvb, tree, xdrs, hf);
} else {
proto_item *ti;
ti = proto_tree_add_item(tree, hf, tvb, start, xdr_getpos(xdrs) - start, ENC_NA);
proto_item_append_text(ti, ": (null)");
return TRUE;
}
}
static gboolean
dissect_xdr_iterable(tvbuff_t *tvb, proto_item *ti, XDR *xdrs, gint ett, int rhf,
guint32 length, vir_xdr_dissector_t dissect, goffset start)
{
proto_tree *tree;
guint32 i;
tree = proto_item_add_subtree(ti, ett);
for (i = 0; i < length; i++) {
if (!dissect(tvb, tree, xdrs, rhf))
return FALSE;
}
proto_item_set_len(ti, xdr_getpos(xdrs) - start);
return TRUE;
}
static gboolean
dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett,
int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dissect)
{
goffset start;
proto_item *ti;
start = xdr_getpos(xdrs);
ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA);
proto_item_append_text(ti, " :: %s[%u]", rtype, size);
return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, size, dissect, start);
}
static gboolean
dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett,
int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dissect)
{
goffset start;
proto_item *ti;
guint32 length;
start = xdr_getpos(xdrs);
if (!xdr_u_int(xdrs, &length))
return FALSE;
if (length > maxlen)
return FALSE;
ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA);
proto_item_append_text(ti, " :: %s<%u>", rtype, length);
return dissect_xdr_iterable(tvb, ti, xdrs, ett, rhf, length, dissect, start);
}
static vir_xdr_dissector_t
find_payload_dissector(guint32 proc, guint32 type,
const vir_dissector_index_t *pds, gsize length)
{
const vir_dissector_index_t *pd;
guint32 first, last, direction;
if (pds == NULL || length < 1)
return NULL;
first = pds[0].proc;
last = pds[length-1].proc;
if (proc < first || proc > last) {
return NULL;
}
pd = &pds[proc-first];
/* There is no guarantee to proc numbers has no gap */
if (pd->proc != proc) {
direction = (pd->proc < proc) ? 1 : -1;
while (pd->proc != proc) {
if (pd->proc == first || pd->proc == last)
return NULL;
pd += direction;
}
}
switch (type) {
case VIR_NET_CALL:
case VIR_NET_CALL_WITH_FDS:
return pd->args;
case VIR_NET_REPLY:
case VIR_NET_REPLY_WITH_FDS:
return pd->ret;
case VIR_NET_MESSAGE:
return pd->msg;
}
return NULL;
}
static void
dissect_libvirt_stream(tvbuff_t *tvb, proto_tree *tree, gint payload_length)
{
proto_tree_add_item(tree, hf_libvirt_stream, tvb, VIR_HEADER_LEN,
payload_length - VIR_HEADER_LEN, ENC_NA);
}
static gint32
dissect_libvirt_num_of_fds(tvbuff_t *tvb, proto_tree *tree)
{
gint32 nfds;
nfds = tvb_get_ntohl(tvb, VIR_HEADER_LEN);
proto_tree_add_int(tree, hf_libvirt_num_of_fds, tvb, VIR_HEADER_LEN, 4, nfds);
return nfds;
}
static void
dissect_libvirt_fds(tvbuff_t *tvb, gint start, gint32 nfds)
{
/* TODO: NOP for now */
}
static void
dissect_libvirt_payload_xdr_data(tvbuff_t *tvb, proto_tree *tree, gint payload_length,
gint32 status, vir_xdr_dissector_t dissect)
{
gint32 nfds = 0;
gint start = VIR_HEADER_LEN;
tvbuff_t *payload_tvb;
caddr_t payload_data;
XDR xdrs;
if (status == VIR_NET_CALL_WITH_FDS ||
status == VIR_NET_REPLY_WITH_FDS) {
nfds = dissect_libvirt_num_of_fds(tvb, tree);
start += 4;
payload_length -= 4;
}
payload_tvb = tvb_new_subset(tvb, start, -1, payload_length);
payload_data = (caddr_t)tvb_memdup(payload_tvb, 0, payload_length);
xdrmem_create(&xdrs, payload_data, payload_length, XDR_DECODE);
dissect(payload_tvb, tree, &xdrs, -1);
xdr_destroy(&xdrs);
g_free(payload_data);
if (nfds != 0) {
dissect_libvirt_fds(tvb, start + payload_length, nfds);
}
}
static void
dissect_libvirt_payload(tvbuff_t *tvb, proto_tree *tree,
guint32 prog, guint32 proc, guint32 type, guint32 status)
{
gssize payload_length;
payload_length = tvb_length(tvb) - VIR_HEADER_LEN;
if (payload_length <= 0)
return; /* No payload */
if (status == VIR_NET_OK) {
vir_xdr_dissector_t xd = find_payload_dissector(proc, type, get_program_data(prog, VIR_PROGRAM_DISSECTORS),
*(gsize *)get_program_data(prog, VIR_PROGRAM_DISSECTORS_LEN));
if (xd == NULL)
goto unknown;
dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, xd);
} else if (status == VIR_NET_ERROR) {
dissect_libvirt_payload_xdr_data(tvb, tree, payload_length, status, VIR_ERROR_MESSAGE_DISSECTOR);
} else if (type == VIR_NET_STREAM) { /* implicitly, status == VIR_NET_CONTINUE */
dissect_libvirt_stream(tvb, tree, payload_length);
} else {
goto unknown;
}
return;
unknown:
dbg("Cannot determine payload: Prog=%u, Proc=%u, Type=%u, Status=%u", prog, proc, type, status);
proto_tree_add_item(tree, hf_libvirt_unknown, tvb, VIR_HEADER_LEN, -1, ENC_NA);
}
static void
dissect_libvirt_message(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
goffset offset;
guint32 prog, proc, type, serial, status;
const value_string *vs;
col_set_str(pinfo->cinfo, COL_PROTOCOL, "Libvirt");
col_clear(pinfo->cinfo, COL_INFO);
offset = 4; /* End of length field */
prog = tvb_get_ntohl(tvb, offset); offset += 4;
offset += 4; /* Ignore version header field */
proc = tvb_get_ntohl(tvb, offset); offset += 4;
type = tvb_get_ntohl(tvb, offset); offset += 4;
serial = tvb_get_ntohl(tvb, offset); offset += 4;
status = tvb_get_ntohl(tvb, offset); offset += 4;
col_add_fstr(pinfo->cinfo, COL_INFO, "Prog=%s",
val_to_str(prog, program_strings, "%x"));
vs = get_program_data(prog, VIR_PROGRAM_PROCSTRINGS);
if (vs == NULL) {
col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%u", proc);
} else {
col_append_fstr(pinfo->cinfo, COL_INFO, " Proc=%s", val_to_str(proc, vs, "%d"));
}
col_append_fstr(pinfo->cinfo, COL_INFO, " Type=%s Serial=%u Status=%s",
val_to_str(type, type_strings, "%d"), serial,
val_to_str(status, status_strings, "%d"));
if (tree) {
gint *hf_proc;
proto_item *ti;
proto_tree *libvirt_tree;
ti = proto_tree_add_item(tree, proto_libvirt, tvb, 0, tvb_length(tvb), ENC_NA);
libvirt_tree = proto_item_add_subtree(ti, ett_libvirt);
offset = 0;
proto_tree_add_item(libvirt_tree, hf_libvirt_length, tvb, offset, 4, ENC_NA); offset += 4;
proto_tree_add_item(libvirt_tree, hf_libvirt_program, tvb, offset, 4, ENC_NA); offset += 4;
proto_tree_add_item(libvirt_tree, hf_libvirt_version, tvb, offset, 4, ENC_NA); offset += 4;
hf_proc = (int *)get_program_data(prog, VIR_PROGRAM_PROCHFVAR);
if (hf_proc != NULL && *hf_proc != -1) {
proto_tree_add_item(libvirt_tree, *hf_proc, tvb, offset, 4, ENC_NA);
} else {
/* No string representation, but still useful displaying proc number */
proto_tree_add_item(libvirt_tree, hf_libvirt_procedure, tvb, offset, 4, ENC_NA);
}
offset += 4;
proto_tree_add_item(libvirt_tree, hf_libvirt_type, tvb, offset, 4, ENC_NA); offset += 4;
proto_tree_add_item(libvirt_tree, hf_libvirt_serial, tvb, offset, 4, ENC_NA); offset += 4;
proto_tree_add_item(libvirt_tree, hf_libvirt_status, tvb, offset, 4, ENC_NA); offset += 4;
/* Dissect payload remaining */
dissect_libvirt_payload(tvb, libvirt_tree, prog, proc, type, status);
}
}
static guint32
get_message_len(packet_info *pinfo __attribute__((unused)), tvbuff_t *tvb, int offset)
{
return tvb_get_ntohl(tvb, offset);
}
static void
dissect_libvirt(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
{
/* Another magic const - 4; simply, how much bytes
* is needed to tell the length of libvirt packet. */
tcp_dissect_pdus(tvb, pinfo, tree, TRUE, 4, get_message_len, dissect_libvirt_message);
}
void
proto_register_libvirt(void)
{
static hf_register_info hf[] = {
{ &hf_libvirt_length,
{ "length", "libvirt.length",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_program,
{ "program", "libvirt.program",
FT_UINT32, BASE_HEX,
VALS(program_strings), 0x0,
NULL, HFILL}
},
{ &hf_libvirt_version,
{ "version", "libvirt.version",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_procedure,
{ "procedure", "libvirt.procedure",
FT_INT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_type,
{ "type", "libvirt.type",
FT_INT32, BASE_DEC,
VALS(type_strings), 0x0,
NULL, HFILL}
},
{ &hf_libvirt_serial,
{ "serial", "libvirt.serial",
FT_UINT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_status,
{ "status", "libvirt.status",
FT_INT32, BASE_DEC,
VALS(status_strings), 0x0,
NULL, HFILL}
},
VIR_DYNAMIC_HFSET
{ &hf_libvirt_stream,
{ "stream", "libvirt.stream",
FT_BYTES, BASE_NONE,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_num_of_fds,
{ "num_of_fds", "libvirt.num_of_fds",
FT_INT32, BASE_DEC,
NULL, 0x0,
NULL, HFILL}
},
{ &hf_libvirt_unknown,
{ "unknown", "libvirt.unknown",
FT_BYTES, BASE_NONE,
NULL, 0x0,
NULL, HFILL}
},
};
static gint *ett[] = {
VIR_DYNAMIC_ETTSET
&ett_libvirt
};
proto_libvirt = proto_register_protocol(
"Libvirt", /* name */
"libvirt", /* short name */
"libvirt" /* abbrev */
);
proto_register_field_array(proto_libvirt, hf, array_length(hf));
proto_register_subtree_array(ett, array_length(ett));
}
void
proto_reg_handoff_libvirt(void)
{
static dissector_handle_t libvirt_handle;
libvirt_handle = create_dissector_handle(dissect_libvirt, proto_libvirt);
dissector_add_uint("tcp.port", LIBVIRT_PORT, libvirt_handle);
}
/* packet-libvirt.h --- Libvirt packet dissector header file.
*
* Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com>
*
* 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/>.
*
* Author: Yuto KAWAMURA(kawamuray)
*/
#ifndef _PACKET_LIBVIRT_H_
# define _PACKET_LIBVIRT_H_
# ifndef LIBVIRT_PORT
# define LIBVIRT_PORT 16509
# endif
# define VIR_HEADER_LEN 28
# ifdef DEBUG
# define dbg(fmt, ...) \
g_print("[LIBVIRT] " fmt " at " __FILE__ " line %d\n", ##__VA_ARGS__, __LINE__)
# else
# define dbg(fmt, ...)
# endif
typedef gboolean (*vir_xdr_dissector_t)(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
typedef struct vir_dissector_index vir_dissector_index_t;
struct vir_dissector_index {
guint32 proc;
vir_xdr_dissector_t args;
vir_xdr_dissector_t ret;
vir_xdr_dissector_t msg;
};
enum vir_net_message_type {
VIR_NET_CALL = 0,
VIR_NET_REPLY = 1,
VIR_NET_MESSAGE = 2,
VIR_NET_STREAM = 3,
VIR_NET_CALL_WITH_FDS = 4,
VIR_NET_REPLY_WITH_FDS = 5,
};
enum vir_net_message_status {
VIR_NET_OK = 0,
VIR_NET_ERROR = 1,
VIR_NET_CONTINUE = 2,
};
enum vir_program_data_index {
VIR_PROGRAM_PROCHFVAR,
VIR_PROGRAM_PROCSTRINGS,
VIR_PROGRAM_DISSECTORS,
VIR_PROGRAM_DISSECTORS_LEN,
VIR_PROGRAM_LAST,
};
static const value_string type_strings[] = {
{ VIR_NET_CALL, "CALL" },
{ VIR_NET_REPLY, "REPLY" },
{ VIR_NET_MESSAGE, "MESSAGE" },
{ VIR_NET_STREAM, "STREAM" },
{ VIR_NET_CALL_WITH_FDS, "CALL_WITH_FDS" },
{ VIR_NET_REPLY_WITH_FDS, "REPLY_WITH_FDS" },
{ -1, NULL }
};
static const value_string status_strings[] = {
{ VIR_NET_OK, "OK" },
{ VIR_NET_ERROR, "ERROR" },
{ VIR_NET_CONTINUE, "CONTINUE" },
{ -1, NULL }
};
/* TODO: These symbols will automatically included in generated headers in the feature */
# define VIR_SECURITY_MODEL_BUFLEN (256 + 1)
# define VIR_SECURITY_LABEL_BUFLEN (4096 + 1)
# define VIR_SECURITY_DOI_BUFLEN (256 + 1)
# define VIR_UUID_BUFLEN (16)
enum {
VIR_TYPED_PARAM_INT = 1, /* integer case */
VIR_TYPED_PARAM_UINT = 2, /* unsigned integer case */
VIR_TYPED_PARAM_LLONG = 3, /* long long case */
VIR_TYPED_PARAM_ULLONG = 4, /* unsigned long long case */
VIR_TYPED_PARAM_DOUBLE = 5, /* double case */
VIR_TYPED_PARAM_BOOLEAN = 6, /* boolean(character) case */
VIR_TYPED_PARAM_STRING = 7, /* string case */
};
/* / */
# define VIR_ERROR_MESSAGE_DISSECTOR dissect_xdr_remote_error
static gboolean dissect_xdr_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_u_int(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_u_short(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_u_char(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_u_hyper(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_float(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_double(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_bool(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf);
static gboolean dissect_xdr_string(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen);
static gboolean dissect_xdr_opaque(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 size);
static gboolean dissect_xdr_bytes(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, guint32 maxlen);
static gboolean dissect_xdr_pointer(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf,
vir_xdr_dissector_t dp);
static gboolean dissect_xdr_vector(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett,
int rhf, gchar *rtype, guint32 size, vir_xdr_dissector_t dp);
static gboolean dissect_xdr_array(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf, gint ett,
int rhf, gchar *rtype, guint32 maxlen, vir_xdr_dissector_t dp);
# include "libvirt/protocol.h"
#endif /* _PACKET_LIBVIRT_H_ */
#!/usr/bin/env perl
# genxdrstub.pl --- Generate C header file which used by packet-libvirt.[ch]
#
# Copyright (C) 2013 Yuto KAWAMURA(kawamuray) <kawamuray.dadada@gmail.com>
#
# 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/>.
#
# Author: Yuto KAWAMURA(kawamuray)
#
# For XDR syntax, see http://tools.ietf.org/html/rfc4506#section-6.3
# This script does not strictly check syntax of xdr protocol specification.
# Make sure the specification files you have are correctly compilable with rpcgen(1).
# If something fails with this script in spite of you had confirmed that the `make' with libvirt was succeed,
# please report your error output to kawamuray<kawamuray.dadada@gmail.com>.
use strict;
use warnings;
use File::Spec;
my $DEBUG = 0; # Enable if you want to see debug output
sub dbg { print STDERR @_ if $DEBUG }
die "ERROR: No arguments" unless @ARGV;
# Context object referenced from entire this script
my $c = Context->new;
for my $proto (@ARGV) {
# We need to do this heuristic parsing to determine
# variable name of enum <protocol>_procedures.
my ($name) = $proto =~ m{(?:vir)?([^/]+?)_?protocol\.x$};
unless ($name) {
warn "WARNING: Cannot extract protocol name from $proto, skipping.";
next;
}
$c->add_to_set(progs => $name);
my $source;
{
open my $fh, '<', $proto
or die "Cannot open $proto: $!";
local $/;
$source = <$fh>;
close $fh;
}
$c->add_header_file($name, sub {
dbg "*** Start parsing $proto\n";
my @lexs = Lexicalizer->parse($source);
for my $lex (@lexs) {
next if $lex->ident eq "enum $name\_procedure";
if ($lex->isa('Sym::Variable')) {
$c->print(sprintf "#define %s (%s)\n", $lex->ident, $lex->value);
} elsif ($lex->isa('Sym::Type')) {
# Top level of name path is type identification of itself
$lex->define_dissector($lex->idstrip);
} else {
die "Unkown lexical appeared: $lex";
}
}
my $procs = $c->symbol("enum $name\_procedure")
or die "Cannot find procedures enumeration: enum $name\_procedure";
# Procedure numbers are expected to be containing gaps, but needed to be sorted in ascending order.
my @procedures = sort { $a->value <=> $b->value } @{ $procs->members };
my @dissectors = map {
(my $ident = lc($_->ident)) =~ s/^$name\_proc/$name/;
+{
value => $_->value,
map { $_ => $c->rinc($c->symbols->{"$ident\_$_"} ? "dissect_xdr_$ident\_$_" : 'NULL') }
qw{ args ret msg }
};
} @procedures;
$c->print(PT->render('code.dissectorlist', {
name => $name,
dissectors => \@dissectors,
}));
$c->print(PT->render('code.procedure_strings', {
name => $name,
procedures => \@procedures,
}));
});
}
$c->add_header_file('protocol', sub {
for my $prog (@{ $c->get_set('progs') }) {
$c->print("#include \"libvirt/$prog.h\"\n");
}
# hf_ variables set
$c->print(PT->render('macro.hfvars', {
programs => $c->get_set('progs'),
hfvars => [ grep $_->{segment}{refcnt}, @{ $c->get_set('hfvars') } ],
}));
# ett_ variables set
$c->print(PT->render('macro.ettvars', {
ettvars => [ map $_->{sym}, grep $_->{refcnt}, @{ $c->get_set('ettvars') } ],
}));
# value_string program_strings
$c->print(PT->render('code.program_strings', { programs => $c->get_set('progs') }));
$c->print("static int hf_$_\_procedure = -1;\n") for @{ $c->get_set('progs') };
$c->print(PT->render('code.program_data', { programs => $c->get_set('progs') }));
});
$c->finalize; exit 0;
# Used for handy class building
sub register_profile {
my %prof = @_;
my $caller = caller;
no strict 'refs';
if ($prof{isa}) {
push @{ "$caller\::ISA" }, $prof{isa};
}
while (my ($name, $v) = each %{ $prof{consts} || {} }) {
*{ "$caller\::$name" } = sub { $v };
}
for my $attr (@{ $prof{attrs} || [] }) {
*{ "$caller\::$attr" } = sub {
if (@_ > 1) { $_[0]->{$attr} = $_[1]; $_[0] }
else { $_[0]->{$attr} }
};
}
while (my ($klass, $meths) = each %{ $prof{roles} || {} }) {
for my $meth (@$meths) {
# This assignment cannot be like: *{ "$caller\::$meth" } = \&{ "$klass\::$meth" }.
# "$klass\::$meth" maybe not defined yet(e.g. Methods defined by PT)
*{ "$caller\::$meth" } = sub { goto &{ "$klass\::$meth" } };
}
}
}
# Minimal template engine for code generating
package PT; # is PicoTemplate
our $Token;
our %Templates;
INIT { # Load templates from __END__ section
$Token = join '', map { chr(65 + rand(26)) } 1..64;
my $current;
while (my $l = <main::DATA>) {
if ($l =~ /^\@\@\s*(.+)/) {
$current = \($Templates{$1} = '');
} else {
$$current .= $l if $current;
}
}
for my $name (keys %Templates) {
$Templates{$name} = __PACKAGE__->compile($Templates{$name});
if ($name =~ /^([\w:]+)#([^#]+)$/) {
no strict 'refs';
my $meth = "$1\::$2";
unless (defined &$meth) {
*$meth = $Templates{$name};
}
}
}
}
sub compile {
my ($class, $tmpl) = @_;
$tmpl =~ s{<%(=)?(.*?)%>\n?|((?:(?!<%).)+)}{
$2 ? $1 ? "\$$Token .= qq{\@{[do{ $2 }]}};" : $2
: "\$$Token .= substr <<$Token, 0, -1;\n".quotemeta($3)."\n$Token\n";
}gse;
eval "sub { my \$$Token = ''; $tmpl \$$Token }"
or die "ERROR: Cannot compile template: $@";
}
sub render {
my ($class, $name, $vars, @args) = @_;
local $_ = $vars || {};
my $renderer = $Templates{$name}
or die "No such template: $name";
$renderer->(@args);
}
# / package PT
package Sym;
BEGIN{::register_profile(
attrs => [qw[ ident ]],
)}
sub new {
my ($class, %args) = @_;
CORE::bless \%args, $class;
}
sub bless {
my ($self, $klass) = @_;
CORE::bless $self, "Sym::$klass"
if ref($self) ne "Sym::$klass";
$self;
}
sub idstrip {
my $ident = shift()->ident;
$ident =~ s/^(?:struct|enum|union)\s+// if $ident;
$ident;
}
# / package Sym
package Sym::Type;
BEGIN{::register_profile(
isa => 'Sym',
attrs => [qw[ alias ]],
)}
sub is_primitive { !(shift)->alias }
sub dealias {
my ($self) = @_;
$self->is_primitive ? $self : $self->alias->dealias;
}
sub xdr_type {
my ($self) = @_;
if (!$self->is_primitive) {
return $self->dealias->xdr_type;
}
my $type = ref $self;
if ($type eq __PACKAGE__) {
$type = $self->ident;
} else {
$type =~ s/^.*:://;
}
uc($type);
}
sub render_caller {
my ($self, $hfid) = @_;
my $name = $c->rinc( 'dissect_xdr_'.($self->idstrip || lc($self->xdr_type)) );
"$name(tvb, tree, xdrs, hf)";
}
sub ft_type {
my ($self) = @_;
return $self->dealias->ft_type unless $self->is_primitive;
my $xt = $self->xdr_type;
+{
INT => 'INT32',
U_INT => 'UINT32',
SHORT => 'INT16',
U_SHORT => 'UINT16',
CHAR => 'INT8',
U_CHAR => 'UINT8',
HYPER => 'INT64',
U_HYPER => 'UINT64',
BOOL => 'BOOLEAN',
}->{$xt} || $xt;
}
sub hf_base {
my ($self) = @_;
$self->is_primitive
? $self->ft_type =~ /INT/ ? 'DEC' : 'NONE'
: $self->dealias->hf_base;
}
sub define_dissector {
my ($self, @path) = @_;
$self->declare_hfvar(@path);
my $path = join '__', @path;
my $code = $self->render_dissector($path);
$c->print({ sym => "dissect_xdr_$path", body => $code })
if $code;
}
sub declare_hfvar {
my ($self, @path) = @_;
my $path = join '__', @path;
$c->add_to_set(hfvars => {
segment => $c->print({
sym => "hf_$path",
body => "static int hf_$path = -1;\n"
}),
name => $path[-1],
abbrev => join('.', @path),
ft_type => $self->ft_type,
hf_base => $self->hf_base,
});
}
# / package Sym
package Sym::Type::HasAnonTypes; # Types which possibly have anonymous subtypes
BEGIN{::register_profile(
isa => 'Sym::Type',
)}
sub declare_anontypes {
my ($self, @path) = @_;
for my $m (@{ $self->members }) {
unless (defined $m->type->ident) {
$m->type->ident(join '__', @path, $m->ident);
}
$m->type->define_dissector(@path, $m->ident);
}
}
sub define_dissector {
my ($self, @path) = @_;
$self->declare_anontypes(@path);
$self->SUPER::define_dissector(@path);
}
package Sym::Type::HasSubtree; # Types which should be declare ett variables
sub declare_ettvar {
my ($self) = @_;
my $ettvar = 'ett_'.$self->idstrip;
$c->add_to_set(ettvars => $c->print({
sym => $ettvar,
body => "static gint $ettvar = -1;\n",
}));
}
package Sym::Type::HasReference; # Types which references subtype
BEGIN{::register_profile(
attrs => [qw[ reftype ]],
consts => { ft_type => 'NONE' },
)}
sub render_caller {
my ($self) = @_;
my ($klass) = ref($self) =~ /([^:]+)$/;
sprintf '%s(tvb, tree, xdrs, hf, %s)',
$c->rinc('dissect_xdr_'.lc($klass)),
$c->rinc('dissect_xdr_'.$self->reftype->idstrip);
}
package Sym::Type::HasLength; # Types which has length attribute
BEGIN{::register_profile(
attrs => [qw[ length ]],
consts => { ft_type => 'NONE' },
)}
sub render_caller {
my ($self, $hfid) = @_;
my ($klass) = ref($self) =~ /([^:]+)$/;
sprintf '%s(tvb, tree, xdrs, hf, %s)',
$c->rinc('dissect_xdr_'.lc($klass)), $self->length || '~0';
}
package Sym::Type::Struct;
BEGIN{::register_profile(
isa => 'Sym::Type',
attrs => [qw[ members ]],
consts => { ft_type => 'NONE' },
roles => {
'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes ]],
'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]],
},
)}
sub define_dissector {
my ($self, @path) = @_;
$self->declare_anontypes(@path);
$self->declare_ettvar;
$self->SUPER::define_dissector(@path);
}
package Sym::Type::Enum;
BEGIN{::register_profile(
isa => 'Sym::Type',
attrs => [qw[ members ]],
consts => { ft_type => 'UINT32' },
)}
package Sym::Type::Union;
BEGIN{::register_profile(
isa => 'Sym::Type',
attrs => [qw[ decl case_specs ]],
consts => { ft_type => 'NONE' },
roles => {
'Sym::Type::HasAnonTypes' => [qw[ declare_anontypes define_dissector ]],
},
)}
sub members {
my ($self) = @_;
[ map { $_->[1] } @{ $self->case_specs } ];
}
package Sym::Type::String;
BEGIN{::register_profile(
isa => 'Sym::Type',
consts => { ft_type => 'STRING' },
roles => {
'Sym::Type::HasLength' => [qw[ length render_caller ]],
},
)}
package Sym::Type::Opaque;
BEGIN{::register_profile(
isa => 'Sym::Type',
consts => { ft_type => 'BYTES' },
roles => {
'Sym::Type::HasLength' => [qw[ length render_caller ]],
},
)}
package Sym::Type::Bytes;
BEGIN{::register_profile(
isa => 'Sym::Type',
consts => { ft_type => 'BYTES' },
roles => {
'Sym::Type::HasLength' => [qw[ length render_caller ]],
},
)}
package Sym::Type::Pointer;
BEGIN{::register_profile(
isa => 'Sym::Type',
roles => {
'Sym::Type::HasReference' => [qw[ reftype render_caller ]],
},
)}
sub ft_type { (shift)->reftype->ft_type }
package Sym::Type::Array; # a.k.a Variable-Length Array
BEGIN{::register_profile(
isa => 'Sym::Type',
roles => {
'Sym::Type::HasLength' => [qw[ length ft_type ]],
'Sym::Type::HasReference' => [qw[ reftype ]],
'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]],
},
)}
sub render_caller {
my ($self, $hfid) = @_;
my ($pname) = reverse split /__/, $hfid;
sprintf 'dissect_xdr_array(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)',
$c->rinc('ett_'.$self->idstrip),
$c->rinc("hf_$hfid\__$pname"),
$self->reftype->idstrip,
$self->length || '~0',
$c->rinc('dissect_xdr_'.$self->reftype->idstrip);
}
sub define_dissector {
my ($self, @path) = @_;
$self->reftype->declare_hfvar(@path, $path[-1]);
$self->declare_ettvar;
$self->SUPER::define_dissector(@path);
}
package Sym::Type::Vector; # a.k.a Fixed-Length Array
BEGIN{::register_profile(
isa => 'Sym::Type',
roles => {
'Sym::Type::HasLength' => [qw[ length ft_type ]],
'Sym::Type::HasReference' => [qw[ reftype ]],
'Sym::Type::Array' => [qw[ define_dissector ]],
'Sym::Type::HasSubtree' => [qw[ declare_ettvar ]],
},
)}
sub render_caller {
my ($self, $hfid) = @_;
my ($pname) = reverse split /__/, $hfid;
sprintf 'dissect_xdr_vector(tvb, tree, xdrs, hf, %s, %s, "%s", %s, %s)',
$c->rinc('ett_'.$self->idstrip),
$c->rinc("hf_$hfid\__$pname"),
$self->reftype->idstrip,
$self->length || '~0',
$c->rinc('dissect_xdr_'.$self->reftype->idstrip);
}
package Sym::Variable;
BEGIN{::register_profile(
isa => 'Sym',
attrs => [qw[ type value ]],
)}
package Context;
BEGIN{::register_profile(
attrs => [qw[ symbols ]],
)}
sub new {
my ($class) = @_;
bless {
symbols => {},
segments => {},
}, $class;
}
sub symbol {
my ($self, $ident) = @_;
my $sym = $self->symbols->{$ident} ||= Sym->new;
$sym->ident($ident);
# In XDR syntax specification, defining struct/enum/union will automatically
# create alias having symbol which excludes its prefix type specifier.
# e.g:
# struct foo { int bar; }; will convert to:
# struct foo { int bar; }; typedef struct foo foo;
if ($ident =~ s/^(?:struct|enum|union)\s+//) {
$self->symbol($ident)->bless('Type')->alias($sym);
}
$sym;
}
sub add_to_set {
my ($self, $set, @elems) = @_;
$self->{sets} ||= {};
$self->{sets}{$set} ||= [];
push @{ $self->{sets}{$set} }, @elems;
}
sub get_set {
my ($self, $set) = @_;
$self->{sets}{$set} || [];
}
# $c->print(...string...); # Does work as regular 'print'
# $c->print({ sym => symbol, body => ...string... });
# Does treat segment as code block should be referenced.
# It will not printed unless it is referenced from other code by $c->rinc();
sub print {
my $self = shift;
my $content;
if (ref $_[0]) {
$content = $self->{segments}{ $_[0]{sym} } ||= $_[0];
$content->{refcnt} //= 0;
$content->{body} = $_[0]{body};
} else {
$content = join '', @_;
}
push @{ $self->{header_contents} }, $content;
$content;
}
sub rinc {
my ($self, $sym) = @_;
($self->{segments}{$sym} ||= { sym => $sym, refcnt => 0 })->{refcnt}++;
$sym;
}
sub add_header_file {
my ($self, $name, $block) = @_;
$self->{headers} ||= [];
local $self->{header_contents} = [];
$self->print("/* *DO NOT MODIFY* this file directly.\n");
$self->print(" * This file was generated by $0 from libvirt version $ENV{LIBVIRT_VERSION} */\n");
my $ucname = uc $name;
$self->print("#ifndef _$ucname\_H_\n");
$self->print("#define _$ucname\_H_\n");
$block->();
$self->print("#endif /* _$ucname\_H_ */");
push @{ $self->{headers} }, [ $name, delete $self->{header_contents} ];
}
sub finalize {
my ($self) = @_;
# Referenced from macro defined in packet-libvirt.h
$self->rinc('dissect_xdr_remote_error');
for my $header (@{ $self->{headers} || [] }) {
my ($name, $contents) = @$header;
my $file = File::Spec->catfile($ENV{PWD}, 'libvirt', "$name.h");
open my $fh, '>', $file
or die "Cannot open file $file: $!";
CORE::print $fh map { ref($_) ? ($_->{refcnt} ? $_->{body} : ()) : $_ } @$contents;
CORE::print $fh "\n";
close $fh;
}
}
# / package Context
package Lexicalizer;
our $Depth;
INIT { # Wrap all lexicalizer subroutine by debugger function
$Depth = 0;
no strict 'refs';
no warnings 'redefine';
for my $name (keys %{ __PACKAGE__.'::' }) {
next if $name =~ /^(?:parse|adv)$/;
my $fullname = __PACKAGE__."::$name";
next unless defined &$fullname;
my $sub = \&$fullname;
*$fullname = sub {
my (undef, undef, $line) = caller;
::dbg ' 'x($Depth*2), "$name L$line", "\n";
local $Depth = $Depth + 1;
$sub->(@_);
};
}
}
# Check if passed regexp does match to next token and advance position.
# Return matched string if matched. Die else.
sub adv {
my ($rx) = @_;
::dbg ' 'x($Depth*2+1), "- adv( $rx ) = ";
# Remove Comments Comments C++ style, PP directives
s{\A(?:\s*(?:/\*.*?\*/|(?://|%).*?(?:\n+|\z)))*\s*}{}s;
if (s/^(?:$rx)//s) {
::dbg "'$&'\n";
return $&;
}
::dbg "UNMATCH\n";
die;
}
sub lexor {
my $snapshot = $_;
while (my $handler = shift) {
my $ret = eval { $handler->() };
if (defined $ret) {
return $ret;
}
$_ = $snapshot;
}
die;
}
sub decimal_constant {
adv '\-?[0-9]+';
}
sub hexadecimal_constant {
adv '\-?0x[0-9A-Fa-f]+';
}
sub octal_constant {
adv '\-?0[0-9]+';
}
sub constant {
lexor \&hexadecimal_constant, \&octal_constant, \&decimal_constant;
}
sub identifier {
adv '[_a-zA-Z][_a-zA-Z0-9]*';
}
sub value {
lexor \&constant, \&identifier;
}
sub enum_type_spec {
adv 'enum';
my $body = lexor \&enum_body, \&identifier;
if (ref $body eq 'ARRAY') {
Sym::Type::Enum->new(members => $body);
} else {
$c->symbol("enum $body")->bless('Type::Enum');
}
}
sub enum_body {
adv '{';
my @members;
do {
my $ident = identifier();
adv '=';
my $value = value();
push @members, $c->symbol($ident)->bless('Variable')->value($value);
} while adv('[},]') eq ',';
\@members;
}
sub struct_type_spec {
adv 'struct';
my $body = lexor \&struct_body, \&identifier;
if (ref $body eq 'ARRAY') {
Sym::Type::Struct->new(members => $body);
} else {
$c->symbol("struct $body")->bless('Type::Struct');
}
}
sub struct_body {
adv '{';
local $c->{symbols} = { %{ $c->{symbols} } };
my @members;
while (my $decl = lexor \&declaration, sub { adv('}') }) {
last if $decl eq '}';
adv ';';
push @members, $decl;
}
\@members;
}
sub case_spec {
my @cases;
while (my $case = eval { adv 'case' }) {
push @cases, value();
adv ':';
}
my $decl = declaration();
adv ';';
[ \@cases, $decl ];
}
sub union_type_spec {
adv 'union';
local $c->{symbols} = { %{ $c->{symbols} } };
my $body = lexor \&union_body, \&identifier;
if (ref $body eq 'ARRAY') {
Sym::Type::Union->new(decl => $body->[0], case_specs => $body->[1]);
} else {
$c->symbol("union $body")->bless('Type::Union');
}
}
sub union_body {
adv 'switch'; adv '\(';
my $decl = declaration();
adv '\)'; adv '{';
my @case_specs;
while (my $spec = eval { case_spec() }) {
push @case_specs, $spec;
}
# TODO: parse default
adv '}';
[ $decl, \@case_specs ];
}
sub constant_def {
adv 'const';
my $ident = identifier();
adv '=';
my $value = lexor \&constant, \&identifier;
adv ';';
$c->symbol($ident)->bless('Variable')->value($value);
}
sub type_def {
my $ret = lexor sub {
adv 'typedef';
my $var = declaration();
my $type = $var->type;
$var->bless('Type')->alias($type);
}, sub {
adv 'enum';
my $ident = identifier();
my $body = enum_body();
$c->symbol("enum $ident")->bless('Type::Enum')->members($body);
}, sub {
adv 'struct';
my $ident = identifier();
my $body = struct_body();
$c->symbol("struct $ident")->bless('Type::Struct')->members($body);
}, sub {
adv 'union';
my $ident = identifier();
my $body = union_body();
$c->symbol("union $ident")->bless('Type::Union')
->decl($body->[0])->case_specs($body->[1]);
};
adv ';';
$ret;
}
sub type_specifier {
lexor sub {
my $ts = adv '(?:unsigned\s+)?(?:int|hyper|char|short)|float|double|quadruple|bool';
$ts =~ s/^unsigned\s+/u_/;
$c->symbol($ts)->bless('Type');
}, \&enum_type_spec, \&struct_type_spec, \&union_type_spec, sub {
my $ident = identifier();
$c->symbol($ident)->bless('Type');
};
}
sub declaration {
lexor sub {
my $type = lexor sub {
my $type = adv 'opaque|string';
my $klass = ucfirst $type;
"Sym::Type::$klass"->new;
}, \&type_specifier;
my $ident = identifier();
# I know that type 'string' does not accept '[]'(fixed length), but I don't care about that
if (my $ex = eval { adv '[<\[]' }) {
my $value = eval { value() };
die if !$value && $ex ne '<'; # Length could be null if it is variable length
adv($ex eq '<' ? '>' : '\]');
if (ref($type) eq 'Sym::Type') { # Expect Array or Vector
my $vtype = ($ex eq '<') ? 'Array' : 'Vector';
$type = "Sym::Type::$vtype"->new(length => $value, reftype => $type);
} else {
$type->length($value);
$type->bless('Type::Bytes') if $type->isa('Sym::Type::Opaque') && $ex eq '<';
}
} elsif ($type->can('length')) { # Found String or Opaque but not followed by length specifier
die;
}
$c->symbol($ident)->bless('Variable')->type($type);
}, sub {
my $type = type_specifier();
adv '\*';
my $ident = identifier();
$c->symbol($ident)->bless('Variable')->type(
Sym::Type::Pointer->new(reftype => $type));
}, sub {
adv 'void';
$c->symbol('void')->bless('Type');
};
}
sub definition {
lexor \&type_def, \&constant_def;
}
sub parse {
my ($class, $source) = @_;
my $nlines = @{[$source =~ /\n/g]};
my @lexs;
while ($source =~ /\S/s) {
(local $_ = $source) =~ s/\A\s*//s;
my $lex = eval { definition() };
if (!$lex) {
my $line = $nlines - @{[/\n/g]} + 1;
my ($near) = /\A((?:.+?\n){0,5})/s;
die "ERROR: Unexpected character near line $line.\n",
"Please check debug output by enabling \$DEBUG flag at top of script.\n",
join("\n", map { ">> $_" } split /\n/, $near);
}
::dbg ' 'x($Depth*2), sprintf "*** Found %s<%s>\n", ref($lex), $lex->ident;
push @lexs, $lex;
$source = $_;
}
@lexs;
}
# Followings are code templates handled by PT
__END__<<DUMMY # Dummy heredoc to disable perl syntax highlighting
@@ Sym::Type#render_dissector
<%
my ($self, $ident) = @_;
return if $self->is_primitive;
%>
static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf)
{
return <%= $self->dealias->render_caller($self->ident eq $ident ? undef : $ident) %>;
}
@@ Sym::Type::Struct#render_dissector
<% my ($self, $ident) = @_;
my $hfvar = $c->rinc('hf_'.$self->idstrip);
%>
static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf)
{
goffset start;
proto_item *ti;
start = xdr_getpos(xdrs);
if (hf == -1) {
ti = proto_tree_add_item(tree, <%= $hfvar %>, tvb, start, -1, ENC_NA);
} else {
header_field_info *hfinfo;
hfinfo = proto_registrar_get_nth(<%= $hfvar %>);
ti = proto_tree_add_item(tree, hf, tvb, start, -1, ENC_NA);
proto_item_append_text(ti, " :: %s", hfinfo->name);
}
tree = proto_item_add_subtree(ti, <%= $c->rinc('ett_'.$self->idstrip) %>);
<% for my $m (@{ $self->members }) { %>
hf = <%= $c->rinc('hf_'.$ident.'__'.$m->ident) %>;
if (!<%= $m->type->render_caller($ident.'__'.$m->ident) %>) return FALSE;
<% } %>
proto_item_set_len(ti, xdr_getpos(xdrs) - start);
return TRUE;
}
@@ Sym::Type::Enum#render_dissector
<% my ($self, $ident) = @_; %>
static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf)
{
goffset start;
enum { DUMMY } es;
start = xdr_getpos(xdrs);
if (xdr_enum(xdrs, (enum_t *)&es)) {
switch ((guint)es) {
<% for my $m (@{ $self->members }) { %>
case <%= $m->value %>:
proto_tree_add_uint_format_value(tree, hf, tvb, start, xdr_getpos(xdrs) - start, (guint)es, "<%= $m->idstrip %>(<%= $m->value %>)");
return TRUE;
<% } %>
}
} else {
proto_tree_add_text(tree, tvb, start, -1, "(unknown)");
}
return FALSE;
}
@@ Sym::Type::Union#render_dissector
<%
my ($self, $ident) = @_;
my $decl_type = $self->decl->type->idstrip;
%>
static gboolean dissect_xdr_<%= $ident %>(tvbuff_t *tvb, proto_tree *tree, XDR *xdrs, int hf)
{
gboolean rc = TRUE;
goffset start;
<%= $decl_type %> type = 0;
start = xdr_getpos(xdrs);
if (!xdr_<%= $decl_type %>(xdrs, &type))
return FALSE;
switch (type) {
<% for my $cs (@{ $self->case_specs }) {
my ($vals, $decl) = @$cs;
%>
<% for my $v (@$vals) { %>
case <%= $v %>:
<% } %>
hf = <%= $c->rinc('hf_'.$ident.'__'.$decl->ident) %>;
rc = <%= $decl->type->render_caller($ident.'__'.$decl->ident) %>; break;
<% } %>
}
if (!rc) {
proto_tree_add_text(tree, tvb, start, -1, "(unknown)");
}
return rc;
}
@@ macro.hfvars
#define VIR_DYNAMIC_HFSET \
<% for my $prog (@{ $_->{programs} }) { %>
{ &hf_<%= $prog %>_procedure,\
{ "procedure", "libvirt.procedure",\
FT_INT32, BASE_DEC,\
VALS(<%= $prog %>_procedure_strings), 0x0,\
NULL, HFILL}\
},\
<% } %>
<% for my $hf (@{ $_->{hfvars} }) { %>
{ &<%= $hf->{segment}{sym} %>,\
{ "<%= $hf->{name} %>", "libvirt.<%= $hf->{abbrev} %>",\
FT_<%= $hf->{ft_type} %>, BASE_<%= $hf->{hf_base} %>,\
NULL, 0x0,\
NULL, HFILL}\
},\
<% } %>
/* End of #define VIR_DYNAMIC_HFSET */
@@ macro.ettvars
#define VIR_DYNAMIC_ETTSET \
<% for my $ett (@{ $_->{ettvars} }) { %>
&<%= $ett %>,\
<% } %>
/* End of #define VIR_DYNAMIC_ETTSET */
@@ code.dissectorlist
static const vir_dissector_index_t <%= $_->{name} %>_dissectors[] = {
<% for my $d (@{ $_->{dissectors} }) { %>
{ <%= $d->{value} %>, <%= $d->{args} %>, <%= $d->{ret} %>, <%= $d->{msg} %> },
<% } %>
};
static const gsize <%= $_->{name} %>_dissectors_len = array_length(<%= $_->{name} %>_dissectors);
@@ code.procedure_strings
static const value_string <%= $_->{name} %>_procedure_strings[] = {
<% for my $proc (@{ $_->{procedures} }) {
my $ident = $proc->ident;
$ident =~ s/^$_->{name}_proc_//i;
%>
{ <%= $proc->value %>, "<%= $ident %>" },
<% } %>
{ 0, NULL }
};
@@ code.program_strings
static const value_string program_strings[] = {
<% for my $prog (map uc, @{ $_->{programs} }) { %>
{ <%= $c->symbol("$prog\_PROGRAM")->value %>, "<%= $prog %>" },
<% } %>
{ 0, NULL }
};
@@ code.program_data
static const void *program_data[][VIR_PROGRAM_LAST] = {
<% for my $p (@{ $_->{programs} }) { %>
{ &hf_<%= $p %>_procedure, <%= $p %>_procedure_strings, <%= $p %>_dissectors, &<%= $p %>_dissectors_len },
<% } %>
};
static const void *
get_program_data(guint32 prog, enum vir_program_data_index index)
{
if (index < VIR_PROGRAM_LAST) {
switch (prog) {
<% my $i = 0; %>
<% for my $prog (@{ $_->{programs} }) { %>
case <%= uc($prog) %>_PROGRAM:
return program_data[<%= $i++ %>][index];
<% } %>
}
}
return NULL;
}
#! /bin/sh
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, see <http://www.gnu.org/licenses/>.
#
# Copied from Wireshark(http://www.wireshark.org/)
#
# The first argument is the directory in which the source files live.
#
srcdir="$1"
shift
#
# The second argument is either "plugin" or "dissectors"; if it's
# "plugin", we build a plugin.c for a plugin, and if it's
# "dissectors", we build a register.c for libwireshark.
#
registertype="$1"
shift
if [ "$registertype" = plugin ]
then
outfile="plugin.c"
elif [ "$registertype" = dissectors ]
then
outfile="register.c"
else
echo "Unknown output type '$registertype'" 1>&2
exit 1
fi
#
# All subsequent arguments are the files to scan.
#
rm -f ${outfile}-tmp
echo '/* Do not modify this file. */' >${outfile}-tmp
echo '/* It is created automatically by the Makefile. */'>>${outfile}-tmp
if [ "$registertype" = plugin ]
then
cat <<"EOF" >>${outfile}-tmp
#include "config.h"
#include <gmodule.h>
/* plugins are DLLs */
#define WS_BUILD_DLL
#include "ws_symbol_export.h"
#ifndef ENABLE_STATIC
WS_DLL_PUBLIC_NOEXTERN const gchar version[] = VERSION;
/* Start the functions we need for the plugin stuff */
WS_DLL_PUBLIC_NOEXTERN void
plugin_register (void)
{
EOF
#
# Build code to call all the protocol registration routines.
#
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp
else
cat <<"EOF" >>${outfile}-tmp
#include "register.h"
void
register_all_protocols(register_cb cb, gpointer client_data)
{
EOF
#
# Build code to call all the protocol registration routines.
#
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^void proto_register_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_REGISTER, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp
fi
echo '}' >>${outfile}-tmp
#
# Build code to call all the protocol handoff registration routines.
#
if [ "$registertype" = plugin ]
then
cat <<"EOF" >>${outfile}-tmp
WS_DLL_PUBLIC_NOEXTERN void
plugin_reg_handoff(void)
{
EOF
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); \1 ();}/' >>${outfile}-tmp
else
cat <<"EOF" >>${outfile}-tmp
void
register_all_protocol_handoffs(register_cb cb, gpointer client_data)
{
EOF
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^\([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp
for f in "$@"
do
if [ -f $f ]
then
srcfile=$f
else
srcfile=$srcdir/$f
fi
grep '^void proto_reg_handoff_[a-z_0-9A-Z]* *(' $srcfile 2>/dev/null | grep -v ';'
done | sed -e 's/^.*://' -e 's/^void \([a-z_0-9A-Z]*\).*/ {extern void \1 (void); if(cb) (*cb)(RA_HANDOFF, \"\1\", client_data); \1 ();}/' >>${outfile}-tmp
fi
echo '}' >>${outfile}-tmp
if [ "$registertype" = plugin ]
then
echo '#endif' >>${outfile}-tmp
else
cat <<"EOF" >>${outfile}-tmp
gulong register_count(void)
{
EOF
proto_regs=`grep RA_REGISTER ${outfile}-tmp | wc -l`
handoff_regs=`grep RA_HANDOFF ${outfile}-tmp | wc -l`
echo " return $proto_regs + $handoff_regs;" >>${outfile}-tmp
echo '}' >>${outfile}-tmp
fi
# Only overwrite outfile if it differs from newly generated file
mv ${outfile}-tmp ${outfile}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册