Merge branch 'master' into ad/memcached
authorFlorian Forster <octo@noris.net>
Tue, 23 Oct 2007 14:34:17 +0000 (16:34 +0200)
committerFlorian Forster <octo@noris.net>
Tue, 23 Oct 2007 14:34:17 +0000 (16:34 +0200)
Conflicts:

src/types.db

README
configure.in
src/Makefile.am
src/collectd.conf.pod
src/email.c
src/nginx.c [new file with mode: 0644]
src/tcpconns.c
src/types.db

diff --git a/README b/README
index 338ea4b..9c1faac 100644 (file)
--- a/README
+++ b/README
@@ -110,6 +110,10 @@ Features
       NFS Procedures: Which NFS command were called how often. Only NFSv2 and
       NFSv3 right now.
 
+    - nginx
+      Collects statistics from `nginx' (speak: engine X), a HTTP and mail
+      server/proxy.
+
     - ntp
       NTP daemon statistics: Local clock drift, offset to peers, etc.
 
index 07b97da..9701085 100644 (file)
@@ -1759,6 +1759,7 @@ if test "x$have_sysctlbyname" = "xyes"
 then
        plugin_cpu="yes"
        plugin_memory="yes"
+       plugin_tcpconns="yes"
 fi
 
 if test "x$have_statfs" = "xyes"
@@ -1843,6 +1844,7 @@ AC_PLUGIN([mysql],       [$with_libmysql],     [MySQL statistics])
 AC_PLUGIN([netlink],     [$with_libnetlink],   [Enhanced Linux network statistics])
 AC_PLUGIN([network],     [yes],                [Network communication plugin])
 AC_PLUGIN([nfs],         [$plugin_nfs],        [NFS statistics])
+AC_PLUGIN([nginx],       [$with_libcurl],      [nginx statistics])
 AC_PLUGIN([ntpd],        [yes],                [NTPd statistics])
 AC_PLUGIN([nut],         [$with_libupsclient], [Network UPS tools statistics])
 AC_PLUGIN([perl],        [$with_libperl],      [Embed a Perl interpreter])
@@ -1968,6 +1970,7 @@ Configuration:
     netlink . . . . . . $enable_netlink
     network . . . . . . $enable_network
     nfs . . . . . . . . $enable_nfs
+    nginx . . . . . . . $enable_nginx
     ntpd  . . . . . . . $enable_ntpd
     nut . . . . . . . . $enable_nut
     perl  . . . . . . . $enable_perl
index ad5f931..0b3ba0d 100644 (file)
@@ -402,6 +402,17 @@ collectd_LDADD += "-dlopen" nfs.la
 collectd_DEPENDENCIES += nfs.la
 endif
 
+if BUILD_PLUGIN_NGINX
+pkglib_LTLIBRARIES += nginx.la
+nginx_la_SOURCES = nginx.c
+nginx_la_LDFLAGS = -module -avoid-version
+if BUILD_WITH_LIBCURL
+nginx_la_LDFLAGS += $(BUILD_WITH_LIBCURL_LIBS)
+endif
+collectd_LDADD += "-dlopen" nginx.la
+collectd_DEPENDENCIES += nginx.la
+endif
+
 if BUILD_PLUGIN_NTPD
 pkglib_LTLIBRARIES += ntpd.la
 ntpd_la_SOURCES = ntpd.c
index 9dcff25..2f4accc 100644 (file)
@@ -569,6 +569,39 @@ either.
 
 =back
 
+=head2 Plugin C<nginx>
+
+This plugin collects the number of connections and requests handeled by the
+C<nginx daemon> (speak: engineE<nbsp>X), a HTTP and mail server/proxy. It
+queries the page provided by the C<ngx_http_stub_status_module> module, which
+isn't compiled by default. Please refer to
+L<http://wiki.codemongers.com/NginxStubStatusModule> for more information on
+how to compile and configure nginx and this module.
+
+The following options are accepted by the C<nginx plugin>:
+
+=over 4
+
+=item B<URL> I<http://host/nginx_status>
+
+Sets the URL of the C<ngx_http_stub_status_module> output.
+
+=item B<User> I<Username>
+
+Optional user name needed for authentication.
+
+=item B<Password> I<Password>
+
+Optional password needed for authentication.
+
+=item B<CACert> I<File>
+
+File that holds one or more SSL certificates. If you want to use HTTPS you will
+possibly need this option. What CA certificates come bundled with C<libcurl>
+and are checked by default depends on the distribution you use.
+
+=back
+
 =head2 Plugin C<ntpd>
 
 =over 4
index f5a544a..869b7c3 100644 (file)
@@ -39,6 +39,8 @@
 
 #include "configfile.h"
 
+#include <stddef.h>
+
 #if HAVE_LIBPTHREAD
 # include <pthread.h>
 #endif
diff --git a/src/nginx.c b/src/nginx.c
new file mode 100644 (file)
index 0000000..69dc49e
--- /dev/null
@@ -0,0 +1,252 @@
+/**
+ * collectd - src/nginx.c
+ * Copyright (C) 2006,2007  Florian octo Forster
+ *
+ * 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, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
+ *
+ * Authors:
+ *   Florian octo Forster <octo at verplant.org>
+ **/
+
+#include "collectd.h"
+#include "common.h"
+#include "plugin.h"
+#include "configfile.h"
+
+#include <curl/curl.h>
+
+static char *url    = NULL;
+static char *user   = NULL;
+static char *pass   = NULL;
+static char *cacert = NULL;
+
+static CURL *curl = NULL;
+
+#define ABUFFER_SIZE 16384
+static char nginx_buffer[ABUFFER_SIZE];
+static int  nginx_buffer_len = 0;
+static char nginx_curl_error[CURL_ERROR_SIZE];
+
+static const char *config_keys[] =
+{
+  "URL",
+  "User",
+  "Password",
+  "CACert"
+};
+static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
+
+static size_t nginx_curl_callback (void *buf, size_t size, size_t nmemb, void *stream)
+{
+  size_t len = size * nmemb;
+
+  if ((nginx_buffer_len + len) >= ABUFFER_SIZE)
+  {
+    len = (ABUFFER_SIZE - 1) - nginx_buffer_len;
+  }
+
+  if (len <= 0)
+    return (len);
+
+  memcpy (nginx_buffer + nginx_buffer_len, (char *) buf, len);
+  nginx_buffer_len += len;
+  nginx_buffer[nginx_buffer_len] = '\0';
+
+  return (len);
+}
+
+static int config_set (char **var, const char *value)
+{
+  if (*var != NULL)
+  {
+    free (*var);
+    *var = NULL;
+  }
+
+  if ((*var = strdup (value)) == NULL)
+    return (1);
+  else
+    return (0);
+}
+
+static int config (const char *key, const char *value)
+{
+  if (strcasecmp (key, "url") == 0)
+    return (config_set (&url, value));
+  else if (strcasecmp (key, "user") == 0)
+    return (config_set (&user, value));
+  else if (strcasecmp (key, "password") == 0)
+    return (config_set (&pass, value));
+  else if (strcasecmp (key, "cacert") == 0)
+    return (config_set (&cacert, value));
+  else
+    return (-1);
+} /* int config */
+
+static int init (void)
+{
+  static char credentials[1024];
+
+  if (curl != NULL)
+    curl_easy_cleanup (curl);
+
+  if ((curl = curl_easy_init ()) == NULL)
+  {
+    ERROR ("nginx plugin: curl_easy_init failed.");
+    return (-1);
+  }
+
+  curl_easy_setopt (curl, CURLOPT_WRITEFUNCTION, nginx_curl_callback);
+  curl_easy_setopt (curl, CURLOPT_USERAGENT, PACKAGE_NAME"/"PACKAGE_VERSION);
+  curl_easy_setopt (curl, CURLOPT_ERRORBUFFER, nginx_curl_error);
+
+  if (user != NULL)
+  {
+    if (snprintf (credentials, 1024, "%s:%s", user, pass == NULL ? "" : pass) >= 1024)
+    {
+      ERROR ("nginx plugin: Credentials would have been truncated.");
+      return (-1);
+    }
+
+    curl_easy_setopt (curl, CURLOPT_USERPWD, credentials);
+  }
+
+  if (url != NULL)
+  {
+    curl_easy_setopt (curl, CURLOPT_URL, url);
+  }
+
+  if (cacert != NULL)
+  {
+    curl_easy_setopt (curl, CURLOPT_CAINFO, cacert);
+  }
+
+  return (0);
+} /* void init */
+
+static void submit (char *type, char *inst, long long value)
+{
+  value_t values[1];
+  value_list_t vl = VALUE_LIST_INIT;
+
+  if (strcpy (type, "nginx_connections") == 0)
+    values[0].gauge = value;
+  else if (strcpy (type, "nginx_requests") == 0)
+    values[0].counter = value;
+  else
+    return;
+
+  vl.values = values;
+  vl.values_len = 1;
+  vl.time = time (NULL);
+  strcpy (vl.host, hostname_g);
+  strcpy (vl.plugin, "nginx");
+  strcpy (vl.plugin_instance, "");
+
+  if (inst != NULL)
+  {
+    strncpy (vl.type_instance, inst, sizeof (vl.type_instance));
+    vl.type_instance[sizeof (vl.type_instance) - 1] = '\0';
+  }
+
+  plugin_dispatch_values (type, &vl);
+} /* void submit */
+
+static int nginx_read (void)
+{
+  int i;
+
+  char *ptr;
+  char *lines[16];
+  int   lines_num = 0;
+
+  char *fields[16];
+  int   fields_num;
+
+  if (curl == NULL)
+    return (-1);
+  if (url == NULL)
+    return (-1);
+
+  nginx_buffer_len = 0;
+  if (curl_easy_perform (curl) != 0)
+  {
+    WARNING ("nginx plugin: curl_easy_perform failed: %s", nginx_curl_error);
+    return (-1);
+  }
+
+  ptr = nginx_buffer;
+  while ((lines[lines_num] = strtok (ptr, "\n\r")) != NULL)
+  {
+    ptr = NULL;
+    lines_num++;
+
+    if (lines_num >= 16)
+      break;
+  }
+
+  /*
+   * Active connections: 291
+   * server accepts handled requests
+   *  16630948 16630948 31070465
+   * Reading: 6 Writing: 179 Waiting: 106
+   */
+  for (i = 0; i < lines_num; i++)
+  {
+    fields_num = strsplit (lines[i], fields,
+       (sizeof (fields) / sizeof (fields[0])));
+
+    if (fields_num == 3)
+    {
+      if ((strcmp (fields[0], "Active") == 0)
+         && (strcmp (fields[1], "connections:") == 0))
+      {
+       submit ("nginx_connections", "active", atoll (fields[2]));
+      }
+      else if ((atoll (fields[0]) != 0)
+         && (atoll (fields[1]) != 0)
+         && (atoll (fields[2]) != 0))
+      {
+       submit ("nginx_requests", NULL, atoll (fields[2]));
+      }
+    }
+    else if (fields_num == 6)
+    {
+      if ((strcmp (fields[0], "Reading:") == 0)
+         && (strcmp (fields[2], "Writing:") == 0)
+         && (strcmp (fields[4], "Waiting:") == 0))
+      {
+       submit ("nginx_connections", "reading", atoll (fields[1]));
+       submit ("nginx_connections", "writing", atoll (fields[3]));
+       submit ("nginx_connections", "waiting", atoll (fields[5]));
+      }
+    }
+  }
+
+  nginx_buffer_len = 0;
+
+  return (0);
+} /* int nginx_read */
+
+void module_register (void)
+{
+  plugin_register_config ("nginx", config, config_keys, config_keys_num);
+  plugin_register_init ("nginx", init);
+  plugin_register_read ("nginx", nginx_read);
+} /* void module_register */
+
+/*
+ * vim: set shiftwidth=2 softtabstop=2 tabstop=8 :
+ */
index 108a4e8..74874ae 100644 (file)
 #include "common.h"
 #include "plugin.h"
 
-#if !KERNEL_LINUX
+#if !KERNEL_LINUX && !HAVE_SYSCTLBYNAME
 # error "No applicable input method."
 #endif
 
-#define TCP_STATE_LISTEN 10
-#define TCP_STATE_MAX 11
+#if KERNEL_LINUX
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+# include <sys/socketvar.h>
+# include <sys/sysctl.h>
+# include <net/route.h>
+# include <netinet/in.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/ip6.h>
+# include <netinet/in_pcb.h>
+# include <netinet/ip_var.h>
+# include <netinet/tcp.h>
+# include <netinet/tcpip.h>
+# include <netinet/tcp_seq.h>
+# include <netinet/tcp_var.h>
+#endif /* HAVE_SYSCTLBYNAME */
+
+#if KERNEL_LINUX
+static const char *tcp_state[] =
+{
+  "", /* 0 */
+  "ESTABLISHED",
+  "SYN_SENT",
+  "SYN_RECV",
+  "FIN_WAIT1",
+  "FIN_WAIT2",
+  "TIME_WAIT",
+  "CLOSED",
+  "CLOSE_WAIT",
+  "LAST_ACK",
+  "LISTEN", /* 10 */
+  "CLOSING"
+};
+
+# define TCP_STATE_LISTEN 10
+# define TCP_STATE_MIN 1
+# define TCP_STATE_MAX 11
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+static const char *tcp_state[] =
+{
+  "CLOSED",
+  "LISTEN",
+  "SYN_SENT",
+  "SYN_RECV",
+  "ESTABLISHED",
+  "CLOSE_WAIT",
+  "FIN_WAIT1",
+  "CLOSING",
+  "LAST_ACK",
+  "FIN_WAIT2",
+  "TIME_WAIT"
+};
+
+# define TCP_STATE_LISTEN 1
+# define TCP_STATE_MIN 0
+# define TCP_STATE_MAX 10
+#endif /* HAVE_SYSCTLBYNAME */
 
 #define PORT_COLLECT_LOCAL  0x01
 #define PORT_COLLECT_REMOTE 0x02
@@ -51,22 +110,6 @@ static const char *config_keys[] =
 };
 static int config_keys_num = STATIC_ARRAY_SIZE (config_keys);
 
-static const char *tcp_state[] =
-{
-  "", /* 0 */
-  "ESTABLISHED",
-  "SYN_SENT",
-  "SYN_RECV",
-  "FIN_WAIT1",
-  "FIN_WAIT2",
-  "TIME_WAIT",
-  "CLOSE",
-  "CLOSE_WAIT",
-  "LAST_ACK",
-  "LISTEN", /* 10 */
-  "CLOSING"
-};
-
 static int port_collect_listening = 0;
 static port_entry_t *port_list_head = NULL;
 
@@ -196,7 +239,11 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
 {
   port_entry_t *pe = NULL;
 
-  if ((state == 0) || (state > TCP_STATE_MAX))
+  if ((state > TCP_STATE_MAX)
+#if TCP_STATE_MIN > 0
+      || (state < TCP_STATE_MIN)
+#endif
+     )
   {
     NOTICE ("tcpconns plugin: Ignoring connection with unknown state 0x%02x.",
        state);
@@ -225,6 +272,7 @@ static int conn_handle_ports (uint16_t port_local, uint16_t port_remote, uint8_t
   return (0);
 } /* int conn_handle_ports */
 
+#if KERNEL_LINUX
 static int conn_handle_line (char *buffer)
 {
   char *fields[32];
@@ -304,6 +352,10 @@ static int conn_read_file (const char *file)
 
   return (0);
 } /* int conn_read_file */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+#endif /* HAVE_SYSCTLBYNAME */
 
 static int conn_config (const char *key, const char *value)
 {
@@ -348,6 +400,7 @@ static int conn_config (const char *key, const char *value)
   return (0);
 } /* int conn_config */
 
+#if KERNEL_LINUX
 static int conn_init (void)
 {
   if (port_list_head == NULL)
@@ -367,12 +420,91 @@ static int conn_read (void)
 
   return (0);
 } /* int conn_read */
+/* #endif KERNEL_LINUX */
+
+#elif HAVE_SYSCTLBYNAME
+static int conn_read (void)
+{
+  int status;
+  char *buffer;
+  size_t buffer_len;;
+
+  struct xinpgen *in_orig;
+  struct xinpgen *in_ptr;
+
+  conn_reset_port_entry ();
+
+  buffer_len = 0;
+  status = sysctlbyname ("net.inet.tcp.pcblist", NULL, &buffer_len, 0, 0);
+  if (status < 0)
+  {
+    ERROR ("tcpconns plugin: sysctlbyname failed.");
+    return (-1);
+  }
+
+  buffer = (char *) malloc (buffer_len);
+  if (buffer == NULL)
+  {
+    ERROR ("tcpconns plugin: malloc failed.");
+    return (-1);
+  }
+
+  status = sysctlbyname ("net.inet.tcp.pcblist", buffer, &buffer_len, 0, 0);
+  if (status < 0)
+  {
+    ERROR ("tcpconns plugin: sysctlbyname failed.");
+    sfree (buffer);
+    return (-1);
+  }
+
+  if (buffer_len <= sizeof (struct xinpgen))
+  {
+    ERROR ("tcpconns plugin: (buffer_len <= sizeof (struct xinpgen))");
+    sfree (buffer);
+    return (-1);
+  }
+
+  in_orig = (struct xinpgen *) buffer;
+  for (in_ptr = (struct xinpgen *) (((char *) in_orig) + in_orig->xig_len);
+      in_ptr->xig_len > sizeof (struct xinpgen);
+      in_ptr = (struct xinpgen *) (((char *) in_ptr) + in_ptr->xig_len))
+  {
+    struct tcpcb *tp = &((struct xtcpcb *) in_ptr)->xt_tp;
+    struct inpcb *inp = &((struct xtcpcb *) in_ptr)->xt_inp;
+    struct xsocket *so = &((struct xtcpcb *) in_ptr)->xt_socket;
+
+    /* Ignore non-TCP sockets */
+    if (so->xso_protocol != IPPROTO_TCP)
+      continue;
+
+    /* Ignore PCBs which were freed during copyout. */
+    if (inp->inp_gencnt > in_orig->xig_gen)
+      continue;
+
+    if (((inp->inp_vflag & INP_IPV4) == 0)
+       && ((inp->inp_vflag & INP_IPV6) == 0))
+      continue;
+
+    conn_handle_ports (inp->inp_lport, inp->inp_fport, tp->t_state);
+  } /* for (in_ptr) */
+
+  in_orig = NULL;
+  in_ptr = NULL;
+  sfree (buffer);
+
+  conn_submit_all ();
+
+  return (0);
+} /* int conn_read */
+#endif /* HAVE_SYSCTLBYNAME */
 
 void module_register (void)
 {
        plugin_register_config ("tcpconns", conn_config,
                        config_keys, config_keys_num);
+#if KERNEL_LINUX
        plugin_register_init ("tcpconns", conn_init);
+#endif
        plugin_register_read ("tcpconns", conn_read);
 } /* void module_register */
 
index bcfbeaf..865335f 100644 (file)
@@ -53,6 +53,8 @@ memcached_connections value:GAUGE:0:U
 memcached_items                value:GAUGE:0:U
 memcached_octets       rx:COUNTER:0:4294967295, tx:COUNTER:0:4294967295
 memcached_ops          value:COUNTER:0:134217728
+nginx_connections      value:GAUGE:0:U
+nginx_requests         value:COUNTER:0:134217728
 percent                        percent:GAUGE:0:100.1
 ping                   ping:GAUGE:0:65535
 power                  value:GAUGE:0:U