understanding stdout, stderr and stdin - linux

root#C6903:/ # ls -l /proc/327/fd/
lrwx------ root root 1970-02-11 01:40 0 -> /dev/null
lrwx------ root root 1970-02-11 01:40 1 -> /dev/null
lrwx------ root root 1970-02-11 01:40 2 -> /dev/null
As i can see, all(0, 1, 2) are pointing to /dev/null. Why? How do they differ internally?
In my case(Coredump environment), the fd's list is something like this.
root#C6903:/ # ls -l /proc/5987/fd/
lr-x------ root root 1970-02-11 01:34 0 -> pipe:[31608]
So stdout and stderr files are not present. In this scenario, if i give a printf(), i am facing undefined behaviour(other opened files are over-written). To solve this,
I am doing something like below:
int check_fd(int fd)
{
return (fcntl(fd, F_GETFD) != -1);
}
...
if (!check_fd(STDOUT_FILENO))
fopen("/dev/null", "w");
if (!check_fd(STDERR_FILENO))
fopen("/dev/null", "w");
This is working well. But am afraid if i am doing something wrong. How can i differentiate between stdout and stderr if i have to open them?

If stdout and stderr have been closed and you want to reopen them, try:
freopen( "/dev/null", "w", stdout );

stdout, stdin, and stderr point to different pipes or sockets according to the way the program was launched. In the first case, they all have been redirected to /dev/null so there is no output whatsoever on the standard output, and as for the input, using /dev/null is a convenient way to pass an empty file.
If you need to differentiate between them, you need to redirect them to different files. In this case, it's not a very good idea to use /dev/null for both. You can for example open two different files: program.stdout and program.stderr and then write in each separately

Related

Disk full log/nginx ¿how to clear?

I need your help, I currently have a full disk.
It happens that I just checked in my ssh
cd/var/log/nginx/
he says ls (He gave me these results)
access.log domain.acc.log
error.log domain.err.log
Then ls -lh (I showed this result)
-rw-r-r-- 1 root root 0 access.log
-rw-r-r-- 1 root root 3.6K error.log
-rw-r-r-- 1 root root 27G domain.acc.log
-rw-r-r-- 1 root root 7.5M domain.err.log
This is where I realized that the part of -rw-r-r-- 1 root root 27G domain.acc.log, has 27 gb?
I would like to eliminate this. could someone help me how to do it? without making a mistake?
I use linux
welcome to Stack Overflow!
This question likely belongs to another community, such as Server Fault and may be migrated.
However, you can empty the domain.acc.log while the process is running (assuming that you don't need to keep the data) by running the command echo "" > /var/log/nginx/domain.acc.log.
To break down what you are doing, the echo "" means you are outputting an empty string, then > takes the input and writes it to a file, overwriting any content in the file.
This is the safest way to empty the log as other processes can continue to write to the file without releasing their file descriptors, and you can be sure that the underlying data is removed even if the path (/var/log/nginx/domain.acc.log) is just a link to the file

Bash: multiple redirection

Early in a script, I see this:
exec 3>&2
And later:
{ $app $conf_file &>$app_log_file & } 1>&3 2>&1
My understanding of this looks something like this:
Create fd 3
Redirect fd 3 output to stderr
(Upon app execution) redirect stdout to fd 3, then redirect stderr to stdout
Isn't that some kind of circular madness? 3>stderr>stdout>3>etc?
I'm especially concerned as to the intention/implications of this line because I'd like to start running some apps using this script with valgrind. I'd like to see valgrind's output interspersed with the app's log statements, so I'm hoping that the default output of stderr is captured by the confusing line above. However, in some of the crashes that have led me to wanting to use valgrind, I've seen glibc errors outputted straight to the terminal, rather than captured in the app's log file.
So, the question(s): What does that execution line do, exactly? Does it capture stderr? If so, why do I see glibc output on the command line when an app crashes? If not, how should I change it to accomplish this goal?
You misread the 3>&2 syntax. It means open fd 3 and make it a duplicate of fd 2. See Duplicating File Descriptors.
In the same way 2>&1 does not mean make fd 2 point to the location of fd 1 it means re-open fd 2 as a duplicate of fd 1 (mostly the same net effect but different semantics).
Also remember that all redirections occur as they happen and that there are no "pointers" here. So 2>&1 1>/dev/null does not redirect standard error to /dev/null it leaves standard error attached to wherever standard output had been attached to (probably the terminal).
So the code in question does this:
Open fd 3 as a duplicate of fd 2
Re-open fd 1 as a duplicate of fd 3
Re-open fd 2 as a duplicate of fd 1
Effectively those lines send everything to standard error (or wherever fd 2 was attached when the initial exec line ran). If the redirections had been 2>&1 1>&3 then they would have swapped locations. I wonder if that was the original intention of that line since, as written, it is fairly pointless.
Not to mention that with the redirection inside the brace list the redirections on the outside of the brace list are fairly useless.
Ok, well let's see what happens in practice:
peter#tesla:/tmp/test$ bash -c 'exec 3>&2; { sleep 60m &>logfile & } 1>&3 2>&1' > stdout 2>stderr
peter#tesla:/tmp/test$ psg sleep
peter 22147 0.0 0.0 7232 836 pts/14 S 15:51 0:00 sleep 60m
peter#tesla:/tmp/test$ ll /proc/22147/fd
total 0
lr-x------ 1 peter peter 64 Jul 8 15:51 0 -> /dev/null
l-wx------ 1 peter peter 64 Jul 8 15:51 1 -> /tmp/test/logfile
l-wx------ 1 peter peter 64 Jul 8 15:51 2 -> /tmp/test/logfile
l-wx------ 1 peter peter 64 Jul 8 15:51 3 -> /tmp/test/stderr
I'm not sure exactly why the author of your script ended up with that line of code. Presumably it made sense to them when they wrote it. The redirections outside the curly braces happen before the redirections inside, so they're both overriden by the &>logfile. Even errors from bash, like command not found would end up in the logfile.
You say you see glibc messages on your terminal when the app crashes. I think your app must be using fd 3 after it starts. i.e., it was written to be started from a script that opened fd 3 for it, or else it opens /dev/tty or something.
BTW, psg is a function I define in my .bashrc:
psg(){ ps aux | grep "${#:-$USER}" | grep -v grep; }
recently updated to:
psg(){ local pids=$(pgrep -f "${#:--u$USER}"); [[ $pids ]] && ps u -p $pids; }
psgw(){ local pids=$(pgrep -f "${#:--u$USER}"); [[ $pids ]] && ps uww -p $pids; }
You need a context first, as in #Peter Cordes example. He provided the context by setting >stdout and 2>stderr first.
I have modified his example a bit.
$ bash -c 'exec 3>&2; { sleep 60m & } 1>&3 2>&1' >stdout 2>stderr
$ ps aux | grep sleep
logan 272163 0.0 0.0 8084 580 pts/2 S 19:22 0:00 sleep 60m
logan 272165 0.0 0.0 8912 712 pts/2 S+ 19:23 0:00 grep --color=auto sleep
$ ll /proc/272163/fd
total 0
dr-x------ 2 logan logan 0 Aug 25 19:23 ./
dr-xr-xr-x 9 logan logan 0 Aug 25 19:23 ../
lr-x------ 1 logan logan 64 Aug 25 19:23 0 -> /dev/null
l-wx------ 1 logan logan 64 Aug 25 19:23 1 -> /tmp/tmp.Vld71a451u/stderr
l-wx------ 1 logan logan 64 Aug 25 19:23 2 -> /tmp/tmp.Vld71a451u/stderr
l-wx------ 1 logan logan 64 Aug 25 19:23 3 -> /tmp/tmp.Vld71a451u/stderr
First, exec 3>&2 sets fd3 to point to stderr file. Then 1>&3 sets fd1 to point to stderr file also. Lastly, 2>&1 sets fd2 to point to stderr file too! (don't get confused with stderr fd2 and in this case stderr just being a random file name)
The reason fd0 is set to /dev/null, I'm guessing, is because the command is run in a non-interactive shell.

Get path to which a command's output has been redirected

Is there a way to find what path a command has had it's output redirected to (if it has been)?
I tried using:
ps -p PID -o cmd
Thinking I could look for a > and extract the path from that, but the output doesn't have that part in it. I'm pretty sure it hasn't just been truncated.
You can use the proc file system /proc/self/fd for this
readlink /proc/self/fd/1
for stdout or 2 for stderr.
If you know the PID, just inspect /proc/ID/fd/1. It should be linked to the actual path:
$ watch date > /tmp/1 &
[1] 27346
$ ls -l /proc/27346/fd/1
l-wx------ 1 choroba users 64 2013-02-15 16:28 /proc/27346/fd/1 -> /tmp/1
Use the lsof (list open files) command to see what files a process has open for writing.
For example:
$ lsof -p 31714
COMMAND PID USER FD TYPE DEVICE SIZE NODE NAME
bash 31714 dogbane 0u CHR 136,4 6 /dev/pts/4
bash 31714 dogbane 1w REG 8,1 15 2032202 /tmp/t
The w in the FD (file descriptor) column means that /tmp/t is open for writing.
How about it?
[root#us04 ~]# ls -l /proc/14170/exe
lrwxrwxrwx 1 root root 0 Feb 15 10:36 /proc/14170/exe -> /usr/sbin/httpd
One more example:
[root#us04 ~]# readlink -f /proc/5352/exe
/sbin/syslogd

How to reroute stdout, stderr back to /dev/tty

I just ssh-ed to some remote server and found that stdout and stderr of all commands/processes I am trying to run in bash is redirected to somewhere.
So, I got following questions
How to detect:
1) Which file stdout, stderr is beeing rerouted in Linux?
and
2) And how reroute by default stdout and stderr back to /dev/tty?
Thank you in advance.
A command that should do literally what you asked for in (2) is
exec >/dev/tty 2>&1
But I suspect that your analysis of the problem is incorrect. It would be useful to see the output of ssh -v ... (where ... is whatever arguments you typed in your original ssh command).
The command:
ls -l /proc/$$/fd/{1,2}
will show you which files are open as stdout (file descriptor 1) and stderr (file descriptor 2).
An answer to your first question could be found in /proc/self/fd. It contains symlinks to the files (or other things, pipes, sockets, etc) that your bash instance is connected to.
root#mammon:~# ls -l /proc/self/fd
total 0
lrwx------ 1 root root 64 May 21 02:18 0 -> /dev/pts/3
lrwx------ 1 root root 64 May 21 02:18 1 -> /dev/pts/3
lrwx------ 1 root root 64 May 21 02:18 2 -> /dev/pts/3
lr-x------ 1 root root 64 May 21 02:18 3 -> /proc/15529/fd/
root#mammon:~# ls -l /proc/self/fd < /dev/null
total 0
lr-x------ 1 root root 64 May 21 02:18 0 -> /dev/null
lrwx------ 1 root root 64 May 21 02:18 1 -> /dev/pts/3
lrwx------ 1 root root 64 May 21 02:18 2 -> /dev/pts/3
lr-x------ 1 root root 64 May 21 02:18 3 -> /proc/15536/fd/
root#mammon:~# ls -l /proc/self/fd | cat
total 0
lrwx------ 1 root root 64 May 21 02:18 0 -> /dev/pts/3
l-wx------ 1 root root 64 May 21 02:18 1 -> pipe:[497711]
lrwx------ 1 root root 64 May 21 02:18 2 -> /dev/pts/3
lr-x------ 1 root root 64 May 21 02:18 3 -> /proc/15537/fd/
root#mammon:~#
In the first example, you can see the first 3 file descriptors (which are the standard output, input, and error, respectively) all point to my pseudo-terminal /dev/pts/3. In the second example I've redirected the input to /dev/null, so the standard input file descriptor points to /dev/null. And in the final example I've sent the output of ls to cat through a pipe, and the standard input file descriptor reflects this. As far as I know there is no way to find which process has the other end of the pipe. In all examples there is the fourth file descriptor that represents the handle that ls has for reading /proc/self/fd. In this case it says /proc/15537 because /proc/self is in fact a symlink to /proc/pid where pid is the PID of the process accessing /proc/self.
It can only be done if your longing shell is started with a pipe to tee command with another console as a parameter.
Let me explain.
If you are logging in /dev/tty1 and someone else is logging in /dev/tty2. If you start your shell (bash) by following command all the STDOUT/STDERR will be rerouted/copied to another shell (/dev/tty2 in this case).
bash 2>&1 | tee /dev/tty2
So, someone sitting in /dev/tty2 will see all of your activity.
If someone logins shell is /bin/bash 2>&1 | tee /dev/tty2 instead of /bin/bash It'll happen every time he logs in. But I am not sure login shell can be set that way.
If someone reroutes all the output of your shell this way you can check it just by checking if any tee is running in background.
ps ax | grep tee
This will output something like
tee /dev/tty2

Less gets keyboard input from stderr?

I'm taking a look at the code to the 'less' utility, specifically how it gets keyboard input. Interestingly, on line 80 of ttyin.c, it sets the file descriptor to read from:
/*
* Try /dev/tty.
* If that doesn't work, use file descriptor 2,
* which in Unix is usually attached to the screen,
* but also usually lets you read from the keyboard.
*/
#if OS2
/* The __open() system call translates "/dev/tty" to "con". */
tty = __open("/dev/tty", OPEN_READ);
#else
tty = open("/dev/tty", OPEN_READ);
#endif
if (tty < 0)
tty = 2;
Isn't file descriptor 2 stderr? If so, WTH?! I thought keyboard input was sent through stdin.
Interestingly, even if you do ls -l * | less, after the file finishes loading, you can still use the keyboard to scroll up and down, but if you do ls -l * | vi, then vi will yell at you because it doesn't read from stdin. What's the big idea? How did I end up in this strange new land where stderr is both a way to report errors to the screen and read from the keyboard? I don't think I'm in Kansas anymore...
$ ls -l /dev/fd/
lrwx------ 1 me me 64 2009-09-17 16:52 0 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 1 -> /dev/pts/4
lrwx------ 1 me me 64 2009-09-17 16:52 2 -> /dev/pts/4
When logged in at an interative terminal, all three standard file descriptors point to the same thing: your TTY (or pseudo-TTY).
$ ls -fl /dev/std{in,out,err}
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdin -> fd/0
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stdout -> fd/1
lrwxrwxrwx 1 root root 4 2009-09-13 01:57 /dev/stderr -> fd/2
By convention, we read from 0 and write to 1 and 2. However, nothing prevents us from doing otherwise.
When your shell runs ls -l * | less, it creates a pipe from ls's file descriptor 1 to less's file descriptor 0. Obviously, less can no longer read the user's keyboard input from file descriptor 0 – it tries to get the TTY back however it can.
If less has not been detached from the terminal, open("/dev/tty") will give it the TTY.
However, in case that fails... what can you do? less makes one last attempt at getting the TTY, assuming that file descriptor 2 is attached to the same thing that file descriptor 0 would be attached to, if it weren't redirected.
This is not failproof:
$ ls -l * | setsid less 2>/dev/null
Here, less is given its own session (so it is no longer a part of the terminal's active process group, causing open("/dev/tty") to fail), and its file descriptor 2 has been changed – now less exits immediately, because it is outputting to a TTY yet it fails to get any user input.
Well... first off, you seem to missing the open() call which opens '/dev/tty'. It only uses file descriptor 2 if the call to open() fails. On a standard Linux system, and probably many Unices, '/dev/tty' exists and is unlikely to cause a fail.
Secondly, the comment at the top provides a limited amount of explanation as to why they fall back to file descriptor 2. My guess is that stdin, stdout, and stderr are pretty much connected to '/dev/tty/' anyway, unless redirected. And since the most common redirections for for stdin and/ or stdout (via piping or < / >), but less often for stderr, odds on are that using stderr would be most likely to still be connect to the "keyboard".
The same question with an answer ultimately from the person who asked it is on linuxquestions although they quote slightly different source from less. And no, I don't understand most of it so I can't help beyond that :)
It appears to be Linux specific functionality that sends keyboard input to FD 2.

Resources