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, 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
[to do]
+
2.7) CSV format
+---------------
0. pxname: proxy name
1. svname: service name (FRONTEND for frontend, BACKEND for backend, any name
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 [<iid> <type> <sid>]": 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 [<iid> <type> <sid>]
+ Dump statistics in the CSV 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 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.
+ - <sid> 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:
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 */
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 */
};
}
}
+
+/* This function is called to send output to the response buffer.
+ * It dumps the sessions states onto the output buffer <rep>.
+ * 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 : "<none>"
+ );
+ 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 : "<none>"
+ );
+
+ 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 },
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;
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;
}
}