Can you fool isatty AND log stdout and stderr separately? Can you fool isatty AND log stdout and stderr separately? python python

Can you fool isatty AND log stdout and stderr separately?


Like this?

% ./challenge.py >stdout 2>stderr% cat stdout This is a real tty :)standard output data% cat stderr standard error data

Because I cheated a little bit. ;-)

% echo $LD_PRELOAD/home/karol/preload.so

Like so...

% gcc preload.c -shared -o preload.so -fPIC

I feel dirty now, but it was fun. :D

% cat preload.c#include <stdlib.h>int isatty(int fd) {    if(fd == 2 || fd == 1) {        return 1;    }    return 0;}char* ttyname(int fd) {    static char* fake_name = "/dev/fake";    if(fd == 2 || fd == 1) {        return fake_name;    }    return NULL;}


For a simpler use-case (e.g. development testing), use strace (linux) or dtruss (OSX). Of course that won't work in privileged process.

Here's a sample, you can distinguish stdout fd1 from stderr fd2:

$ strace -ewrite python2 test.py[snip]write(1, "This is a real tty :)\n", 22This is a real tty :)) = 22write(2, "standard error data", 19standard error data)     = 19write(1, "standard output data", 20standard output data)    = 20+++ exited with 0 +++

In the sample above you see each standard xxx data doubled, because you can't redirect stdout/stderr. You can, however ask strace to save its output to a file.

On a theoretical side, if stdout and stderr refer to the same terminal, you can only distinguish between the 2 while still in the context of your process, either in user mode (LD_PRELOAD), or kernel space (ptrace interface that strace tool uses). Once the data hits actual device, real of pseudo, the distinction is lost.


You can always allocate Pseudo-TTY, that's what screen does.

In Python you'd access it using pty.openpty()

This "master" code passes your test:

import subprocess, pty, osm, s = pty.openpty()fm = os.fdopen(m, "rw")p = subprocess.Popen(["python2", "test.py"], stdin=s, stdout=s, stderr=s)p.communicate()os.close(s)print fm.read()

Of course if you want to distinguish between stdin/out/err, your "slave" process will see different PYT names:

inp = pty.openpty()oup = pty.openpty()erp = pty.openpty()subprocess.Popen([command, args], stdin=inp[1], stdout=uop[1], stderr=erp[1])