]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
[MEDIUM] introduce the "stats" keyword in global section
authorWilly Tarreau <w@1wt.eu>
Thu, 18 Oct 2007 11:53:22 +0000 (13:53 +0200)
committerWilly Tarreau <w@1wt.eu>
Thu, 18 Oct 2007 12:16:11 +0000 (14:16 +0200)
Removed old unused MODE_LOG and MODE_STATS, and replaced the "stats"
keyword in the global section. The new "stats" keyword in the global
section is used to create a UNIX socket on which the statistics will
be accessed.  The client must issue a "show stat\n" command in order
to get a CSV-formated output similar to the output on the HTTP socket
in CSV mode.

doc/configuration.txt
include/proto/dumpstats.h
include/types/global.h
src/cfgparse.c
src/dumpstats.c
src/haproxy.c

index 97c427c86acbd5ae6fa71be2dc3604b04194a251..f72a95b7b1b3ceb4908ea6ca0dfd0a7c220f6a35 100644 (file)
@@ -4,7 +4,7 @@
                          ----------------------
                              version 1.3.13
                              willy tarreau
-                               2007/10/15
+                               2007/10/18
 
 
 This document covers the configuration language as implemented in the version
@@ -39,6 +39,7 @@ The following keywords are supported in the "global" section :
    - uid
    - ulimit-n
    - user
+   - stats
   
  * Performance tuning
    - maxconn
@@ -52,7 +53,6 @@ The following keywords are supported in the "global" section :
  * Debugging
    - debug
    - quiet
-   - stats
 
 
 1.1) Process management and security
@@ -111,6 +111,28 @@ pidfile <pidfile>
   the "-p" command line argument. The file must be accessible to the user
   starting the process. See also "daemon".
 
+stats socket <path> [{uid | user} <uid>] [{gid | group} <gid>] [mode <mode>]
+  Creates a UNIX socket in stream mode at location <path>. Any previously
+  existing socket will be backed up then replaced. Connections to this socket
+  will get a CSV-formated output of the process statistics in response to the
+  "show stat" command followed by a line feed. On platforms which support it,
+  it is possible to restrict access to this socket by specifying numerical IDs
+  after "uid" and "gid", or valid user and group names after the "user" and
+  "group" keywords. It is also possible to restrict permissions on the socket
+  by passing an octal value after the "mode" keyword (same syntax as chmod).
+  Depending on the platform, the permissions on the socket will be inherited
+  from the directory which hosts it, or from the user the process is started
+  with.
+
+stats timeout <timeout, in milliseconds>
+  The default timeout on the stats socket is set to 10 seconds. It is possible
+  to change this value with "stats timeout". The value must be passed in
+  milliseconds.
+
+stats maxconn <connections>
+  By default, the stats socket is limited to 10 concurrent connections. It is
+  possible to change this value with "stats maxconn".
+
 uid <number>
   Changes the process' user ID to <number>. It is recommended that the user ID
   is dedicated to HAProxy or to a small set of similar daemons. HAProxy must
@@ -186,10 +208,6 @@ quiet
   Do not display any message during startup. It is equivalent to the command-
   line argument "-q".
 
-stats
-  Dump internal statistics to stdout at regular interval. It is available for
-  development purposes only and should never be set.
-
 
 2) Proxies
 ----------
index bcf1540061ea32dbecf41958a2fb1bf35686e9dc..efe5fa919a55226454a706e9c19b99bd5126ece2 100644 (file)
@@ -29,6 +29,7 @@
 
 #define STAT_FMT_HTML 0x1
 
+int stats_parse_global(const char **args, char *err, int errlen);
 int stats_dump_raw(struct session *s, struct uri_auth *uri, int flags);
 int stats_dump_http(struct session *s, struct uri_auth *uri, int flags);
 int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri, int flags);
index bf95ffdc35c565d9175ca2351bf8165cf3f207e6..bed62d894ec61c18c1fa068c0be82d4cd517e018 100644 (file)
 #include <types/task.h>
 
 /* modes of operation (global.mode) */
-#define        MODE_DEBUG      1
-#define        MODE_STATS      2
-#define        MODE_LOG        4
-#define        MODE_DAEMON     8
-#define        MODE_QUIET      16
-#define        MODE_CHECK      32
-#define        MODE_VERBOSE    64
-#define        MODE_STARTING   128
-#define        MODE_FOREGROUND 256
+#define        MODE_DEBUG      0x01
+#define        MODE_DAEMON     0x02
+#define        MODE_QUIET      0x04
+#define        MODE_CHECK      0x08
+#define        MODE_VERBOSE    0x10
+#define        MODE_STARTING   0x20
+#define        MODE_FOREGROUND 0x40
 
 /* list of last checks to perform, depending on config options */
 #define LSTCHK_CAP_BIND        0x00000001      /* check that we can bind to any port */
index 53c900a697b1971af2c326d61503cb7f1df54ffb..d9e01a7a4e2aca1ac8441da36d1f42cd643436a9 100644 (file)
@@ -41,6 +41,7 @@
 #include <proto/backend.h>
 #include <proto/buffers.h>
 #include <proto/checks.h>
+#include <proto/dumpstats.h>
 #include <proto/httperr.h>
 #include <proto/log.h>
 #include <proto/proxy.h>
@@ -279,7 +280,11 @@ int cfg_parse_global(const char *file, int linenum, char **args)
                global.mode |= MODE_QUIET;
        }
        else if (!strcmp(args[0], "stats")) {
-               global.mode |= MODE_STATS;
+               memcpy(trash, "error near 'stats'", 19);
+               if (stats_parse_global((const char **)args + 1, trash, sizeof(trash)) < 0) {
+                       Alert("parsing [%s:%d] : %s\n", file, linenum, trash);
+                       return -1;
+               }
        }
        else if (!strcmp(args[0], "tune.maxpollevents")) {
                if (global.tune.maxpollevents != 0) {
index 3fdac866663ac2eb67cc770f4864d082722476d2..bba8c2ce76608c064b8e70cc1a53ad2b8e40ce59 100644 (file)
@@ -17,6 +17,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <time.h>
+#include <pwd.h>
+#include <grp.h>
 
 #include <sys/socket.h>
 #include <sys/stat.h>
 #include <proto/buffers.h>
 #include <proto/dumpstats.h>
 #include <proto/fd.h>
+#include <proto/proto_uxst.h>
 #include <proto/senddata.h>
 #include <proto/session.h>
 
+/* This function parses a "stats" statement in the "global" section. It returns
+ * -1 if there is any error, otherwise zero. If it returns -1, it may write an
+ * error message into ther <err> buffer, for at most <errlen> bytes, trailing
+ * zero included. The trailing '\n' must not be written. The function must be
+ * called with <args> pointing to the first word after "stats".
+ */
+int stats_parse_global(const char **args, char *err, int errlen)
+{
+       if (!strcmp(args[0], "socket")) {
+               struct sockaddr_un su;
+               int cur_arg;
+
+               if (*args[1] == 0) {
+                       snprintf(err, errlen, "'stats socket' in global section expects a path to a UNIX socket");
+                       return -1;
+               }
+
+               if (global.stats_sock.state != LI_NEW) {
+                       snprintf(err, errlen, "'stats socket' already specified in global section");
+                       return -1;
+               }
+
+               su.sun_family = AF_UNIX;
+               strncpy(su.sun_path, args[1], sizeof(su.sun_path));
+               su.sun_path[sizeof(su.sun_path) - 1] = 0;
+               memcpy(&global.stats_sock.addr, &su, sizeof(su)); // guaranteed to fit
+
+               global.stats_sock.state = LI_INIT;
+               global.stats_sock.accept = uxst_event_accept;
+               global.stats_sock.handler = process_uxst_stats;
+               global.stats_sock.private = NULL;
+
+               cur_arg = 2;
+               while (*args[cur_arg]) {
+                       if (!strcmp(args[cur_arg], "uid")) {
+                               global.stats_sock.perm.ux.uid = atol(args[cur_arg + 1]);
+                               cur_arg += 2;
+                       }
+                       else if (!strcmp(args[cur_arg], "gid")) {
+                               global.stats_sock.perm.ux.gid = atol(args[cur_arg + 1]);
+                               cur_arg += 2;
+                       }
+                       else if (!strcmp(args[cur_arg], "mode")) {
+                               global.stats_sock.perm.ux.mode = strtol(args[cur_arg + 1], NULL, 8);
+                               cur_arg += 2;
+                       }
+                       else if (!strcmp(args[cur_arg], "user")) {
+                               struct passwd *user;
+                               user = getpwnam(args[cur_arg + 1]);
+                               if (!user) {
+                                       snprintf(err, errlen, "unknown user '%s' in 'global' section ('stats user')",
+                                                args[cur_arg + 1]);
+                                       return -1;
+                               }
+                               global.stats_sock.perm.ux.uid = user->pw_uid;
+                               cur_arg += 2;
+                       }
+                       else if (!strcmp(args[cur_arg], "group")) {
+                               struct group *group;
+                               group = getgrnam(args[cur_arg + 1]);
+                               if (!group) {
+                                       snprintf(err, errlen, "unknown group '%s' in 'global' section ('stats group')",
+                                                args[cur_arg + 1]);
+                                       return -1;
+                               }
+                               global.stats_sock.perm.ux.gid = group->gr_gid;
+                               cur_arg += 2;
+                       }
+                       else {
+                               snprintf(err, errlen, "'stats socket' only supports 'user', 'uid', 'group', 'gid', and 'mode'");
+                               return -1;
+                       }
+               }
+                       
+               uxst_add_listener(&global.stats_sock);
+               global.maxsock++;
+       }
+       else if (!strcmp(args[0], "timeout")) {
+               int timeout = atol(args[1]);
+
+               if (timeout <= 0) {
+                       snprintf(err, errlen, "a positive value is expected for 'stats timeout' in 'global section'");
+                       return -1;
+               }
+               __tv_from_ms(&global.stats_timeout, timeout);
+       }
+       else if (!strcmp(args[0], "maxconn")) {
+               int maxconn = atol(args[1]);
+
+               if (maxconn <= 0) {
+                       snprintf(err, errlen, "a positive value is expected for 'stats maxconn' in 'global section'");
+                       return -1;
+               }
+               global.maxsock -= global.stats_sock.maxconn;
+               global.stats_sock.maxconn = maxconn;
+               global.maxsock += global.stats_sock.maxconn;
+       }
+       else {
+               snprintf(err, errlen, "'stats' only supports 'socket', 'maxconn' and 'timeout' in 'global' section");
+               return -1;
+       }
+       return 0;
+}
+
 /*
  * Produces statistics data for the session <s>. Expects to be called with
  * s->cli_state == CL_STSHUTR. It *may* make use of informations from <uri>
index c13c392d6c942ee79b6ea00ed5ae70c2ca3738df..c57e23f89fd9c9e7481b37c144e501504ad19a7e 100644 (file)
@@ -116,6 +116,12 @@ struct global global = {
        logfac2 : -1,
        loglev1 : 7, /* max syslog level : debug */
        loglev2 : 7,
+       .stats_timeout = { .tv_sec = 10, .tv_usec = 0 }, /* stats timeout = 10 seconds */
+       .stats_sock.timeout = &global.stats_timeout,
+       .stats_sock.maxconn = 10, /* 10 concurrent stats connections */
+       .stats_sock.perm.ux.uid = -1,
+       .stats_sock.perm.ux.gid = -1,
+       .stats_sock.perm.ux.mode = 0,
        /* others NULL OK */
 };
 
@@ -542,7 +548,7 @@ void init(int argc, char **argv)
                global.mode &= ~(MODE_DAEMON | MODE_QUIET);
        }
        global.mode |= (arg_mode & (MODE_DAEMON | MODE_FOREGROUND | MODE_QUIET |
-                                   MODE_VERBOSE | MODE_DEBUG | MODE_STATS | MODE_LOG));
+                                   MODE_VERBOSE | MODE_DEBUG ));
 
        if ((global.mode & MODE_DEBUG) && (global.mode & (MODE_DAEMON | MODE_QUIET))) {
                Warning("<debug> mode incompatible with <quiet> and <daemon>. Keeping <debug> only.\n");