diff --git a/doc/configuration.txt b/doc/configuration.txt index 943158a93..596f2abfd 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -151,8 +151,10 @@ 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, and more general process - information in response to the "show info" command followed by a line feed. + "show stat" command followed by a line feed, more general process information + in response to the "show info" command followed by a line feed, and a + complete list of all existing sessions in response to the "show sess" 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 @@ -4339,7 +4341,9 @@ Notes related to these keywords : [to do] + 2.7) CSV format +--------------- 0. pxname: proxy name 1. svname: service name (FRONTEND for frontend, BACKEND for backend, any name @@ -4376,18 +4380,33 @@ Notes related to these keywords : 31. tracked: id of proxy/server if tracking is enabled 32. type (0=frontend, 1=backend, 2=server) + 2.8) Unix Socket commands +------------------------- - - "show stat [ ]": dump statistics in the cvs format. By - passing id, type and sid it is possible to dump only selected items: - - iid is a proxy id, -1 to dump everything - - type selects type of dumpable objects: 1 for frontend, 2 for backend, 4 for - server, -1 for everything. Values can be ORed, for example: - 1+2=3 -> frontend+backend. - 1+2+4=7 -> frontend+backend+server. - - sid is a service id, -1 to dump everything from the selected proxy. +The following commands are supported on the UNIX stats socket ; all of them +must be terminated by a line feed. It is important to understand that when +multiple haproxy processes are started on the same sockets, any process may +pick up the request and will output its own stats. + +show stat [ ] + Dump statistics in the CSV format. By passing , and , it is + possible to dump only selected items : + - is a proxy ID, -1 to dump everything + - selects the type of dumpable objects : 1 for frontends, 2 for + backends, 4 for servers, -1 for everything. These values can be ORed, + for example: + 1 + 2 = 3 -> frontend + backend. + 1 + 2 + 4 = 7 -> frontend + backend + server. + - is a server ID, -1 to dump everything from the selected proxy. + +show info + Dump info about haproxy status on current process. + +show sess + Dump all known sessions. Avoid doing this on slow connections as this can + be huge. - - "show info": dump info about current haproxy status. /* * Local variables: diff --git a/include/proto/dumpstats.h b/include/proto/dumpstats.h index 8fd3ccb50..cf9ae29b8 100644 --- a/include/proto/dumpstats.h +++ b/include/proto/dumpstats.h @@ -48,6 +48,7 @@ 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); int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri); +void stats_dump_sess_to_buffer(struct session *s, struct buffer *rep); #endif /* _PROTO_DUMPSTATS_H */ diff --git a/include/types/session.h b/include/types/session.h index 7630bc30c..fb6334771 100644 --- a/include/types/session.h +++ b/include/types/session.h @@ -201,6 +201,9 @@ struct session { unsigned int flags; /* STAT_* */ int iid, type, sid; /* proxy id, type and service id if bounding of stats is enabled */ } stats; + struct { + struct bref bref; + } sess; } data_ctx; /* used by produce_content to dump the stats right now */ unsigned int uniq_id; /* unique ID used for the traces */ }; diff --git a/src/dumpstats.c b/src/dumpstats.c index 30cb0edf3..f67d21728 100644 --- a/src/dumpstats.c +++ b/src/dumpstats.c @@ -1081,6 +1081,132 @@ int stats_dump_proxy(struct session *s, struct proxy *px, struct uri_auth *uri) } } + +/* This function is called to send output to the response buffer. + * It dumps the sessions states onto the output buffer . + * Expects to be called with client socket shut down on input. + * s->data_ctx must have been zeroed first, and the flags properly set. + * It automatically clears the HIJACK bit from the response buffer. + */ +void stats_dump_sess_to_buffer(struct session *s, struct buffer *rep) +{ + struct chunk msg; + + if (unlikely(rep->flags & (BF_WRITE_ERROR|BF_SHUTW))) { + /* If we're forced to shut down, we might have to remove our + * reference to the last session being dumped. + */ + if (s->data_state == DATA_ST_LIST) { + if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) + LIST_DEL(&s->data_ctx.sess.bref.users); + } + s->data_state = DATA_ST_FIN; + buffer_stop_hijack(rep); + s->ana_state = STATS_ST_CLOSE; + return; + } + + if (s->ana_state != STATS_ST_REP) + return; + + msg.len = 0; + msg.str = trash; + + switch (s->data_state) { + case DATA_ST_INIT: + /* the function had not been called yet, let's prepare the + * buffer for a response. We initialize the current session + * pointer to the first in the global list. + */ + stream_int_retnclose(rep->cons, &msg); + LIST_INIT(&s->data_ctx.sess.bref.users); + s->data_ctx.sess.bref.ref = sessions.n; + s->data_state = DATA_ST_LIST; + /* fall through */ + + case DATA_ST_LIST: + while (s->data_ctx.sess.bref.ref != &sessions) { + char pn[INET6_ADDRSTRLEN + strlen(":65535")]; + struct session *curr_sess; + + curr_sess = LIST_ELEM(s->data_ctx.sess.bref.ref, struct session *, list); + + /* first, let's detach the back-ref from a possible previous session */ + if (!LIST_ISEMPTY(&s->data_ctx.sess.bref.users)) + LIST_DEL(&s->data_ctx.sess.bref.users); + + chunk_printf(&msg, sizeof(trash), + "%p: proto=%s", + curr_sess, + curr_sess->listener->proto->name); + + switch (curr_sess->listener->proto->sock_family) { + case AF_INET: + inet_ntop(AF_INET, + (const void *)&((struct sockaddr_in *)&curr_sess->cli_addr)->sin_addr, + pn, sizeof(pn)); + + chunk_printf(&msg, sizeof(trash), + " src=%s:%d fe=%s be=%s srv=%s", + pn, + ntohs(((struct sockaddr_in *)&curr_sess->cli_addr)->sin_port), + curr_sess->fe->id, + curr_sess->be->id, + curr_sess->srv ? curr_sess->srv->id : "" + ); + break; + case AF_INET6: + inet_ntop(AF_INET6, + (const void *)&((struct sockaddr_in6 *)(&curr_sess->cli_addr))->sin6_addr, + pn, sizeof(pn)); + + chunk_printf(&msg, sizeof(trash), + " src=%s:%d fe=%s be=%s srv=%s", + pn, + ntohs(((struct sockaddr_in6 *)&curr_sess->cli_addr)->sin6_port), + curr_sess->fe->id, + curr_sess->be->id, + curr_sess->srv ? curr_sess->srv->id : "" + ); + + break; + case AF_UNIX: + /* no more information to print right now */ + break; + } + + chunk_printf(&msg, sizeof(trash), + " si=(%d,%d) as=%d age=%s", + curr_sess->si[0].state, curr_sess->si[1].state, + curr_sess->ana_state, + human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1)); + + chunk_printf(&msg, sizeof(trash), + " exp=%s\n", + curr_sess->task->expire ? + human_time(TICKS_TO_MS(tick_remain(now_ms, curr_sess->task->expire)), + TICKS_TO_MS(1000)) : "never"); + + if (buffer_write_chunk(rep, &msg) >= 0) { + /* let's try again later */ + LIST_ADDQ(&curr_sess->back_refs, &s->data_ctx.sess.bref.users); + return; + } + + s->data_ctx.sess.bref.ref = curr_sess->list.n; + } + s->data_state = DATA_ST_FIN; + /* fall through */ + + default: + s->data_state = DATA_ST_FIN; + buffer_stop_hijack(rep); + s->ana_state = STATS_ST_CLOSE; + return; + } +} + + static struct cfg_kw_list cfg_kws = {{ },{ { CFG_GLOBAL, "stats", stats_parse_global }, { 0, NULL, NULL }, diff --git a/src/proto_uxst.c b/src/proto_uxst.c index 25432687a..e31398a95 100644 --- a/src/proto_uxst.c +++ b/src/proto_uxst.c @@ -472,6 +472,8 @@ int uxst_event_accept(int fd) { memset(&s->logs, 0, sizeof(s->logs)); memset(&s->txn, 0, sizeof(s->txn)); + s->logs.tv_accept = now; /* corrected date for internal use */ + s->data_state = DATA_ST_INIT; s->data_source = DATA_SRC_NONE; s->uniq_id = totalconn; @@ -611,7 +613,11 @@ int unix_sock_parse_request(struct session *s, char *line) s->ana_state = STATS_ST_REP; buffer_install_hijacker(s, s->rep, stats_dump_raw_to_buffer); } - else { /* neither "stat" nor "info" */ + 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 { /* neither "stat" nor "info" nor "sess" */ return 0; } }