bug-ed
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

ed doesn't handle SIGPIPE


From: Andrew L. Moore
Subject: ed doesn't handle SIGPIPE
Date: Thu, 2 Jan 2025 04:53:49 -0500
User-agent: Mozilla Thunderbird Beta

Hi Antonio,

ed doesn't handle SIGPIPE and so ungracefully exits upon receiving it.
To illustrate, write a file larger than the pipe buffer (64K on Linux)
to a shell command that terminates before writing can be completed. The
following script demonstrates the issue. Note that if the script is
run interactively, the size of the file that triggers SIGPIPE might be
greater than 64K + 1, i.e., there may be additional buffering.

==== CUT HERE ====
#!/usr/bin/env bash
#
buf_size=$(
    {
        timeout -s2 1 dd if=<(yes) bs=1  |
            sleep 1.1
    } 2>&1 |
        sed -En 's/^([0-9]+).*out$/\1/p'
        )

fallocate -l "$buf_size" buf_size.tmp
fallocate -l $(( ++buf_size )) buf_size_over.tmp

ed buf_size.tmp <<EOF
w !exit
!echo This is reached!
EOF

ed buf_size_over.tmp <<EOF
w !exit
!echo Never reached!
EOF

rm -f buf_size{,_over}.tmp
==== CUT HERE ====

A simple fix is to just ignore SIGPIPE, although I also record the cause
of the interrupted system call. A patch follows.  While you're at it,
you might want to revisit your file I/O.  For example,

$ /bin/ed -p '* '
* ! exit 1
!
*

does not generate an ed error (as it shouldn't), but

$ /bin/ed -p '* '
* r ! exit 1
! exit 1: Success
?
* h
Cannot close input file
*

That's not right.

-AM

diff --git a/src/io.c b/src/io.c
index 36b2dd9..35b9b65 100644
--- a/src/io.c
+++ b/src/io.c
@@ -540,6 +548,11 @@ get_stream_line (FILE *fp, size_t *len, ed_buffer_t *ed)
         clearerr (fp);
         errno = 0;
         goto top;
+      case EPIPE:
+        ed->exec->err = _("Broken pipe");
+
+        /* Propagate stream status - don't call clearerr(3). */
+        return NULL;
       default:
 #ifdef F_GETPATH

@@ -765,6 +786,11 @@ put_stream_line (FILE *fp, const char *s, size_t len, ed_buffer_t *ed)
         clearerr (fp);
         errno = 0;
         goto top;
+      case EPIPE:
+        ed->exec->err = _("Broken pipe");
+
+        /* Propagate stream status - don't call clearerr(3). */
+        return ERR;
       default:
         fprintf (stderr, "%s\n", strerror (errno));
         ed->exec->err = _("File write error");
diff --git a/src/signal.c b/src/signal.c
index 6929962..6b5f39a 100644
--- a/src/signal.c
+++ b/src/signal.c
@@ -126,6 +141,7 @@ init_signal_handler (ed_buffer_t *ed)
   if (reliable_signal (SIGCHLD, SIG_DFL) == SIG_ERR
       || reliable_signal (SIGHUP, signal_handler) == SIG_ERR
       || reliable_signal (SIGINT, signal_handler) == SIG_ERR
+      || reliable_signal (SIGPIPE, SIG_IGN) == SIG_ERR
 #ifdef SIGWINCH
       || (isatty (0) && reliable_signal (SIGWINCH, signal_handler) == SIG_ERR)
#endif




reply via email to

[Prev in Thread] Current Thread [Next in Thread]