Search code examples
cforkx86-64

how can I communicate (eg. run commands via write) with a child process when execv("/bin/sh", NULL) was called in C?


I am trying to make my program able to interact with another program which takes input. I tried that with fork() by executing execv("/bin/sh", NULL) in the child process. Now it should be replaced with the new process, /bin/sh. Now I want my parent process to communicate with /bin/sh like send commands. I tried that using pipes, but I guess I am doing something wrong. Here is my code:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main() {
  int pipefd[2];
  pipe(pipefd);
  pid_t pid = fork();

  if(pid == 0) {               // child code
    close(0);                  // close stdin fd
    dup(pipefd[0]);            // duplicate the pipe fd (will return 0)
    close(pipefd[1]);          // close the pipefd 1 (I dont need to write for child now)
    execv("/bin/sh", NULL);    // execute /bin/sh
  } else {                     // parent code
    close(1);                  // close stdout fd
    dup(pipefd[1]);            // duplicate the pipe fd (will return 1)
    close(pipefd[0]);          // close the pipefd 0 (I dont need to read right now)
  }

  write(0, "touch xxx\n", 10); // try to run a command with the parent process
  waitpid(-1, WNOHANG, 0);     // wait for the child to exit
}

I tried to write to stdin from the parent, because I thought /bin/sh takes input from stdin I can write to it. Sadly my command isn't executed. After that I can enter something, but it does nothing. Here is the output of strace:

execve("./a.out", ["./a.out"], 0x7fffffffe870 /* 41 vars */) = 0
brk(NULL)                               = 0x555555559000
arch_prctl(0x3001 /* ARCH_??? */, 0x7fffffffe710) = -1 EINVAL (Das Argument ist ungültig)
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (Datei oder Verzeichnis nicht gefunden)
openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
newfstatat(3, "", {st_mode=S_IFREG|0644, st_size=210476, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 210476, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7ffff7f92000
close(3)                                = 0
openat(AT_FDCWD, "/usr/lib/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0`|\2\0\0\0\0\0"..., 832) = 832
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
pread64(3, "\4\0\0\0@\0\0\0\5\0\0\0GNU\0\2\0\0\300\4\0\0\0\3\0\0\0\0\0\0\0"..., 80, 848) = 80
pread64(3, "\4\0\0\0\24\0\0\0\3\0\0\0GNU\0T\246\344\4\347\334\35\347\301CJ\0\267\261\2552"..., 68, 928) = 68
newfstatat(3, "", {st_mode=S_IFREG|0755, st_size=2154488, ...}, AT_EMPTY_PATH) = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f90000
pread64(3, "\6\0\0\0\4\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0@\0\0\0\0\0\0\0"..., 784, 64) = 784
mmap(NULL, 1884632, PROT_READ, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7ffff7dc3000
mmap(0x7ffff7de9000, 1359872, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x26000) = 0x7ffff7de9000
mmap(0x7ffff7f35000, 311296, PROT_READ, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x172000) = 0x7ffff7f35000
mmap(0x7ffff7f81000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1bd000) = 0x7ffff7f81000
mmap(0x7ffff7f87000, 33240, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7ffff7f87000
close(3)                                = 0
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7ffff7dc1000
arch_prctl(ARCH_SET_FS, 0x7ffff7f91580) = 0
mprotect(0x7ffff7f81000, 12288, PROT_READ) = 0
mprotect(0x555555557000, 4096, PROT_READ) = 0
mprotect(0x7ffff7ffb000, 8192, PROT_READ) = 0
munmap(0x7ffff7f92000, 210476)          = 0
pipe([3, 4])                            = 0
clone(child_stack=NULL, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7ffff7f91850) = 29114
close(1)                                = 0
dup(4)                                  = 1
close(3)                                = 0
write(0, "touch xxx\n", 10touch xxx
)             = 10
wait4(-1,

I hope someone can help.


Solution

  • Your parent process (the else block) is closing and replacing fd 1, corresponding to stdout, meaning writes to its own stdout will go to the stdin of the child. But you write to the parent's unmodified fd 0, stdin.

    Change the code so the parent writes to fd 1, stdout, which is attached to the pipe that leads to the child's fd 0, stdin.

    There are other issues here as well (which compiling with any level of warnings enabled would make clear). Fix those, and this should work (assuming, as you state, your system is liberal in allowing execv to accept NULL as the second argument).