I have encountered a bug when running a certain script. The following is the simplest script I can make that triggers the bug:
1) create a cpio archive: gzip -dc /boot/initrd >initrd.cpio
2) run this script:
if cpio --quiet -t <initrd.cpio ; then echo true-$? >&2 ; else echo false-$? >&2 ; fi | grep libz
The output is: lib64/libz.so.1.2.7 lib64/libz.so.1 true-0
But if I add the option -q to grep: if cpio --quiet -t <initrd.cpio ; then echo true-$? >&2 ; else echo false-$? >&2 ; fi | grep -q libz
the output is: false-141 instead of the correct one: true-0
This bug is present in opensuse 12.2, 12.1 and 11.4 and also CentOS 6 (if it matters) opensuse 11.3 is bug free as well as CentOS 5.
I discovered the bug while running: lsinitrd /boot/initrd | grep libz
whithout the -q option to grep the otuput is correct, when I add the option -q the output is: xz: /boot/initrd: File format not recognized
I have tested different version of cpio and grep but it doesn't make any difference. I'm guessing it's a kernel bug or may be it is a compiler bug?
By the way, I hope someone is intrested and can hunt the bug.
Giacomo
On Tuesday 04 of December 2012 13:05EN, Giacomo Comes wrote:
But if I add the option -q to grep: if cpio --quiet -t <initrd.cpio ; then echo true-$? >&2 ; else echo false-$? >&2 ; fi | grep -q libz
the output is: false-141 instead of the correct one: true-0
Return code 141 means the process was killed by signal 141 - 128 = 13 which is SIGPIPE. And grep(1) tells you that with "-q", grep exits immediately as soon as it finds a match. Unfortunately cpio doesn't handle SIGPIPE so it fails when grep exits.
You can either try to report it as a cpio bug or use a simple workaround like
if ... fi | cat | grep -q libz
Anyway, this is definitely not a kernel problem.
Michal Kubeček
On Wed, Dec 05, 2012 at 08:11:24AM +0100, Michal Kubeček wrote:
On Tuesday 04 of December 2012 13:05EN, Giacomo Comes wrote:
But if I add the option -q to grep: if cpio --quiet -t <initrd.cpio ; then echo true-$? >&2 ; else echo false-$? >&2 ; fi | grep -q libz
the output is: false-141 instead of the correct one: true-0
Return code 141 means the process was killed by signal 141 - 128 = 13 which is SIGPIPE. And grep(1) tells you that with "-q", grep exits immediately as soon as it finds a match. Unfortunately cpio doesn't handle SIGPIPE so it fails when grep exits.
You can either try to report it as a cpio bug or use a simple workaround like
if ... fi | cat | grep -q libz
Anyway, this is definitely not a kernel problem.
There is certanly a SIGPIPE issue here, but I'm not sure cpio is the one to blame, or at least the only one, for the following reasons:
1) the script fails if I run it on openSUSE 12.2 using the cpio binary from 11.3, but it doesn't fail if I run it on openSUSE 11.3 using the cpio binary from 12.2 (the version of cpio is 2.11 since openSUSE 11.3)
2) simply running: cpio --quiet -t <initrd.cpio | grep -q libz ; echo $? returns: 0 If cpio doesn't handle SIGPIPE it should fail here as well.
For the record, it is not a shell issue because I see the bug with bash, ksh and zsh.
Giacomo
On Wed, Dec 05, 2012 at 11:22:07AM -0400, Giacomo Comes wrote:
There is certanly a SIGPIPE issue here, but I'm not sure cpio is the one to blame, or at least the only one, for the following reasons:
- the script fails if I run it on openSUSE 12.2 using the cpio binary from 11.3, but it doesn't fail if I run it on openSUSE 11.3 using the cpio binary from 12.2 (the version of cpio is 2.11 since openSUSE 11.3)
I guess it is up to you to find out what exactly is different. Maybe different contents of initrd, maybe different pipe buffer size. Using strace on cpio may help a lot.
- simply running: cpio --quiet -t <initrd.cpio | grep -q libz ; echo $?
returns: 0
...and so does
false | true ; echo $?
In both cases it doesn't say anything about the return code of first command in the pipeline. See bash(1): "The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled."
If cpio doesn't handle SIGPIPE it should fail here as well.
...and it most likely does.
Michal Kubeček
Le mercredi 05 décembre 2012 à 19:41 +0100, Michal Kubecek a écrit :
On Wed, Dec 05, 2012 at 11:22:07AM -0400, Giacomo Comes wrote:
There is certanly a SIGPIPE issue here, but I'm not sure cpio is the one to blame, or at least the only one, for the following reasons:
- the script fails if I run it on openSUSE 12.2 using the cpio binary from 11.3, but it doesn't fail if I run it on openSUSE 11.3 using the cpio binary from 12.2 (the version of cpio is 2.11 since openSUSE 11.3)
I guess it is up to you to find out what exactly is different. Maybe different contents of initrd, maybe different pipe buffer size. Using strace on cpio may help a lot.
- simply running: cpio --quiet -t <initrd.cpio | grep -q libz ; echo $?
returns: 0
...and so does
false | true ; echo $?
In both cases it doesn't say anything about the return code of first command in the pipeline. See bash(1): "The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled."
Note that you can access the status of every command in a pipe using ${PIPESTATUS[0]}, ${PIPESTATUS[1]} etc.
On Wed, Dec 05, 2012 at 07:41:45PM +0100, Michal Kubecek wrote:
On Wed, Dec 05, 2012 at 11:22:07AM -0400, Giacomo Comes wrote:
There is certanly a SIGPIPE issue here, but I'm not sure cpio is the one to blame, or at least the only one, for the following reasons:
- the script fails if I run it on openSUSE 12.2 using the cpio binary from 11.3, but it doesn't fail if I run it on openSUSE 11.3 using the cpio binary from 12.2 (the version of cpio is 2.11 since openSUSE 11.3)
I guess it is up to you to find out what exactly is different. Maybe different contents of initrd, maybe different pipe buffer size. Using strace on cpio may help a lot.
- simply running: cpio --quiet -t <initrd.cpio | grep -q libz ; echo $?
returns: 0
...and so does
false | true ; echo $?
In both cases it doesn't say anything about the return code of first command in the pipeline. See bash(1): "The return status of a pipeline is the exit status of the last command, unless the pipefail option is enabled."
If cpio doesn't handle SIGPIPE it should fail here as well.
...and it most likely does.
Right. I run the following command: gzip -dc /boot/initrd | cpio --quiet -t | grep -q libz ; echo ${PIPESTATUS[*]} and here are the results:
openSUSE 11.3: 0 0 0 openSUSE 11.4: 0 141 0 openSUSE 12.1: 141 141 0 openSUSE 12.2: 141 141 0
I'm unable to find where the bug is. The only thing I'm sure is that because of it some shell script that used to work now fail.
Giacomo
On Friday 07 of December 2012 11:57EN, Giacomo Comes wrote:
Right. I run the following command: gzip -dc /boot/initrd | cpio --quiet -t | grep -q libz ; echo ${PIPESTATUS[*]} and here are the results:
openSUSE 11.3: 0 0 0 openSUSE 11.4: 0 141 0 openSUSE 12.1: 141 141 0 openSUSE 12.2: 141 141 0
I'm unable to find where the bug is. The only thing I'm sure is that because of it some shell script that used to work now fail.
Change of the result doesn't necessarily mean a regression. The fact that "grep -q" finishes as soon as it finds a match, is a documented behaviour. If you write to a pipe and reader finishes, you get SIGPIPE, that's standard behaviour. If you are lucky and the output is short, you can avoid being shut down by SIGPIPE thanks to buffering but the only reliable way to avoid it is to ignore or handle it. And I seriously doubt gzip and/or cpio handled/ignored SIGPIPE in older versions and they stopped to do so. It is much more likely that you were just lucky not to hit the problem earlier.
If you want to avoid it reliably, use the workaround suggested by Jiri Bohac, i.e. redirecting output of grep to /dev/null instead of "-q".
Anyway, this discussion is definitely off-topic in opensuse-kernel list for some time. So far I haven't seen anything indicating this is a kernel problem.
Michal Kubeček
On Wednesday 05 of December 2012 08:11EN, Michal Kubeček wrote:
You can either try to report it as a cpio bug or use a simple workaround like
if ... fi | cat | grep -q libz
For the record, the fact that this worked for me was just a piece of luck that thanks to buffering, cat didn't finish before cpio passed all its output and finished. In general, this doesn't help as cat closes its input and quits when it receives SIGPIPE.
Michal Kubeček
On Thu, Dec 06, 2012 at 10:56:45AM +0100, Michal Kubeček wrote:
In general, this doesn't help as cat closes its input and quits when it receives SIGPIPE.
... so I think the best solution is: if ... fi | grep libz > /dev/null instead of grep -q