From b9f96d950808da565691bf69464fd53e329808a9 Mon Sep 17 00:00:00 2001 From: Andrew Wood Date: Fri, 28 Jul 2023 23:14:14 +0100 Subject: [PATCH] Provide new option "--debug" instead of using insecure environment variable DEBUG. --- src/include/pv.h | 7 +- src/main/debug.c | 64 +++++++++++++---- src/main/help.c | 174 ++++++++++++++++++++++----------------------- src/main/options.c | 92 +++++++++++++----------- 4 files changed, 194 insertions(+), 143 deletions(-) diff --git a/src/include/pv.h b/src/include/pv.h index 104db1e..fc6f953 100644 --- a/src/include/pv.h +++ b/src/include/pv.h @@ -170,7 +170,7 @@ extern void pv_state_free(pvstate_t); #ifdef ENABLE_DEBUGGING -# if __STDC_VERSION__ < 199901L +# if __STDC_VERSION__ < 199901L && !defined(__func__) # if __GNUC__ >= 2 # define __func__ __FUNCTION__ # else @@ -182,6 +182,11 @@ extern void pv_state_free(pvstate_t); # define debug(x,...) do { } while (0) #endif +/* + * Set the debugging destination file, if debugging is enabled. + */ +void debugging_output_destination(const char *); + /* * Output debugging information, if debugging is enabled. */ diff --git a/src/main/debug.c b/src/main/debug.c index e0b28ac..1aaa3e5 100644 --- a/src/main/debug.c +++ b/src/main/debug.c @@ -18,57 +18,93 @@ #ifdef ENABLE_DEBUGGING +/*@null@*/ static const char *debug_filename = NULL; + /* - * Output debugging information to the file given in the DEBUG environment - * variable, if it is defined. + * Set the destination for debugging information. + */ +void debugging_output_destination(const char *filename) +{ + debug_filename = filename; +} + + +/* + * Output debugging information to the file specified earlier by a call to + * debugging_output_destination(), if any. */ void debugging_output(const char *function, const char *file, int line, const char *format, ...) { static bool tried_open = false; static FILE *debugfptr = NULL; - char *debugfile; va_list ap; time_t t; struct tm *tm; - char tbuf[128]; + char tbuf[128]; /* flawfinder: ignore */ + + /* + * flawfinder note: tbuf is only written to by strftime() which + * takes its size, and we enforce string termination. + */ if (false == tried_open) { - debugfile = getenv("DEBUG"); - if (NULL != debugfile) - debugfptr = fopen(debugfile, "a"); + if (NULL != debug_filename) { + debugfptr = fopen(debug_filename, "a"); /* flawfinder: ignore */ + /* + * flawfinder note: caller directly controls + * filename, the safest we can manage is to use + * append mode. + */ + } tried_open = true; } - if (NULL == debugfptr) + if (NULL == debugfptr) { return; + } (void) time(&t); tm = localtime(&t); tbuf[0] = '\0'; - if (0 == strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm)) + if (0 == strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M:%S", tm)) { tbuf[0] = '\0'; + } + tbuf[sizeof(tbuf) - 1] = '\0'; /* enforce termination */ (void) fprintf(debugfptr, "[%s] (%d) %s (%s:%d): ", tbuf, getpid(), function, file, line); va_start(ap, format); - (void) vfprintf(debugfptr, format, ap); + (void) vfprintf(debugfptr, format, ap); /* flawfinder: ignore */ va_end(ap); + /* + * flawfinder note: vfprintf format is explicitly controlled by the + * caller of this function - no mitigation possible or desirable. + */ + (void) fprintf(debugfptr, "\n"); (void) fflush(debugfptr); } #else /* ! ENABLE_DEBUGGING */ +/* + * Stub debugging destination function. + */ +void debugging_output_destination( __attribute__((unused)) + const char *filename) +{ +} + /* * Stub debugging output function. */ -void debugging_output( __attribute__ ((unused)) - const char *function, __attribute__ ((unused)) - const char *file, __attribute__ ((unused)) - int line, __attribute__ ((unused)) +void debugging_output( __attribute__((unused)) + const char *function, __attribute__((unused)) + const char *file, __attribute__((unused)) + int line, __attribute__((unused)) const char *format, ...) { } diff --git a/src/main/help.c b/src/main/help.c index e10d935..5824d88 100644 --- a/src/main/help.c +++ b/src/main/help.c @@ -27,90 +27,95 @@ struct optdesc_s { void display_help(void) { struct optdesc_s optlist[] = { - {"-p", "--progress", 0, - N_("show progress bar")}, - {"-t", "--timer", 0, - N_("show elapsed time")}, - {"-e", "--eta", 0, - N_("show estimated time of arrival (completion)")}, - {"-I", "--fineta", 0, + { "-p", "--progress", 0, + N_("show progress bar") }, + { "-t", "--timer", 0, + N_("show elapsed time") }, + { "-e", "--eta", 0, + N_("show estimated time of arrival (completion)") }, + { "-I", "--fineta", 0, + N_("show absolute estimated time of arrival (completion)") + }, + { "-r", "--rate", 0, + N_("show data transfer rate counter") }, + { "-a", "--average-rate", 0, + N_("show data transfer average rate counter") }, + { "-m", "--average-rate-window", N_("SEC"), N_ - ("show absolute estimated time of arrival (completion)")}, - {"-r", "--rate", 0, - N_("show data transfer rate counter")}, - {"-a", "--average-rate", 0, - N_("show data transfer average rate counter")}, - {"-m", "--average-rate-window", N_("SEC"), - N_ - ("compute average rate over past SEC seconds (default 30s)")}, - {"-b", "--bytes", 0, - N_("show number of bytes transferred")}, - {"-8", "--bits", 0, - N_("show number of bits transferred")}, - {"-T", "--buffer-percent", 0, - N_("show percentage of transfer buffer in use")}, - {"-A", "--last-written", _("NUM"), - N_("show NUM bytes last written")}, - {"-F", "--format", N_("FORMAT"), - N_("set output format to FORMAT")}, - {"-n", "--numeric", 0, - N_("output percentages, not visual information")}, - {"-q", "--quiet", 0, - N_("do not output any transfer information at all")}, - {"", 0, 0, 0}, - {"-W", "--wait", 0, - N_("display nothing until first byte transferred")}, - {"-D", "--delay-start", N_("SEC"), - N_("display nothing until SEC seconds have passed")}, - {"-s", "--size", N_("SIZE"), - N_("set estimated data size to SIZE bytes")}, - {"-l", "--line-mode", 0, - N_("count lines instead of bytes")}, - {"-0", "--null", 0, - N_("lines are null-terminated")}, - {"-i", "--interval", N_("SEC"), - N_("update every SEC seconds")}, - {"-w", "--width", N_("WIDTH"), - N_("assume terminal is WIDTH characters wide")}, - {"-H", "--height", N_("HEIGHT"), - N_("assume terminal is HEIGHT rows high")}, - {"-N", "--name", N_("NAME"), - N_("prefix visual information with NAME")}, - {"-f", "--force", 0, - N_("output even if standard error is not a terminal")}, - {"-c", "--cursor", 0, - N_("use cursor positioning escape sequences")}, - {"", 0, 0, 0}, - {"-L", "--rate-limit", N_("RATE"), - N_("limit transfer to RATE bytes per second")}, - {"-B", "--buffer-size", N_("BYTES"), - N_("use a buffer size of BYTES")}, - {"-C", "--no-splice", 0, - N_("never use splice(), always use read/write")}, - {"-E", "--skip-errors", 0, - N_("skip read errors in input")}, - {"-S", "--stop-at-size", 0, - N_("stop after --size bytes have been transferred")}, - {"-Y", "--sync", 0, - N_("flush cache to disk after every write")}, - {"-K", "--direct-io", 0, - N_("use direct I/O to bypass cache")}, + ("compute average rate over past SEC seconds (default 30s)") + }, + { "-b", "--bytes", 0, + N_("show number of bytes transferred") }, + { "-8", "--bits", 0, + N_("show number of bits transferred") }, + { "-T", "--buffer-percent", 0, + N_("show percentage of transfer buffer in use") }, + { "-A", "--last-written", _("NUM"), + N_("show NUM bytes last written") }, + { "-F", "--format", N_("FORMAT"), + N_("set output format to FORMAT") }, + { "-n", "--numeric", 0, + N_("output percentages, not visual information") }, + { "-q", "--quiet", 0, + N_("do not output any transfer information at all") }, + { "", 0, 0, 0 }, + { "-W", "--wait", 0, + N_("display nothing until first byte transferred") }, + { "-D", "--delay-start", N_("SEC"), + N_("display nothing until SEC seconds have passed") }, + { "-s", "--size", N_("SIZE"), + N_("set estimated data size to SIZE bytes") }, + { "-l", "--line-mode", 0, + N_("count lines instead of bytes") }, + { "-0", "--null", 0, + N_("lines are null-terminated") }, + { "-i", "--interval", N_("SEC"), + N_("update every SEC seconds") }, + { "-w", "--width", N_("WIDTH"), + N_("assume terminal is WIDTH characters wide") }, + { "-H", "--height", N_("HEIGHT"), + N_("assume terminal is HEIGHT rows high") }, + { "-N", "--name", N_("NAME"), + N_("prefix visual information with NAME") }, + { "-f", "--force", 0, + N_("output even if standard error is not a terminal") }, + { "-c", "--cursor", 0, + N_("use cursor positioning escape sequences") }, + { "", 0, 0, 0 }, + { "-L", "--rate-limit", N_("RATE"), + N_("limit transfer to RATE bytes per second") }, + { "-B", "--buffer-size", N_("BYTES"), + N_("use a buffer size of BYTES") }, + { "-C", "--no-splice", 0, + N_("never use splice(), always use read/write") }, + { "-E", "--skip-errors", 0, + N_("skip read errors in input") }, + { "-S", "--stop-at-size", 0, + N_("stop after --size bytes have been transferred") }, + { "-Y", "--sync", 0, + N_("flush cache to disk after every write") }, + { "-K", "--direct-io", 0, + N_("use direct I/O to bypass cache") }, #ifdef HAVE_IPC - {"-R", "--remote", N_("PID"), - N_("update settings of process PID")}, + { "-R", "--remote", N_("PID"), + N_("update settings of process PID") }, #endif /* HAVE_IPC */ - {"", 0, 0, 0}, - {"-P", "--pidfile", N_("FILE"), - N_("save process ID in FILE")}, - {"", 0, 0, 0}, - {"-d", "--watchfd", N_("PID[:FD]"), - N_("watch file FD opened by process PID")}, - {"", 0, 0, 0}, - {"-h", "--help", 0, - N_("show this help and exit")}, - {"-V", "--version", 0, - N_("show version information and exit")}, - {0, 0, 0, 0} + { "", 0, 0, 0 }, + { "-P", "--pidfile", N_("FILE"), + N_("save process ID in FILE") }, + { "", 0, 0, 0 }, + { "-d", "--watchfd", N_("PID[:FD]"), + N_("watch file FD opened by process PID") }, + { "", 0, 0, 0 }, + { "-h", "--help", 0, + N_("show this help and exit") }, + { "-V", "--version", 0, + N_("show version information and exit") }, +#ifdef ENABLE_DEBUGGING + { "-!", "--debug", N_("FILE"), + N_("write debug logs to FILE") }, +#endif + { 0, 0, 0, 0 } }; unsigned int i, col1max = 0, tw = 77; char *optbuf; @@ -208,13 +213,6 @@ void display_help(void) printf("%s\n", start); } -#ifdef ENABLE_DEBUGGING - printf("\n"); - printf("%s", - _ - ("Debugging is enabled; export the DEBUG environment variable to define the\noutput filename.\n")); -#endif - printf("\n"); printf(_("Please report any bugs to %s."), BUG_REPORTS_TO); printf("\n"); diff --git a/src/main/options.c b/src/main/options.c index 7dcf290..9adc644 100644 --- a/src/main/options.c +++ b/src/main/options.c @@ -51,49 +51,56 @@ opts_t opts_parse(int argc, char **argv) { #ifdef HAVE_GETOPT_LONG struct option long_options[] = { - {"help", 0, NULL, (int) 'h'}, - {"version", 0, NULL, (int) 'V'}, - {"progress", 0, NULL, (int) 'p'}, - {"timer", 0, NULL, (int) 't'}, - {"eta", 0, NULL, (int) 'e'}, - {"fineta", 0, NULL, (int) 'I'}, - {"rate", 0, NULL, (int) 'r'}, - {"average-rate", 0, NULL, (int) 'a'}, - {"bytes", 0, NULL, (int) 'b'}, - {"bits", 0, NULL, (int) '8'}, - {"buffer-percent", 0, NULL, (int) 'T'}, - {"last-written", 1, NULL, (int) 'A'}, - {"force", 0, NULL, (int) 'f'}, - {"numeric", 0, NULL, (int) 'n'}, - {"quiet", 0, NULL, (int) 'q'}, - {"cursor", 0, NULL, (int) 'c'}, - {"wait", 0, NULL, (int) 'W'}, - {"delay-start", 1, NULL, (int) 'D'}, - {"size", 1, NULL, (int) 's'}, - {"line-mode", 0, NULL, (int) 'l'}, - {"null", 0, NULL, (int) '0'}, - {"interval", 1, NULL, (int) 'i'}, - {"width", 1, NULL, (int) 'w'}, - {"height", 1, NULL, (int) 'H'}, - {"name", 1, NULL, (int) 'N'}, - {"format", 1, NULL, (int) 'F'}, - {"rate-limit", 1, NULL, (int) 'L'}, - {"buffer-size", 1, NULL, (int) 'B'}, - {"no-splice", 0, NULL, (int) 'C'}, - {"skip-errors", 0, NULL, (int) 'E'}, - {"stop-at-size", 0, NULL, (int) 'S'}, - {"sync", 0, NULL, (int) 'Y'}, - {"direct-io", 0, NULL, (int) 'K'}, - {"remote", 1, NULL, (int) 'R'}, - {"pidfile", 1, NULL, (int) 'P'}, - {"watchfd", 1, NULL, (int) 'd'}, - {"average-rate-window", 1, NULL, (int) 'm'}, - {NULL, 0, NULL, 0} + { "help", 0, NULL, (int) 'h' }, + { "version", 0, NULL, (int) 'V' }, + { "progress", 0, NULL, (int) 'p' }, + { "timer", 0, NULL, (int) 't' }, + { "eta", 0, NULL, (int) 'e' }, + { "fineta", 0, NULL, (int) 'I' }, + { "rate", 0, NULL, (int) 'r' }, + { "average-rate", 0, NULL, (int) 'a' }, + { "bytes", 0, NULL, (int) 'b' }, + { "bits", 0, NULL, (int) '8' }, + { "buffer-percent", 0, NULL, (int) 'T' }, + { "last-written", 1, NULL, (int) 'A' }, + { "force", 0, NULL, (int) 'f' }, + { "numeric", 0, NULL, (int) 'n' }, + { "quiet", 0, NULL, (int) 'q' }, + { "cursor", 0, NULL, (int) 'c' }, + { "wait", 0, NULL, (int) 'W' }, + { "delay-start", 1, NULL, (int) 'D' }, + { "size", 1, NULL, (int) 's' }, + { "line-mode", 0, NULL, (int) 'l' }, + { "null", 0, NULL, (int) '0' }, + { "interval", 1, NULL, (int) 'i' }, + { "width", 1, NULL, (int) 'w' }, + { "height", 1, NULL, (int) 'H' }, + { "name", 1, NULL, (int) 'N' }, + { "format", 1, NULL, (int) 'F' }, + { "rate-limit", 1, NULL, (int) 'L' }, + { "buffer-size", 1, NULL, (int) 'B' }, + { "no-splice", 0, NULL, (int) 'C' }, + { "skip-errors", 0, NULL, (int) 'E' }, + { "stop-at-size", 0, NULL, (int) 'S' }, + { "sync", 0, NULL, (int) 'Y' }, + { "direct-io", 0, NULL, (int) 'K' }, + { "remote", 1, NULL, (int) 'R' }, + { "pidfile", 1, NULL, (int) 'P' }, + { "watchfd", 1, NULL, (int) 'd' }, + { "average-rate-window", 1, NULL, (int) 'm' }, +#ifdef ENABLE_DEBUGGING + { "debug", 1, NULL, (int) '!' }, +#endif /* ENABLE_DEBUGGING */ + { NULL, 0, NULL, 0 } }; int option_index = 0; -#endif +#endif /* HAVE_GETOPT_LONG */ char *short_options = - "hVpteIrab8TA:fnqcWD:s:l0i:w:H:N:F:L:B:CESYKR:P:d:m:"; + "hVpteIrab8TA:fnqcWD:s:l0i:w:H:N:F:L:B:CESYKR:P:d:m:" +#ifdef ENABLE_DEBUGGING + "!:" +#endif + ; int c, numopts; unsigned int check_pid; int check_fd; @@ -360,6 +367,11 @@ opts_t opts_parse(int argc, char **argv) case 'm': opts->average_rate_window = pv_getnum_ui(optarg); break; +#ifdef ENABLE_DEBUGGING + case '!': + debugging_output_destination(optarg); + break; +#endif /* ENABLE_DEBUGGING */ default: #ifdef HAVE_GETOPT_LONG fprintf(stderr,