This functionality is only compiled into a developer build, It allows
test functions to be invoked by name from the vtysh as follows.
Changes:
* vtysh/vtysh.c
Handle the 'invoke <component> function <function-name> [args]' command.
Forward it the client with the given name.
* lib/vty_invoke.c
Code that handles an 'invoke function' command inside a vtysh
client process (e;g., a routing protocol daemon). It invokes the
named function, and prints out some stats about the execution
time.
* lib/thread.h
Extern timeval_elapsed().
* lib/vty.[ch]
- vty_init():
Install element for 'invoke' command handler. This sets up the
handler that runs inside of a vtysh client.
* lib/Makefile.am
Also compile vty_invoke.c.
* configure.ac
Include '-rdynamic -ldl' in LDFLAGS for a development build. These
options allow dlsym() to work for functions inside a quagga
program.
Signed-off-by: Avneesh Sachdev <[email protected]>
---
configure.ac | 4 ++
lib/Makefile.am | 2 +-
lib/thread.h | 3 ++
lib/vty.c | 4 ++
lib/vty_invoke.c | 120 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
lib/vty_invoke.h | 30 ++++++++++++++
vtysh/vtysh.c | 78 ++++++++++++++++++++++++++++++++++++
7 files changed, 240 insertions(+), 1 deletion(-)
create mode 100644 lib/vty_invoke.c
create mode 100644 lib/vty_invoke.h
diff --git a/configure.ac b/configure.ac
index ae2e527..60a9118 100755
--- a/configure.ac
+++ b/configure.ac
@@ -331,6 +331,10 @@ fi
if test "x${enable_dev_build}" = "xyes"; then
AC_DEFINE(DEV_BUILD,,Build for development)
+
+ # Link with -ldl so that the developer build can use dlsym and
+ # company.
+ LDFLAGS="${LDFLAGS} -rdynamic -ldl"
fi
AM_CONDITIONAL([DEV_BUILD], [test "x$enable_dev_build" = "xyes"])
diff --git a/lib/Makefile.am b/lib/Makefile.am
index ac51fc6..17dc575 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -13,7 +13,7 @@ libzebra_la_SOURCES = \
sockunion.c prefix.c thread.c if.c memory.c buffer.c table.c hash.c \
filter.c routemap.c distribute.c stream.c str.c log.c plist.c \
zclient.c sockopt.c smux.c agentx.c snmp.c md5.c if_rmap.c keychain.c
privs.c \
- sigevent.c pqueue.c jhash.c memtypes.c workqueue.c vrf.c
+ sigevent.c pqueue.c jhash.c memtypes.c workqueue.c vrf.c vty_invoke.c
BUILT_SOURCES = memtypes.h route_types.h gitversion.h
diff --git a/lib/thread.h b/lib/thread.h
index 5bc756c..215ac5d 100644
--- a/lib/thread.h
+++ b/lib/thread.h
@@ -229,6 +229,9 @@ extern time_t quagga_time (time_t *);
extern unsigned long thread_consumed_time(RUSAGE_T *after, RUSAGE_T *before,
unsigned long *cpu_time_elapsed);
+/* Returns elapsed time in microseconds */
+extern unsigned long timeval_elapsed (struct timeval a, struct timeval b);
+
/* Global variable containing a recent result from gettimeofday. This can
be used instead of calling gettimeofday if a recent value is sufficient.
This is guaranteed to be refreshed before a thread is called. */
diff --git a/lib/vty.c b/lib/vty.c
index 8befcb0..657ed97 100644
--- a/lib/vty.c
+++ b/lib/vty.c
@@ -40,6 +40,8 @@
#include <arpa/telnet.h>
#include <termios.h>
+#include "vty_invoke.h"
+
/* Vty events */
enum event
{
@@ -3073,6 +3075,8 @@ vty_init (struct thread_master *master_thread)
install_element (VTY_NODE, &vty_ipv6_access_class_cmd);
install_element (VTY_NODE, &no_vty_ipv6_access_class_cmd);
#endif /* HAVE_IPV6 */
+
+ vty_invoke_init ();
}
void
diff --git a/lib/vty_invoke.c b/lib/vty_invoke.c
new file mode 100644
index 0000000..3c43568
--- /dev/null
+++ b/lib/vty_invoke.c
@@ -0,0 +1,120 @@
+/*
+ * vty_invoke.c
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <[email protected]>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+/*
+ * Support for invoking functions by name from a vty shell.
+ *
+ * This code is only compiled into a developer build, and allows
+ * functions that follow certain signatures to be invoked by name from
+ * the vtysh.
+ */
+
+#include <zebra.h>
+
+#include <dlfcn.h>
+
+#include "vector.h"
+#include "vty.h"
+#include "command.h"
+
+#include "vty_invoke.h"
+
+typedef int (*vty_invoke_func_t) (int argc, const char **argv);
+
+#ifdef DEV_BUILD
+
+DEFUN (invoke_function,
+ invoke_function_cmd,
+ "invoke function NAME [ARG1] [ARG2] [ARG3] [ARG4] [ARG5] [ARG6]",
+ "Invoke\n"
+ "Invoke a function\n"
+ "Name of function to invoke\n"
+ "First argument\n"
+ "Second argument\n"
+ "Third argument\n"
+ "Fourth argument\n"
+ "Fifth argument\n"
+ "Sixth argument\n")
+{
+ int i;
+ const char *func_name;
+ vty_invoke_func_t func;
+ RUSAGE_T before, after;
+ ulong elapsed;
+
+ assert (argc >= 1);
+
+ func_name = argv[0];
+
+ func = dlsym (NULL, func_name);
+ if (!func)
+ {
+ vty_out (vty, "Can't find function %s", func_name);
+ return CMD_WARNING;
+ }
+
+ vty_out (vty, "Invoking %s(", func_name);
+
+ for (i = 1; i < argc; i++)
+ {
+ vty_out (vty, "%s%s", i == 1 ? "" : ", ", argv[i]);
+ }
+ vty_out (vty, ")\n");
+
+ GETRUSAGE (&before);
+ i = func (argc - 1, argv + 1);
+ GETRUSAGE (&after);
+
+ vty_out (vty, "Return value: %d\n\n", i);
+
+ elapsed = timeval_elapsed (after.real, before.real);
+ vty_out (vty, "%-20s %9lu ms\n", "Real time:", elapsed / 1000);
+
+#ifdef HAVE_RUSAGE
+ {
+ elapsed = timeval_elapsed (after.cpu.ru_utime, before.cpu.ru_utime);
+
+ vty_out (vty, "%-20s %9lu ms\n", "User time:", elapsed / 1000);
+
+ elapsed = timeval_elapsed (after.cpu.ru_stime, before.cpu.ru_stime);
+ vty_out (vty, "%-20s %9lu ms\n", "System time:", elapsed / 1000);
+ }
+#endif
+
+ return CMD_SUCCESS;
+}
+
+#endif /* DEV_BUILD */
+
+/*
+ * vty_invoke_init
+ */
+void
+vty_invoke_init (void)
+{
+#ifdef DEV_BUILD
+ install_element (ENABLE_NODE, &invoke_function_cmd);
+#endif
+}
diff --git a/lib/vty_invoke.h b/lib/vty_invoke.h
new file mode 100644
index 0000000..a2364a2
--- /dev/null
+++ b/lib/vty_invoke.h
@@ -0,0 +1,30 @@
+/*
+ * vty_invoke.h
+ *
+ * @copyright Copyright (C) 2016 Sproute Networks, Inc.
+ *
+ * @author Avneesh Sachdev <[email protected]>
+ *
+ * This file is part of GNU Zebra.
+ *
+ * GNU Zebra 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, or (at your option) any
+ * later version.
+ *
+ * GNU Zebra 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 GNU Zebra; see the file COPYING. If not, write to the Free
+ * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef _VTY_INVOKE_H
+#define _VTY_INVOKE_H
+
+extern void vty_invoke_init (void);
+
+#endif /* _VTY_INVOKE_H */
diff --git a/vtysh/vtysh.c b/vtysh/vtysh.c
index b55c671..b0f96d9 100644
--- a/vtysh/vtysh.c
+++ b/vtysh/vtysh.c
@@ -2149,6 +2149,80 @@ DEFUN (vtysh_start_zsh,
return CMD_SUCCESS;
}
+#ifdef DEV_BUILD
+
+/*
+ * 'invoke' command
+ *
+ * Command that allows user to invoke functions from the cli shell in
+ * development build.
+ */
+DEFUN (invoke_function,
+ invoke_function_cmd,
+ "invoke COMPONENT function NAME [ARG1] [ARG2] [ARG3] [ARG4] [ARG5]
[ARG6]",
+ "Invoke\n"
+ "Component name in which to invoke function\n"
+ "Invoke a function\n"
+ "Name of function to invoke\n"
+ "First argument\n"
+ "Second argument\n"
+ "Third argument\n"
+ "Fourth argument\n"
+ "Fifth argument\n"
+ "Sixth argument\n")
+{
+ unsigned int u;
+ char line[1000];
+ char *cur, *end;
+ int ret, i;
+ const char *component_name;
+ struct vtysh_client *client;
+
+ cur = line;
+ end = cur + sizeof (line);
+
+ cur += snprintf (cur, end - cur, "invoke function ");
+
+ /*
+ * Build command string, skipping over the component name.
+ */
+ for (i = 1; i < argc; i++)
+ {
+ ret = snprintf (cur, end - cur, "%s ", argv[i]);
+ if (ret < 0 || ret == (end - cur))
+ {
+ vty_out (vty, "Command string is too long");
+ return CMD_WARNING;
+ }
+ cur += ret;
+ }
+
+ component_name = argv[0];
+
+ for (u = 0; u < array_size (vtysh_client); u++)
+ {
+ client = &vtysh_client[u];
+ if (strcmp (client->name, component_name))
+ {
+ continue;
+ }
+ if (client->fd < 0)
+ {
+ fprintf (stdout, "Not connected to component %s\n", component_name);
+ return CMD_WARNING;
+ }
+
+ ret = vtysh_client_execute (client, line, stdout);
+ fprintf (stdout, "\n");
+ return ret;
+ }
+
+ fprintf (stdout, "Could not find component %s\n", component_name);
+ return CMD_WARNING;
+}
+
+#endif /* DEV_BUILD */
+
static void
vtysh_install_default (enum node_type node)
{
@@ -2530,4 +2604,8 @@ vtysh_init_vty (void)
install_element (CONFIG_NODE, &vtysh_enable_password_text_cmd);
install_element (CONFIG_NODE, &no_vtysh_enable_password_cmd);
+#ifdef DEV_BUILD
+ install_element (ENABLE_NODE, &invoke_function_cmd);
+#endif
+
}
--
1.9.1
_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev