REORG: cli: move "show errors" out of cli.c
It really belongs to proto_http.c since it's a dump for HTTP request
and response errors. Note that it's possible that some parts do not
need to be exported anymore since it really is the only place where
errors are manipulated.
diff --git a/include/types/cli.h b/include/types/cli.h
index ebe4adb..0ebfc69 100644
--- a/include/types/cli.h
+++ b/include/types/cli.h
@@ -47,7 +47,6 @@
STAT_CLI_PROMPT, /* display the prompt (first output, same code) */
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_ERR, /* dump errors */
STAT_CLI_O_ENV, /* dump environment */
STAT_CLI_O_CUSTOM, /* custom callback pointer */
};
diff --git a/src/cli.c b/src/cli.c
index 39fe01f..b1d2e54 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -56,7 +56,6 @@
#include <proto/pipe.h>
#include <proto/listener.h>
#include <proto/map.h>
-#include <proto/proto_http.h>
#include <proto/proto_uxst.h>
#include <proto/proxy.h>
#include <proto/sample.h>
@@ -68,7 +67,6 @@
#include <proto/task.h>
static int stats_dump_env_to_buffer(struct stream_interface *si);
-static int stats_dump_errors_to_buffer(struct stream_interface *si);
static struct applet cli_applet;
@@ -79,7 +77,6 @@
" prompt : toggle interactive mode with prompt\n"
" quit : disconnect\n"
" show env [var] : dump environment variables known to the process\n"
- " show errors : report last request and response errors for each proxy\n"
" set timeout : change a timeout setting\n"
" set maxconn : change a maxconn setting\n"
" set rate-limit : change a rate limiting value\n"
@@ -577,20 +574,6 @@
appctx->st2 = STAT_ST_END;
}
}
- 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;
- appctx->st0 = STAT_CLI_PRINT;
- return 1;
- }
- if (*args[2])
- appctx->ctx.errors.iid = atoi(args[2]);
- else
- appctx->ctx.errors.iid = -1;
- appctx->ctx.errors.px = NULL;
- appctx->st2 = STAT_ST_INIT;
- appctx->st0 = STAT_CLI_O_ERR; // stats_dump_errors_to_buffer
- }
else { /* neither "stat" nor "info" nor "sess" nor "errors" nor "table" */
return 0;
}
@@ -1291,10 +1274,6 @@
else
si_applet_cant_put(si);
break;
- case STAT_CLI_O_ERR: /* errors dump */
- if (stats_dump_errors_to_buffer(si))
- appctx->st0 = STAT_CLI_PROMPT;
- break;
case STAT_CLI_O_ENV: /* environment dump */
if (stats_dump_env_to_buffer(si))
appctx->st0 = STAT_CLI_PROMPT;
@@ -1383,169 +1362,6 @@
free(appctx->ctx.cli.err);
appctx->ctx.cli.err = NULL;
}
-}
-
-/* This function dumps all captured errors 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.
- */
-static int stats_dump_errors_to_buffer(struct stream_interface *si)
-{
- struct appctx *appctx = __objt_appctx(si->end);
- extern const char *monthname[12];
-
- if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
- return 1;
-
- chunk_reset(&trash);
-
- if (!appctx->ctx.errors.px) {
- /* the function had not been called yet, let's prepare the
- * buffer for a response.
- */
- struct tm tm;
-
- get_localtime(date.tv_sec, &tm);
- chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
- tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
- tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
- error_snapshot_id);
-
- if (bi_putchk(si_ic(si), &trash) == -1) {
- /* Socket buffer full. Let's try again later from the same point */
- si_applet_cant_put(si);
- return 0;
- }
-
- appctx->ctx.errors.px = proxy;
- appctx->ctx.errors.buf = 0;
- appctx->ctx.errors.bol = 0;
- appctx->ctx.errors.ptr = -1;
- }
-
- /* we have two inner loops here, one for the proxy, the other one for
- * the buffer.
- */
- while (appctx->ctx.errors.px) {
- struct error_snapshot *es;
-
- if (appctx->ctx.errors.buf == 0)
- es = &appctx->ctx.errors.px->invalid_req;
- else
- es = &appctx->ctx.errors.px->invalid_rep;
-
- if (!es->when.tv_sec)
- goto next;
-
- if (appctx->ctx.errors.iid >= 0 &&
- appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
- es->oe->uuid != appctx->ctx.errors.iid)
- goto next;
-
- if (appctx->ctx.errors.ptr < 0) {
- /* just print headers now */
-
- char pn[INET6_ADDRSTRLEN];
- struct tm tm;
- int port;
-
- get_localtime(es->when.tv_sec, &tm);
- chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
- tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
- tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
-
- switch (addr_to_str(&es->src, pn, sizeof(pn))) {
- case AF_INET:
- case AF_INET6:
- port = get_host_port(&es->src);
- break;
- default:
- port = 0;
- }
-
- switch (appctx->ctx.errors.buf) {
- case 0:
- chunk_appendf(&trash,
- " frontend %s (#%d): invalid request\n"
- " backend %s (#%d)",
- appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
- (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
- (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
- break;
- case 1:
- chunk_appendf(&trash,
- " backend %s (#%d): invalid response\n"
- " frontend %s (#%d)",
- appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
- es->oe->id, es->oe->uuid);
- break;
- }
-
- chunk_appendf(&trash,
- ", server %s (#%d), event #%u\n"
- " src %s:%d, session #%d, session flags 0x%08x\n"
- " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
- " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
- " buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
- " pending %d bytes, wrapping at %d, error at position %d:\n \n",
- es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
- es->ev_id,
- pn, port, es->sid, es->s_flags,
- es->state, es->m_flags, es->t_flags,
- es->m_clen, es->m_blen,
- es->b_flags, es->b_out, es->b_tot,
- es->len, es->b_wrap, es->pos);
-
- if (bi_putchk(si_ic(si), &trash) == -1) {
- /* Socket buffer full. Let's try again later from the same point */
- si_applet_cant_put(si);
- return 0;
- }
- appctx->ctx.errors.ptr = 0;
- appctx->ctx.errors.sid = es->sid;
- }
-
- if (appctx->ctx.errors.sid != es->sid) {
- /* the snapshot changed while we were dumping it */
- chunk_appendf(&trash,
- " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
- if (bi_putchk(si_ic(si), &trash) == -1) {
- si_applet_cant_put(si);
- return 0;
- }
- goto next;
- }
-
- /* OK, ptr >= 0, so we have to dump the current line */
- while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
- int newptr;
- int newline;
-
- newline = appctx->ctx.errors.bol;
- newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
- if (newptr == appctx->ctx.errors.ptr)
- return 0;
-
- if (bi_putchk(si_ic(si), &trash) == -1) {
- /* Socket buffer full. Let's try again later from the same point */
- si_applet_cant_put(si);
- return 0;
- }
- appctx->ctx.errors.ptr = newptr;
- appctx->ctx.errors.bol = newline;
- };
- next:
- appctx->ctx.errors.bol = 0;
- appctx->ctx.errors.ptr = -1;
- appctx->ctx.errors.buf++;
- if (appctx->ctx.errors.buf > 1) {
- appctx->ctx.errors.buf = 0;
- appctx->ctx.errors.px = appctx->ctx.errors.px->next;
- }
- }
-
- /* dump complete */
- return 1;
}
/* This function dumps all environmnent variables to the buffer. It returns 0
diff --git a/src/proto_http.c b/src/proto_http.c
index 181dfc2..a792fcf 100644
--- a/src/proto_http.c
+++ b/src/proto_http.c
@@ -39,6 +39,7 @@
#include <common/version.h>
#include <types/capture.h>
+#include <types/cli.h>
#include <types/filters.h>
#include <types/global.h>
#include <types/stats.h>
@@ -50,6 +51,7 @@
#include <proto/backend.h>
#include <proto/channel.h>
#include <proto/checks.h>
+#include <proto/cli.h>
#include <proto/compression.h>
#include <proto/stats.h>
#include <proto/fd.h>
@@ -12823,6 +12825,196 @@
return action_lookup(&http_res_keywords.list, kw);
}
+
+/* "show errors" handler for the CLI. Returns 0 if wants to continue, 1 to stop
+ * now.
+ */
+static int cli_parse_show_errors(char **args, struct appctx *appctx, void *private)
+{
+ if (!cli_has_level(appctx, ACCESS_LVL_OPER))
+ return 1;
+
+ if (*args[2])
+ appctx->ctx.errors.iid = atoi(args[2]);
+ else
+ appctx->ctx.errors.iid = -1;
+ appctx->ctx.errors.px = NULL;
+ appctx->st2 = STAT_ST_INIT;
+ return 0;
+}
+
+/* This function dumps all captured errors 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.
+ */
+static int cli_io_handler_show_errors(struct appctx *appctx)
+{
+ struct stream_interface *si = appctx->owner;
+ extern const char *monthname[12];
+
+ if (unlikely(si_ic(si)->flags & (CF_WRITE_ERROR|CF_SHUTW)))
+ return 1;
+
+ chunk_reset(&trash);
+
+ if (!appctx->ctx.errors.px) {
+ /* the function had not been called yet, let's prepare the
+ * buffer for a response.
+ */
+ struct tm tm;
+
+ get_localtime(date.tv_sec, &tm);
+ chunk_appendf(&trash, "Total events captured on [%02d/%s/%04d:%02d:%02d:%02d.%03d] : %u\n",
+ tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(date.tv_usec/1000),
+ error_snapshot_id);
+
+ if (bi_putchk(si_ic(si), &trash) == -1) {
+ /* Socket buffer full. Let's try again later from the same point */
+ si_applet_cant_put(si);
+ return 0;
+ }
+
+ appctx->ctx.errors.px = proxy;
+ appctx->ctx.errors.buf = 0;
+ appctx->ctx.errors.bol = 0;
+ appctx->ctx.errors.ptr = -1;
+ }
+
+ /* we have two inner loops here, one for the proxy, the other one for
+ * the buffer.
+ */
+ while (appctx->ctx.errors.px) {
+ struct error_snapshot *es;
+
+ if (appctx->ctx.errors.buf == 0)
+ es = &appctx->ctx.errors.px->invalid_req;
+ else
+ es = &appctx->ctx.errors.px->invalid_rep;
+
+ if (!es->when.tv_sec)
+ goto next;
+
+ if (appctx->ctx.errors.iid >= 0 &&
+ appctx->ctx.errors.px->uuid != appctx->ctx.errors.iid &&
+ es->oe->uuid != appctx->ctx.errors.iid)
+ goto next;
+
+ if (appctx->ctx.errors.ptr < 0) {
+ /* just print headers now */
+
+ char pn[INET6_ADDRSTRLEN];
+ struct tm tm;
+ int port;
+
+ get_localtime(es->when.tv_sec, &tm);
+ chunk_appendf(&trash, " \n[%02d/%s/%04d:%02d:%02d:%02d.%03d]",
+ tm.tm_mday, monthname[tm.tm_mon], tm.tm_year+1900,
+ tm.tm_hour, tm.tm_min, tm.tm_sec, (int)(es->when.tv_usec/1000));
+
+ switch (addr_to_str(&es->src, pn, sizeof(pn))) {
+ case AF_INET:
+ case AF_INET6:
+ port = get_host_port(&es->src);
+ break;
+ default:
+ port = 0;
+ }
+
+ switch (appctx->ctx.errors.buf) {
+ case 0:
+ chunk_appendf(&trash,
+ " frontend %s (#%d): invalid request\n"
+ " backend %s (#%d)",
+ appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
+ (es->oe->cap & PR_CAP_BE) ? es->oe->id : "<NONE>",
+ (es->oe->cap & PR_CAP_BE) ? es->oe->uuid : -1);
+ break;
+ case 1:
+ chunk_appendf(&trash,
+ " backend %s (#%d): invalid response\n"
+ " frontend %s (#%d)",
+ appctx->ctx.errors.px->id, appctx->ctx.errors.px->uuid,
+ es->oe->id, es->oe->uuid);
+ break;
+ }
+
+ chunk_appendf(&trash,
+ ", server %s (#%d), event #%u\n"
+ " src %s:%d, session #%d, session flags 0x%08x\n"
+ " HTTP msg state %d, msg flags 0x%08x, tx flags 0x%08x\n"
+ " HTTP chunk len %lld bytes, HTTP body len %lld bytes\n"
+ " buffer flags 0x%08x, out %d bytes, total %lld bytes\n"
+ " pending %d bytes, wrapping at %d, error at position %d:\n \n",
+ es->srv ? es->srv->id : "<NONE>", es->srv ? es->srv->puid : -1,
+ es->ev_id,
+ pn, port, es->sid, es->s_flags,
+ es->state, es->m_flags, es->t_flags,
+ es->m_clen, es->m_blen,
+ es->b_flags, es->b_out, es->b_tot,
+ es->len, es->b_wrap, es->pos);
+
+ if (bi_putchk(si_ic(si), &trash) == -1) {
+ /* Socket buffer full. Let's try again later from the same point */
+ si_applet_cant_put(si);
+ return 0;
+ }
+ appctx->ctx.errors.ptr = 0;
+ appctx->ctx.errors.sid = es->sid;
+ }
+
+ if (appctx->ctx.errors.sid != es->sid) {
+ /* the snapshot changed while we were dumping it */
+ chunk_appendf(&trash,
+ " WARNING! update detected on this snapshot, dump interrupted. Please re-check!\n");
+ if (bi_putchk(si_ic(si), &trash) == -1) {
+ si_applet_cant_put(si);
+ return 0;
+ }
+ goto next;
+ }
+
+ /* OK, ptr >= 0, so we have to dump the current line */
+ while (es->buf && appctx->ctx.errors.ptr < es->len && appctx->ctx.errors.ptr < global.tune.bufsize) {
+ int newptr;
+ int newline;
+
+ newline = appctx->ctx.errors.bol;
+ newptr = dump_text_line(&trash, es->buf, global.tune.bufsize, es->len, &newline, appctx->ctx.errors.ptr);
+ if (newptr == appctx->ctx.errors.ptr)
+ return 0;
+
+ if (bi_putchk(si_ic(si), &trash) == -1) {
+ /* Socket buffer full. Let's try again later from the same point */
+ si_applet_cant_put(si);
+ return 0;
+ }
+ appctx->ctx.errors.ptr = newptr;
+ appctx->ctx.errors.bol = newline;
+ };
+ next:
+ appctx->ctx.errors.bol = 0;
+ appctx->ctx.errors.ptr = -1;
+ appctx->ctx.errors.buf++;
+ if (appctx->ctx.errors.buf > 1) {
+ appctx->ctx.errors.buf = 0;
+ appctx->ctx.errors.px = appctx->ctx.errors.px->next;
+ }
+ }
+
+ /* dump complete */
+ return 1;
+}
+
+/* register cli keywords */
+static struct cli_kw_list cli_kws = {{ },{
+ { { "show", "errors", NULL },
+ "show errors : report last request and response errors for each proxy",
+ cli_parse_show_errors, cli_io_handler_show_errors, NULL,
+ },
+ {{},}
+}};
+
/************************************************************************/
/* All supported ACL keywords must be declared here. */
/************************************************************************/
@@ -13076,6 +13268,7 @@
sample_register_convs(&sample_conv_kws);
http_req_keywords_register(&http_req_actions);
http_res_keywords_register(&http_res_actions);
+ cli_register_kw(&cli_kws);
}