www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - writing to closed stdout (II): exit status

reply kdevel <kdevel vogtner.de> writes:
Sometimes the file descriptor 1 is closed when a program is 
executed (cf. [1]). Due to buffering [2] a write error is not 
detected until the buffers are flushed. But the flushing occurs 
after the return from main:

```d
int main ()
{
    import std.stdio;
    writeln ("OK.");
    return 0;
}
```

(`1>&-` or `>&-` can be used to close stdout in BASH, likewise 
descriptor 1 can be closed in the D program using `extern (C) int 
close (int); close (1);`.)

```sh
$ dmd return0.d
$ ./return0 1>&-; echo $?
Failed to flush stdout: Bad file descriptor
1
$
```

Surprisingly when the return value is anything but 0 the exit 
code of the program becomes `rv & 255`. So in order to return 0 
one can return 256:

```d
int main ()
{
    import std.stdio;
    writeln ("OK.");
    return 256; // returns (256 & 0xff) == 0
}
```

Even more surprising is the fact that is behavior strongly 
resembles the Perl since v5.24.4 [3]


[1] Goodbye World! The perils of relying on output streams in C 
[PDF], p. 15 
https://www.gnu.org/ghm/2011/paris/slides/jim-meyering-goodbye-world.pdf
[2] What do fully buffered, line buffered and unbuffered mean in 
C? [closed] 
https://stackoverflow.com/questions/36573074/what-do-fully-buffered-line-buffered-and-unbuffered-mean-in-c
[3] 
https://stackoverflow.com/questions/50507849/weird-error-after-perl-upgrade-unable-to-flush-stdout/52106594#52106594
Dec 15
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 15 December 2024 at 14:32:13 UTC, kdevel wrote:
 Surprisingly when the return value is anything but 0 the exit 
 code of the program becomes `rv & 255`.
The POSIX standard specifies that the status code you get from `wait` and `waitpid` is truncated to 8 bits. [1] Probably this is because it needs to be packed into a single `int` variable alongside other metadata (`WIFEXITED`, `WIFSIGNALED`, and so on), and the C standard only guarantees that `int` will have at least 16 bits. [2] It's possible to get the full exit status from `waitid` [3], so you can't necessarily *assume* that the upper bits are ignored, but relying on them probably isn't a great idea either. [1] https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html [3] https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html
Dec 15
next sibling parent reply kdevel <kdevel vogtner.de> writes:
On Sunday, 15 December 2024 at 17:16:11 UTC, Paul Backus wrote:
 On Sunday, 15 December 2024 at 14:32:13 UTC, kdevel wrote:
 Surprisingly when the return value is anything but 0 the exit 
 code of the program becomes `rv & 255`.
The POSIX standard specifies that the status code you get from `wait` and `waitpid` is truncated to 8 bits.
Sure. My wording is somewhat misleading. What I find surprising is that if 256 is returned the exit status of the program becomes 0 whereas it becomes 1 in the case of a returned 0: ``` $ cat return0.d int main () { import std.stdio; writeln ("OK."); return 0; } $ dmd -run return0 1>&-; echo $? Failed to flush stdout: Bad file descriptor 1 $ cat return256.d int main () { import std.stdio; writeln ("OK."); return 256; } $ dmd -run return256 1>&-; echo $? Failed to flush stdout: Bad file descriptor 0 ```
Dec 15
parent reply Paul Backus <snarwin gmail.com> writes:
On Sunday, 15 December 2024 at 18:46:26 UTC, kdevel wrote:
 On Sunday, 15 December 2024 at 17:16:11 UTC, Paul Backus wrote:
 On Sunday, 15 December 2024 at 14:32:13 UTC, kdevel wrote:
 Surprisingly when the return value is anything but 0 the exit 
 code of the program becomes `rv & 255`.
The POSIX standard specifies that the status code you get from `wait` and `waitpid` is truncated to 8 bits.
Sure. My wording is somewhat misleading. What I find surprising is that if 256 is returned the exit status of the program becomes 0 whereas it becomes 1 in the case of a returned 0:
I did some more digging and it turns out that this is a feature of the GNU C runtime (i.e., the code that calls `main`):
  While nominally the return value is of type `int`, in fact the 
 exit status gets truncated to eight bits; if `main` returns the 
 value 256, the exit status is 0.
https://www.gnu.org/software/c-intro-and-ref/manual/html_node/Values-from-main.html
Dec 15
parent reply kdevel <kdevel vogtner.de> writes:
On Sunday, 15 December 2024 at 19:02:33 UTC, Paul Backus wrote:
 [...]
Sure. My wording is somewhat misleading. What I find surprising is that if 256 is returned the exit status of the program becomes 0 whereas it becomes 1 in the case of a returned 0:
I did some more digging and it turns out that this is a feature of the GNU C runtime (i.e., the code that calls `main`):
Sorry for replying again. The truncation to eight bit is not my point. My point is that *in the case of a closed stdout* ``` return 0; ``` causes an exit status of 1 but ``` return 256; ``` lets exit status become 0.
Dec 15
parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 15 December 2024 at 19:53:34 UTC, kdevel wrote:
 Sorry for replying again. The truncation to eight bit is not my 
 point. My point is that *in the case of a closed stdout*

 ```
    return 0;
 ```

 causes an exit status of 1 but

 ```
    return 256;
 ```

 lets exit status become 0.
Oh, yeah, that happens because druntime specifically checks for exit status 0: https://github.com/dlang/dmd/blob/v2.109.1/druntime/src/rt/dmain2.d#L535-L543 I guess the correct thing to do here would be to check if `result & 0xFF == 0`.
Dec 15
prev sibling parent reply kdevel <kdevel vogtner.de> writes:
On Sunday, 15 December 2024 at 17:16:11 UTC, Paul Backus wrote:
 It's possible to get the full exit status from `waitid` [3],
Actually not on my linux machine: ```C $ cat status.c #include <stdio.h> #include <stdlib.h> #include <assert.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main () { int rc = fork (); assert (rc != -1); if (rc == 0) /* child */ _exit (-1); else { siginfo_t si; waitid (P_ALL, 0, &si, WEXITED); printf ("%x\n", si.si_status); } return 0; } $ cc status.c $ ./a.out ff ``` But your reference [3] does not say that it should. Also [4] only says that the lower 8 bit constitute the the corresponding part of si_status. Not more not less. Likewise the code on SO [5] does not yield more than 8 bits here on my linux box. [4] https://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_13 [5] https://stackoverflow.com/questions/179565/exit-codes-bigger-than-255-possible
Dec 15
parent Kagamin <spam here.lot> writes:
On Sunday, 15 December 2024 at 19:41:00 UTC, kdevel wrote:
 On Sunday, 15 December 2024 at 17:16:11 UTC, Paul Backus wrote:
 It's possible to get the full exit status from `waitid` [3],
Actually not on my linux machine:
Try ssi_status from signalfd.
Dec 16