New plugin API and porting of the plugins (+ several changes in the DNS plugin)

Initial
Stephane Bortzmeyer 20 years ago
parent 85adcec177
commit ace6dee486

@ -1,22 +1,42 @@
If you want to write your own plugins, they will have to provide three If you want to write your own plugins, you will first have to decide
functions: wether your plugin uses a "cooked" hostname or a "raw" one.
char * init (const int argc, const char **argv) In the first case, the cooked hostname, your plugin will receive from
Accepts remaining arguments (you have to use popt to parse them, or echoping a struct addrinfo. All the DNS stuff, including IDN, is
do it by hand, getopt does not allow you to resume the parsing) and performed by echoping. You can immediately start using the struct
returns a string identifying the port name. If it makes no sense, you addrinfo.
can safely return NULL.
But some libraries (typically, the one used by the DBMS) do not work
on struct addrinfo but on strings such as "dbname=test
hostname=foo.bar". Plugins using these libraries will have to use the
raw interface.
You indicate to echoping wether you use the raw interface or the
cooked one by returning a port name or NULL from the init() function.
You will have to provide three functions:
char * init (const int argc, const char **argv) Accepts remaining
arguments (you have to use popt to parse them, or do it by hand,
getopt does not allow you to resume the parsing) and returns a
string identifying the port name (cooked interface) or NULL (raw
interface).
For the cooked interface:
void start (struct addrinfo *res) void start (struct addrinfo *res)
Typically just stores the res structure for later use. Typically just stores the res structure for later use.
TODO: two start() routines, one which takes a struct addrinfo and one For the raw interface:
which does not (PostgreSQL or MySQL does not use struct addrinfo but a
string)? void start_raw ()
Typically connects to the server.
int execute () int execute ()
Connects and do whatever the protocol requires. It is called once Connects and do whatever the protocol requires. It is called once
per iteration. It returns >=0 if it succeeds and -1 if it failed. per iteration. It returns >=0 if it succeeds, -1 if it failed
temporarily (so echoping will continue its loop) and -2 if it failes
permanently (so echoping will stop the iteration).
void terminate () void terminate ()
Cleans everything. It is called after all iterations. Cleans everything. It is called after all iterations.
@ -24,11 +44,12 @@ void terminate ()
Start your plugin source code with: Start your plugin source code with:
#define IN_PLUGIN #define IN_PLUGIN
#include "../echoping.h" #include "/wherever/echoping/is/installed/echoping.h"
You can look at random.c, the simplest plugin, and whois.c, the You can look at random.c, the simplest plugin, and whois.c, the
simplest which still does something useful. simplest which still does something useful.
TODO: documentation of the plugin. A man page echoping_PLUGINNAME? The documentation of the plugin should be in a manual page named
echoping_PLUGINNAME. See the above plugins for examples.
$Id$ $Id$

1
SRC/configure vendored

@ -18940,6 +18940,7 @@ _ACEOF
fi fi
fi; fi;
# Check whether --with-popt or --without-popt was given. # Check whether --with-popt or --without-popt was given.
if test "${with_popt+set}" = set; then if test "${with_popt+set}" = set; then
withval="$with_popt" withval="$with_popt"

@ -98,8 +98,15 @@ AC_ARG_WITH(gnutls,
CPPFLAGS="${CPPFLAGS} -I$GNUTLSROOT/include" CPPFLAGS="${CPPFLAGS} -I$GNUTLSROOT/include"
fi fi
fi], fi],
dnl Default: disable it dnl Default: disable it
) )
dnl TODO: test if the getopt variable optreset exists and, if it
dnl does, we can use getopt (Free BSDs and MacOS X). This implies to
dnl maintain the two versions, popt and getopt, while echoping has
dnl many options. It probably means a high-level language to describe
dnl the options and to produce the two versions. gengetopt is an obvious
dnl candidate.
AC_ARG_WITH(popt, AC_ARG_WITH(popt,
[ --with-popt[=DIR] popt command-line parsing library],dnl [ --with-popt[=DIR] popt command-line parsing library],dnl
[if test "$withval" != "no"; then [if test "$withval" != "no"; then

@ -87,6 +87,7 @@ main (argc, argv)
char *plugin_name, *complete_plugin_name = NULL; char *plugin_name, *complete_plugin_name = NULL;
char *ext; char *ext;
void *plugin; void *plugin;
int plugin_result;
char *dl_result; char *dl_result;
void to_alarm (); /* our alarm() signal handler */ void to_alarm (); /* our alarm() signal handler */
@ -100,6 +101,7 @@ main (argc, argv)
#endif #endif
char *plugin_port_name, *port_name; char *plugin_port_name, *port_name;
unsigned short plugin_raw;
unsigned short port_to_use = USE_ECHO; unsigned short port_to_use = USE_ECHO;
unsigned short http = 0; unsigned short http = 0;
unsigned short smtp = 0; unsigned short smtp = 0;
@ -143,6 +145,7 @@ main (argc, argv)
/* popt variables */ /* popt variables */
const struct poptOption options[] = { const struct poptOption options[] = {
{"verbose", 'v', POPT_ARG_NONE, &verbose, 'v'}, {"verbose", 'v', POPT_ARG_NONE, &verbose, 'v'},
{"help", '?', POPT_ARG_NONE, NULL, '?'},
{"size", 's', POPT_ARG_INT, &size, 's'}, {"size", 's', POPT_ARG_INT, &size, 's'},
{"number", 'n', POPT_ARG_INT, &number, 'n'}, {"number", 'n', POPT_ARG_INT, &number, 'n'},
#ifdef HAVE_USLEEP #ifdef HAVE_USLEEP
@ -167,7 +170,7 @@ main (argc, argv)
{"ipv4", '4', POPT_ARG_NONE, NULL, '4'}, {"ipv4", '4', POPT_ARG_NONE, NULL, '4'},
{"ipv6", '6', POPT_ARG_NONE, NULL, '6'}, {"ipv6", '6', POPT_ARG_NONE, NULL, '6'},
{"module", 'm', POPT_ARG_STRING, &plugin_name, 'm'}, {"module", 'm', POPT_ARG_STRING, &plugin_name, 'm'},
{NULL, 0, 0, NULL, 0, NULL, NULL} POPT_TABLEEND
}; };
poptContext poptcon; poptContext poptcon;
@ -192,20 +195,25 @@ main (argc, argv)
} }
progname = (char *) argv[0]; progname = (char *) argv[0];
poptcon = poptGetContext (NULL, argc, argv, options, 0); poptcon =
poptGetContext (NULL, argc, argv, options, POPT_CONTEXT_POSIXMEHARDER);
while ((!module_find) && (result = poptGetNextOpt (poptcon)) != -1) while ((result = poptGetNextOpt (poptcon)) != -1)
{ {
if (result < -1) if (result < -1)
{ {
printf ("%s: %s", fprintf (stderr, "%s: %s\n",
poptBadOption (poptcon, POPT_BADOPTION_NOALIAS), poptBadOption (poptcon, POPT_BADOPTION_NOALIAS),
poptStrerror (result)); poptStrerror (result));
usage (); usage (poptcon);
} }
remaining--; remaining--;
switch ((char) result) switch ((char) result)
{ {
case '?':
poptPrintHelp (poptcon, stdout, 0);
fprintf (stdout, " hostname [plugin-options...]\n");
exit (0);
case 'v': case 'v':
break; break;
case 'r': case 'r':
@ -324,7 +332,7 @@ main (argc, argv)
break; break;
default: default:
printf ("Unknown character option %d (%c)", result, (char) result); printf ("Unknown character option %d (%c)", result, (char) result);
usage (); usage (poptcon);
} }
} }
if (udp && ((port_to_use == USE_CHARGEN) || if (udp && ((port_to_use == USE_CHARGEN) ||
@ -445,29 +453,43 @@ main (argc, argv)
plugin_name, PLUGINS_DIR, dlerror ()); plugin_name, PLUGINS_DIR, dlerror ());
} }
plugin_init = dlsym (plugin, "init"); plugin_init = dlsym (plugin, "init");
if (! plugin_init) if (!plugin_init)
{ {
err_sys ("Cannot find init in %s: %s", plugin_name, dlerror()); err_sys ("Cannot find init in %s: %s", plugin_name, dlerror ());
} }
plugin_port_name = plugin_init (remaining, (const char **) leftover); plugin_port_name = plugin_init (remaining, (const char **) leftover);
if (plugin_port_name != NULL) if (plugin_port_name != NULL)
strcpy (port_name, plugin_port_name); {
strcpy (port_name, plugin_port_name);
plugin_raw = FALSE;
plugin_start = dlsym (plugin, "start");
if (!plugin_start)
{
err_sys ("Cannot find start in %s: %s", plugin_name,
dlerror ());
}
}
else else
port_name = 0;
plugin_start = dlsym (plugin, "start");
if (! plugin_start)
{ {
err_sys ("Cannot find start in %s: %s", plugin_name, dlerror()); port_name = 0;
plugin_raw = TRUE;
plugin_raw_start = dlsym (plugin, "start_raw");
if (!plugin_raw_start)
{
err_sys ("Cannot find start_raw in %s: %s", plugin_name,
dlerror ());
}
} }
plugin_execute = dlsym (plugin, "execute"); plugin_execute = dlsym (plugin, "execute");
if (!plugin_execute) if (!plugin_execute)
{ {
err_sys ("Cannot find execute in %s: %s", plugin_name, dlerror()); err_sys ("Cannot find execute in %s: %s", plugin_name, dlerror ());
} }
plugin_terminate = dlsym (plugin, "terminate"); plugin_terminate = dlsym (plugin, "terminate");
if (! plugin_terminate) if (!plugin_terminate)
{ {
err_sys ("Cannot find terminate in %s: %s", plugin_name, dlerror()); err_sys ("Cannot find terminate in %s: %s", plugin_name,
dlerror ());
} }
} }
if (!udp && !ttcp) if (!udp && !ttcp)
@ -475,11 +497,11 @@ main (argc, argv)
tcp = 1; tcp = 1;
} }
if (remaining == 0) if (remaining == 0)
usage (); usage (poptcon);
if (!module_find && remaining != 1) if (!module_find && remaining != 1)
{ {
printf ("%d args remaning, should be 1\n", remaining); printf ("%d args remaining, should be 1\n", remaining);
usage (); usage (poptcon);
} }
if (verbose) if (verbose)
{ {
@ -613,18 +635,21 @@ main (argc, argv)
} }
} /* Else it is an address, do not IDNize it */ } /* Else it is an address, do not IDNize it */
#endif #endif
error = getaddrinfo (server, port_name, &hints, &res); if (!plugin || !plugin_raw)
if (error)
{ {
err_quit ("getaddrinfo error for host: %s %s", error = getaddrinfo (server, port_name, &hints, &res);
server, gai_strerror (error)); if (error)
} {
err_quit ("getaddrinfo error for host: %s %s",
server, gai_strerror (error));
}
if (getnameinfo (res->ai_addr, res->ai_addrlen, hbuf, sizeof (hbuf), if (getnameinfo (res->ai_addr, res->ai_addrlen, hbuf, sizeof (hbuf),
pbuf, sizeof (pbuf), niflags) != 0) pbuf, sizeof (pbuf), niflags) != 0)
{ {
strcpy (hbuf, "?"); strcpy (hbuf, "?");
strcpy (pbuf, "?"); strcpy (pbuf, "?");
}
} }
if (plugin) if (plugin)
{ {
@ -632,7 +657,10 @@ main (argc, argv)
{ {
printf ("Running start() for the plugin %s...\n", plugin_name); printf ("Running start() for the plugin %s...\n", plugin_name);
} }
plugin_start (res); if (plugin_raw)
plugin_raw_start ();
else
plugin_start (res);
} }
#ifdef HTTP #ifdef HTTP
if (http) if (http)
@ -823,9 +851,12 @@ main (argc, argv)
} }
else else
{ {
printf if (plugin_raw)
("Trying to call plugin %s for internet address %s %s...\n", printf ("Trying to call plugin %s...\n", plugin_name);
plugin_name, hbuf, pbuf); else
printf
("Trying to call plugin %s for internet address %s %s...\n",
plugin_name, hbuf, pbuf);
} }
} }
#ifdef FLUSH_OUTPUT #ifdef FLUSH_OUTPUT
@ -856,8 +887,9 @@ main (argc, argv)
(void) gettimeofday (&oldtv, (struct timezone *) NULL); (void) gettimeofday (&oldtv, (struct timezone *) NULL);
if (plugin) if (plugin)
{ {
plugin_execute (); plugin_result = plugin_execute ();
/* TODO: do something with the return code */ if (plugin_result == -2)
err_quit("");
} }
else else
{ {

@ -127,6 +127,8 @@ typedef char * (*init_f) (const int argc, const char **argv);
init_f plugin_init; init_f plugin_init;
typedef void (*start_f) (struct addrinfo *); typedef void (*start_f) (struct addrinfo *);
start_f plugin_start; start_f plugin_start;
typedef void (*start_raw_f) ();
start_raw_f plugin_raw_start;
typedef int (*execute_f) (); typedef int (*execute_f) ();
execute_f plugin_execute; execute_f plugin_execute;
typedef void (*terminate_f) (); typedef void (*terminate_f) ();

@ -85,11 +85,10 @@ err_sys (char *str, ...)
} }
void void
usage () usage (poptContext context)
{ {
fprintf (stderr, poptPrintUsage (context, stderr, 0);
"Usage: %s [-4] [-6] [-v] [-r] [-f fill] [-t timeout] [-c] [-d] [-u] [-s size] [-n number] [-w delay] [-h url] [-i url] [-p priority] [-P tos] [-C] [-S] [-m plugin] hostname[:port]\n", fprintf (stderr, " hostname [plugin-options...]\n");
progname);
exit (1); exit (1);
} }
@ -110,11 +109,11 @@ sys_err_str ()
if (errno != 0) if (errno != 0)
{ {
sprintf (msgstr, "(%s)", strerror(errno)); sprintf (msgstr, "(%s)", strerror (errno));
} }
else else
{ {
msgstr[0] = '\0'; msgstr[0] = '\0';
} }
return (msgstr); return (msgstr);
} }

@ -1,7 +1,5 @@
/* /*
* DNS plugin. * DNS plugin.
* TODO: return errors to echoping (name server not existing, for instance)
* TODO: allow options like TCP
* $Id$ * $Id$
*/ */
@ -16,6 +14,8 @@ struct addrinfo name_server;
poptContext dns_poptcon; poptContext dns_poptcon;
char *request; char *request;
int type; int type;
short use_tcp = FALSE;
short no_recurse = FALSE;
/* nsError stolen from Liu & Albitz check_soa (in their book "DNS and BIND") */ /* nsError stolen from Liu & Albitz check_soa (in their book "DNS and BIND") */
@ -78,6 +78,12 @@ init (const int argc, const char **argv)
{"type", 't', POPT_ARG_STRING, &type_name, 0, {"type", 't', POPT_ARG_STRING, &type_name, 0,
"Type of resources queried (A, MX, SOA, etc)", "Type of resources queried (A, MX, SOA, etc)",
"type"}, "type"},
{"tcp", NULL, POPT_ARG_NONE, &use_tcp, 0,
"Use TCP for the request (virtual circuit)",
"tcp"},
{"no-recurse", NULL, POPT_ARG_NONE, &no_recurse, 0,
"Do not ask recursion",
"no-recurse"},
POPT_AUTOHELP POPT_TABLEEND POPT_AUTOHELP POPT_TABLEEND
}; };
dns_poptcon = poptGetContext (NULL, argc, dns_poptcon = poptGetContext (NULL, argc,
@ -100,14 +106,18 @@ init (const int argc, const char **argv)
{ /* TODO: a better algorithm. Use dns_rdatatype_fromtext in BIND ? */ { /* TODO: a better algorithm. Use dns_rdatatype_fromtext in BIND ? */
if (!strcmp (type_name, "A")) if (!strcmp (type_name, "A"))
type = T_A; type = T_A;
else if (!strcmp (type_name, "AAAA"))
type = T_AAAA;
else if (!strcmp (type_name, "NS")) else if (!strcmp (type_name, "NS"))
type = T_NS; type = T_NS;
else if (!strcmp (type_name, "SOA")) else if (!strcmp (type_name, "SOA"))
type = T_SOA; type = T_SOA;
else if (!strcmp (type_name, "MX")) else if (!strcmp (type_name, "MX"))
type = T_MX; type = T_MX;
else if (!strcmp (type_name, "AAAA")) else if (!strcmp (type_name, "SRV"))
type = T_AAAA; type = T_SRV;
else if (!strcmp (type_name, "TXT"))
type = T_TXT;
else else
dns_usage ("Unknown type"); dns_usage ("Unknown type");
} }
@ -129,6 +139,14 @@ start (struct addrinfo *res)
_res.nsaddr_list[0] = name_server_sockaddr_in; /* TODO: and IPv6? Detect _resext with autoconf (*BSD) and use it */ _res.nsaddr_list[0] = name_server_sockaddr_in; /* TODO: and IPv6? Detect _resext with autoconf (*BSD) and use it */
_res.nscount = 1; _res.nscount = 1;
_res.options &= ~(RES_DNSRCH | RES_DEFNAMES | RES_NOALIASES); _res.options &= ~(RES_DNSRCH | RES_DEFNAMES | RES_NOALIASES);
if (use_tcp)
{
_res.options &= RES_USEVC;
}
if (no_recurse)
{
_res.options &= ~RES_RECURSE;
}
} }
int int
@ -142,13 +160,15 @@ execute ()
int response_length; /* buffer length */ int response_length; /* buffer length */
if ((response_length = res_query (request, /* the domain we care about */ if ((response_length = res_query (request, /* the domain we care about */
C_IN, /* Internet class records */ C_IN, /* Internet class records */
type, type, (u_char *) & response, /*response buffer */
(u_char *) & response, /*response buffer */
sizeof (response))) /*buffer size */ sizeof (response))) /*buffer size */
< 0) < 0)
{ /*If negative */ { /*If negative */
nsError (h_errno, request); /* report the error */ nsError (h_errno, request); /* report the error */
return -1; /* and quit */ if (h_errno == TRY_AGAIN)
return -1; /* More luck next time? */
else
return -2; /* Give in */
} }
/* TODO: better analysis of the replies. For instance, replies can be in the authority section (delegation info) */ /* TODO: better analysis of the replies. For instance, replies can be in the authority section (delegation info) */
return 0; return 0;

@ -58,7 +58,7 @@ init (const int argc, const char **argv)
postgresql_usage (msg); postgresql_usage (msg);
} }
} }
if (request == NULL) /* TODO: a default like SELECT now()? */ if (request == NULL) /* TODO: a default like SELECT now()? */
postgresql_usage ("Mandatory request missing"); postgresql_usage ("Mandatory request missing");
if (conninfo == NULL) if (conninfo == NULL)
postgresql_usage ("Mandatory connection information missing"); postgresql_usage ("Mandatory connection information missing");
@ -66,18 +66,16 @@ init (const int argc, const char **argv)
} }
void void
start (struct addrinfo *res) start_raw ()
{ {
conn = PQconnectdb (conninfo); conn = PQconnectdb (conninfo);
if (conn == NULL) if (conn == NULL)
{ {
printf ("Cannot create connection\n"); err_quit ("Cannot create connection\n");
exit (1);
} }
if (PQstatus (conn) == CONNECTION_BAD) if (PQstatus (conn) == CONNECTION_BAD)
{ {
printf ("Connection failed: %s\n", PQerrorMessage (conn)); err_quit ("Connection failed: %s\n", PQerrorMessage (conn));
exit (1);
} }
} }

Loading…
Cancel
Save