From 0d79c9bedfa564e3c032c1e910c29949f5133d91 Mon Sep 17 00:00:00 2001 From: Valentine Krasnobaeva Date: Wed, 29 May 2024 11:27:21 +0200 Subject: [PATCH] MINOR: cli/debug: show dev: add cmdline and version 'show dev' command is very convenient to obtain haproxy debugging information, while process is run in container. Let's extend its output with version and cmdline. cmdline is useful in a way, as it shows absolute binary path and its arguments, because sometimes the person, who is debugging failing container is not the same, who has created and deployed it. argc and argv are stored in the exported global structure, because feed_post_mortem() is added as a post check function callback in the post_check_list. So we can't simply change the signature of feed_post_mortem(), without breaking other post check callbacks APIs. Parsers are not supposed to modify argv, so we can safely bypass its pointer to debug_parse_cli_show_dev(), without copying all argument stings somewhere in the heap or on stack. --- include/haproxy/global-t.h | 7 ++++++- src/debug.c | 13 +++++++++++++ src/haproxy.c | 5 +++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/include/haproxy/global-t.h b/include/haproxy/global-t.h index 7665ef24d..6e66a1d81 100644 --- a/include/haproxy/global-t.h +++ b/include/haproxy/global-t.h @@ -217,10 +217,15 @@ struct global { int thread_limit; /* hard limit on the number of threads */ int prealloc_fd; uchar clt_privileged_ports; /* bitmask to allow client privileged ports exchanges per protocol */ - /* 3-bytes hole */ + unsigned char argc; /* cast int argc to unsigned char in order to fill better the previous + * 3 bytes hole, it seems unreal, that oneday we could start with more + * than 255 arguments + */ + /* 2-bytes hole */ int cfg_curr_line; /* line number currently being parsed */ const char *cfg_curr_file; /* config file currently being parsed or NULL */ char *cfg_curr_section; /* config section name currently being parsed or NULL */ + char **argv; /* ptr to array with args */ /* The info above is config stuff, it doesn't change during the process' life */ /* A number of the elements below are updated by all threads in real time and diff --git a/src/debug.c b/src/debug.c index 5f21f0271..a7626b18f 100644 --- a/src/debug.c +++ b/src/debug.c @@ -47,6 +47,7 @@ #include #include #include +#include #include @@ -114,6 +115,7 @@ struct post_mortem { gid_t boot_gid; struct rlimit limit_fd; // RLIMIT_NOFILE struct rlimit limit_ram; // RLIMIT_DATA + char **argv; #if defined(USE_THREAD) struct { @@ -121,6 +123,7 @@ struct post_mortem { void *stack_top; // top of the stack } thread_info[MAX_THREADS]; #endif + unsigned char argc; } process; #if defined(HA_HAVE_DUMP_LIBS) @@ -498,12 +501,14 @@ static int debug_parse_cli_show_libs(char **args, char *payload, struct appctx * static int debug_parse_cli_show_dev(char **args, char *payload, struct appctx *appctx, void *private) { const char **build_opt; + int i; if (*args[2]) return cli_err(appctx, "This command takes no argument.\n"); chunk_reset(&trash); + chunk_appendf(&trash, "HAProxy version %s\n", haproxy_version); chunk_appendf(&trash, "Features\n %s\n", build_features); chunk_appendf(&trash, "Build options\n"); @@ -545,8 +550,14 @@ static int debug_parse_cli_show_dev(char **args, char *payload, struct appctx *a chunk_appendf(&trash, "Process info\n"); chunk_appendf(&trash, " pid: %d\n", post_mortem.process.pid); + chunk_appendf(&trash, " cmdline: "); + for (i = 0; i < post_mortem.process.argc; i++) + chunk_appendf(&trash, "%s ", post_mortem.process.argv[i]); + chunk_appendf(&trash, "\n"); chunk_appendf(&trash, " boot uid: %d\n", post_mortem.process.boot_uid); + chunk_appendf(&trash, " runtime uid: %d\n", geteuid()); chunk_appendf(&trash, " boot gid: %d\n", post_mortem.process.boot_gid); + chunk_appendf(&trash, " runtime gid: %d\n", getegid()); if ((ulong)post_mortem.process.limit_fd.rlim_cur != RLIM_INFINITY) chunk_appendf(&trash, " fd limit (soft): %lu\n", (ulong)post_mortem.process.limit_fd.rlim_cur); @@ -2271,6 +2282,8 @@ static int feed_post_mortem() post_mortem.process.pid = getpid(); post_mortem.process.boot_uid = geteuid(); post_mortem.process.boot_gid = getegid(); + post_mortem.process.argc = global.argc; + post_mortem.process.argv = global.argv; getrlimit(RLIMIT_NOFILE, &post_mortem.process.limit_fd); getrlimit(RLIMIT_DATA, &post_mortem.process.limit_ram); diff --git a/src/haproxy.c b/src/haproxy.c index c987fdbfa..afccf0912 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -1557,6 +1557,11 @@ static void init_early(int argc, char **argv) totalconn = actconn = listeners = stopping = 0; killed = pid = 0; + /* cast to one byte in order to fill better a 3 bytes hole in the global struct, + * we hopefully will never start with > than 255 args + */ + global.argc = (unsigned char)argc; + global.argv = argv; global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */ global.rlimit_memmax_all = HAPROXY_MEMMAX; global.mode = MODE_STARTING;