📄 dpopen.c
字号:
// -*- 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
*
*/
#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 */
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 = (dpipe_t *)malloc(sizeof(dpipe_t));
if (chain == NULL) {
fclose(stream);
return NULL;
}
/* Store necessary info for dpclose, and adjust chain header */
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);
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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -