Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C code to check if command line is redirected to /dev/null

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?

like image 343
LKT Avatar asked Feb 08 '23 22:02

LKT


2 Answers

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)
$
like image 136
kaylum Avatar answered Feb 16 '23 02:02

kaylum


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.

like image 39
Dr. Alex RE Avatar answered Feb 16 '23 02:02

Dr. Alex RE