How can a Unix program display output on screen even when stdout and stderr are redirected?

后端 未结 3 1596
半阙折子戏
半阙折子戏 2021-01-07 21:51

I was running a program (valgrind, actually) on my Ubuntu machine, and had redirected both stdout and stderr to different files. I was surprised to see a short message appea

相关标签:
3条回答
  • 2021-01-07 22:12

    The message is most probably from GCC's stack protector feature or from glib itself. If it's from GCC, it is output using the fail() function, which directly opens /dev/tty:

    fd = open (_PATH_TTY, O_WRONLY);
    

    _PATH_TTY is not really standard, but SingleUnix actually demands that /dev/tty exists.

    0 讨论(0)
  • 2021-01-07 22:15

    Here is some sample code that does exactly what was asked (thanks to earlier answers pointing me in the right direction). Both are compiled with g++, and will print a message to the screen even when stdout and stderr are redirected.

    For Linux (Ubuntu 14):

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    #include <string.h>
    
    int main( int, char *[]) {
    
        printf("This goes to stdout\n");
    
        fprintf(stderr, "This goes to stderr\n");
    
        int ttyfd = open("/dev/tty", O_RDWR);
        const char *msg = "This goes to screen\n";
        write(ttyfd, msg, strlen(msg));
    }
    

    For Windows 7, using MinGW:

    #include <stdio.h>
    #include <fcntl.h>
    #include <string.h>
    #include <conio.h>
    
    void writeConsole( const char *s) {
        while( *s) {
            putch(*(s++));
        }
    }
    
    int main( int, char *[]) {  
        printf("This goes to stdout\n");
    
        fprintf(stderr, "This goes to stderr\n");
    
        writeConsole( "This goes to screen\n");
    }
    
    0 讨论(0)
  • 2021-01-07 22:16

    It is not written by valgrind but rather glibc and your ./myprogram is using glibc:

    #define _PATH_TTY   "/dev/tty"
    
    /* Open a descriptor for /dev/tty unless the user explicitly
       requests errors on standard error.  */
    const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
    if (on_2 == NULL || *on_2 == '\0')
      fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
    
    if (fd == -1)
      fd = STDERR_FILENO;
    
    ...
    written = WRITEV_FOR_FATAL (fd, iov, nlist, total);
    

    Below are some relevant parts of glibc:

    void
    __attribute__ ((noreturn))
    __stack_chk_fail (void)
    {
      __fortify_fail ("stack smashing detected");
    }
    
    void
    __attribute__ ((noreturn))
    __fortify_fail (msg)
         const char *msg;
    {
      /* The loop is added only to keep gcc happy.  */
      while (1)
        __libc_message (2, "*** %s ***: %s terminated\n",
                msg, __libc_argv[0] ?: "<unknown>");
    }
    
    
    /* Abort with an error message.  */
    void
    __libc_message (int do_abort, const char *fmt, ...)
    {
      va_list ap;
      int fd = -1;
    
      va_start (ap, fmt);
    
    #ifdef FATAL_PREPARE
      FATAL_PREPARE;
    #endif
    
      /* Open a descriptor for /dev/tty unless the user explicitly
         requests errors on standard error.  */
      const char *on_2 = __libc_secure_getenv ("LIBC_FATAL_STDERR_");
      if (on_2 == NULL || *on_2 == '\0')
        fd = open_not_cancel_2 (_PATH_TTY, O_RDWR | O_NOCTTY | O_NDELAY);
    
      if (fd == -1)
        fd = STDERR_FILENO;
    
      ...
      written = WRITEV_FOR_FATAL (fd, iov, nlist, total);
    
    0 讨论(0)
提交回复
热议问题