popen3

I was a bit confused; there didn't seem to be a popen3 function for c/c++ anywhere.  Sure, there are some popen2 out there, but to my eye all those I found were buggy. And what about STDERR?

Remember, if you're using this, to make heavy use of O_CLOEXEC (that's backwards! I think its broken that you have to say which you want closed, rather than having to specifically say which you want to remain open). Or you could add some horrid forced closing of all possible file handles (there is a fast, portable way to do that?).

I tend to immediately set the returned descriptors to O_NONBLOCKING and that works great.

Here's a bowdlerised version of my popen3():

 3 int popen3(int fd[3],const char **const cmd) {
 4         int i, e;
 5         int p[3][2];
 6         pid_t pid;
 7         // set all the FDs to invalid
 8         for(i=0; i<3; i++)
 9                 p[i][0] = p[i][1] = -1;
10         // create the pipes
11         for(int i=0; i<3; i++) 
12                 if(pipe(p[i]))
13                         goto error;
14         // and fork
15         pid = fork();
16         if(-1 == pid)
17                 goto error;
18         // in the parent?
19         if(pid) {
20                 // parent
21                 fd[STDIN_FILENO] = p[STDIN_FILENO][1];
22                 close(p[STDIN_FILENO][0]);
23                 fd[STDOUT_FILENO] = p[STDOUT_FILENO][0];
24                 close(p[STDOUT_FILENO][1]);
25                 fd[STDERR_FILENO] = p[STDERR_FILENO][0];
26                 close(p[STDERR_FILENO][1]);
27                 // success
28                 return 0;
29         } else {
30                 // child
31                 dup2(p[STDIN_FILENO][0],STDIN_FILENO);
32                 close(p[STDIN_FILENO][1]);
33                 dup2(p[STDOUT_FILENO][1],STDOUT_FILENO);
34                 close(p[STDOUT_FILENO][0]);
35                 dup2(p[STDERR_FILENO][1],STDERR_FILENO);
36                 close(p[STDERR_FILENO][0]);
37                 // here we try and run it
38                 execv(*cmd,const_cast<char*const*>(cmd));
39                 // if we are there, then we failed to launch our program
40                 perror("Could not launch");
41                 fprintf(stderr," \"%s\"\n",*cmd);
42                 _exit(EXIT_FAILURE);
43         }
44 error:
45         // preserve original error
46         e = errno;
47         for(i=0; i<3; i++) {
48                 close(p[i][0]);
49                 close(p[i][1]);
50         }
51         errno = e;
52         return -1;
53 }
54 
Comments