Welcome, guest | Sign In | My Account | Store | Cart

commanding via a duplex pipe stream.

C, 201 lines
  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);
}
Created by J Y on Wed, 20 May 2009 (MIT)
C recipes (32)
J Y's recipes (21)

Required Modules

  • (none specified)

Other Information and Tasks