From: Florian Forster Date: Sun, 9 Dec 2007 10:54:52 +0000 (+0100) Subject: Merge branch 'ps/reuse' X-Git-Tag: collectd-4.3.0beta0~70 X-Git-Url: https://git.octo.it/?a=commitdiff_plain;h=d32581d70544999bc4442c5890c7e152a0af5625;hp=501142a3e19dbd5e30684d075ae3db87f5bfef82;p=collectd.git Merge branch 'ps/reuse' --- diff --git a/README b/README index d4964711..2a5502c2 100644 --- a/README +++ b/README @@ -133,9 +133,6 @@ Features write your own plugins in Perl and return arbitrary values using this API. See collectd-perl(5). - This plugin is still considered to be experimental and subject to change - between minor releases. - - ping Network latency: Time to reach the default gateway or another given host. diff --git a/contrib/examples/MyPlugin.pm b/contrib/examples/MyPlugin.pm index 1b98d5b8..b8522721 100644 --- a/contrib/examples/MyPlugin.pm +++ b/contrib/examples/MyPlugin.pm @@ -17,25 +17,28 @@ package Collectd::Plugin::MyPlugin; use strict; use warnings; +use Collectd qw( :all ); + # data set definition: # see section "DATA TYPES" in collectd-perl(5) for details +# (take a look at the types.db file for a large list of predefined data-sets) my $dataset = [ { name => 'my_ds', - type => Collectd::DS_TYPE_GAUGE, + type => DS_TYPE_GAUGE, min => 0, max => 65535, }, ]; # This code is executed after loading the plugin to register it with collectd. -Collectd::plugin_register (Collectd::TYPE_LOG, 'myplugin', \&my_log); -Collectd::plugin_register (Collectd::TYPE_DATASET, 'myplugin', $dataset); -Collectd::plugin_register (Collectd::TYPE_INIT, 'myplugin', \&my_init); -Collectd::plugin_register (Collectd::TYPE_READ, 'myplugin', \&my_read); -Collectd::plugin_register (Collectd::TYPE_WRITE, 'myplugin', \&my_write); -Collectd::plugin_register (Collectd::TYPE_SHUTDOWN, 'myplugin', \&my_shutdown); +plugin_register (TYPE_LOG, 'myplugin', 'my_log'); +plugin_register (TYPE_DATASET, 'myplugin', $dataset); +plugin_register (TYPE_INIT, 'myplugin', 'my_init'); +plugin_register (TYPE_READ, 'myplugin', 'my_read'); +plugin_register (TYPE_WRITE, 'myplugin', 'my_write'); +plugin_register (TYPE_SHUTDOWN, 'myplugin', 'my_shutdown'); # For each of the functions below see collectd-perl(5) for details about # arguments and the like. @@ -67,7 +70,7 @@ sub my_read # dispatch the values to collectd which passes them on to all registered # write functions - the first argument is used to lookup the data set # definition - Collectd::plugin_dispatch_values ('myplugin', $vl); + plugin_dispatch_values ('myplugin', $vl); # A false return value indicates an error and the plugin will be skipped # for an increasing amount of time. @@ -82,7 +85,7 @@ sub my_write my $vl = shift; if (scalar (@$ds) != scalar (@{$vl->{'values'}})) { - Collectd::plugin_log (Collectd::LOG_WARNING, + plugin_log (LOG_WARNING, "DS number does not match values length"); return; } diff --git a/src/Makefile.am b/src/Makefile.am index 06f45dca..f5a64827 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -19,7 +19,7 @@ AM_CPPFLAGS += -DPIDFILE='"${localstatedir}/run/${PACKAGE_NAME}.pid"' endif AM_CPPFLAGS += -DPLUGINDIR='"${pkglibdir}"' -sbin_PROGRAMS = collectd +sbin_PROGRAMS = collectd collectdmon bin_PROGRAMS = collectd-nagios collectd_SOURCES = collectd.c collectd.h \ @@ -77,6 +77,9 @@ else collectd_LDFLAGS += -loconfig endif +collectdmon_SOURCES = collectdmon.c +collectdmon_CPPFLAGS = $(AM_CPPFLAGS) + collectd_nagios_SOURCES = collectd-nagios.c collectd_nagios_LDFLAGS = if BUILD_WITH_LIBSOCKET @@ -627,7 +630,7 @@ endif dist_man_MANS = collectd.1 collectd-nagios.1 collectd.conf.5 \ collectd-email.5 collectd-exec.5 collectd-perl.5 \ - collectd-snmp.5 collectd-unixsock.5 + collectd-snmp.5 collectd-unixsock.5 collectdmon.1 #collectd_1_SOURCES = collectd.pod @@ -635,7 +638,7 @@ EXTRA_DIST = types.db EXTRA_DIST += collectd-email.pod collectd-exec.pod collectd-nagios.pod \ collectd-perl.pod collectd-snmp.pod collectd-unixsock.pod \ - collectd.conf.pod collectd.pod + collectd.conf.pod collectd.pod collectdmon.pod .pod.1: pod2man --release=$(VERSION) --center=$(PACKAGE) $< >.pod2man.tmp 2>/dev/null && mv -f .pod2man.tmp $@ || true diff --git a/src/collectd-perl.pod b/src/collectd-perl.pod index ffe5177d..3306f39e 100644 --- a/src/collectd-perl.pod +++ b/src/collectd-perl.pod @@ -21,9 +21,6 @@ for collectd in Perl. This is a lot more efficient than executing a Perl-script every time you want to read a value with the C (see L) and provides a lot more functionality, too. -Please note that this is still considered to be experimental and subject to -change between minor releases. - =head1 CONFIGURATION =over 4 @@ -404,12 +401,38 @@ To register those functions with collectd: See the section "DATA TYPES" above for a complete documentation of the data types used by the read and write functions. -=head1 BUGS +=head1 CAVEATS + +=over 4 + +=item + +collectd is heavily multi-threaded. Each collectd thread accessing the perl +plugin will be mapped to a Perl interpreter thread (see L). +Any such thread will be created and destroyed transparently and on-the-fly. + +Hence, any plugin has to be thread-safe if it provides several entry points +from collectd (i.Ee. if it registers more than one callback). Please +note that no data is shared between threads by default. You have to use the +B module to do so. -This plugin does not yet work correctly if collectd uses multiple threads. -Perl does not allow multiple threads to access a single interpreter at the -same time. As a temporary workaround you should use a single read thread only -(see collectd's B configuration option). +=item + +Each function name registered with collectd has to be available before the +first thread has been created (i.Ee. basically at compile time). This +basically means that hacks (yes, I really consider this to be a hack) like +C<*foo = \&bar; plugin_register (TYPE_READ, "plugin", "foo");> most likely +will not work. This is due to the fact that the symbol table is not shared +across different threads. + +=item + +Each plugin is usually only loaded once and kept in memory for performance +reasons. Therefore, END blocks are only executed once when collectd shuts +down. You should not rely on END blocks anyway - use B +instead. + +=back =head1 SEE ALSO @@ -417,6 +440,8 @@ L, L, L, L, +L, +L, L =head1 AUTHOR diff --git a/src/collectd.conf.in b/src/collectd.conf.in index 8d10ad31..93b97be3 100644 --- a/src/collectd.conf.in +++ b/src/collectd.conf.in @@ -173,6 +173,7 @@ FQDNLookup true # # Host "localhost" # Port 123 +# ReverseLookups false # # diff --git a/src/collectd.conf.pod b/src/collectd.conf.pod index 3ba88102..f642c961 100644 --- a/src/collectd.conf.pod +++ b/src/collectd.conf.pod @@ -735,6 +735,13 @@ Hostname of the host running B. Defaults to B. UDP-Port to connect to. Defaults to B<123>. +=item B B|B + +Sets wether or not to perform reverse lookups on peers. Since the name or +IP-address may be used in a filename it is recommended to disable reverse +lookups. The default is to do reverse lookups to preserve backwards +compatibility, though. + =back =head2 Plugin C diff --git a/src/collectdmon.c b/src/collectdmon.c new file mode 100644 index 00000000..0295ad3d --- /dev/null +++ b/src/collectdmon.c @@ -0,0 +1,377 @@ +/** + * collectd - src/collectdmon.c + * Copyright (C) 2007 Sebastian Harl + * + * 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; only version 2 of the License is applicable. + * + * 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, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Author: + * Sebastian Harl + **/ + +#include "config.h" + +#include + +#include + +#include + +#include + +#include +#include + +#include + +#include + +#include +#include +#include +#include +#include + +#include + +#include + +#ifndef COLLECTDMON_PIDFILE +# define COLLECTDMON_PIDFILE LOCALSTATEDIR"/run/collectdmon.pid" +#endif /* ! COLLECTDMON_PIDFILE */ + +#ifndef WCOREDUMP +# define WCOREDUMP(s) 0 +#endif /* ! WCOREDUMP */ + +static int loop = 0; +static int restart = 0; + +static char *pidfile = NULL; +static pid_t collectd_pid = 0; + +static void exit_usage (char *name) +{ + printf ("Usage: %s [-- ]\n" + + "\nAvailable options:\n" + " -h Display this help and exit.\n" + " -c Path to the collectd binary.\n" + " -P PID-file.\n" + + "\nFor see collectd.conf(5).\n" + + "\n"PACKAGE" "VERSION", http://collectd.org/\n" + "by Florian octo Forster \n" + "for contributions see `AUTHORS'\n", name); + exit (0); +} /* exit_usage */ + +static int pidfile_create (void) +{ + FILE *file = NULL; + + if (NULL == pidfile) + pidfile = COLLECTDMON_PIDFILE; + + if (NULL == (file = fopen (pidfile, "w"))) { + syslog (LOG_ERR, "Error: couldn't open PID-file (%s) for writing: %s", + pidfile, strerror (errno)); + return -1; + } + + fprintf (file, "%i\n", (int)getpid ()); + fclose (file); + return 0; +} /* pidfile_create */ + +static int pidfile_delete (void) +{ + assert (NULL != pidfile); + + if (0 != unlink (pidfile)) { + syslog (LOG_ERR, "Error: couldn't delete PID-file (%s): %s", + pidfile, strerror (errno)); + return -1; + } + return 0; +} /* pidfile_remove */ + +static int daemonize (void) +{ + struct rlimit rl; + + pid_t pid = 0; + int i = 0; + + if (0 != chdir ("/")) { + fprintf (stderr, "Error: chdir() failed: %s\n", strerror (errno)); + return -1; + } + + if (0 != getrlimit (RLIMIT_NOFILE, &rl)) { + fprintf (stderr, "Error: getrlimit() failed: %s\n", strerror (errno)); + return -1; + } + + if (0 > (pid = fork ())) { + fprintf (stderr, "Error: fork() failed: %s\n", strerror (errno)); + return -1; + } + else if (pid != 0) { + exit (0); + } + + if (0 != pidfile_create ()) + return -1; + + setsid (); + + if (RLIM_INFINITY == rl.rlim_max) + rl.rlim_max = 1024; + + for (i = 0; i < rl.rlim_max; ++i) + close (i); + + errno = 0; + if (open ("/dev/null", O_RDWR) != 0) { + syslog (LOG_ERR, "Error: couldn't connect STDIN to /dev/null: %s", + strerror (errno)); + return -1; + } + + errno = 0; + if (dup (0) != 1) { + syslog (LOG_ERR, "Error: couldn't connect STDOUT to /dev/null: %s", + strerror (errno)); + return -1; + } + + errno = 0; + if (dup (0) != 2) { + syslog (LOG_ERR, "Error: couldn't connect STDERR to /dev/null: %s", + strerror (errno)); + return -1; + } + return 0; +} /* daemonize */ + +static int collectd_start (int argc, char **argv) +{ + pid_t pid = 0; + + if (0 > (pid = fork ())) { + syslog (LOG_ERR, "Error: fork() failed: %s", strerror (errno)); + return -1; + } + else if (pid != 0) { + collectd_pid = pid; + return 0; + } + + execvp (argv[0], argv); + syslog (LOG_ERR, "Error: execvp(%s) failed: %s", + argv[0], strerror (errno)); + exit (-1); +} /* collectd_start */ + +static int collectd_stop (void) +{ + if (0 == collectd_pid) + return 0; + + if (0 != kill (collectd_pid, SIGTERM)) { + syslog (LOG_ERR, "Error: kill() failed: %s", strerror (errno)); + return -1; + } + return 0; +} /* collectd_stop */ + +static void sig_int_term_handler (int signo) +{ + ++loop; + return; +} /* sig_int_term_handler */ + +static void sig_hup_handler (int signo) +{ + ++restart; + return; +} /* sig_hup_handler */ + +static void log_status (int status) +{ + if (WIFEXITED (status)) { + if (0 == WEXITSTATUS (status)) + syslog (LOG_INFO, "Info: collectd terminated with exit status %i", + WEXITSTATUS (status)); + else + syslog (LOG_WARNING, + "Warning: collectd terminated with exit status %i", + WEXITSTATUS (status)); + } + else if (WIFSIGNALED (status)) { + syslog (LOG_WARNING, "Warning: collectd was terminated by signal %i%s", + WTERMSIG (status), WCOREDUMP (status) ? " (core dumped)" : ""); + } + return; +} /* log_status */ + +static void check_respawn (void) +{ + time_t t = time (NULL); + + static time_t timestamp = 0; + static int counter = 0; + + if ((t - 120) < timestamp) + ++counter; + else { + timestamp = t; + counter = 0; + } + + if (10 < counter) { + unsigned int time_left = 300; + + syslog (LOG_ERR, "Error: collectd is respawning too fast - " + "disabled for %i seconds", time_left); + + while ((0 < (time_left = sleep (time_left))) && (0 == loop)); + } + return; +} /* check_respawn */ + +int main (int argc, char **argv) +{ + int collectd_argc = 0; + char *collectd = NULL; + char **collectd_argv = NULL; + + struct sigaction sa; + + int i = 0; + + /* parse command line options */ + while (42) { + int c = getopt (argc, argv, "hc:P:"); + + if (-1 == c) + break; + + switch (c) { + case 'c': + collectd = optarg; + break; + case 'P': + pidfile = optarg; + break; + case 'h': + default: + exit_usage (argv[0]); + } + } + + for (i = optind; i < argc; ++i) + if (0 == strcmp (argv[i], "-f")) + break; + + /* i < argc => -f already present */ + collectd_argc = 1 + argc - optind + ((i < argc) ? 0 : 1); + collectd_argv = (char **)calloc (collectd_argc + 1, sizeof (char *)); + + if (NULL == collectd_argv) { + fprintf (stderr, "Out of memory."); + return 3; + } + + collectd_argv[0] = (NULL == collectd) ? "collectd" : collectd; + + if (i == argc) + collectd_argv[collectd_argc - 1] = "-f"; + + for (i = optind; i < argc; ++i) + collectd_argv[i - optind + 1] = argv[i]; + + collectd_argv[collectd_argc] = NULL; + + openlog ("collectdmon", LOG_CONS | LOG_PID, LOG_DAEMON); + + if (-1 == daemonize ()) + return 1; + + sa.sa_handler = sig_int_term_handler; + sa.sa_flags = 0; + sigemptyset (&sa.sa_mask); + + if (0 != sigaction (SIGINT, &sa, NULL)) { + syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno)); + return 1; + } + + if (0 != sigaction (SIGTERM, &sa, NULL)) { + syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno)); + return 1; + } + + sa.sa_handler = sig_hup_handler; + + if (0 != sigaction (SIGHUP, &sa, NULL)) { + syslog (LOG_ERR, "Error: sigaction() failed: %s", strerror (errno)); + return 1; + } + + sigaddset (&sa.sa_mask, SIGCHLD); + if (0 != sigprocmask (SIG_BLOCK, &sa.sa_mask, NULL)) { + syslog (LOG_ERR, "Error: sigprocmask() failed: %s", strerror (errno)); + return 1; + } + + while (0 == loop) { + int status = 0; + + if (0 != collectd_start (collectd_argc, collectd_argv)) { + syslog (LOG_ERR, "Error: failed to start collectd."); + break; + } + + assert (0 < collectd_pid); + while ((collectd_pid != waitpid (collectd_pid, &status, 0)) + && (EINTR == errno)) + if ((0 != loop) || (0 != restart)) + collectd_stop (); + + collectd_pid = 0; + + log_status (status); + check_respawn (); + + if (0 != restart) { + syslog (LOG_INFO, "Info: restarting collectd"); + restart = 0; + } + else if (0 == loop) + syslog (LOG_WARNING, "Warning: restarting collectd"); + } + + syslog (LOG_INFO, "Info: shutting down collectdmon"); + + pidfile_delete (); + closelog (); + + free (collectd_argv); + return 0; +} /* main */ + +/* vim: set sw=4 ts=4 tw=78 noexpandtab : */ + diff --git a/src/collectdmon.pod b/src/collectdmon.pod new file mode 100644 index 00000000..5ae85fb0 --- /dev/null +++ b/src/collectdmon.pod @@ -0,0 +1,55 @@ +=head1 NAME + +collectdmon - Monitoring daemon for collectd + +=head1 SYNOPSIS + +collectdmon I<[options]> [-- I] + +=head1 DESCRIPTION + +collectdmon is a small "wrapper" daemon which starts and monitors the collectd +daemon. If collectd terminates it will automatically be restarted, unless +collectdmon was told to shut it down. + +=head1 OPTIONS + +collectdmon supports the following options: + +=over 4 + +=item B<-c> IpathE> + +Specify the pathname of the collectd binary. You may either specify an +absolute path or simply the name of the binary in which case the B +variable will be searched for it. The default is "B". + +=item B<-P> Ipid-fileE> + +Specify the pid file. The default is "I". + +=item B<-h> + +Output usage information and exit. + +=item I + +Specify options that are passed on to collectd. If it is not already included, +B<-f> will be added to these options. See L. + +=back + +=head1 SEE ALSO + +L, +L, +L + +=head1 AUTHOR + +collectd has been written by Florian Forster Eocto at verplant.orgE +and many contributors (see `AUTHORS'). + +collectdmon has been written by Sebastian Harl Esh@tokkee.orgE. + +=cut diff --git a/src/ntpd.c b/src/ntpd.c index 9e09f819..c5dcb8e5 100644 --- a/src/ntpd.c +++ b/src/ntpd.c @@ -50,9 +50,11 @@ static const char *config_keys[] = { "Host", "Port", - NULL + "ReverseLookups" }; -static int config_keys_num = 2; +static int config_keys_num = STATIC_ARRAY_SIZE (config_keys); + +static int do_reverse_lookups = 1; # define NTPD_DEFAULT_HOST "localhost" # define NTPD_DEFAULT_PORT "123" @@ -247,9 +249,9 @@ static char *refclock_names[] = "CHRONOLOG", "DUMBCLOCK", "ULINK_M320", "PCF", /* 32-35 */ "WWV_AUDIO", "GPS_FG", "HOPF_S", "HOPF_P", /* 36-39 */ "JJY", "TT_IRIG", "GPS_ZYFER", "GPS_RIPENCC", /* 40-43 */ - "NEOCLK4X", NULL /* 44 */ + "NEOCLK4X" /* 44 */ }; -static int refclock_names_num = 45; +static int refclock_names_num = STATIC_ARRAY_SIZE (refclock_names); /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * End of the copied stuff.. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ @@ -273,6 +275,15 @@ static int ntpd_config (const char *key, const char *value) strncpy (ntpd_port, value, sizeof (ntpd_port)); ntpd_port[sizeof (ntpd_port) - 1] = '\0'; } + else if (strcasecmp (key, "ReverseLookups") == 0) + { + if ((strcasecmp (value, "True") == 0) + || (strcasecmp (value, "Yes") == 0) + || (strcasecmp (value, "On") == 0)) + do_reverse_lookups = 1; + else + do_reverse_lookups = 0; + } else { return (-1); @@ -848,38 +859,13 @@ static int ntpd_read (void) ptr = ps + i; refclock_id = 0; - /* - if (((ntohl (ptr->dstadr) & 0xFFFFFF00) == 0x7F000000) || (ptr->dstadr == 0)) - continue; - */ - /* Convert the `long floating point' offset value to double */ M_LFPTOD (ntohl (ptr->offset_int), ntohl (ptr->offset_frc), offset); - if (ptr->v6_flag) - { - struct sockaddr_in6 sa; - - memset (&sa, 0, sizeof (sa)); - sa.sin6_family = AF_INET6; - sa.sin6_port = htons (123); - memcpy (&sa.sin6_addr, &ptr->srcadr6, sizeof (struct in6_addr)); - - status = getnameinfo ((const struct sockaddr *) &sa, - sizeof (sa), - peername, sizeof (peername), - NULL, 0, 0 /* no flags */); - if (status != 0) - { - char errbuf[1024]; - ERROR ("ntpd plugin: getnameinfo failed: %s", - (status == EAI_SYSTEM) - ? sstrerror (errno, errbuf, sizeof (errbuf)) - : gai_strerror (status)); - continue; - } - } - else if ((ntohl (ptr->srcadr) & REFCLOCK_MASK) == REFCLOCK_ADDR) + /* Special IP addresses for hardware clocks and stuff.. */ + if (!ptr->v6_flag + && ((ntohl (ptr->srcadr) & REFCLOCK_MASK) + == REFCLOCK_ADDR)) { struct in_addr addr_obj; char *addr_str; @@ -900,25 +886,53 @@ static int ntpd_read (void) strncpy (peername, addr_str, sizeof (peername)); } } - else /* IPv4 */ + else /* Normal network host. */ { - struct in_addr addr_obj; - struct hostent *addr_he; - char *addr_str; + struct sockaddr_storage sa; + socklen_t sa_len; + int flags = 0; - memset ((void *) &addr_obj, '\0', sizeof (addr_obj)); - addr_obj.s_addr = ptr->srcadr; - addr_str = inet_ntoa (addr_obj); + memset (&sa, '\0', sizeof (sa)); - addr_he = gethostbyaddr ((const void *) &addr_obj, - sizeof (addr_obj), AF_INET); - if (addr_he != NULL) + if (ptr->v6_flag) { - strncpy (peername, addr_he->h_name, sizeof (peername)); + struct sockaddr_in6 *sa_ptr; + sa_ptr = (struct sockaddr_in6 *) &sa; + + sa_ptr->sin6_family = AF_INET6; + sa_ptr->sin6_port = htons (123); + memcpy (&sa_ptr->sin6_addr, &ptr->srcadr6, + sizeof (struct in6_addr)); + sa_len = sizeof (struct sockaddr_in6); } else { - strncpy (peername, addr_str, sizeof (peername)); + struct sockaddr_in *sa_ptr; + sa_ptr = (struct sockaddr_in *) &sa; + + sa_ptr->sin_family = AF_INET; + sa_ptr->sin_port = htons (123); + memcpy (&sa_ptr->sin_addr, &ptr->srcadr, + sizeof (struct in_addr)); + sa_len = sizeof (struct sockaddr_in); + } + + if (do_reverse_lookups == 0) + flags |= NI_NUMERICHOST; + + status = getnameinfo ((const struct sockaddr *) &sa, + sa_len, + peername, sizeof (peername), + NULL, 0, /* No port name */ + flags); + if (status != 0) + { + char errbuf[1024]; + ERROR ("ntpd plugin: getnameinfo failed: %s", + (status == EAI_SYSTEM) + ? sstrerror (errno, errbuf, sizeof (errbuf)) + : gai_strerror (status)); + continue; } } diff --git a/src/perl.c b/src/perl.c index c0e99f5f..dc548b25 100644 --- a/src/perl.c +++ b/src/perl.c @@ -388,6 +388,7 @@ static char *get_module_name (char *buf, size_t buf_len, const char *module) { static int pplugin_register_data_set (pTHX_ char *name, AV *dataset) { int len = -1; + int ret = 0; int i = 0; data_source_t *ds = NULL; @@ -428,7 +429,12 @@ static int pplugin_register_data_set (pTHX_ char *name, AV *dataset) set->ds_num = len + 1; set->ds = ds; - return plugin_register_data_set (set); + + ret = plugin_register_data_set (set); + + free (ds); + free (set); + return ret; } /* static int pplugin_register_data_set (char *, SV *) */ /*