⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 dpopen.c

📁 管道通讯
💻 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 + -