Reputation: 341
I'm writing a C program that outputs to stdout
and errors to stderr
. The program takes a command such as:
./myprogram function_to_run file_to_read
My program can either output to stdout
or be directed to output a file, but it must not be redirected to /dev/null
. For example:
./myprogram function_to_run file_to_read //OK
./myprogram function_to_run file_to_read > file.txt //OK
./myprogram function_to_run file_to_read > /dev/null // NOT OK, should produce error in stderr
I tried to use isatty(1)
, but it only can detect if stdout
is outputting to a terminal. Therefore, it fails for the case where stdout
is redirected to a file, which is acceptable in my case
Is there a way to check for this in C? If not, any suggestion how I could check for the /dev/null scenario?
Upvotes: 4
Views: 3239
Reputation: 1708
A quick way to check that standard output is redirected to /dev/null
is to check that STDOUT_FILENO
and /dev/null
are both devices with the same inode:
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/stat.h>
int main()
{
struct stat std_out;
struct stat dev_null;
if (fstat(STDOUT_FILENO, &std_out) == 0 &&
S_ISCHR(std_out.st_mode) &&
stat("/dev/null", &dev_null) == 0 &&
std_out.st_dev == dev_null.st_dev &&
std_out.st_ino == dev_null.st_ino)
{
fprintf(stderr, "Redirect to /dev/null not allowed!\n");
exit(EXIT_FAILURE);
}
fprintf(stderr, "All OK\n");
return 0;
}
Checking the inodes is portable to all Unix-like systems:
$ ./a.out
All OK
$ ./a.out | cat
All OK
$ ./a.out > /dev/null
Redirect to /dev/null not allowed!
We should not rely on /proc/self/fd/1
. It is not supported by all Unix-like systems, notably Mac OS X Darwin and some BSD variants.
Upvotes: 4
Reputation: 14046
If you are only interested in *nix systems then one solution is to check what /proc/self/fd/1
is linked to. Below is a sample program that does that (error checking omitted for brevity).
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <assert.h>
int main (void)
{
char link[256];
ssize_t rval;
rval = readlink("/proc/self/fd/1", link, sizeof(link));
link[rval] = '\0';
if (!strcmp(link, "/dev/null")) {
assert(!"Redirect to /dev/null not allowed!");
} else {
printf("All OK\n");
}
return 0;
}
Sample test runs:
$ ./a.out
All OK
$ ./a.out > some_file
$ cat some_file
All OK
$ ./a.out > /dev/null
a.out: test.c:14: main: Assertion `!"Redirect to /dev/null not allowed!"' failed.
Aborted (core dumped)
$
Upvotes: 5
Reputation: 206697
Is there a way to check for this in C?
No, there isn't. The file where stderr
is redirected is controlled by the shell that runs the program. The C program has no knowledge of that.
If not, any suggestion how I could check for the /dev/null scenario?
You could change your program to accept a second argument and use it as the destination of stderr
using freopen
. If the second argument is /dev/null
, you could error out.
if ( strcmp(argv[2], "/dev/null") == 0 )
{
// Deal with error.
return EXIT_FAILURE;
}
if (freopen(argv[2], "w", stderr) == NULL)
{
perror("freopen() failed");
return EXIT_FAILURE;
}
Upvotes: 1