diff options
-rw-r--r-- | ChangeLog | 20 | ||||
-rw-r--r-- | awk.h | 12 | ||||
-rw-r--r-- | builtin.c | 21 | ||||
-rw-r--r-- | io.c | 69 | ||||
-rw-r--r-- | main.c | 5 |
5 files changed, 89 insertions, 38 deletions
@@ -1,3 +1,23 @@ +2017-03-20 Arnold D. Robbins <arnold@skeeve.com> + + Improve handling of EPIPE. Problems reported by + Alexandre Ferrieux <alexandre.ferrieux@orange.com> + and David Kerns <david.t.kerns@gmail.com>. + + * awk.h (ignore_sigpipe, set_sigpipe_to_default, + non_fatal_flush_std): Declare new functions. + (ignore_sigpipe, set_sigpipe_to_default, + non_fatal_flush_std): New macros. + * builtin.c (do_fflush): When nonfatal not in force, flush + of stdout/stderr and EPIPE exits, simulating SIGPIPE, as + in nawk/mawk. Flush of other redirections with EPIPE now + also fatals. + (do_system): Use ignore_sipipe and set_sigpipe_to_default + instead of uglier inline ifdefed code. + * main.c (main): Ditto. + * io.c (redirect_string, two_way_open, gawk_popen): Ditto. + (flush_io): Use non_fatal_flush_std for stdout and stderr. + 2017-03-16 Arnold D. Robbins <arnold@skeeve.com> * configure.ac: Some cleanups. @@ -1584,6 +1584,10 @@ extern bool inrec(IOBUF *iop, int *errcode); extern int nextfile(IOBUF **curfile, bool skipping); extern bool is_non_fatal_std(FILE *fp); extern bool is_non_fatal_redirect(const char *str, size_t len); +extern void ignore_sigpipe(void); +extern void set_sigpipe_to_default(void); +extern bool non_fatal_flush_std_file(FILE *fp); + /* main.c */ extern int arg_assign(char *arg, bool initing); extern int is_std_var(const char *var); @@ -1958,3 +1962,11 @@ erealloc_real(void *ptr, size_t count, const char *where, const char *var, const return ret; } + +#ifdef SIGPIPE +#define ignore_sigpipe() signal(SIGPIPE, SIG_IGN) +#define set_sigpipe_to_default() signal(SIGPIPE, SIG_DFL) +#else +#define ignore_sigpipe() +#define set_sigpipe_to_default() +#endif @@ -133,7 +133,6 @@ wrerror: if (fp == stdout && errno == EPIPE) gawk_exit(EXIT_FATAL); - /* otherwise die verbosely */ if ((rp != NULL) ? is_non_fatal_redirect(rp->value, strlen(rp->value)) : is_non_fatal_std(fp)) update_ERRNO_int(errno); @@ -245,13 +244,19 @@ do_fflush(int nargs) return make_number((AWKNUM) status); } fp = rp->output.fp; - if (fp != NULL) + if (fp != NULL) { status = rp->output.gawk_fflush(fp, rp->output.opaque); - else if ((rp->flag & RED_TWOWAY) != 0) + + if (status != 0) { + if (! is_non_fatal_redirect(tmp->stptr, tmp->stlen)) + fatal(_("fflush: cannot flush file `%.*s': %s"), + len, file, strerror(errno)); + } + } else if ((rp->flag & RED_TWOWAY) != 0) warning(_("fflush: cannot flush: two-way pipe `%.*s' has closed write end"), len, file); } else if ((fp = stdfile(tmp->stptr, tmp->stlen)) != NULL) { - status = fflush(fp); + status = (non_fatal_flush_std_file(fp) == false); } else { status = -1; warning(_("fflush: `%.*s' is not an open file, pipe or co-process"), len, file); @@ -2148,9 +2153,7 @@ do_system(int nargs) cmd[tmp->stlen] = '\0'; os_restore_mode(fileno(stdin)); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_DFL); -#endif + set_sigpipe_to_default(); status = system(cmd); /* @@ -2176,9 +2179,7 @@ do_system(int nargs) if ((BINMODE & BINMODE_INPUT) != 0) os_setbinmode(fileno(stdin), O_BINARY); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif + ignore_sigpipe(); cmd[tmp->stlen] = save; } @@ -899,9 +899,7 @@ redirect_string(const char *str, size_t explen, bool not_string, (void) flush_io(); os_restore_mode(fileno(stdin)); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_DFL); -#endif + set_sigpipe_to_default(); /* * Don't check failure_fatal; see input pipe below. * Note that the failure happens upon failure to fork, @@ -911,9 +909,7 @@ redirect_string(const char *str, size_t explen, bool not_string, if ((rp->output.fp = popen(str, binmode("w"))) == NULL) fatal(_("can't open pipe `%s' for output (%s)"), str, strerror(errno)); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif + ignore_sigpipe(); /* set close-on-exec */ os_close_on_exec(fileno(rp->output.fp), str, "pipe", "to"); @@ -1392,6 +1388,36 @@ checkwarn: return status; } +/* non_fatal_flush_std_file --- flush a standard output file allowing for nonfatal setting */ + +bool +non_fatal_flush_std_file(FILE *fp) +{ + int status = fflush(fp); + + if (status != 0) { + bool is_fatal = ! is_non_fatal_std(fp); + + if (is_fatal) { + if (errno == EPIPE) + exit(EXIT_SUCCESS); // simulate SIGPIPE + else + fatal(fp == stdout + ? _("fflush: cannot flush standard output: %s") + : _("fflush: cannot flush standard error: %s"), + strerror(errno)); + } else { + warning(fp == stdout + ? _("error writing standard output (%s)") + : _("error writing standard error (%s)"), + strerror(errno)); + } + return false; + } + + return true; +} + /* flush_io --- flush all open output files */ int @@ -1401,21 +1427,18 @@ flush_io() int status = 0; errno = 0; - /* we don't warn about stdout/stderr if EPIPE, but we do error exit */ - if (fflush(stdout)) { - if (errno != EPIPE) - warning(_("error writing standard output (%s)"), strerror(errno)); + if (! non_fatal_flush_std_file(stdout)) status++; - } - if (fflush(stderr)) { - if (errno != EPIPE) - warning(_("error writing standard error (%s)"), strerror(errno)); + + errno = 0; + if (! non_fatal_flush_std_file(stderr)) status++; - } + + // now for all open redirections for (rp = red_head; rp != NULL; rp = rp->next) { /* flush both files and pipes, what the heck */ if ((rp->flag & RED_WRITE) != 0 && rp->output.fp != NULL) { - if (rp->output.gawk_fflush(rp->output.fp, rp->output.opaque)) { + if (rp->output.gawk_fflush(rp->output.fp, rp->output.opaque) != 0) { if ((rp->flag & RED_PIPE) != 0) warning(_("pipe flush of `%s' failed (%s)."), rp->value, strerror(errno)); @@ -2076,7 +2099,7 @@ two_way_open(const char *str, struct redirect *rp, int extfd) /* stderr does NOT get dup'ed onto child's stdout */ - signal(SIGPIPE, SIG_DFL); + set_sigpipe_to_default(); execl("/bin/sh", "sh", "-c", str, NULL); _exit(errno == ENOENT ? 127 : 126); @@ -2253,7 +2276,7 @@ use_pipes: || close(ctop[0]) == -1 || close(ctop[1]) == -1) fatal(_("close of pipe failed (%s)"), strerror(errno)); /* stderr does NOT get dup'ed onto child's stdout */ - signal(SIGPIPE, SIG_DFL); + set_sigpipe_to_default(); execl("/bin/sh", "sh", "-c", str, NULL); _exit(errno == ENOENT ? 127 : 126); } @@ -2490,7 +2513,7 @@ gawk_popen(const char *cmd, struct redirect *rp) fatal(_("moving pipe to stdout in child failed (dup: %s)"), strerror(errno)); if (close(p[0]) == -1 || close(p[1]) == -1) fatal(_("close of pipe failed (%s)"), strerror(errno)); - signal(SIGPIPE, SIG_DFL); + set_sigpipe_to_default(); execl("/bin/sh", "sh", "-c", cmd, NULL); _exit(errno == ENOENT ? 127 : 126); } @@ -2555,17 +2578,13 @@ gawk_popen(const char *cmd, struct redirect *rp) FILE *current; os_restore_mode(fileno(stdin)); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_DFL); -#endif + set_sigpipe_to_default(); current = popen(cmd, binmode("r")); if ((BINMODE & BINMODE_INPUT) != 0) os_setbinmode(fileno(stdin), O_BINARY); -#ifdef SIGPIPE - signal(SIGPIPE, SIG_IGN); -#endif + ignore_sigpipe(); if (current == NULL) return NULL; @@ -255,7 +255,7 @@ main(int argc, char **argv) #ifdef SIGBUS (void) signal(SIGBUS, catchsig); #endif -#ifdef SIGPIPE + /* * Ignore SIGPIPE so that writes to pipes that fail don't * kill the process but instead return -1 and set errno. @@ -269,8 +269,7 @@ main(int argc, char **argv) * should not give us "broken pipe" messages --- mainly because * it did not do so in the past and people would complain. */ - signal(SIGPIPE, SIG_IGN); -#endif + ignore_sigpipe(); (void) sigsegv_install_handler(catchsegv); #define STACK_SIZE (16*1024) |