Subversion Repositories DevTools

Rev

Rev 267 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
267 dpurdie 1
/*============================================================================
2
**
3
**    ERG TRANSIT SYSTEMS      Licensed software
4
**    (C) 2008                 All rights reserved
5
**
6
**============================================================================
7
**
8
**  Project/Product : 
9
**  Filename        : pipe.c
10
**  Author(s)       : DDP
11
**
12
**  Description     : Unix program to
13
**                      Create an other process
14
**                      Monitor its stdout/stderr
15
**                      Tag data with STDOUT or STDERR
16
**                      Write it out my STDOUT
17
**
18
**                    Used to help Perl monitor the status of
19
**                    complex processes. Current Perl IPC has
20
**                    problems monitoring both STDOUT and STDERR
21
**                    in cases where the output s non-trivial
22
**
23
**                    Only a problem for WIN32 under Perl as Open3
24
**                    cannot be used to read both streams - with complete
25
**                    sucess
26
**
27
** Usage            : stdmux <options>* command <command args>*
28
**                    Where options are:
29
**                      -out:<text>     - Text to prefix stdout. Default is "STDOUT:"
30
**                      -err:<text>     - Text to prefix stderr. Default is "STDERR:"
31
**                    Command           - Users command
32
**                    command args      - Arguments passed to the command
33
**
34
**  Information     :
35
**   Compiler       : ANSI C
36
**   Target         : UNIX ONLY
37
**
38
**
39
**==========================================================================*/
40
 
41
#include <stdio.h>
42
#include <unistd.h>
43
#include <fcntl.h>
44
#include <stdlib.h>
45
#include <sys/wait.h>
46
#include "string.h"
47
#include "strings.h"
48
 
49
#define MAX_LINE    1024                /* Limit of user lines */
50
 
51
/*
52
**  A structure to contain the data from stdin and stdout before
53
**  it is printed to stdout, with a prefix
54
*/
55
typedef struct proc {
56
    char    *name;              /* Prefix to add */
57
    size_t  name_len;           /* Length of *name */
58
    char    *ptr;               /* Current pointer */
59
    char    *buf;               /* Buffer */
60
    size_t  size;               /* Sise of above */
61
} proc;
62
 
63
char buf[MAX_LINE];
64
char buf1[MAX_LINE];
65
char buf2[MAX_LINE];
66
 
67
proc    line_out = {"STDOUT:", 7, buf1, buf1, sizeof(buf1) } ;
68
proc    line_err = {"STDERR:", 7, buf2, buf2, sizeof(buf2) } ;
69
 
70
void proc_data( char *buf, size_t nread, proc *p );
71
void flush_data( proc *p );
72
 
73
int main (int argc, char **argv)
74
{
75
    int     filedes1[2], filedes2[2], filedes3[2];
76
    int     pid;
77
    fd_set  fds;
78
    int     maxfd;
79
    int     nready;
80
    int     stderr_isopen;
81
    int     stdout_isopen;
82
    int     argp;
83
 
84
    /*
85
    **  Process command line arguments
86
    **  Options for this program will be located first
87
    */
88
    for ( argp = 1; argp < argc ; argp++ )
89
    {
90
        /*
91
        **  Scan until first non option argument
92
        */
93
        if ( *argv[argp] != '-' )
94
            break;
95
 
96
        if ( strncasecmp( argv[argp], "-OUT:", 5 ) == 0)
97
        {
98
            line_out.name = argv[argp] + 5;
99
        }
100
        else if ( strncasecmp( argv[argp], "-ERR:", 5 ) == 0)
101
        {
102
            line_err.name = argv[argp] + 5;
103
        }
104
        else
105
        {
106
            fprintf(stderr, "Unknown option: %s\n", argv[argp] );
107
            exit(-1);
108
        }
109
    }
110
 
111
    /*
112
    **  Fixup the string lenghts
113
    */
114
    line_out.name_len = strlen( line_out.name);
115
    line_err.name_len = strlen( line_err.name);
116
 
117
    /*
118
    **  Need at least one more argument - the programto run
119
    */
120
    if ( argp >= argc )
121
    {
122
      fputs("Insufficient number of arguments given\n", stderr);
123
      fputs("Need at least the name of the program\n", stderr);
124
      exit(-1);
125
    }
126
 
127
 
128
  /* Argument checking. */
129
 
130
  if (argc <= 1)
131
    {
132
      fputs("Insufficient number of arguments given\n", stderr);
133
      fputs("Need at least the name of the program\n", stderr);
134
      exit(-1);
135
    }
136
 
137
  /* Make our pipes. */
138
 
139
  if (pipe(filedes1) == -1)
140
    {
141
      perror ("pipe");
142
      exit(-1);
143
    }
144
 
145
  if (pipe(filedes2) == -1)
146
    {
147
      perror ("pipe");
148
      exit(-1);
149
    }
150
 
151
  if (pipe(filedes3) == -1)
152
    {
153
      perror ("pipe");
154
      exit(-1);
155
    }
156
 
157
 
158
  /* Fork a child */
159
 
160
    if ((pid = fork()) == 0)
161
    {
162
        /*
163
        **    Child
164
        */
165
        dup2(filedes1[0], fileno(stdin));     /* Copy the reading end of the pipe. */
166
        dup2(filedes2[1], fileno(stdout));    /* Copy the writing end of the pipe */
167
        dup2(filedes3[1], fileno(stderr));    /* Copy the writing end of the pipe */
168
 
169
        close( filedes1[1] );
170
        close( filedes2[0] );
171
        close( filedes3[0] );
172
 
173
         /* If execv() returns at all, there was an error. */
174
 
175
        if (execv(argv[argp], &argv[argp]) == -1)
176
        {
177
            perror("execv");
178
            exit(128);
179
        }
180
    }
181
    else if (pid == -1)
182
    {
183
        perror("fork");
184
        exit(128);
185
    }
186
    else
187
    {
188
        /*
189
        **    Parent
190
        */
191
 
192
        int     stat_loc;
193
        pid_t   result;
194
        close( filedes1[0] );
195
        close( filedes2[1] );
196
        close( filedes3[1] );
197
 
198
        /*
199
        **  Read from childs STDERR and STDOUT as the data becomes
200
        **  available
201
        **
202
        **  Prefex the data with an indication of the stream that
203
        **  it came from
204
        */
205
 
206
        /*
207
        ** Set up the select information
208
        */
209
        maxfd = (filedes2[0] > filedes3[0] ? filedes2[0] : filedes3[0]) + 1;
210
 
211
        stdout_isopen = 1;
212
        stderr_isopen = 1;
213
        while(stdout_isopen || stderr_isopen)
214
        {
215
 
216
	        /* Set up polling using select. */
217
		    FD_ZERO(&fds);
218
		    if (stdout_isopen)
219
                FD_SET(filedes2[0],&fds);
220
		    if (stderr_isopen)
221
                FD_SET(filedes3[0],&fds);
222
 
223
		    /* Wait for some input. */
224
		    nready = select(maxfd, &fds, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0);
225
            if ( nready < 0 )
226
            {
227
                break;
228
            }
229
		    /* If either descriptor has some input, read it */
230
 
231
		    if( FD_ISSET(filedes2[0], &fds))
232
		    {
233
			    int nread = read(filedes2[0], buf, sizeof(buf));
234
			    /* If error or eof, terminate. */
235
			    if(nread < 1){
236
        		    stdout_isopen = 0;
237
			    }
238
                proc_data( buf, nread, &line_out);
239
		    }
240
 
241
		    if( FD_ISSET(filedes3[0], &fds))
242
		    {
243
			    int nread = read(filedes3[0], buf, sizeof(buf));
244
			    /* If error or eof, terminate. */
245
			    if(nread < 1){
246
        		    stderr_isopen = 0;
247
			    }
248
                proc_data( buf, nread, &line_err );
249
		    }
250
        }
251
 
252
        flush_data( &line_out);
253
        flush_data( &line_err);
254
 
255
        /*
256
        **  Determine child exit status
257
        */
258
        result = waitpid(pid, &stat_loc, 0 );
259
        if ( result == pid )
260
        {
261
            if ( WIFEXITED(stat_loc)  )
262
                exit( WEXITSTATUS(stat_loc));
263
        }
264
    }
265
 
266
    /*
267
    **  Bad pid (and keep compiler happy)
268
    */
269
    return (-1);
270
}
271
 
272
/*----------------------------------------------------------------------------
273
** FUNCTION           : proc_data
274
**
275
** DESCRIPTION        : Multiplex data to stdout
276
**                      Prefix each line with a 'TAG' that indicates where
277
**                      it came from.
278
**
279
**
280
** INPUTS             : buf                     - Data to process
281
**                      nread                   - Bytes of data
282
**                      p                       - p structure
283
**
284
** RETURNS            : Nothing
285
**
286
----------------------------------------------------------------------------*/
287
 
288
void proc_data( char *buf, size_t nread, proc *p )
289
{
290
    while ( nread-- )
291
    {
292
        char ch = *buf++;
293
 
294
        if ( ch == '\r' )
295
            continue;
296
 
297
        if ( ch == '\n' )
298
        {
299
            flush_data(p);
300
            continue;
301
        }
302
 
303
        *p->ptr++ = ch;
304
        if ( (size_t)(p->ptr - p->buf) >= p->size - 2  )
305
        {
306
            flush_data(p);
307
            continue;
308
        }
309
    }
310
}
311
 
312
/*----------------------------------------------------------------------------
313
** FUNCTION           : flush_data
314
**
315
** DESCRIPTION        : Write a complete line to the stdout
316
**                      Needs to be atomic write
317
**
318
**
319
** INPUTS             : p               - Ref to proc structure
320
**
321
** RETURNS            : Nothing
322
**
323
----------------------------------------------------------------------------*/
324
 
325
void flush_data( proc *p )
326
{
327
    if ( p->ptr != p->buf )
328
    {
329
        *p->ptr++ = '\n';
330
        *p->ptr = 0;
331
 
332
        if ( p->name_len )
333
            fputs(p->name, stdout);
334
        fputs(p->buf, stdout);
335
        p->ptr = p->buf;
336
    }
337
}
338
 
339
 
340