commanding via a duplex pipe stream.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | // -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
// vim:tabstop=4:shiftwidth=4:expandtab:
/**
* @file dpopen.c
*
* Implementation of a duplex pipe stream.
*
* @version 1.10, 2004/08/31
* @author Wu Yongwei
*
* @ref http://www.ibm.com/developerworks/cn/linux/l-pipebid/index.html
*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#ifdef _REENTRANT
#include <pthread.h>
static pthread_mutex_t chain_mtx = PTHREAD_MUTEX_INITIALIZER;
#endif
#include "dpopen.h"
/** Struct to store duplex-pipe-specific information. */
struct dpipe_chain {
FILE *stream; ///< Pointer to the duplex pipe stream
pid_t pid; ///< Process ID of the command
struct dpipe_chain *next; ///< Pointer to the next one in chain
};
/** Typedef to make the struct easier to use. */
typedef struct dpipe_chain dpipe_t;
/** Header of the chain of opened duplex pipe streams. */
static dpipe_t *chain_hdr;
/**
* Initiates a duplex pipe stream from/to a process.
*
* Like \e popen, all previously #dpopen'd pipe streams will be closed
* in the child process.
*
* @param command the command to execute on \c sh
* @return a pointer to an open stream on successful
* completion; \c NULL otherwise
*/
FILE *dpopen(const char *command)
{
int fd[2], parent, child;
pid_t pid;
FILE *stream;
dpipe_t *chain;
/* Create a duplex pipe using the BSD socketpair call */
if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0)
return NULL;
parent = fd[0];
child = fd[1];
/* Fork the process and check whether it is successful */
if ( (pid = fork()) < 0) {
close(parent);
close(child);
return NULL;
}
if (pid == 0) { /* child */
/* Close the other end */
close(parent);
/* Duplicate to stdin and stdout */
if (child != STDIN_FILENO)
if (dup2(child, STDIN_FILENO) < 0) {
close(child);
return NULL;
}
if (child != STDOUT_FILENO)
if (dup2(child, STDOUT_FILENO) < 0) {
close(child);
return NULL;
}
/* Close this end too after it is duplicated to standard I/O */
close(child);
/* Close all previously opened pipe streams, as popen does
----why the prviously opend pipes are closed?
----each time this func is called, a new chain_hdr is added into the global static variable, as well as a new child process and the **input stream** is forked.
the chain is used to manage all the input streams for the parent process: the parent process is responsible for all the streams. Accturally, the parent need to close the stream(dpclose).
ONly the STDIN and STDOUT is valuable to the child proces, so we close all other streams.
*/
for (chain = chain_hdr; chain != NULL; chain = chain->next)
close(fileno(chain_hdr->stream));
/* Execute the command via sh */
execl("/bin/sh", "sh", "-c", command, NULL);
/* Exit the child process if execl fails */
_exit(127);
} else { /* parent */
/* Close the other end */
close(child);
/* Open a new stream with the file descriptor of the pipe */
stream = fdopen(parent, "r+");
if (stream == NULL) {
close(parent);
return NULL;
}
/* Allocate memory for the dpipe_t struct. chain is a static variable, which is allocated globally */
chain = (dpipe_t *)malloc(sizeof(dpipe_t));
if (chain == NULL) {
fclose(stream);
return NULL;
}
/* Store necessary info for dpclose, and adjust chain header : to the end of the chain*/
chain->stream = stream;
chain->pid = pid;
#ifdef _REENTRANT
pthread_mutex_lock(&chain_mtx);
#endif
chain->next = chain_hdr;
chain_hdr = chain;
#ifdef _REENTRANT
pthread_mutex_unlock(&chain_mtx);
#endif
/* Successfully return here */
return stream;
}
}
/**
* Closes a duplex pipe stream from/to a process.
*
* @param stream pointer to a pipe stream returned from a previous
* #dpopen call
* @return the exit status of the command if successful; \c -1
* if an error occurs
*/
int dpclose(FILE *stream)
{
int status;
pid_t pid, wait_res;
dpipe_t *cur;
dpipe_t **ptr;
/* Search for the stream starting from chain header */
#ifdef _REENTRANT
pthread_mutex_lock(&chain_mtx);
#endif
ptr = &chain_hdr;
while ( (cur = *ptr) != NULL) { /* Not end of chain */
if (cur->stream == stream) { /* Stream found */
pid = cur->pid;
*ptr = cur->next;
#ifdef _REENTRANT
pthread_mutex_unlock(&chain_mtx);
#endif
free(cur);
//the structure is freed, but the chain is not linked again??????????????????????????????/
if (fclose(stream) != 0)
return -1;
do {
wait_res = waitpid(pid, &status, 0);
} while (wait_res == -1 && errno == EINTR);
if (wait_res == -1)
return -1;
return status;
}
ptr = &cur->next; /* Check next */
}
#ifdef _REENTRANT
pthread_mutex_unlock(&chain_mtx);
#endif
errno = EBADF; /* If the given stream is not found */
return -1;
}
/**
* Flushes the buffer and sends \c EOF to the process at the other end
* of the duplex pipe stream.
*
* @param stream pointer to a pipe stream returned from a previous
* #dpopen call
* @return \c 0 if successful; \c -1 if an error occurs
*/
int dphalfclose(FILE *stream)
{
/* Ensure all data are flushed */
if (fflush(stream) == EOF)
return -1;
/* Close pipe for writing */
return shutdown(fileno(stream), SHUT_WR);
}
|