diff --git a/include/types/cli.h b/include/types/cli.h index e177b1867..72b2c2dac 100644 --- a/include/types/cli.h +++ b/include/types/cli.h @@ -111,7 +111,6 @@ enum { STAT_CLI_PRINT, /* display message in cli->msg */ STAT_CLI_PRINT_FREE, /* display message in cli->msg. After the display, free the pointer */ STAT_CLI_O_INFO, /* dump info */ - STAT_CLI_O_SESS, /* dump streams */ STAT_CLI_O_ERR, /* dump errors */ STAT_CLI_O_TAB, /* dump tables */ STAT_CLI_O_CLR, /* clear tables */ diff --git a/src/cli.c b/src/cli.c index 606ccc71c..11f7e445d 100644 --- a/src/cli.c +++ b/src/cli.c @@ -136,8 +136,6 @@ static struct field info[INF_TOTAL_FIELDS]; static int stats_dump_backend_to_buffer(struct stream_interface *si); static int stats_dump_env_to_buffer(struct stream_interface *si); static int stats_dump_info_to_buffer(struct stream_interface *si); -static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess); -static int stats_dump_sess_to_buffer(struct stream_interface *si); static int stats_dump_errors_to_buffer(struct stream_interface *si); static int stats_table_request(struct stream_interface *si, int show); @@ -156,7 +154,6 @@ static const char stats_sock_usage_msg[] = " 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 [id] : report the list of current sessions or dump this session\n" " show table [id]: report table usage stats or dump this table's contents\n" " get weight : report a server's current weight\n" " set weight : change a server's weight\n" @@ -1110,23 +1107,6 @@ static int stats_sock_parse_request(struct stream_interface *si, char *line) appctx->st2 = STAT_ST_INIT; appctx->st0 = STAT_CLI_O_INFO; // stats_dump_info_to_buffer } - else if (strcmp(args[1], "sess") == 0) { - appctx->st2 = STAT_ST_INIT; - if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) { - appctx->ctx.cli.msg = stats_permission_denied_msg; - appctx->st0 = STAT_CLI_PRINT; - return 1; - } - if (*args[2] && strcmp(args[2], "all") == 0) - appctx->ctx.sess.target = (void *)-1; - else if (*args[2]) - appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0); - else - appctx->ctx.sess.target = NULL; - appctx->ctx.sess.section = 0; /* start with stream status */ - appctx->ctx.sess.pos = 0; - appctx->st0 = STAT_CLI_O_SESS; // stats_dump_sess_to_buffer - } else if (strcmp(args[1], "errors") == 0) { if (strm_li(s)->bind_conf->level < ACCESS_LVL_OPER) { appctx->ctx.cli.msg = stats_permission_denied_msg; @@ -1914,10 +1894,6 @@ static void cli_io_handler(struct appctx *appctx) if (stats_dump_stat_to_buffer(si, NULL)) appctx->st0 = STAT_CLI_PROMPT; break; - case STAT_CLI_O_SESS: - if (stats_dump_sess_to_buffer(si)) - appctx->st0 = STAT_CLI_PROMPT; - break; case STAT_CLI_O_ERR: /* errors dump */ if (stats_dump_errors_to_buffer(si)) appctx->st0 = STAT_CLI_PROMPT; @@ -2200,589 +2176,6 @@ static int stats_dump_backend_to_buffer(struct stream_interface *si) return 1; } -static inline const char *get_conn_ctrl_name(const struct connection *conn) -{ - if (!conn_ctrl_ready(conn)) - return "NONE"; - return conn->ctrl->name; -} - -static inline const char *get_conn_xprt_name(const struct connection *conn) -{ - static char ptr[19]; - - if (!conn_xprt_ready(conn)) - return "NONE"; - - if (conn->xprt == &raw_sock) - return "RAW"; - -#ifdef USE_OPENSSL - if (conn->xprt == &ssl_sock) - return "SSL"; -#endif - snprintf(ptr, sizeof(ptr), "%p", conn->xprt); - return ptr; -} - -static inline const char *get_conn_data_name(const struct connection *conn) -{ - static char ptr[19]; - - if (!conn->data) - return "NONE"; - - if (conn->data == &sess_conn_cb) - return "SESS"; - - if (conn->data == &si_conn_cb) - return "STRM"; - - if (conn->data == &check_conn_cb) - return "CHCK"; - - snprintf(ptr, sizeof(ptr), "%p", conn->data); - return ptr; -} - -/* This function dumps a complete stream state onto the stream interface's - * read buffer. The stream has to be set in sess->target. It returns - * 0 if the output buffer is full and it needs to be called again, otherwise - * non-zero. It is designed to be called from stats_dump_sess_to_buffer() below. - */ -static int stats_dump_full_sess_to_buffer(struct stream_interface *si, struct stream *sess) -{ - struct appctx *appctx = __objt_appctx(si->end); - struct tm tm; - extern const char *monthname[12]; - char pn[INET6_ADDRSTRLEN]; - struct connection *conn; - struct appctx *tmpctx; - - chunk_reset(&trash); - - if (appctx->ctx.sess.section > 0 && appctx->ctx.sess.uid != sess->uniq_id) { - /* stream changed, no need to go any further */ - chunk_appendf(&trash, " *** session terminated while we were watching it ***\n"); - if (bi_putchk(si_ic(si), &trash) == -1) { - si_applet_cant_put(si); - return 0; - } - appctx->ctx.sess.uid = 0; - appctx->ctx.sess.section = 0; - return 1; - } - - switch (appctx->ctx.sess.section) { - case 0: /* main status of the stream */ - appctx->ctx.sess.uid = sess->uniq_id; - appctx->ctx.sess.section = 1; - /* fall through */ - - case 1: - get_localtime(sess->logs.accept_date.tv_sec, &tm); - chunk_appendf(&trash, - "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s", - sess, - tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, - tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(sess->logs.accept_date.tv_usec), - sess->uniq_id, - strm_li(sess) ? strm_li(sess)->proto->name : "?"); - - conn = objt_conn(strm_orig(sess)); - switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { - case AF_INET: - case AF_INET6: - chunk_appendf(&trash, " source=%s:%d\n", - pn, get_host_port(&conn->addr.from)); - break; - case AF_UNIX: - chunk_appendf(&trash, " source=unix:%d\n", strm_li(sess)->luid); - break; - default: - /* no more information to print right now */ - chunk_appendf(&trash, "\n"); - break; - } - - chunk_appendf(&trash, - " flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n", - sess->flags, sess->si[1].conn_retries, sess->srv_conn, sess->pend_pos); - - chunk_appendf(&trash, - " frontend=%s (id=%u mode=%s), listener=%s (id=%u)", - strm_fe(sess)->id, strm_fe(sess)->uuid, strm_fe(sess)->mode ? "http" : "tcp", - strm_li(sess) ? strm_li(sess)->name ? strm_li(sess)->name : "?" : "?", - strm_li(sess) ? strm_li(sess)->luid : 0); - - if (conn) - conn_get_to_addr(conn); - - switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) { - case AF_INET: - case AF_INET6: - chunk_appendf(&trash, " addr=%s:%d\n", - pn, get_host_port(&conn->addr.to)); - break; - case AF_UNIX: - chunk_appendf(&trash, " addr=unix:%d\n", strm_li(sess)->luid); - break; - default: - /* no more information to print right now */ - chunk_appendf(&trash, "\n"); - break; - } - - if (sess->be->cap & PR_CAP_BE) - chunk_appendf(&trash, - " backend=%s (id=%u mode=%s)", - sess->be->id, - sess->be->uuid, sess->be->mode ? "http" : "tcp"); - else - chunk_appendf(&trash, " backend= (id=-1 mode=-)"); - - conn = objt_conn(sess->si[1].end); - if (conn) - conn_get_from_addr(conn); - - switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { - case AF_INET: - case AF_INET6: - chunk_appendf(&trash, " addr=%s:%d\n", - pn, get_host_port(&conn->addr.from)); - break; - case AF_UNIX: - chunk_appendf(&trash, " addr=unix\n"); - break; - default: - /* no more information to print right now */ - chunk_appendf(&trash, "\n"); - break; - } - - if (sess->be->cap & PR_CAP_BE) - chunk_appendf(&trash, - " server=%s (id=%u)", - objt_server(sess->target) ? objt_server(sess->target)->id : "", - objt_server(sess->target) ? objt_server(sess->target)->puid : 0); - else - chunk_appendf(&trash, " server= (id=-1)"); - - if (conn) - conn_get_to_addr(conn); - - switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) { - case AF_INET: - case AF_INET6: - chunk_appendf(&trash, " addr=%s:%d\n", - pn, get_host_port(&conn->addr.to)); - break; - case AF_UNIX: - chunk_appendf(&trash, " addr=unix\n"); - break; - default: - /* no more information to print right now */ - chunk_appendf(&trash, "\n"); - break; - } - - chunk_appendf(&trash, - " task=%p (state=0x%02x nice=%d calls=%d exp=%s%s", - sess->task, - sess->task->state, - sess->task->nice, sess->task->calls, - sess->task->expire ? - tick_is_expired(sess->task->expire, now_ms) ? "" : - human_time(TICKS_TO_MS(sess->task->expire - now_ms), - TICKS_TO_MS(1000)) : "", - task_in_rq(sess->task) ? ", running" : ""); - - chunk_appendf(&trash, - " age=%s)\n", - human_time(now.tv_sec - sess->logs.accept_date.tv_sec, 1)); - - if (sess->txn) - chunk_appendf(&trash, - " txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s waiting=%d\n", - sess->txn, sess->txn->flags, sess->txn->meth, sess->txn->status, - http_msg_state_str(sess->txn->req.msg_state), http_msg_state_str(sess->txn->rsp.msg_state), !LIST_ISEMPTY(&sess->buffer_wait)); - - chunk_appendf(&trash, - " si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n", - &sess->si[0], - si_state_str(sess->si[0].state), - sess->si[0].flags, - obj_type_name(sess->si[0].end), - obj_base_ptr(sess->si[0].end), - sess->si[0].exp ? - tick_is_expired(sess->si[0].exp, now_ms) ? "" : - human_time(TICKS_TO_MS(sess->si[0].exp - now_ms), - TICKS_TO_MS(1000)) : "", - sess->si[0].err_type); - - chunk_appendf(&trash, - " si[1]=%p (state=%s flags=0x%02x endp1=%s:%p exp=%s, et=0x%03x)\n", - &sess->si[1], - si_state_str(sess->si[1].state), - sess->si[1].flags, - obj_type_name(sess->si[1].end), - obj_base_ptr(sess->si[1].end), - sess->si[1].exp ? - tick_is_expired(sess->si[1].exp, now_ms) ? "" : - human_time(TICKS_TO_MS(sess->si[1].exp - now_ms), - TICKS_TO_MS(1000)) : "", - sess->si[1].err_type); - - if ((conn = objt_conn(sess->si[0].end)) != NULL) { - chunk_appendf(&trash, - " co0=%p ctrl=%s xprt=%s data=%s target=%s:%p\n", - conn, - get_conn_ctrl_name(conn), - get_conn_xprt_name(conn), - get_conn_data_name(conn), - obj_type_name(conn->target), - obj_base_ptr(conn->target)); - - chunk_appendf(&trash, " flags=0x%08x", conn->flags); - - if (conn->t.sock.fd >= 0) { - chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n", - conn->t.sock.fd, fdtab[conn->t.sock.fd].state, - fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated); - } - else - chunk_appendf(&trash, " fd=\n"); - } - else if ((tmpctx = objt_appctx(sess->si[0].end)) != NULL) { - chunk_appendf(&trash, - " app0=%p st0=%d st1=%d st2=%d applet=%s\n", - tmpctx, - tmpctx->st0, - tmpctx->st1, - tmpctx->st2, - tmpctx->applet->name); - } - - if ((conn = objt_conn(sess->si[1].end)) != NULL) { - chunk_appendf(&trash, - " co1=%p ctrl=%s xprt=%s data=%s target=%s:%p\n", - conn, - get_conn_ctrl_name(conn), - get_conn_xprt_name(conn), - get_conn_data_name(conn), - obj_type_name(conn->target), - obj_base_ptr(conn->target)); - - chunk_appendf(&trash, " flags=0x%08x", conn->flags); - - if (conn->t.sock.fd >= 0) { - chunk_appendf(&trash, " fd=%d fd.state=%02x fd.cache=%d updt=%d\n", - conn->t.sock.fd, fdtab[conn->t.sock.fd].state, - fdtab[conn->t.sock.fd].cache, fdtab[conn->t.sock.fd].updated); - } - else - chunk_appendf(&trash, " fd=\n"); - } - else if ((tmpctx = objt_appctx(sess->si[1].end)) != NULL) { - chunk_appendf(&trash, - " app1=%p st0=%d st1=%d st2=%d applet=%s\n", - tmpctx, - tmpctx->st0, - tmpctx->st1, - tmpctx->st2, - tmpctx->applet->name); - } - - chunk_appendf(&trash, - " req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n" - " an_exp=%s", - &sess->req, - sess->req.flags, sess->req.analysers, - sess->req.pipe ? sess->req.pipe->data : 0, - sess->req.to_forward, sess->req.total, - sess->req.analyse_exp ? - human_time(TICKS_TO_MS(sess->req.analyse_exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " rex=%s", - sess->req.rex ? - human_time(TICKS_TO_MS(sess->req.rex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " wex=%s\n" - " buf=%p data=%p o=%d p=%d req.next=%d i=%d size=%d\n", - sess->req.wex ? - human_time(TICKS_TO_MS(sess->req.wex - now_ms), - TICKS_TO_MS(1000)) : "", - sess->req.buf, - sess->req.buf->data, sess->req.buf->o, - (int)(sess->req.buf->p - sess->req.buf->data), - sess->txn ? sess->txn->req.next : 0, sess->req.buf->i, - sess->req.buf->size); - - chunk_appendf(&trash, - " res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n" - " an_exp=%s", - &sess->res, - sess->res.flags, sess->res.analysers, - sess->res.pipe ? sess->res.pipe->data : 0, - sess->res.to_forward, sess->res.total, - sess->res.analyse_exp ? - human_time(TICKS_TO_MS(sess->res.analyse_exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " rex=%s", - sess->res.rex ? - human_time(TICKS_TO_MS(sess->res.rex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " wex=%s\n" - " buf=%p data=%p o=%d p=%d rsp.next=%d i=%d size=%d\n", - sess->res.wex ? - human_time(TICKS_TO_MS(sess->res.wex - now_ms), - TICKS_TO_MS(1000)) : "", - sess->res.buf, - sess->res.buf->data, sess->res.buf->o, - (int)(sess->res.buf->p - sess->res.buf->data), - sess->txn ? sess->txn->rsp.next : 0, sess->res.buf->i, - sess->res.buf->size); - - if (bi_putchk(si_ic(si), &trash) == -1) { - si_applet_cant_put(si); - return 0; - } - - /* use other states to dump the contents */ - } - /* end of dump */ - appctx->ctx.sess.uid = 0; - appctx->ctx.sess.section = 0; - return 1; -} - -/* This function dumps all streams' states onto the stream interface's - * read buffer. It returns 0 if the output buffer is full and it needs - * to be called again, otherwise non-zero. It is designed to be called - * from stats_dump_sess_to_buffer() below. - */ -static int stats_dump_sess_to_buffer(struct stream_interface *si) -{ - struct appctx *appctx = __objt_appctx(si->end); - struct connection *conn; - - if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) { - /* If we're forced to shut down, we might have to remove our - * reference to the last stream being dumped. - */ - if (appctx->st2 == STAT_ST_LIST) { - if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) { - LIST_DEL(&appctx->ctx.sess.bref.users); - LIST_INIT(&appctx->ctx.sess.bref.users); - } - } - return 1; - } - - chunk_reset(&trash); - - switch (appctx->st2) { - case STAT_ST_INIT: - /* the function had not been called yet, let's prepare the - * buffer for a response. We initialize the current stream - * pointer to the first in the global list. When a target - * stream is being destroyed, it is responsible for updating - * this pointer. We know we have reached the end when this - * pointer points back to the head of the streams list. - */ - LIST_INIT(&appctx->ctx.sess.bref.users); - appctx->ctx.sess.bref.ref = streams.n; - appctx->st2 = STAT_ST_LIST; - /* fall through */ - - case STAT_ST_LIST: - /* first, let's detach the back-ref from a possible previous stream */ - if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) { - LIST_DEL(&appctx->ctx.sess.bref.users); - LIST_INIT(&appctx->ctx.sess.bref.users); - } - - /* and start from where we stopped */ - while (appctx->ctx.sess.bref.ref != &streams) { - char pn[INET6_ADDRSTRLEN]; - struct stream *curr_sess; - - curr_sess = LIST_ELEM(appctx->ctx.sess.bref.ref, struct stream *, list); - - if (appctx->ctx.sess.target) { - if (appctx->ctx.sess.target != (void *)-1 && appctx->ctx.sess.target != curr_sess) - goto next_sess; - - LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users); - /* call the proper dump() function and return if we're missing space */ - if (!stats_dump_full_sess_to_buffer(si, curr_sess)) - return 0; - - /* stream dump complete */ - LIST_DEL(&appctx->ctx.sess.bref.users); - LIST_INIT(&appctx->ctx.sess.bref.users); - if (appctx->ctx.sess.target != (void *)-1) { - appctx->ctx.sess.target = NULL; - break; - } - else - goto next_sess; - } - - chunk_appendf(&trash, - "%p: proto=%s", - curr_sess, - strm_li(curr_sess) ? strm_li(curr_sess)->proto->name : "?"); - - conn = objt_conn(strm_orig(curr_sess)); - switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { - case AF_INET: - case AF_INET6: - chunk_appendf(&trash, - " src=%s:%d fe=%s be=%s srv=%s", - pn, - get_host_port(&conn->addr.from), - strm_fe(curr_sess)->id, - (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "", - objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "" - ); - break; - case AF_UNIX: - chunk_appendf(&trash, - " src=unix:%d fe=%s be=%s srv=%s", - strm_li(curr_sess)->luid, - strm_fe(curr_sess)->id, - (curr_sess->be->cap & PR_CAP_BE) ? curr_sess->be->id : "", - objt_server(curr_sess->target) ? objt_server(curr_sess->target)->id : "" - ); - break; - } - - chunk_appendf(&trash, - " ts=%02x age=%s calls=%d", - curr_sess->task->state, - human_time(now.tv_sec - curr_sess->logs.tv_accept.tv_sec, 1), - curr_sess->task->calls); - - chunk_appendf(&trash, - " rq[f=%06xh,i=%d,an=%02xh,rx=%s", - curr_sess->req.flags, - curr_sess->req.buf->i, - curr_sess->req.analysers, - curr_sess->req.rex ? - human_time(TICKS_TO_MS(curr_sess->req.rex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - ",wx=%s", - curr_sess->req.wex ? - human_time(TICKS_TO_MS(curr_sess->req.wex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - ",ax=%s]", - curr_sess->req.analyse_exp ? - human_time(TICKS_TO_MS(curr_sess->req.analyse_exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " rp[f=%06xh,i=%d,an=%02xh,rx=%s", - curr_sess->res.flags, - curr_sess->res.buf->i, - curr_sess->res.analysers, - curr_sess->res.rex ? - human_time(TICKS_TO_MS(curr_sess->res.rex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - ",wx=%s", - curr_sess->res.wex ? - human_time(TICKS_TO_MS(curr_sess->res.wex - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - ",ax=%s]", - curr_sess->res.analyse_exp ? - human_time(TICKS_TO_MS(curr_sess->res.analyse_exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - conn = objt_conn(curr_sess->si[0].end); - chunk_appendf(&trash, - " s0=[%d,%1xh,fd=%d,ex=%s]", - curr_sess->si[0].state, - curr_sess->si[0].flags, - (conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1, - curr_sess->si[0].exp ? - human_time(TICKS_TO_MS(curr_sess->si[0].exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - conn = objt_conn(curr_sess->si[1].end); - chunk_appendf(&trash, - " s1=[%d,%1xh,fd=%d,ex=%s]", - curr_sess->si[1].state, - curr_sess->si[1].flags, - (conn && conn->t.sock.fd >= 0) ? conn->t.sock.fd : -1, - curr_sess->si[1].exp ? - human_time(TICKS_TO_MS(curr_sess->si[1].exp - now_ms), - TICKS_TO_MS(1000)) : ""); - - chunk_appendf(&trash, - " exp=%s", - curr_sess->task->expire ? - human_time(TICKS_TO_MS(curr_sess->task->expire - now_ms), - TICKS_TO_MS(1000)) : ""); - if (task_in_rq(curr_sess->task)) - chunk_appendf(&trash, " run(nice=%d)", curr_sess->task->nice); - - chunk_appendf(&trash, "\n"); - - if (bi_putchk(si_ic(si), &trash) == -1) { - /* let's try again later from this stream. We add ourselves into - * this stream's users so that it can remove us upon termination. - */ - si_applet_cant_put(si); - LIST_ADDQ(&curr_sess->back_refs, &appctx->ctx.sess.bref.users); - return 0; - } - - next_sess: - appctx->ctx.sess.bref.ref = curr_sess->list.n; - } - - if (appctx->ctx.sess.target && appctx->ctx.sess.target != (void *)-1) { - /* specified stream not found */ - if (appctx->ctx.sess.section > 0) - chunk_appendf(&trash, " *** session terminated while we were watching it ***\n"); - else - chunk_appendf(&trash, "Session not found.\n"); - - if (bi_putchk(si_ic(si), &trash) == -1) { - si_applet_cant_put(si); - return 0; - } - - appctx->ctx.sess.target = NULL; - appctx->ctx.sess.uid = 0; - return 1; - } - - appctx->st2 = STAT_ST_FIN; - /* fall through */ - - default: - appctx->st2 = STAT_ST_FIN; - return 1; - } -} - /* This is called when the stream interface is closed. For instance, upon an * external abort, we won't call the i/o handler anymore so we may need to * remove back references to the stream currently being dumped. @@ -2793,10 +2186,6 @@ static void cli_release_handler(struct appctx *appctx) appctx->io_release(appctx); appctx->io_release = NULL; } - if (appctx->st0 == STAT_CLI_O_SESS && appctx->st2 == STAT_ST_LIST) { - if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) - LIST_DEL(&appctx->ctx.sess.bref.users); - } else if ((appctx->st0 == STAT_CLI_O_TAB || appctx->st0 == STAT_CLI_O_CLR) && appctx->st2 == STAT_ST_LIST) { appctx->ctx.table.entry->ref_cnt--; diff --git a/src/stream.c b/src/stream.c index 346e7b61b..0d987452b 100644 --- a/src/stream.c +++ b/src/stream.c @@ -22,6 +22,7 @@ #include #include +#include #include #include #include @@ -32,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -3350,6 +3352,574 @@ void service_keywords_register(struct action_kw_list *kw_list) LIST_ADDQ(&service_keywords, &kw_list->list); } + +/* This function dumps a complete stream state onto the stream interface's + * read buffer. The stream has to be set in strm. It returns 0 if the output + * buffer is full and it needs to be called again, otherwise non-zero. It is + * designed to be called from stats_dump_strm_to_buffer() below. + */ +static int stats_dump_full_strm_to_buffer(struct stream_interface *si, struct stream *strm) +{ + struct appctx *appctx = __objt_appctx(si->end); + struct tm tm; + extern const char *monthname[12]; + char pn[INET6_ADDRSTRLEN]; + struct connection *conn; + struct appctx *tmpctx; + + chunk_reset(&trash); + + if (appctx->ctx.sess.section > 0 && appctx->ctx.sess.uid != strm->uniq_id) { + /* stream changed, no need to go any further */ + chunk_appendf(&trash, " *** session terminated while we were watching it ***\n"); + if (bi_putchk(si_ic(si), &trash) == -1) { + si_applet_cant_put(si); + return 0; + } + appctx->ctx.sess.uid = 0; + appctx->ctx.sess.section = 0; + return 1; + } + + switch (appctx->ctx.sess.section) { + case 0: /* main status of the stream */ + appctx->ctx.sess.uid = strm->uniq_id; + appctx->ctx.sess.section = 1; + /* fall through */ + + case 1: + get_localtime(strm->logs.accept_date.tv_sec, &tm); + chunk_appendf(&trash, + "%p: [%02d/%s/%04d:%02d:%02d:%02d.%06d] id=%u proto=%s", + strm, + tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900, + tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(strm->logs.accept_date.tv_usec), + strm->uniq_id, + strm_li(strm) ? strm_li(strm)->proto->name : "?"); + + conn = objt_conn(strm_orig(strm)); + switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { + case AF_INET: + case AF_INET6: + chunk_appendf(&trash, " source=%s:%d\n", + pn, get_host_port(&conn->addr.from)); + break; + case AF_UNIX: + chunk_appendf(&trash, " source=unix:%d\n", strm_li(strm)->luid); + break; + default: + /* no more information to print right now */ + chunk_appendf(&trash, "\n"); + break; + } + + chunk_appendf(&trash, + " flags=0x%x, conn_retries=%d, srv_conn=%p, pend_pos=%p\n", + strm->flags, strm->si[1].conn_retries, strm->srv_conn, strm->pend_pos); + + chunk_appendf(&trash, + " frontend=%s (id=%u mode=%s), listener=%s (id=%u)", + strm_fe(strm)->id, strm_fe(strm)->uuid, strm_fe(strm)->mode ? "http" : "tcp", + strm_li(strm) ? strm_li(strm)->name ? strm_li(strm)->name : "?" : "?", + strm_li(strm) ? strm_li(strm)->luid : 0); + + if (conn) + conn_get_to_addr(conn); + + switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) { + case AF_INET: + case AF_INET6: + chunk_appendf(&trash, " addr=%s:%d\n", + pn, get_host_port(&conn->addr.to)); + break; + case AF_UNIX: + chunk_appendf(&trash, " addr=unix:%d\n", strm_li(strm)->luid); + break; + default: + /* no more information to print right now */ + chunk_appendf(&trash, "\n"); + break; + } + + if (strm->be->cap & PR_CAP_BE) + chunk_appendf(&trash, + " backend=%s (id=%u mode=%s)", + strm->be->id, + strm->be->uuid, strm->be->mode ? "http" : "tcp"); + else + chunk_appendf(&trash, " backend= (id=-1 mode=-)"); + + conn = objt_conn(strm->si[1].end); + if (conn) + conn_get_from_addr(conn); + + switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { + case AF_INET: + case AF_INET6: + chunk_appendf(&trash, " addr=%s:%d\n", + pn, get_host_port(&conn->addr.from)); + break; + case AF_UNIX: + chunk_appendf(&trash, " addr=unix\n"); + break; + default: + /* no more information to print right now */ + chunk_appendf(&trash, "\n"); + break; + } + + if (strm->be->cap & PR_CAP_BE) + chunk_appendf(&trash, + " server=%s (id=%u)", + objt_server(strm->target) ? objt_server(strm->target)->id : "", + objt_server(strm->target) ? objt_server(strm->target)->puid : 0); + else + chunk_appendf(&trash, " server= (id=-1)"); + + if (conn) + conn_get_to_addr(conn); + + switch (conn ? addr_to_str(&conn->addr.to, pn, sizeof(pn)) : AF_UNSPEC) { + case AF_INET: + case AF_INET6: + chunk_appendf(&trash, " addr=%s:%d\n", + pn, get_host_port(&conn->addr.to)); + break; + case AF_UNIX: + chunk_appendf(&trash, " addr=unix\n"); + break; + default: + /* no more information to print right now */ + chunk_appendf(&trash, "\n"); + break; + } + + chunk_appendf(&trash, + " task=%p (state=0x%02x nice=%d calls=%d exp=%s%s", + strm->task, + strm->task->state, + strm->task->nice, strm->task->calls, + strm->task->expire ? + tick_is_expired(strm->task->expire, now_ms) ? "" : + human_time(TICKS_TO_MS(strm->task->expire - now_ms), + TICKS_TO_MS(1000)) : "", + task_in_rq(strm->task) ? ", running" : ""); + + chunk_appendf(&trash, + " age=%s)\n", + human_time(now.tv_sec - strm->logs.accept_date.tv_sec, 1)); + + if (strm->txn) + chunk_appendf(&trash, + " txn=%p flags=0x%x meth=%d status=%d req.st=%s rsp.st=%s waiting=%d\n", + strm->txn, strm->txn->flags, strm->txn->meth, strm->txn->status, + http_msg_state_str(strm->txn->req.msg_state), http_msg_state_str(strm->txn->rsp.msg_state), !LIST_ISEMPTY(&strm->buffer_wait)); + + chunk_appendf(&trash, + " si[0]=%p (state=%s flags=0x%02x endp0=%s:%p exp=%s, et=0x%03x)\n", + &strm->si[0], + si_state_str(strm->si[0].state), + strm->si[0].flags, + obj_type_name(strm->si[0].end), + obj_base_ptr(strm->si[0].end), + strm->si[0].exp ? + tick_is_expired(strm->si[0].exp, now_ms) ? "" : + human_time(TICKS_TO_MS(strm->si[0].exp - now_ms), + TICKS_TO_MS(1000)) : "", + strm->si[0].err_type); + + chunk_appendf(&trash, + " si[1]=%p (state=%s flags=0x%02x endp1=%s:%p exp=%s, et=0x%03x)\n", + &strm->si[1], + si_state_str(strm->si[1].state), + strm->si[1].flags, + obj_type_name(strm->si[1].end), + obj_base_ptr(strm->si[1].end), + strm->si[1].exp ? + tick_is_expired(strm->si[1].exp, now_ms) ? "" : + human_time(TICKS_TO_MS(strm->si[1].exp - now_ms), + TICKS_TO_MS(1000)) : "", + strm->si[1].err_type); + + if ((conn = objt_conn(strm->si[0].end)) != NULL) { + chunk_appendf(&trash, + " co0=%p ctrl=%s xprt=%s data=%s target=%s:%p\n", + conn, + conn_get_ctrl_name(conn), + conn_get_xprt_name(conn), + conn_get_data_name(conn), + obj_type_name(conn->target), + obj_base_ptr(conn->target)); + + chunk_appendf(&trash, + " flags=0x%08x fd=%d fd.state=%02x fd.cache=%d updt=%d\n", + conn->flags, + conn->t.sock.fd, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].state : 0, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].cache : 0, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].updated : 0); + } + else if ((tmpctx = objt_appctx(strm->si[0].end)) != NULL) { + chunk_appendf(&trash, + " app0=%p st0=%d st1=%d st2=%d applet=%s\n", + tmpctx, + tmpctx->st0, + tmpctx->st1, + tmpctx->st2, + tmpctx->applet->name); + } + + if ((conn = objt_conn(strm->si[1].end)) != NULL) { + chunk_appendf(&trash, + " co1=%p ctrl=%s xprt=%s data=%s target=%s:%p\n", + conn, + conn_get_ctrl_name(conn), + conn_get_xprt_name(conn), + conn_get_data_name(conn), + obj_type_name(conn->target), + obj_base_ptr(conn->target)); + + chunk_appendf(&trash, + " flags=0x%08x fd=%d fd.state=%02x fd.cache=%d updt=%d\n", + conn->flags, + conn->t.sock.fd, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].state : 0, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].cache : 0, + conn->t.sock.fd >= 0 ? fdtab[conn->t.sock.fd].updated : 0); + } + else if ((tmpctx = objt_appctx(strm->si[1].end)) != NULL) { + chunk_appendf(&trash, + " app1=%p st0=%d st1=%d st2=%d applet=%s\n", + tmpctx, + tmpctx->st0, + tmpctx->st1, + tmpctx->st2, + tmpctx->applet->name); + } + + chunk_appendf(&trash, + " req=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n" + " an_exp=%s", + &strm->req, + strm->req.flags, strm->req.analysers, + strm->req.pipe ? strm->req.pipe->data : 0, + strm->req.to_forward, strm->req.total, + strm->req.analyse_exp ? + human_time(TICKS_TO_MS(strm->req.analyse_exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " rex=%s", + strm->req.rex ? + human_time(TICKS_TO_MS(strm->req.rex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " wex=%s\n" + " buf=%p data=%p o=%d p=%d req.next=%d i=%d size=%d\n", + strm->req.wex ? + human_time(TICKS_TO_MS(strm->req.wex - now_ms), + TICKS_TO_MS(1000)) : "", + strm->req.buf, + strm->req.buf->data, strm->req.buf->o, + (int)(strm->req.buf->p - strm->req.buf->data), + strm->txn ? strm->txn->req.next : 0, strm->req.buf->i, + strm->req.buf->size); + + chunk_appendf(&trash, + " res=%p (f=0x%06x an=0x%x pipe=%d tofwd=%d total=%lld)\n" + " an_exp=%s", + &strm->res, + strm->res.flags, strm->res.analysers, + strm->res.pipe ? strm->res.pipe->data : 0, + strm->res.to_forward, strm->res.total, + strm->res.analyse_exp ? + human_time(TICKS_TO_MS(strm->res.analyse_exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " rex=%s", + strm->res.rex ? + human_time(TICKS_TO_MS(strm->res.rex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " wex=%s\n" + " buf=%p data=%p o=%d p=%d rsp.next=%d i=%d size=%d\n", + strm->res.wex ? + human_time(TICKS_TO_MS(strm->res.wex - now_ms), + TICKS_TO_MS(1000)) : "", + strm->res.buf, + strm->res.buf->data, strm->res.buf->o, + (int)(strm->res.buf->p - strm->res.buf->data), + strm->txn ? strm->txn->rsp.next : 0, strm->res.buf->i, + strm->res.buf->size); + + if (bi_putchk(si_ic(si), &trash) == -1) { + si_applet_cant_put(si); + return 0; + } + + /* use other states to dump the contents */ + } + /* end of dump */ + appctx->ctx.sess.uid = 0; + appctx->ctx.sess.section = 0; + return 1; +} + + +static int cli_parse_show_sess(char **args, struct appctx *appctx, void *private) +{ + appctx->st2 = STAT_ST_INIT; + if (!cli_has_level(appctx, ACCESS_LVL_OPER)) + return 1; + + if (*args[2] && strcmp(args[2], "all") == 0) + appctx->ctx.sess.target = (void *)-1; + else if (*args[2]) + appctx->ctx.sess.target = (void *)strtoul(args[2], NULL, 0); + else + appctx->ctx.sess.target = NULL; + appctx->ctx.sess.section = 0; /* start with stream status */ + appctx->ctx.sess.pos = 0; + + return 0; +} + +/* This function dumps all streams' states onto the stream interface's + * read buffer. It returns 0 if the output buffer is full and it needs + * to be called again, otherwise non-zero. It is designed to be called + * from stats_dump_sess_to_buffer() below. + */ +static int cli_io_handler_dump_sess(struct appctx *appctx) +{ + struct stream_interface *si = appctx->owner; + struct connection *conn; + + if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW))) { + /* If we're forced to shut down, we might have to remove our + * reference to the last stream being dumped. + */ + if (appctx->st2 == STAT_ST_LIST) { + if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) { + LIST_DEL(&appctx->ctx.sess.bref.users); + LIST_INIT(&appctx->ctx.sess.bref.users); + } + } + return 1; + } + + chunk_reset(&trash); + + switch (appctx->st2) { + case STAT_ST_INIT: + /* the function had not been called yet, let's prepare the + * buffer for a response. We initialize the current stream + * pointer to the first in the global list. When a target + * stream is being destroyed, it is responsible for updating + * this pointer. We know we have reached the end when this + * pointer points back to the head of the streams list. + */ + LIST_INIT(&appctx->ctx.sess.bref.users); + appctx->ctx.sess.bref.ref = streams.n; + appctx->st2 = STAT_ST_LIST; + /* fall through */ + + case STAT_ST_LIST: + /* first, let's detach the back-ref from a possible previous stream */ + if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) { + LIST_DEL(&appctx->ctx.sess.bref.users); + LIST_INIT(&appctx->ctx.sess.bref.users); + } + + /* and start from where we stopped */ + while (appctx->ctx.sess.bref.ref != &streams) { + char pn[INET6_ADDRSTRLEN]; + struct stream *curr_strm; + + curr_strm = LIST_ELEM(appctx->ctx.sess.bref.ref, struct stream *, list); + + if (appctx->ctx.sess.target) { + if (appctx->ctx.sess.target != (void *)-1 && appctx->ctx.sess.target != curr_strm) + goto next_sess; + + LIST_ADDQ(&curr_strm->back_refs, &appctx->ctx.sess.bref.users); + /* call the proper dump() function and return if we're missing space */ + if (!stats_dump_full_strm_to_buffer(si, curr_strm)) + return 0; + + /* stream dump complete */ + LIST_DEL(&appctx->ctx.sess.bref.users); + LIST_INIT(&appctx->ctx.sess.bref.users); + if (appctx->ctx.sess.target != (void *)-1) { + appctx->ctx.sess.target = NULL; + break; + } + else + goto next_sess; + } + + chunk_appendf(&trash, + "%p: proto=%s", + curr_strm, + strm_li(curr_strm) ? strm_li(curr_strm)->proto->name : "?"); + + conn = objt_conn(strm_orig(curr_strm)); + switch (conn ? addr_to_str(&conn->addr.from, pn, sizeof(pn)) : AF_UNSPEC) { + case AF_INET: + case AF_INET6: + chunk_appendf(&trash, + " src=%s:%d fe=%s be=%s srv=%s", + pn, + get_host_port(&conn->addr.from), + strm_fe(curr_strm)->id, + (curr_strm->be->cap & PR_CAP_BE) ? curr_strm->be->id : "", + objt_server(curr_strm->target) ? objt_server(curr_strm->target)->id : "" + ); + break; + case AF_UNIX: + chunk_appendf(&trash, + " src=unix:%d fe=%s be=%s srv=%s", + strm_li(curr_strm)->luid, + strm_fe(curr_strm)->id, + (curr_strm->be->cap & PR_CAP_BE) ? curr_strm->be->id : "", + objt_server(curr_strm->target) ? objt_server(curr_strm->target)->id : "" + ); + break; + } + + chunk_appendf(&trash, + " ts=%02x age=%s calls=%d", + curr_strm->task->state, + human_time(now.tv_sec - curr_strm->logs.tv_accept.tv_sec, 1), + curr_strm->task->calls); + + chunk_appendf(&trash, + " rq[f=%06xh,i=%d,an=%02xh,rx=%s", + curr_strm->req.flags, + curr_strm->req.buf->i, + curr_strm->req.analysers, + curr_strm->req.rex ? + human_time(TICKS_TO_MS(curr_strm->req.rex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + ",wx=%s", + curr_strm->req.wex ? + human_time(TICKS_TO_MS(curr_strm->req.wex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + ",ax=%s]", + curr_strm->req.analyse_exp ? + human_time(TICKS_TO_MS(curr_strm->req.analyse_exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " rp[f=%06xh,i=%d,an=%02xh,rx=%s", + curr_strm->res.flags, + curr_strm->res.buf->i, + curr_strm->res.analysers, + curr_strm->res.rex ? + human_time(TICKS_TO_MS(curr_strm->res.rex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + ",wx=%s", + curr_strm->res.wex ? + human_time(TICKS_TO_MS(curr_strm->res.wex - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + ",ax=%s]", + curr_strm->res.analyse_exp ? + human_time(TICKS_TO_MS(curr_strm->res.analyse_exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + conn = objt_conn(curr_strm->si[0].end); + chunk_appendf(&trash, + " s0=[%d,%1xh,fd=%d,ex=%s]", + curr_strm->si[0].state, + curr_strm->si[0].flags, + conn ? conn->t.sock.fd : -1, + curr_strm->si[0].exp ? + human_time(TICKS_TO_MS(curr_strm->si[0].exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + conn = objt_conn(curr_strm->si[1].end); + chunk_appendf(&trash, + " s1=[%d,%1xh,fd=%d,ex=%s]", + curr_strm->si[1].state, + curr_strm->si[1].flags, + conn ? conn->t.sock.fd : -1, + curr_strm->si[1].exp ? + human_time(TICKS_TO_MS(curr_strm->si[1].exp - now_ms), + TICKS_TO_MS(1000)) : ""); + + chunk_appendf(&trash, + " exp=%s", + curr_strm->task->expire ? + human_time(TICKS_TO_MS(curr_strm->task->expire - now_ms), + TICKS_TO_MS(1000)) : ""); + if (task_in_rq(curr_strm->task)) + chunk_appendf(&trash, " run(nice=%d)", curr_strm->task->nice); + + chunk_appendf(&trash, "\n"); + + if (bi_putchk(si_ic(si), &trash) == -1) { + /* let's try again later from this stream. We add ourselves into + * this stream's users so that it can remove us upon termination. + */ + si_applet_cant_put(si); + LIST_ADDQ(&curr_strm->back_refs, &appctx->ctx.sess.bref.users); + return 0; + } + + next_sess: + appctx->ctx.sess.bref.ref = curr_strm->list.n; + } + + if (appctx->ctx.sess.target && appctx->ctx.sess.target != (void *)-1) { + /* specified stream not found */ + if (appctx->ctx.sess.section > 0) + chunk_appendf(&trash, " *** session terminated while we were watching it ***\n"); + else + chunk_appendf(&trash, "Session not found.\n"); + + if (bi_putchk(si_ic(si), &trash) == -1) { + si_applet_cant_put(si); + return 0; + } + + appctx->ctx.sess.target = NULL; + appctx->ctx.sess.uid = 0; + return 1; + } + + appctx->st2 = STAT_ST_FIN; + /* fall through */ + + default: + appctx->st2 = STAT_ST_FIN; + return 1; + } +} + +static void cli_release_show_sess(struct appctx *appctx) +{ + if (appctx->st2 == STAT_ST_LIST) { + if (!LIST_ISEMPTY(&appctx->ctx.sess.bref.users)) + LIST_DEL(&appctx->ctx.sess.bref.users); + } +} + +/* register cli keywords */ +static struct cli_kw_list cli_kws = {{ },{ + { { "show", "sess", NULL }, "show sess [id] : report the list of current sessions or dump this session", cli_parse_show_sess, cli_io_handler_dump_sess, cli_release_show_sess }, + {{},} +}}; + /* main configuration keyword registration. */ static struct action_kw_list stream_tcp_keywords = { ILH, { { "use-service", stream_parse_use_service }, @@ -3483,6 +4053,7 @@ static void __stream_init(void) acl_register_keywords(&acl_kws); tcp_req_cont_keywords_register(&stream_tcp_keywords); http_req_keywords_register(&stream_http_keywords); + cli_register_kw(&cli_kws); } /*