4

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?

Jongware
  • 22,200
  • 8
  • 54
  • 100
LKT
  • 311
  • 1
  • 7
  • 17
  • 3
    What are you trying to solve? If the user wants to discard the output of your program, they can do that, even if you somehow turn off /dev/null. For example pipe to `/tmp/junk` and then `rm /tmp/junk`. Or just obfuscate a little `./myprogram | cat > /dev/null`. – Thilo Jan 26 '16 at 06:23
  • 1
    Here is one idea, 1. find out programs pid, 2. then check /proc//fd, to see if there is any one linked to /dev/null. – Eric Jan 26 '16 at 06:27
  • Note that your check will reduce the usability of your program, or shows an unwarranted arrogance about the value of its output. There can be reasons why people need to discard the output. You making it harder for them to do so won't win friends. It really shouldn't matter to you. If you wanted to optimize your program to write nothing when connected to `/dev/null`, you could, I suppose, do so — but that would be ruling out possible valid uses of the program. Basically, you almost certainly shouldn't do it. – Jonathan Leffler Jan 26 '16 at 14:11
  • A use case for this is in Android. If you run something from a shell, you want logging to go to the terminal or a redirected file. If it is run as a daemon stdout will be /dev/null, in which case using Android logging (logcat) would be a good workaround. – Renate Nov 17 '17 at 15:08

3 Answers3

5

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)
$
kaylum
  • 13,833
  • 2
  • 22
  • 31
  • Thanks @kaylum. This is exactly what I'm looking for and it works perfectly for my scenario described above. I work on Unix system btw, sorry for not mentioning that in the question – LKT Jan 26 '16 at 08:13
  • 1
    Hmm, of course it will work, but IMHO it is really bad practice. The rule should be *the caller knows where output should go not the callee*. Even when using syslog, you can redirect almost anything anywhere (including /dev/null) – Serge Ballesta Jan 26 '16 at 08:30
  • 1
    Note that some Unix-like systems do not have a `/proc` file system at all. There are differences in the details of those that do have it. – Jonathan Leffler Jan 26 '16 at 14:07
2

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.

Dr. Alex RE
  • 1,772
  • 1
  • 15
  • 23
1

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;
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • A C program can have the knowledge of who the owners of the stdin, stdout, and stderr links are by checking `/proc/self/fd/[0-2]`. –  Jan 05 '17 at 20:46