To which file descriptor execve redirect? - c

int main (void) {
int rc=fork();
if(rc==0){
close(1); //close stdout BEFORE opening my file
open("./c.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
//execve "wc"
char *cmd[3];
cmd[0] = strdup("wc"); //file to execuable
cmd[1]=strdup("c.c"); //first arg to command 'wc' -> c.c
cmd[2]=NULL;
execvp(cmd[0], cmd);
}
If I close() stdout, then the output of execve ("wc"), will be in file c.txt but ONLY if I close stdout BEFORE open()ing. If I call it AFTER
open("./c.txt", O_CREAT|O_WRONLY|O_TRUNC, S_IRWXU);
close(1);
then -> wc: write error: Bad file descriptor.
I have read, that for open() (probably in my case for wc output) is OS reaching file descriptor from 0, and so it first find 1 as stdout to printf() to screen. So I need to close() it, in order to use file descriptor from open("./c.txt") for wc. But If that is correct (I do not know where I have understood it correctly), then it would not matter whether I close stdout before or after open() call, does it? Once it is closed, OS has no other FD to use as output. I maybe does not understand it clearly.
question: why must be fd1 closed first in order to make redirection to c.txt?

A few concepts to establish first.
stdout is one of the streams automatically opened as part of program startup. The program startup code uses file descriptor 1 for stdout).
execve creates a new process with the same open file descriptors as the parent/calling process (there are exceptions and naunces which can be read from the execve man page.
open will look for the lowest available file descriptor to use.
Ok, so now to your code.
Case 1 - close, open, execve
In this case the following sequence of events happens:
Program starts with stdout=>fd 1.
close(1) makes fd 1 available.
open("c.txt") returns 1 which effectively redirects stdout to the file.
execve creates a new process which has 1 open and redirected to the file.
wc writes to fd 1 which now ends up in the file.
Case 2 - open,close,execve
In this case the following sequence of events happens:
Program starts with stdout=>fd 1.
open("c.txt")is called but fd 1 is not available so it returns 2.
close(1) means there is now effectively no stdout.
execve creates a new process which has no open stream on fd 1 (ie no stdout).
wc tries to write to fd 1 and gets a bad file descriptor error since fd 1 is not open.

Related

Piping the stdout of a command to the stdin of another other using shared file and dup2()

I am writing a porgram with takes two arguments - the name of commands. The program should redirect the output of the first to a file 'tmp' than execute it, than redirect the stdin of the second command to 'tmp' and execute the second command.
#include<unistd.h>
#include<fcntl.h>
#include<wait.h>
#include<stdio.h>
int main(int argc, char** argv){
int fd = open("tmp", O_RDWR |O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR);
int cpid = fork();
if(cpid == 0){
dup2(fd, 1);
execlp(argv[1], "", NULL);
}
wait(NULL);
//If we uncoment this line the program gives correct output
//fd = open("tmp", O_RDWR, S_IRUSR | S_IWUSR);
dup2(fd, 0);
execlp(argv[2], "", NULL);
}
However when i run the program like ./main ls wc instead of
5 5 50
i get output 0 0 0 which means the wc command reads 0 bytes from stdin.
But if I instead create the file descriptor anew on the same file 'tmp' the program gives the correct output. How can this behaviour be explained?
This question is basically a duplicate of Can anyone explain a simple description regarding 'file descriptor' after fork()? but since this is a bit subtle I'll explain this specific case.
The process opens a file. This creates a file description. File descriptions are an intermediate concept between files and file descriptors. They are not directly exposed in the Unix API, but they have an important property in addition to the file that they point to, which we'll see in a minute.
The child writes to this file description. The parent waits.
The parent reads from the file description.
At the end of step 2, the file position on this file description is the end of the file. So at step 3, the parent starts reading at the end of the file.
If you add a call to rewind(fd) after wait(NULL), the child will read from the beginning of the file.
If you open the same file with a new open call, this creates a new file description. open puts the position on the new file description at the beginning of the file unless you set append mode.
The same file descriptions can be accessed through any number of file descriptors, potentially in different processes. The file position is a property of the file description, so anything that moves it (reading, writing, seeking) through one file descriptor also moves it for the other file descriptors, even in different processes.

stdout redirection does not work if stdout is a file

I am using code like the following to redirect stdout before calling a noisy function from an external library written in Fortran:
// copy standard output
out = dup(STDOUT_FILENO);
// close standard output
close(STDOUT_FILENO);
// use log file as standard output
log = open(log_file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
if(log != STDOUT_FILENO)
fprintf(stderr, "could not create log file %s", log_file);
// call the library function that uses a lot of printf
func();
// restore original standard output
dup2(out, STDOUT_FILENO);
// close copy of standard output
close(out);
To summarise my intention of the above code snippet: copy stdout, close stdout (frees file descriptor 0), open file (uses lowest file descriptor = 0 = stdout), run code with redirected stdout, and reset stdout.
This works perfectly well when I run my code using the terminal as stdout. However, when I set stdout to a file (using $ mycode > myfile.txt) the redirection fails, and I end up with the output of func() in myfile.txt instead of the log file. How is this possible?
You need to do fflush(stdout) before you restore the original stdout with dup2.
The reason it works with a terminal is because stdout is line buffered for a terminal. So your output gets flushed immediately to the redirected file. But when you start your program with stdout to a file, stdout becomes fully buffered, so your func output will be in a buffer waiting to be flushed. When you restore the original stdout without flushing, the output gets written to the original stdout when the program exits.

What does dup2() do in C

I looked it up in the man page but I still don't get it...
let's say you have dup2(f1,0). Does that switch filedesc.1 with stdin and then locks stdin?
dup2 doesn't switch the file descriptors, it makes them equivalent. After dup2(f1, 0), whatever file was opened on descriptor f1 is now also opened (with the same mode and position) on descriptor 0, i.e. on standard input.
If the target file descriptor (here, 0) was open, it is closed by the dup2 call. Thus:
before after
0: closed, f1: somefile 0: somefile, f1:somefile
0: otherfile, f1: somefile 0: somefile, f1:somefile
No locking is involved.
dup2 is useful (among other things) when you have part of a program that reads or write from the standard file descriptors. For example, suppose that somefunc() reads from standard input, but you want it to read from a different file from where the rest of the program is getting its standard input. Then you can do (error checking omitted):
int save_stdin = dup(0);
int somefunc_input_fd = open("input-for-somefunc.data", O_RDONLY);
dup2(somefunc_input_fd, 0);
/* Now the original stdin is open on save_stdin, and input-for-somefunc.data on both somefunc_input_fd and 0. */
somefunc();
close(somefunc_input_fd);
dup2(save_stdin, 0);
close(save_stdin);

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.

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