Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

execv* and write in stdin

Tags:

c

unix

pipe

I'm trying to run a program with a specific standard input. I succeed by using a file descriptor of a file where there is what I want to put in the stdin, but I fail to write directly on the stdin :

$cat input.test
echo Hello
$

Code C :

int main(int argc, char **argv)
{
  int fd = 0;

  fd = open("input.test", O_CREAT);
  close(STDIN_FILENO);
  dup2(fd, STDIN_FILENO);
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
}

That works :

$./a.out
Hello
$

But if I try to write directly on the STDIN using pipe the program displays nothing and keeps running :

int main(int argc, char **argv)
{
  int fds[2];

  pipe(fds);
  close(STDIN_FILENO);
  dup2(fds[1], STDIN_FILENO);
  write(fds[1], "echo Hello;", 11); // Résults are identics with fds[0]
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
}

Thanks for your help

Cordially, Bastien.

EDIT Problem solved:

Thanks for your answers, here the code which works :

int main(void)
{
    int fd[2];
    pid_t pid;

    if (pipe(fd) < 0)
        return EXIT_FAILURE;

    if ((pid = fork()) < 0)
        return EXIT_FAILURE;
    else if (pid != 0) { /* father */
        close(fd[1]);
        dup2(fd[0], STDIN_FILENO);
        execlp("bash", "bash", (char *)0);
    } else { /* son */
        close(fd[0]);
        write(fd[1], "echo hello\n", 11);
    }

    return EXIT_SUCCESS;
}
like image 507
Bastien Avatar asked Nov 11 '11 11:11

Bastien


2 Answers

You need to dup the read side of the pipe to stdin, not the write side. (And write to the write side, obviously.)

#include <unistd.h>
#include <string.h>
#include <stdio.h>
int main(int argc, char **argv)
{
  int fds[2];
  char cmd[] = "echo hello\nexit\n";

  pipe(fds);
  close(STDIN_FILENO);
  dup2(fds[0], STDIN_FILENO);
  write(fds[1], cmd, strlen(cmd));
  char *const args[] = { "bash", NULL };
  execvp("bash", args);
  return 0;
}

Make sure you check the return values of all those functions though, you'll never manage to debug your code if you don't.

like image 121
Mat Avatar answered Oct 08 '22 10:10

Mat


execv and friends replace the current running program with the specified one; they do not return - execution continues at the start of new program instead.

So what you normally do is fork and, in one of the forks, call execv. You then read and write through the pipe from your program continuing in the other fork. There are usually popen functions to do this in most languages; sadly in POSIX the popen() is strictly read or write and not bidirectional.

Luckily, I've made, tested and published a popen3 function. This gives you back three file descriptors - one for stdin to the process, and two for stdout and stderr. You can then use write() on the stdin.

like image 24
Will Avatar answered Oct 08 '22 09:10

Will