From: Willy Tarreau Date: Thu, 18 Oct 2007 11:53:22 +0000 (+0200) Subject: [MEDIUM] introduce the "stats" keyword in global section X-Git-Tag: v1.3.13~6 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=fbee71331dee1135151866de8d57ec4048ac6e8c;p=thirdparty%2Fhaproxy.git [MEDIUM] introduce the "stats" keyword in global section 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. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 97c427c86a..f72a95b7b1 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -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 the "-p" command line argument. The file must be accessible to the user starting the process. See also "daemon". +stats socket [{uid | user} ] [{gid | group} ] [mode ] + Creates a UNIX socket in stream mode at location . 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 + 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 + By default, the stats socket is limited to 10 concurrent connections. It is + possible to change this value with "stats maxconn". + uid Changes the process' user ID to . 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 ---------- diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index bcf1540061..efe5fa919a 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -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); diff --git a/include/types/global.h b/include/types/global.h index bf95ffdc35..bed62d894e 100644 --- a/include/types/global.h +++ b/include/types/global.h @@ -29,15 +29,13 @@ #include /* 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 */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 53c900a697..d9e01a7a4e 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -41,6 +41,7 @@ #include #include #include +#include #include #include #include @@ -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) { diff --git a/src/dumpstats.c b/src/dumpstats.c index 3fdac86666..bba8c2ce76 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -17,6 +17,8 @@ #include #include #include +#include +#include #include #include @@ -42,9 +44,114 @@ #include #include #include +#include #include #include +/* 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 buffer, for at most bytes, trailing + * zero included. The trailing '\n' must not be written. The function must be + * called with 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 . Expects to be called with * s->cli_state == CL_STSHUTR. It *may* make use of informations from diff --git a/src/haproxy.c b/src/haproxy.c index c13c392d6c..c57e23f89f 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -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(" mode incompatible with and . Keeping only.\n");