Why doesn't dup2 occur in sequential order? - c

Here is a code snippet.
int saved_stdout = dup(1);
int fd = open("file.txt", O_WRONLY | O_CREAT, 0640);
close(1);
dup(fd);
close(fd);
printf("This text should go into the file\n");
//restore stdout
dup2(saved_stdout, 1);
printf("stdout restore");
I am trying to learn about dup and dup2. So I initially connected my file.txt to stdout. So whenever I use printf, I should be writing to file.txt instead of stdout. But I want to restore it back as well once I am done with this usage, so I use dup2 at the end as well.
The problem is that the text "This text should go into the file\n" never actually goes into the file, but gets printed on stdout. Why so? I straced for it, only to find that dup2 call occurs before that printf("This text..."); statement, why so?

The problem may be due to output buffering. stdout is fully buffered if it's not writing to a terminal, so when you redirect it to the file with dup() it will be buffered. Try flushing the output after the printf().
printf("This text should go into the file\n");
fflush(stdout);

I removed my prior answer as it's wrong.... But when you use printf() you're using the FILE * for stdout, inside of which there's a descriptor or something pointing to the terminal. Changing fd 1 is apparently not changing that.
I'm getting inconsistent results with testing, so giving up here. I just want to add that putting this line just after the open() makes it work for me, which highlights the obscure behaviour:
printf("fileno is %i\n", fileno(stdout));
Don't mix FILE * operations such as printf() with file descriptor manipulation. You should use write() with the descriptor.

Related

puts() doesn't flush the buffer in io redirection program

the code as follows:
int main(int argc, char **argv)
{
const char *file = "/tmp/out";
int fd = open(file, O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
int stdout_tmp = dup(1);
close(1);
dup(fd);
puts("hello world!");
// fflush(stdout);
close(1);
close(fd);
dup(stdout_tmp);
puts("redirect completed!");
exit(0);
}
I compile the code succssfully without any warning using gcc10.2.0, Againest my expectation, the two line both are output to the stdout instead "hello world" in the /tmp/out file and "redirect completed!" in stdout. When uncomment the fflush(stdout), it works!
I guess that the puts() doesn't refresh the buffer in user space, after restoring the stdout and exit, the buffer is auto refreshed.
gets() output string with trailing '\n' and the stdout buffer will be auto refreshed when encounter '\n'. Why need to call fflush(stdout) manually?
The stdout file (where puts writes) is line-buffered (i.e. flushes the buffer on newline) only when connected to a terminal (basically when isatty(fileno(stdout)) is true).
When connected to another non-terminal output then it's fully buffered, which means you either need to fill the buffer completely, or call fflush explicitly to flush the buffer.
man 3 setvbuf says:
Normally all files are block buffered.
If a stream refers to a terminal (as stdout normally does), it is line buffered.
Since puts() uses stdout we should expect a flush
(because of the \n).
However, since in your example stdout has not been used before
the redirection, I guess that the buffering behaviour is not chosen
yet.
At your first write attempt to stdout, the underlying file descriptor
is not a terminal anymore but a regular file.
I guess the buffering behaviour is chosen at this moment.
If you add another call to puts() in your example before the
redirection, then the buffering behaviour is chosen for the terminal
and then does not change afterwards when the redirection is performed.
In this case, your example works as you expect (without the explicit
fflush()).
edit
Still in man 3 setvbuf:
When the first I/O operation occurs on a file, malloc(3) is called, and a buffer is obtained.
and further:
The setvbuf() function may only be used after opening a stream and before any other operations have been performed on it.
On linux, this is consistent with your example.
In the MSDN page for setvbuf:
stream must refer to an open file that has not undergone an I/O operation since it was opened.

How to redirect stdout to a file and then restore stdout back?

Here is my code and I can't get it to work.
int pfd = open("file", O_WRONLY, 0777);
int saved = dup(1);
close(1);
dup(pfd);
close(pfd);
printf("This goes into file\n");
// restore it back
dup2(saved, 1);
close(saved);
printf("this goes to stdout");
I have added some edits to my code.
You need to check the return values of your function calls. For most functions, you should check for error conditions. Doing so might have revealed the problem that if you want open() to create the requested file in the event that it does not initially exist, then you need to add the O_CREAT flag.
But that's not your main problem here -- you are dealing with a buffering issue. The output from the first printf() is buffered in memory, so even though file descriptor 1 refers to your file at the time that printf() is called, the data you write do not immediately get flushed to the destination file. You then restore the original stdout file handle, so when the data are actually flushed, they go to the (restored) original stdout. Solve this by fflush()ing before switching stdout back:
int pfd = open("file", O_WRONLY | O_CREAT, 0777);
int saved = dup(1);
close(1);
dup(pfd);
close(pfd);
printf("This goes into file\n");
fflush(stdout); // <-- THIS
// restore it back
dup2(saved, 1);
close(saved);
printf("this goes to stdout");
Note also that dup2() is cleaner and safer for duping a file descriptor onto a specific file descriptor number. You do that when you restore, but you should also do it for the initial redirection.

C restore stdout to terminal

I am working with a multi-thread program.
First I redirect my stdout to a certain file. No problem there (I used dup2(fd, 1) where fd is the file descriptor for the file).
Afterwards, I need to redirect my stdout to the terminal again.
My first approach:
/*Declaration*/
fpost_t stream_sdout;
/*code*/
if ( fgetpos( stdout, &stream_sdout) == -1 )
perror(Error:);
It says illegal seek.
No idea why this is happening.
But if I get this to work, then I only need to use fsetpos(stdout, &stream_stdout) and it should work.
My second idea, was to to copy the stdout using dup2(stdout, 4) to the file descriptor table, at position 4. But that ain't working either.
How can I switch the standard output back to its original destination (terminal, pipe, file, whatever)?
#include <unistd.h>
...
int saved_stdout;
...
/* Save current stdout for use later */
saved_stdout = dup(1);
dup2(my_temporary_stdout_fd, 1);
... do some work on your new stdout ...
/* Restore stdout */
dup2(saved_stdout, 1);
close(saved_stdout);
Before you do the dup2(fd, STDOUT_FILENO), you should save the current open file descriptor for standard output by doing int saved_stdout = dup(STDOUT_FILENO); (letting dup() choose an available file descriptor number for you). Then, after you've finished with the output redirected to a file, you can do dup2(saved_stdout, STDOUT_FILENO) to restore standard output to where it was before you started all this (and you should close saved_stdout too).
You do need to worry about flushing standard I/O streams (fflush(stdout)) at appropriate times as you mess around with this. That means 'before you switch stdout over'.
If the program runs on a Linux environment, you can freopen ("/dev/stdout", "a", stdout).
But if you know that stdout was the terminal, freopen ("/dev/tty", "a", stdout) or the equivalent for other OSs—even Windows.

How to restore standard output's file descriptor?

I need help to understand the file descriptors
So here is my code:
int main()
{
char ch;
close(1);
//now opening a file so that it gets the lowest possible fd i.e. 1
int fd=open("txt",O_RDWR);
//check..
printf("first printtf is executed\n");
scanf("%c",&ch);
printf("ur value is %c\n",ch);
printf("second printf is executed\n");
return 0;
}
in the above program, I tried to redirect the output of printf to the txt file rather than the standard output, i.e. the terminal.
But how to restore the standard output file descriptor so that the printf again works as normal for the second case, i.e the second printtf should give output to the terminal only..
The simplest way to do this would be to duplicate the output descriptor before closing it. You must look at dup.
Before you close it I think you want to dup() it.
When you need it back, you can dup() the dup.
dup will always use the lowest descriptor
int out = dup(1);
close(1);
int fd = open();
...
close(fd);
dup(out);
close(out);
Warning: this is from memory and untested ;-)

Can someone explain what dup() in C does?

I know that dup, dup2, dup3 "create a copy of the file descriptor oldfd"(from man pages). However I can't digest it.
As I know file descriptors are just numbers to keep track of file locations and their direction(input/output). Wouldn't it be easier to just
fd=fd2;
Whenever we want to duplicate a file descriptor?
And something else..
dup() uses the lowest-numbered unused descriptor for the new descriptor.
Does that mean that it can also take as value stdin, stdout or stderr if we assume that we have close()-ed one of those?
Just wanted to respond to myself on the second question after experimenting a bit.
The answer is YES. A file descriptor that you make can take a value 0, 1, 2 if stdin, stdout or stderr are closed.
Example:
close(1); //closing stdout
newfd=dup(1); //newfd takes value of least available fd number
Where this happens to file descriptors:
0 stdin .--------------. 0 stdin .--------------. 0 stdin
1 stdout =| close(1) :=> 2 stderr =| newfd=dup(1) :=> 1 newfd
2 stderr '--------------' '--------------' 2 stderr
A file descriptor is a bit more than a number. It also carries various semi-hidden state with it (whether it's open or not, to which file description it refers, and also some flags). dup duplicates this information, so you can e.g. close the two descriptors independently. fd=fd2 does not.
Let's say you're writing a shell program and you want to redirect stdin and stdout in a program you want to run. It could look something like this:
fdin = open(infile, O_RDONLY);
fdout = open(outfile, O_WRONLY);
// Check for errors, send messages to stdout.
...
int pid = fork(0);
if(pid == 0) {
close(0);
dup(fdin);
close(fdin);
close(1);
dup(fdout);
close(fdout);
execvp(program, argv);
}
// Parent process cleans up, maybe waits for child.
...
dup2() is a little more convenient way to do it the close() dup() can be replaced by:
dup2(fdin, 0);
dup2(fdout, 1);
The reason why you want to do this is that you want to report errors to stdout (or stderr) so you can't just close them and open a new file in the child process. Secondly, it would be a waste to do the fork if either open() call returned an error.
The single most important thing about dup() is it returns the smallest integer available for a new file descriptor. That's the basis of redirection:
int fd_redirect_to = open("file", O_CREAT);
close(1); /* stdout */
int fd_to_redirect = dup(fd_redirect_to); /* magically returns 1: stdout */
close(fd_redirect_to); /* we don't need this */
After this anything written to file descriptor 1 (stdout), magically goes into "file".
Example:
close(1); //closing stdout
newfd=dup(1); //newfd takes value of least available fd number
Where this happens to file descriptors:
0 stdin .--------------. 0 stdin .--------------. 0 stdin
1 stdout =| close(1) :=> 2 stderr =| newfd=dup(1) :=> 1 newfd
2 stderr '--------------' '--------------' 2 stderr
A question arose again: How can I dup() a file descriptor that I already closed?
I doubt that you conducted the above experiment with the shown result, because that would not be standard-conforming - cf. dup:
The dup() function shall fail if:
[EBADF]
The fildes argument is not a valid open file descriptor.
So, after the shown code sequence, newfd must be not 1, but rather -1, and errno EBADF.
see this page, stdout can be aliased as dup(1)...
Just a tip about "duplicating standard output".
On some Unix Systems (but not GNU/Linux)
fd = open("/dev/fd/1", O_WRONLY);
it is equivalent to:
fd = dup(1);
dup() and dup2() system call
•The dup() system call duplicates an open file descriptor and returns the new file
descriptor.
•The new file descriptor has the following properties in common with
the original
file descriptor:
1. refers to the same open file or pipe.
2. has the same file pointer -- that is, both file descriptors share one file pointer.
3. has the same access mode, whether read, write, or read and write.
• dup() is guaranteed to return a file descriptor with the lowest integer value available.It is because of this feature of returning the lowest unused file descriptor available that processes accomplish I/O redirection.
int dup(file_descriptor)
int dup2(file_descriptor1, file_descriptor2)

Resources