Avneesh -

Can you expand a bit on why adding the ability to call random functions
from the cli is better than just adding a 'test XXX' function as needed?
Additionally how do you plan to allow the end user to pass in useful data
to functions if the function you want to call takes a complex data
structure?  I'm pretty dubious about adding a cause a crash button even for
developer builds, especially since adding a test XXX function is an easy
task.

donald

On Fri, Mar 11, 2016 at 3:21 PM, Avneesh Sachdev <[email protected]>
wrote:

> 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
>
_______________________________________________
Quagga-dev mailing list
[email protected]
https://lists.quagga.net/mailman/listinfo/quagga-dev

Reply via email to