Как программа Unix выводит вывод на экран, даже когда stdout и stderr перенаправляются?
На моей машине Ubuntu была запущена программа (valgrind, на самом деле) и перенаправлена как stdout, так и stderr в разные файлы. Я был удивлен, увидев короткое сообщение на экране - как это возможно? Как я мог сделать это сам в программе на С++?
EDIT: Здесь используется команда, и вывод:
$ valgrind ./myprogram > val.out 2> val.err
*** stack smashing detected ***: ./myprogram terminated
EDIT2: играя с ним немного больше, оказывается, что myprogram, а не valgrind, вызывает печать сообщения, и, как сказано ниже, похоже, что код обнаружения разлома gcc-стека печатает на/dev/tty
Ответы
Ответ 1
Это не написано valgrind, а скорее glibc, а ваша. /myprogram использует 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);
Ниже приведены некоторые релевантные части 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);
Ответ 2
Сообщение, скорее всего, связано с функцией защиты стека GCC или от самого glib. Если он из GCC, он выводится с помощью функции fail()
, которая непосредственно открывается /dev/tty
:
fd = open (_PATH_TTY, O_WRONLY);
_PATH_TTY
не является стандартным, но SingleUnix фактически требует, что /dev/tty
существует.
Ответ 3
Вот пример кода, который выполняет именно то, что было задано (благодаря более ранним ответам, указывающим на меня в правильном направлении). Оба скомпилированы с g++ и будут печатать сообщение на экране, даже когда stdout и stderr будут перенаправлены.
Для 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));
}
Для Windows 7 с помощью 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");
}