diff --git a/include/common/defaults.h b/include/common/defaults.h index f0873cfae..966ef4fbd 100644 --- a/include/common/defaults.h +++ b/include/common/defaults.h @@ -64,8 +64,8 @@ // max # args on a configuration line #define MAX_LINE_ARGS 64 -// max # args on a uxts socket -#define MAX_UXST_ARGS 16 +// max # args on a stats socket +#define MAX_STATS_ARGS 16 // max # of added headers per request #define MAX_NEWHDR 10 diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index 844879ab2..a0167a7f1 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -44,6 +44,8 @@ #define STATS_ST_REP 2 #define STATS_ST_CLOSE 3 +int stats_sock_parse_request(struct session *s, char *line); +int stats_sock_req_analyser(struct session *s, struct buffer *req, int an_bit); int stats_dump_raw(struct session *s, struct buffer *rep, struct uri_auth *uri); void stats_dump_raw_to_buffer(struct session *s, struct buffer *req); int stats_dump_http(struct session *s, struct buffer *rep, struct uri_auth *uri); diff --git a/include/types/buffers.h b/include/types/buffers.h index 055311663..b9836b23e 100644 --- a/include/types/buffers.h +++ b/include/types/buffers.h @@ -113,7 +113,7 @@ #define AN_REQ_HTTP_INNER 0x00000020 /* inner processing of HTTP request */ #define AN_REQ_HTTP_TARPIT 0x00000040 /* wait for end of HTTP tarpit */ #define AN_REQ_HTTP_BODY 0x00000080 /* inspect HTTP request body */ -#define AN_REQ_UNIX_STATS 0x00000100 /* process unix stats socket request */ +#define AN_REQ_STATS_SOCK 0x00000100 /* process stats socket request */ #define AN_RTR_HTTP_HDR 0x00000200 /* inspect HTTP response headers */ #define AN_REQ_PRST_RDP_COOKIE 0x00000400 /* persistence on rdp cookie */ diff --git a/src/dumpstats.c b/src/dumpstats.c index 2683b5298..91c90e922 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -50,6 +50,19 @@ #include #include +const char stats_sock_usage_msg[] = + "Unknown command. Please enter one of the following commands only :\n" + " show info : report information about the running process\n" + " show stat : report counters for each proxy and server\n" + " show errors : report last request and response errors for each proxy\n" + " show sess : report the list of current sessions\n" + "\n"; + +const struct chunk stats_sock_usage = { + .str = (char *)&stats_sock_usage_msg, + .len = sizeof(stats_sock_usage_msg)-1 +}; + /* 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 @@ -107,7 +120,7 @@ static int stats_parse_global(char **args, int section_type, struct proxy *curpx global.stats_sock.options = LI_O_NONE; global.stats_sock.accept = uxst_event_accept; global.stats_sock.handler = process_session; - global.stats_sock.analysers = AN_REQ_UNIX_STATS; + global.stats_sock.analysers = AN_REQ_STATS_SOCK; global.stats_sock.private = global.stats_fe; /* must point to the frontend */ global.stats_fe->timeout.client = MS_TO_TICKS(10000); /* default timeout of 10 seconds */ @@ -211,6 +224,147 @@ int print_csv_header(struct chunk *msg, int size) "\n"); } +/* Parses the request line in and possibly starts dumping stats on + * s->rep with the hijack bit set. Returns 1 if OK, 0 in case of any error. + * The line is modified after parsing. + */ +int stats_sock_parse_request(struct session *s, char *line) +{ + char *args[MAX_STATS_ARGS + 1]; + int arg; + + while (isspace((unsigned char)*line)) + line++; + + arg = 0; + args[arg] = line; + + while (*line && arg < MAX_STATS_ARGS) { + if (isspace((unsigned char)*line)) { + *line++ = '\0'; + + while (isspace((unsigned char)*line)) + line++; + + args[++arg] = line; + continue; + } + + line++; + } + + while (++arg <= MAX_STATS_ARGS) + args[arg] = line; + + if (strcmp(args[0], "show") == 0) { + if (strcmp(args[1], "stat") == 0) { + if (*args[2] && *args[3] && *args[4]) { + s->data_ctx.stats.flags |= STAT_BOUND; + s->data_ctx.stats.iid = atoi(args[2]); + s->data_ctx.stats.type = atoi(args[3]); + s->data_ctx.stats.sid = atoi(args[4]); + } + + s->data_ctx.stats.flags |= STAT_SHOW_STAT; + s->data_ctx.stats.flags |= STAT_FMT_CSV; + s->ana_state = STATS_ST_REP; + buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer); + } + else if (strcmp(args[1], "info") == 0) { + s->data_ctx.stats.flags |= STAT_SHOW_INFO; + s->data_ctx.stats.flags |= STAT_FMT_CSV; + s->ana_state = STATS_ST_REP; + buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer); + } + else if (strcmp(args[1], "sess") == 0) { + s->ana_state = STATS_ST_REP; + buffer_install_hijacker(s, s->rep, stats_dump_sess_to_buffer); + } + else if (strcmp(args[1], "errors") == 0) { + if (*args[2]) + s->data_ctx.errors.iid = atoi(args[2]); + else + s->data_ctx.errors.iid = -1; + s->data_ctx.errors.px = NULL; + s->ana_state = STATS_ST_REP; + buffer_install_hijacker(s, s->rep, stats_dump_errors_to_buffer); + } + else { /* neither "stat" nor "info" nor "sess" */ + return 0; + } + } + else { /* not "show" */ + return 0; + } + return 1; +} + +/* Processes the stats interpreter on the statistics socket. + * In order to ease the transition, we simply simulate the server status + * for now. It only knows states STATS_ST_INIT, STATS_ST_REQ, STATS_ST_REP, and + * STATS_ST_CLOSE. It removes its analyser bit from req->analysers once done. + * It always returns 0. + */ +int stats_sock_req_analyser(struct session *s, struct buffer *req, int an_bit) +{ + char *line, *p; + + switch (s->ana_state) { + case STATS_ST_INIT: + /* Stats output not initialized yet */ + memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats)); + s->data_source = DATA_SRC_STATS; + s->ana_state = STATS_ST_REQ; + buffer_write_dis(s->req); + /* fall through */ + + case STATS_ST_REQ: + /* Now, stats are initialized, hijack is not set, and + * we are waiting for a complete request line. + */ + + line = s->req->data; + p = memchr(line, '\n', s->req->l); + + if (p) { + *p = '\0'; + if (!stats_sock_parse_request(s, line)) { + /* invalid request */ + stream_int_retnclose(s->req->prod, &stats_sock_usage); + s->ana_state = 0; + req->analysers = 0; + return 0; + } + } + + /* processing a valid or incomplete request */ + if ((req->flags & BF_FULL) || /* invalid request */ + (req->flags & BF_READ_ERROR) || /* input error */ + (req->flags & BF_READ_TIMEOUT) || /* read timeout */ + tick_is_expired(req->analyse_exp, now_ms) || /* request timeout */ + (req->flags & BF_SHUTR)) { /* input closed */ + buffer_shutw_now(s->rep); + s->ana_state = 0; + req->analysers = 0; + return 0; + } + /* don't forward nor abort */ + req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */ + return 0; + + case STATS_ST_REP: + /* do nothing while response is being processed */ + return 0; + + case STATS_ST_CLOSE: + /* end of dump */ + s->req->analysers &= ~an_bit; + s->ana_state = 0; + break; + } + return 0; +} + /* * Produces statistics data for the session . Expects to be called with * client socket shut down on input. It *may* make use of informations from diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 1c751d653..507836979 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -41,7 +41,6 @@ #include #include #include -#include #include #include #include @@ -78,19 +77,6 @@ static struct protocol proto_unix = { .nb_listeners = 0, }; -const char unix_sock_usage_msg[] = - "Unknown command. Please enter one of the following commands only :\n" - " show info : report information about the running process\n" - " show stat : report counters for each proxy and server\n" - " show errors : report last request and response errors for each proxy\n" - " show sess : report the list of current sessions\n" - "\n"; - -const struct chunk unix_sock_usage = { - .str = (char *)&unix_sock_usage_msg, - .len = sizeof(unix_sock_usage_msg)-1 -}; - /******************************** * 1) low-level socket functions ********************************/ @@ -572,148 +558,6 @@ int uxst_event_accept(int fd) { return 0; } -/* Parses the request line in and possibly starts dumping stats on - * s->rep with the hijack bit set. Returns 1 if OK, 0 in case of any error. - * The line is modified after parsing. - */ -int unix_sock_parse_request(struct session *s, char *line) -{ - char *args[MAX_UXST_ARGS + 1]; - int arg; - - while (isspace((unsigned char)*line)) - line++; - - arg = 0; - args[arg] = line; - - while (*line && arg < MAX_UXST_ARGS) { - if (isspace((unsigned char)*line)) { - *line++ = '\0'; - - while (isspace((unsigned char)*line)) - line++; - - args[++arg] = line; - continue; - } - - line++; - } - - while (++arg <= MAX_UXST_ARGS) - args[arg] = line; - - if (strcmp(args[0], "show") == 0) { - if (strcmp(args[1], "stat") == 0) { - if (*args[2] && *args[3] && *args[4]) { - s->data_ctx.stats.flags |= STAT_BOUND; - s->data_ctx.stats.iid = atoi(args[2]); - s->data_ctx.stats.type = atoi(args[3]); - s->data_ctx.stats.sid = atoi(args[4]); - } - - s->data_ctx.stats.flags |= STAT_SHOW_STAT; - s->data_ctx.stats.flags |= STAT_FMT_CSV; - s->ana_state = STATS_ST_REP; - buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer); - } - else if (strcmp(args[1], "info") == 0) { - s->data_ctx.stats.flags |= STAT_SHOW_INFO; - s->data_ctx.stats.flags |= STAT_FMT_CSV; - s->ana_state = STATS_ST_REP; - buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer); - } - else if (strcmp(args[1], "sess") == 0) { - s->ana_state = STATS_ST_REP; - buffer_install_hijacker(s, s->rep, stats_dump_sess_to_buffer); - } - else if (strcmp(args[1], "errors") == 0) { - if (*args[2]) - s->data_ctx.errors.iid = atoi(args[2]); - else - s->data_ctx.errors.iid = -1; - s->data_ctx.errors.px = NULL; - s->ana_state = STATS_ST_REP; - buffer_install_hijacker(s, s->rep, stats_dump_errors_to_buffer); - } - else { /* neither "stat" nor "info" nor "sess" */ - return 0; - } - } - else { /* not "show" */ - return 0; - } - return 1; -} - -/* Processes the stats interpreter on the statistics socket. - * In order to ease the transition, we simply simulate the server status - * for now. It only knows states STATS_ST_INIT, STATS_ST_REQ, STATS_ST_REP, and - * STATS_ST_CLOSE. It removes the AN_REQ_UNIX_STATS bit from req->analysers - * once done. It always returns 0. - */ -int uxst_req_analyser_stats(struct session *s, struct buffer *req, int an_bit) -{ - char *line, *p; - - switch (s->ana_state) { - case STATS_ST_INIT: - /* Stats output not initialized yet */ - memset(&s->data_ctx.stats, 0, sizeof(s->data_ctx.stats)); - s->data_source = DATA_SRC_STATS; - s->ana_state = STATS_ST_REQ; - buffer_write_dis(s->req); - /* fall through */ - - case STATS_ST_REQ: - /* Now, stats are initialized, hijack is not set, and - * we are waiting for a complete request line. - */ - - line = s->req->data; - p = memchr(line, '\n', s->req->l); - - if (p) { - *p = '\0'; - if (!unix_sock_parse_request(s, line)) { - /* invalid request */ - stream_int_retnclose(s->req->prod, &unix_sock_usage); - s->ana_state = 0; - req->analysers = 0; - return 0; - } - } - - /* processing a valid or incomplete request */ - if ((req->flags & BF_FULL) || /* invalid request */ - (req->flags & BF_READ_ERROR) || /* input error */ - (req->flags & BF_READ_TIMEOUT) || /* read timeout */ - tick_is_expired(req->analyse_exp, now_ms) || /* request timeout */ - (req->flags & BF_SHUTR)) { /* input closed */ - buffer_shutw_now(s->rep); - s->ana_state = 0; - req->analysers = 0; - return 0; - } - /* don't forward nor abort */ - req->flags |= BF_READ_DONTWAIT; /* we plan to read small requests */ - return 0; - - case STATS_ST_REP: - /* do nothing while response is being processed */ - return 0; - - case STATS_ST_CLOSE: - /* end of dump */ - s->req->analysers &= ~an_bit; - s->ana_state = 0; - break; - } - return 0; -} - - __attribute__((constructor)) static void __uxst_protocol_init(void) { diff --git a/src/session.c b/src/session.c index 56bf102e3..809ee2bd1 100644 --- a/src/session.c +++ b/src/session.c @@ -22,13 +22,13 @@ #include #include #include +#include #include #include #include #include #include #include -#include #include #include #include @@ -841,9 +841,9 @@ resync_stream_interface: break; } - if (s->req->analysers & AN_REQ_UNIX_STATS) { - last_ana |= AN_REQ_UNIX_STATS; - if (!uxst_req_analyser_stats(s, s->req, AN_REQ_UNIX_STATS)) + if (s->req->analysers & AN_REQ_STATS_SOCK) { + last_ana |= AN_REQ_STATS_SOCK; + if (!stats_sock_req_analyser(s, s->req, AN_REQ_STATS_SOCK)) break; }