From ac6dd7536f379fbb50b19175696d9ee6d5f2f0ad Mon Sep 17 00:00:00 2001 From: Sami Kerola Date: Mon, 29 Aug 2016 19:17:32 +0100 Subject: [PATCH] reliability: check writing to stdout and stderr was successful This is expected behaviour. $ mtr --help > /dev/full; echo $? ./mtr: write error: No space left on device 1 Earlier write errors returned successful zero. Reference: https://www.irill.org/events/ghm-gnu-hackers-meeting/videos/jim-meyering-goodbye-world-the-perils-of-relying-on-output-streams-in-c --- configure.ac | 2 ++ mtr.c | 3 +++ utils.c | 38 ++++++++++++++++++++++++++++++++++++++ utils.h | 2 ++ 4 files changed, 45 insertions(+) diff --git a/configure.ac b/configure.ac index d2e88bc..0926bbc 100644 --- a/configure.ac +++ b/configure.ac @@ -46,6 +46,7 @@ AC_CHECK_HEADERS([ \ ncurses/curses.h \ socket.h \ sys/socket.h \ + stdio_ext.h \ sys/types.h \ sys/xti.h \ values.h \ @@ -53,6 +54,7 @@ AC_CHECK_HEADERS([ \ # Check functions. AC_CHECK_FUNCS([ \ + __fpending \ fcntl \ ]) diff --git a/mtr.c b/mtr.c index 14502e3..e28b0f2 100644 --- a/mtr.c +++ b/mtr.c @@ -655,6 +655,9 @@ extern int main(int argc, char **argv) error(EXIT_FAILURE, errno, "Unable to drop permissions"); } + /* This will check if stdout/stderr writing is successful */ + atexit(close_stdout); + /* reset the random seed */ srand (getpid()); diff --git a/utils.c b/utils.c index f6c4326..0a98e6c 100644 --- a/utils.c +++ b/utils.c @@ -16,12 +16,16 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +#include "config.h" + #include #include #include #include +#include #include #include +#include #ifdef HAVE_ERROR_H # include @@ -29,6 +33,10 @@ # include "portability/error.h" #endif +#ifdef HAVE_STDIO_EXT_H +# include +#endif + #include "utils.h" extern char *trim(char *s) @@ -109,3 +117,33 @@ extern char *xstrdup(const char *str) error(EXIT_FAILURE, errno, "cannot duplicate string: %s", str); return ret; } + +#ifndef HAVE___FPENDING +static inline int __fpending(FILE *stream __attribute__((__unused__))) +{ + return 0; +} +#endif +static inline int close_stream(FILE *stream) +{ + const int some_pending = (__fpending(stream) != 0); + const int prev_fail = (ferror(stream) != 0); + const int fclose_fail = (fclose(stream) != 0); + + if (prev_fail || (fclose_fail && (some_pending || errno != EBADF))) { + if (!fclose_fail && !(errno == EPIPE)) + errno = 0; + return EOF; + } + return 0; +} +/* Meant to be used atexit(close_stdout); */ +extern void close_stdout(void) +{ + if (close_stream(stdout) != 0 && !(errno == EPIPE)) { + error(0, errno, "write error"); + _exit(EXIT_FAILURE); + } + if (close_stream(stderr) != 0) + _exit(EXIT_FAILURE); +} diff --git a/utils.h b/utils.h index 5f9609d..437bca0 100644 --- a/utils.h +++ b/utils.h @@ -35,3 +35,5 @@ static inline void xstrncpy(char *dest, const char *src, size_t n) extern void *xmalloc(const size_t size); extern char *xstrdup(const char *str); + +extern void close_stdout(void); -- 2.47.2