[GH-ISSUE #369] “Broken pipe” errors when piping inside asciinema #848

Closed
opened 2026-03-15 10:43:19 +03:00 by kerem · 16 comments
Owner

Originally created by @klmr on GitHub (Sep 4, 2019).
Original GitHub issue: https://github.com/asciinema/asciinema/issues/369

I’ve noticed the following behaviour in asciinema 2.0.2:

$ for i in {1..30000}; do echo $i; done >large-file
$ asciinema rec
asciinema: recording asciicast to /var/folders/dr/rp46tjqs5qzfj_ghjpxqrttr0000gn/T/tmprapla7oa-ascii.cast
asciinema: press <ctrl-d> or type "exit" when you're done
$ cat large-file | head
1
2
3
4
5
6
7
8
9
10
cat: stdout: Broken pipe

This happens whenever the first program in a pipeline receives a SIGPIPE from the second command, and the data to be piped exceeds a certain length (in my terminal, in the above, using {1..20000} is not reproducibly enough to trigger the behaviour, but it sometimes is). The behaviour is the same for a wide range of programs, not just cat.

(macOS 10.14.6, bash 4.4.19 with iTerm 3.3.2 as well as Terminal.app)

Originally created by @klmr on GitHub (Sep 4, 2019). Original GitHub issue: https://github.com/asciinema/asciinema/issues/369 I’ve noticed the following behaviour in asciinema 2.0.2: ``` $ for i in {1..30000}; do echo $i; done >large-file $ asciinema rec asciinema: recording asciicast to /var/folders/dr/rp46tjqs5qzfj_ghjpxqrttr0000gn/T/tmprapla7oa-ascii.cast asciinema: press <ctrl-d> or type "exit" when you're done $ cat large-file | head 1 2 3 4 5 6 7 8 9 10 cat: stdout: Broken pipe ``` This happens whenever the first program in a pipeline receives a SIGPIPE from the second command, and the data to be piped exceeds a certain length (in my terminal, in the above, using `{1..20000}` is not reproducibly enough to trigger the behaviour, but it *sometimes* is). The behaviour is the same for a wide range of programs, not just `cat`. (macOS 10.14.6, bash 4.4.19 with iTerm 3.3.2 as well as Terminal.app)
kerem closed this issue 2026-03-15 10:43:24 +03:00
Author
Owner

@karlkfi commented on GitHub (Feb 2, 2021):

I see this pretty repeatably when reading from urandom:

SUFFIX=$(< /dev/urandom tr -dc '[:digit:]' | head -c4)
tr: write error: Broken pipe
tr: write error
<!-- gh-comment-id:771850445 --> @karlkfi commented on GitHub (Feb 2, 2021): I see this pretty repeatably when reading from urandom: ``` SUFFIX=$(< /dev/urandom tr -dc '[:digit:]' | head -c4) tr: write error: Broken pipe tr: write error ```
Author
Owner

@ku1ik commented on GitHub (May 17, 2022):

I cannot reproduce the problem on either Linux or macOS anymore. I believe this got fixed in either 2.1.0 or 2.2.0. Feel free to re-open if it still doesn't work for you. Thx!

<!-- gh-comment-id:1128842927 --> @ku1ik commented on GitHub (May 17, 2022): I cannot reproduce the problem on either Linux or macOS anymore. I believe this got fixed in either 2.1.0 or 2.2.0. Feel free to re-open if it still doesn't work for you. Thx!
Author
Owner

@klmr commented on GitHub (May 17, 2022):

Unfortunately I am still able to reproduce this issue with asciinema 2.2.0 — on both macOS 10.14.6 as well as Ubuntu 18.04.

Just to check whether something in my shell configuration might be interfering, I even launched it with a clean environment, but the issue persists:

env -i LANG=C.UTF-8 HOME=$HOME $(which asciinema) rec

(I can’t reopen the issue, only contributors can.)

<!-- gh-comment-id:1128971422 --> @klmr commented on GitHub (May 17, 2022): Unfortunately I am still able to reproduce this issue with asciinema 2.2.0 — on both macOS 10.14.6 as well as Ubuntu 18.04. Just to check whether something in my shell configuration might be interfering, I even launched it with a clean environment, but the issue persists: ```sh env -i LANG=C.UTF-8 HOME=$HOME $(which asciinema) rec ``` (I can’t reopen the issue, only contributors can.)
Author
Owner

@ku1ik commented on GitHub (May 17, 2022):

Now I know why I couldn't reproduce. My default shell is fish and I was launching bash from within started recording session (which was running fish in the first place).

For me it was roughly an equivalent of:

asciinema rec -c fish
bash
cat large-file | head

The above doesn't break the pipe.

If your default shell is bash then in your case it's an equivalent of:

asciinema rec -c bash
cat large-file | head

In this case I see the broken pipe error. Same for the SUFFIX=... expression @karlkfi had trouble with.

I'm not sure yet what's going on but I suspect something related to signal handling we have for the top level process (direct child of asciinema).

<!-- gh-comment-id:1129000983 --> @ku1ik commented on GitHub (May 17, 2022): Now I know why I couldn't reproduce. My default shell is fish and I was launching bash from within started recording session (which was running fish in the first place). For me it was roughly an equivalent of: ```bash asciinema rec -c fish bash cat large-file | head ``` The above doesn't break the pipe. If your default shell is bash then in your case it's an equivalent of: ```bash asciinema rec -c bash cat large-file | head ``` In this case I see the broken pipe error. Same for the `SUFFIX=...` expression @karlkfi had trouble with. I'm not sure yet what's going on but I suspect something related to signal handling we have for the top level process (direct child of asciinema).
Author
Owner

@Low-power commented on GitHub (Oct 18, 2023):

I can reproduce this issue on FreeBSD with latest commit (e4fb2d6)

<!-- gh-comment-id:1768499566 --> @Low-power commented on GitHub (Oct 18, 2023): I can reproduce this issue on FreeBSD with latest commit (e4fb2d6)
Author
Owner

@Low-power commented on GitHub (Oct 18, 2023):

The simplest test case I came up is:

$ yes | true
yes: stdout: Broken pipe

This command line would normally produce no output.

Some truss(1) logs

Outside asciinema:

$ truss -f yes | true
...
83196: write(1,"y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny"...,4096) ERR#32 'Broken pipe'
83196: SIGNAL 13 (SIGPIPE)
83196: process killed, signal = 13

Inside asciinema:

$ truss -f yes | true
...
88253: write(1,"y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny"...,4096) ERR#32 'Broken pipe'
yes: 88253: write(2,"yes: ",5)			 = 5 (0x5)
stdout88253: write(2,"stdout",6)			 = 6 (0x6)
: 88253: write(2,": ",2)				 = 2 (0x2)
88253: stat("/usr/share/nls/C/libc.cat",0x7fffffffe3f8) ERR#2 'No such file or directory'
88253: stat("/usr/share/nls/libc/C",0x7fffffffe3f8) ERR#2 'No such file or directory'
88253: stat("/usr/local/share/nls/C/libc.cat",0x7fffffffe3f8) ERR#2 'No such file or directory'
88253: stat("/usr/local/share/nls/libc/C",0x7fffffffe3f8) ERR#2 'No such file or directory'
Broken pipe
88253: write(2,"Broken pipe\n",12)		 = 12 (0xc)
88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0)
88253: sigprocmask(SIG_SETMASK,{ },0x0)		 = 0 (0x0)
88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0)
88253: sigprocmask(SIG_SETMASK,{ },0x0)		 = 0 (0x0)
88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0)
88253: sigprocmask(SIG_SETMASK,{ },0x0)		 = 0 (0x0)
88253: exit(0x1)				
88253: process exit, rval = 1

Truss(1)ing asciinema all together:

$ truss -f python3.6 -m asciinema rec test9.cast
...
91551: sigaction(SIGPIPE,{ SIG_IGN 0x0 ss_t },{ SIG_DFL SA_RESTART ss_t }) = 0 (0x0)
...
91551: sigaction(SIGPIPE,0x0,{ SIG_IGN 0x0 ss_t }) = 0 (0x0)
...
92091: <new process>
91551: fork()                                    = 92091 (0x167bb)
...
92092: <new process>
92092: thr_self(0x801e16000)                     = 0 (0x0)
91551: fork()                                    = 92092 (0x167bc)
...
91551: posix_openpt(O_RDWR|O_NOCTTY)             = 11 (0xb)
...
91551: ioctl(11,TIOCPTMASTER,0x0)                = 0 (0x0)
...
91551: ioctl(11,FIODGNAME,0x7fffffffd290)        = 0 (0x0)
91551: openat(AT_FDCWD,"/dev/pts/23",O_RDWR,00)  = 12 (0xc)
...
92093: <new process>
91551: fork()                                    = 92093 (0x167bd)
...
92093: ioctl(12,TIOCSCTTY,0x0)                   = 0 (0x0)
92092: openat(AT_FDCWD,"test9.cast",O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0666) = 12 (0xc)
92093: dup2(0xc,0x0)                             = 0 (0x0)
92092: fstat(12,{ mode=-rw-r--r-- ,inode=3231999,size=0,blksize=131072 }) = 0 (0x0)
92093: dup2(0xc,0x1)                             = 1 (0x1)
92092: ioctl(12,TIOCGETA,0x7fffffffb720)         ERR#25 'Inappropriate ioctl for device'
92093: dup2(0xc,0x2)                             = 2 (0x2)
92093: close(12)                                 = 0 (0x0)
91551: fcntl(11,F_GETFL,)                        = 2 (0x2)
92092: lseek(12,0x0,SEEK_CUR)                    = 0 (0x0)
91551: fcntl(11,F_SETFL,O_RDWR|O_NONBLOCK)       = 0 (0x0)
92092: lseek(12,0x0,SEEK_CUR)                    = 0 (0x0)
92092: lseek(12,0x0,SEEK_CUR)                    = 0 (0x0)
91551: ioctl(4,TIOCGWINSZ,0x7fffffffd288)        = 0 (0x0)
91551: ioctl(11,TIOCSWINSZ,0x7fffffffce70)       = 0 (0x0)
91551: pipe2(0x7fffffffd128,O_CLOEXEC)           = 0 (0x0)
92092: write(12,"{"version": 2, "width": 100, "he"...,127) = 127 (0x7f)
...
92093: execve("/bin/sh",0x801aec8b0,0x805ce3e88) = 0 (0x0)
...
92093: execve("/usr/local/bin/bash",0x801415278,0x8014152f0) = 0 (0x0)
...

Looks like somehow either Python or asciinema (PID 91551) ignored SIGPIPE, then fork-exec'ed bash(1) (PID 92093) without restoring the signal action.

<!-- gh-comment-id:1768575013 --> @Low-power commented on GitHub (Oct 18, 2023): The simplest test case I came up is: ``` $ yes | true yes: stdout: Broken pipe ``` This command line would normally produce no output. ### Some **truss(1)** logs Outside asciinema: ``` $ truss -f yes | true ... 83196: write(1,"y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny"...,4096) ERR#32 'Broken pipe' 83196: SIGNAL 13 (SIGPIPE) 83196: process killed, signal = 13 ``` Inside asciinema: ``` $ truss -f yes | true ... 88253: write(1,"y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny"...,4096) ERR#32 'Broken pipe' yes: 88253: write(2,"yes: ",5) = 5 (0x5) stdout88253: write(2,"stdout",6) = 6 (0x6) : 88253: write(2,": ",2) = 2 (0x2) 88253: stat("/usr/share/nls/C/libc.cat",0x7fffffffe3f8) ERR#2 'No such file or directory' 88253: stat("/usr/share/nls/libc/C",0x7fffffffe3f8) ERR#2 'No such file or directory' 88253: stat("/usr/local/share/nls/C/libc.cat",0x7fffffffe3f8) ERR#2 'No such file or directory' 88253: stat("/usr/local/share/nls/libc/C",0x7fffffffe3f8) ERR#2 'No such file or directory' Broken pipe 88253: write(2,"Broken pipe\n",12) = 12 (0xc) 88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0) 88253: sigprocmask(SIG_SETMASK,{ },0x0) = 0 (0x0) 88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0) 88253: sigprocmask(SIG_SETMASK,{ },0x0) = 0 (0x0) 88253: sigprocmask(SIG_BLOCK,{ SIGHUP|SIGINT|SIGQUIT|SIGKILL|SIGPIPE|SIGALRM|SIGTERM|SIGURG|SIGSTOP|SIGTSTP|SIGCONT|SIGCHLD|SIGTTIN|SIGTTOU|SIGIO|SIGXCPU|SIGXFSZ|SIGVTALRM|SIGPROF|SIGWINCH|SIGINFO|SIGUSR1|SIGUSR2 },{ }) = 0 (0x0) 88253: sigprocmask(SIG_SETMASK,{ },0x0) = 0 (0x0) 88253: exit(0x1) 88253: process exit, rval = 1 ``` Truss(1)ing asciinema all together: ``` $ truss -f python3.6 -m asciinema rec test9.cast ... 91551: sigaction(SIGPIPE,{ SIG_IGN 0x0 ss_t },{ SIG_DFL SA_RESTART ss_t }) = 0 (0x0) ... 91551: sigaction(SIGPIPE,0x0,{ SIG_IGN 0x0 ss_t }) = 0 (0x0) ... 92091: <new process> 91551: fork() = 92091 (0x167bb) ... 92092: <new process> 92092: thr_self(0x801e16000) = 0 (0x0) 91551: fork() = 92092 (0x167bc) ... 91551: posix_openpt(O_RDWR|O_NOCTTY) = 11 (0xb) ... 91551: ioctl(11,TIOCPTMASTER,0x0) = 0 (0x0) ... 91551: ioctl(11,FIODGNAME,0x7fffffffd290) = 0 (0x0) 91551: openat(AT_FDCWD,"/dev/pts/23",O_RDWR,00) = 12 (0xc) ... 92093: <new process> 91551: fork() = 92093 (0x167bd) ... 92093: ioctl(12,TIOCSCTTY,0x0) = 0 (0x0) 92092: openat(AT_FDCWD,"test9.cast",O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC,0666) = 12 (0xc) 92093: dup2(0xc,0x0) = 0 (0x0) 92092: fstat(12,{ mode=-rw-r--r-- ,inode=3231999,size=0,blksize=131072 }) = 0 (0x0) 92093: dup2(0xc,0x1) = 1 (0x1) 92092: ioctl(12,TIOCGETA,0x7fffffffb720) ERR#25 'Inappropriate ioctl for device' 92093: dup2(0xc,0x2) = 2 (0x2) 92093: close(12) = 0 (0x0) 91551: fcntl(11,F_GETFL,) = 2 (0x2) 92092: lseek(12,0x0,SEEK_CUR) = 0 (0x0) 91551: fcntl(11,F_SETFL,O_RDWR|O_NONBLOCK) = 0 (0x0) 92092: lseek(12,0x0,SEEK_CUR) = 0 (0x0) 92092: lseek(12,0x0,SEEK_CUR) = 0 (0x0) 91551: ioctl(4,TIOCGWINSZ,0x7fffffffd288) = 0 (0x0) 91551: ioctl(11,TIOCSWINSZ,0x7fffffffce70) = 0 (0x0) 91551: pipe2(0x7fffffffd128,O_CLOEXEC) = 0 (0x0) 92092: write(12,"{"version": 2, "width": 100, "he"...,127) = 127 (0x7f) ... 92093: execve("/bin/sh",0x801aec8b0,0x805ce3e88) = 0 (0x0) ... 92093: execve("/usr/local/bin/bash",0x801415278,0x8014152f0) = 0 (0x0) ... ``` Looks like somehow either Python or asciinema (PID 91551) ignored `SIGPIPE`, then fork-exec'ed **bash(1)** (PID 92093) without restoring the signal action.
Author
Owner

@Low-power commented on GitHub (Oct 18, 2023):

My previous testings are done on a FreeBSD 11 OS with Python 3.6.

Now I also tested on FreeBSD 12 with Python 3.7 and 3.8, which both behaved exactly same (both ignored SIGPIPE).

Trying to break sigaction(2) in GDB to findout where it was get called:

GNU gdb (GDB) 9.1 [GDB v9.1 for FreeBSD]                                                            
...
This GDB was configured as "x86_64-portbld-freebsd12.1".
...
Reading symbols from python3.7...
(No debugging symbols found in python3.7)
(gdb) add-auto-load-safe-path /usr/local/lib/libpython3.7m.so.1.0-gdb.py
(gdb) b sigaction
Function "sigaction" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (sigaction) pending.
(gdb) r
Starting program: /usr/local/bin/python3.7 -m asciinema rec test9.cast

Breakpoint 1, 0x0000000800de56e4 in sigaction () from /lib/libc.so.7
(gdb) p $edi
$1 = 13
(gdb) # 13 is SIGPIPE
(gdb) bt
#0  0x0000000800de56e4 in sigaction () from /lib/libc.so.7
#1  0x000000080091f060 in PyOS_setsig () from /usr/local/lib/libpython3.7m.so.1.0
#2  0x000000080091f213 in _Py_InitializeMainInterpreter () from /usr/local/lib/libpython3.7m.so.1.0
#3  0x000000080093a3d0 in ?? () from /usr/local/lib/libpython3.7m.so.1.0
#4  0x000000080093a82e in ?? () from /usr/local/lib/libpython3.7m.so.1.0
#5  0x000000080093af1a in _Py_UnixMain () from /usr/local/lib/libpython3.7m.so.1.0
#6  0x000000000040069b in _start ()

Well, I guess SIGPIPE was ignored during the early initialization of Python. May be asciinema can workaround this Python bug by restoring this signal action to SIG_DFL before running user's command.

<!-- gh-comment-id:1768630986 --> @Low-power commented on GitHub (Oct 18, 2023): My previous testings are done on a FreeBSD 11 OS with Python 3.6. Now I also tested on FreeBSD 12 with Python 3.7 and 3.8, which both behaved exactly same (both ignored `SIGPIPE`). Trying to break **sigaction(2)** in GDB to findout where it was get called: ``` GNU gdb (GDB) 9.1 [GDB v9.1 for FreeBSD] ... This GDB was configured as "x86_64-portbld-freebsd12.1". ... Reading symbols from python3.7... (No debugging symbols found in python3.7) (gdb) add-auto-load-safe-path /usr/local/lib/libpython3.7m.so.1.0-gdb.py (gdb) b sigaction Function "sigaction" not defined. Make breakpoint pending on future shared library load? (y or [n]) y Breakpoint 1 (sigaction) pending. (gdb) r Starting program: /usr/local/bin/python3.7 -m asciinema rec test9.cast Breakpoint 1, 0x0000000800de56e4 in sigaction () from /lib/libc.so.7 (gdb) p $edi $1 = 13 (gdb) # 13 is SIGPIPE (gdb) bt #0 0x0000000800de56e4 in sigaction () from /lib/libc.so.7 #1 0x000000080091f060 in PyOS_setsig () from /usr/local/lib/libpython3.7m.so.1.0 #2 0x000000080091f213 in _Py_InitializeMainInterpreter () from /usr/local/lib/libpython3.7m.so.1.0 #3 0x000000080093a3d0 in ?? () from /usr/local/lib/libpython3.7m.so.1.0 #4 0x000000080093a82e in ?? () from /usr/local/lib/libpython3.7m.so.1.0 #5 0x000000080093af1a in _Py_UnixMain () from /usr/local/lib/libpython3.7m.so.1.0 #6 0x000000000040069b in _start () ``` Well, I guess `SIGPIPE` was ignored during the early initialization of Python. May be asciinema can workaround this **Python bug** by restoring this signal action to `SIG_DFL` before running user's command.
Author
Owner

@ku1ik commented on GitHub (Oct 21, 2023):

Great sleuthing @Low-power 👏 This is very interesting. Indeed yes | true does it, as well as yes | head (which I've been testing with mostly).

So, funny thing... I recreated the core part of the pty.py in Rust (see here github.com/asciinema/asciinema@909a496154/src/pty.rs), and the bug can be observed there too. Therefore, maybe what Python does during its initialization doesn't matter here? 🤔

<!-- gh-comment-id:1773911199 --> @ku1ik commented on GitHub (Oct 21, 2023): Great sleuthing @Low-power :clap: This is very interesting. Indeed `yes | true` does it, as well as `yes | head` (which I've been testing with mostly). So, funny thing... I recreated the core part of the `pty.py` in Rust (see here https://github.com/asciinema/asciinema/blob/909a4961548fbdb53d51cfbc040dac35a3c8a3ff/src/pty.rs), and the bug can be observed there too. Therefore, maybe what Python does during its initialization doesn't matter here? :thinking:
Author
Owner

@Low-power commented on GitHub (Oct 22, 2023):

So... I implemented a very simple pseudo terminal testing program in C# (https://gist.github.com/Low-power/3637796bb0e8df298d3ff1c47e4ff2ea/), and experienced the same hehavior when running it in Mono.

$ tty
/dev/pts/1
$ mono pty.exe 
$ tty
/dev/pts/61
$ yes | true
yes: stdout: Broken pipe

Because I have the full source code of Mono on hand, I took a quick search for its handling of SIGPIPE; sure enough it has ignored this signal on starts up (Mono source file mono/mini/mini-posix.c`):

void
mono_runtime_posix_install_handlers (void)
{
...
        signal (SIGPIPE, SIG_IGN);

And this is the only place where SIGPIPE appeared in the Mono VM source directory, as found by grep(1).

I think most of the script interpreters and virtual machines ignore this signal without ever restore it.

<!-- gh-comment-id:1774047804 --> @Low-power commented on GitHub (Oct 22, 2023): So... I implemented a very simple pseudo terminal testing program in C# (https://gist.github.com/Low-power/3637796bb0e8df298d3ff1c47e4ff2ea/), and experienced the same hehavior when running it in Mono. ``` $ tty /dev/pts/1 $ mono pty.exe $ tty /dev/pts/61 $ yes | true yes: stdout: Broken pipe ``` Because I have the full source code of Mono on hand, I took a quick search for its handling of `SIGPIPE; sure enough it has ignored this signal on starts up (Mono source file `mono/mini/mini-posix.c`): ```c void mono_runtime_posix_install_handlers (void) { ... signal (SIGPIPE, SIG_IGN); ``` And this is the only place where `SIGPIPE` appeared in the Mono VM source directory, as found by **grep(1)**. I think most of the script interpreters and virtual machines ignore this signal without ever restore it.
Author
Owner

@ku1ik commented on GitHub (Oct 22, 2023):

I've tested #578 with following commands:

  • cat large-file | head
  • SUFFIX=$(< /dev/urandom tr -dc '[:digit:]' | head -c4)
  • yes | true
  • yes | head

With the patch it seems to work without any errors.

<!-- gh-comment-id:1774047940 --> @ku1ik commented on GitHub (Oct 22, 2023): I've tested #578 with following commands: - `cat large-file | head` - `SUFFIX=$(< /dev/urandom tr -dc '[:digit:]' | head -c4)` - `yes | true` - `yes | head` With the patch it seems to work without any errors.
Author
Owner

@ku1ik commented on GitHub (Oct 22, 2023):

I think most of the script interpreters and virtual machines ignore this signal without ever restore it.

Yeah, I believe it goes like this: if parent process ignores SIGPIPE then child will by default ignore it. If parent uses custom handler for SIGPIPE then child will have it set to SIG_DFL. Or maybe it's always ignored by default as by your Mono example. Either way, #578 should do the trick.

<!-- gh-comment-id:1774049506 --> @ku1ik commented on GitHub (Oct 22, 2023): > I think most of the script interpreters and virtual machines ignore this signal without ever restore it. Yeah, I believe it goes like this: if parent process ignores SIGPIPE then child will by default ignore it. If parent uses custom handler for SIGPIPE then child will have it set to SIG_DFL. Or maybe it's always ignored by default as by your Mono example. Either way, #578 should do the trick.
Author
Owner

@Low-power commented on GitHub (Oct 22, 2023):

Custom signal handlers will be reset to SIG_DFL when exec a new program, while SIG_IGN will remain across exec; it is stated by POSIX (https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html).

Signals set to the default action (SIG_DFL) in the calling process image shall be set to the default action in the new process image. Except for SIGCHLD, signals set to be ignored (SIG_IGN) by the calling process image shall be set to be ignored by the new process image. Signals set to be caught by the calling process image shall be set to the default action in the new process image (see <signal.h>).

<!-- gh-comment-id:1774180981 --> @Low-power commented on GitHub (Oct 22, 2023): Custom signal handlers will be reset to `SIG_DFL` when exec a new program, while `SIG_IGN` will remain across exec; it is stated by POSIX (https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html). > Signals set to the default action (SIG_DFL) in the calling process image shall be set to the default action in the new process image. Except for SIGCHLD, signals set to be ignored (SIG_IGN) by the calling process image shall be set to be ignored by the new process image. Signals set to be caught by the calling process image shall be set to the default action in the new process image (see <signal.h>).
Author
Owner

@ku1ik commented on GitHub (Oct 22, 2023):

There we have it. So I suppose we can merge #578, right?

<!-- gh-comment-id:1774183943 --> @ku1ik commented on GitHub (Oct 22, 2023): There we have it. So I suppose we can merge #578, right?
Author
Owner

@Low-power commented on GitHub (Oct 23, 2023):

Yes, I think that's a right thing to do.

<!-- gh-comment-id:1774836842 --> @Low-power commented on GitHub (Oct 23, 2023): Yes, I think that's a right thing to do.
Author
Owner

@ku1ik commented on GitHub (Oct 23, 2023):

This has been fixed in v2.4.0: https://github.com/asciinema/asciinema/releases/tag/v2.4.0

<!-- gh-comment-id:1775514602 --> @ku1ik commented on GitHub (Oct 23, 2023): This has been fixed in v2.4.0: https://github.com/asciinema/asciinema/releases/tag/v2.4.0
Author
Owner

@klmr commented on GitHub (Oct 23, 2023):

I just want to say, awesome job! It’s been a pleasure to watch this being solved step by step. Thanks to everyone who contributed to fixing this.

<!-- gh-comment-id:1776002126 --> @klmr commented on GitHub (Oct 23, 2023): I just want to say, awesome job! It’s been a pleasure to watch this being solved step by step. Thanks to everyone who contributed to fixing this.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/asciinema#848
No description provided.