In C how do you redirect stdin/stdout/stderr to files when making an execvp() or similar call?
Asked Answered
P

3

24

I have the following code:

pid_t pid = fork();
if (pid == -1)
{
    // ...
}
else if (pid == 0)
{
    stdin = someopenfile;
    stdout = someotherfile;
    stderr = somethirdopenfile;
    execvp(args[0], args);
    // handle error ...
}
else
{
    // ...
}

The problem is, the input/output of the execvp() call is still the console, rather than the files. Clearly I am doing something wrong, what is the right way to do this?

Phraseology answered 27/1, 2013 at 1:13 Comment(0)
A
51

The right way to do it is to replace the file descriptors STDIN_FILENO, STDOUT_FILENO and STDERR_FILENO with the opened files using dup2(). You should also then close the original files in the child process:

else if (pid == 0)
{
    dup2(fileno(someopenfile), STDIN_FILENO);
    dup2(fileno(someotherfile), STDOUT_FILENO);
    dup2(fileno(somethirdopenfile), STDERR_FILENO);
    fclose(someopenfile);
    fclose(someotheropenfile);
    fclose(somethirdopenfile);
    execvp(args[0], args);
    // handle error ...
}
Asoka answered 27/1, 2013 at 1:20 Comment(5)
Most syscalls can fail. dup2(2) is no exception. In particular it can fail with errno == EMFILE when reaching the file descriptor limit (which you could lower with ulimit bash builtin or setrlimit(2) syscall). Read kernel.org/doc/man-pages/online/pages/man2/dup2.2.htmlVarden
Don't original stdin/out/err need to be closed before doing this?Slack
@rustyx: No, dup2() closes the destination file descriptor first if it's open.Asoka
How do you revert this?Marxmarxian
@Marxmarxian I'm sure you figured out by now, but the correct answer is using dup(), this duplicated descriptor can then be used in the same manner with dup2() to replace current file descriptors ones once more.Postpone
G
13

Take a look at freopen function.

I had to do something similar with stdout and wrote two functions that do the work for me:

static int fd;
static fpos_t pos;

void switchStdout(const char *newStream)
{
  fflush(stdout);
  fgetpos(stdout, &pos);
  fd = dup(fileno(stdout));
  freopen(newStream, "w", stdout);
}

void revertStdout()
{
  fflush(stdout);
  dup2(fd, fileno(stdout));
  close(fd);
  clearerr(stdout);
  fsetpos(stdout, &pos);
}
Gluttonize answered 27/1, 2013 at 1:15 Comment(0)
M
2

You can use this when stdin , stdout , stderr are terminal-

//change stdin,stdout,stderr
    freopen("new_stdin","r",stdin);
    freopen("new_stdout","r",stdout);
    freopen("new_stderr","r",stderr);

    //----do something;

//reset stdin,stdout,stderr
     freopen("/dev/tty","r",stdin);
     freopen("/dev/tty","r",stdout);
     freopen("/dev/tty","r",stderr);
Maunder answered 10/3, 2017 at 13:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.