OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [insight/] [tcl/] [unix/] [tclUnixPipe.c] - Blame information for rev 1774

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 578 markom
/*
2
 * tclUnixPipe.c --
3
 *
4
 *      This file implements the UNIX-specific exec pipeline functions,
5
 *      the "pipe" channel driver, and the "pid" Tcl command.
6
 *
7
 * Copyright (c) 1991-1994 The Regents of the University of California.
8
 * Copyright (c) 1994-1997 Sun Microsystems, Inc.
9
 *
10
 * See the file "license.terms" for information on usage and redistribution
11
 * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
12
 *
13
 * RCS: @(#) $Id: tclUnixPipe.c,v 1.1.1.1 2002-01-16 10:25:37 markom Exp $
14
 */
15
 
16
#include "tclInt.h"
17
#include "tclPort.h"
18
 
19
/*
20
 * The following macros convert between TclFile's and fd's.  The conversion
21
 * simple involves shifting fd's up by one to ensure that no valid fd is ever
22
 * the same as NULL.
23
 */
24
 
25
#define MakeFile(fd) ((TclFile)(((int)fd)+1))
26
#define GetFd(file) (((int)file)-1)
27
 
28
/*
29
 * This structure describes per-instance state of a pipe based channel.
30
 */
31
 
32
typedef struct PipeState {
33
    Tcl_Channel channel;/* Channel associated with this file. */
34
    TclFile inFile;     /* Output from pipe. */
35
    TclFile outFile;    /* Input to pipe. */
36
    TclFile errorFile;  /* Error output from pipe. */
37
    int numPids;        /* How many processes are attached to this pipe? */
38
    Tcl_Pid *pidPtr;    /* The process IDs themselves. Allocated by
39
                         * the creator of the pipe. */
40
    int isNonBlocking;  /* Nonzero when the pipe is in nonblocking mode.
41
                         * Used to decide whether to wait for the children
42
                         * at close time. */
43
} PipeState;
44
 
45
/*
46
 * Declarations for local procedures defined in this file:
47
 */
48
 
49
static int      PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData,
50
                    int mode));
51
static int      PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
52
                    Tcl_Interp *interp));
53
static int      PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData,
54
                    int direction, ClientData *handlePtr));
55
static int      PipeInputProc _ANSI_ARGS_((ClientData instanceData,
56
                    char *buf, int toRead, int *errorCode));
57
static int      PipeOutputProc _ANSI_ARGS_((
58
                    ClientData instanceData, char *buf, int toWrite,
59
                    int *errorCode));
60
static void     PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask));
61
static void     RestoreSignals _ANSI_ARGS_((void));
62
static int      SetupStdFile _ANSI_ARGS_((TclFile file, int type));
63
 
64
/*
65
 * This structure describes the channel type structure for command pipe
66
 * based IO:
67
 */
68
 
69
static Tcl_ChannelType pipeChannelType = {
70
    "pipe",                             /* Type name. */
71
    PipeBlockModeProc,                  /* Set blocking/nonblocking mode.*/
72
    PipeCloseProc,                      /* Close proc. */
73
    PipeInputProc,                      /* Input proc. */
74
    PipeOutputProc,                     /* Output proc. */
75
    NULL,                               /* Seek proc. */
76
    NULL,                               /* Set option proc. */
77
    NULL,                               /* Get option proc. */
78
    PipeWatchProc,                      /* Initialize notifier. */
79
    PipeGetHandleProc,                  /* Get OS handles out of channel. */
80
};
81
 
82
/*
83
 *----------------------------------------------------------------------
84
 *
85
 * TclpMakeFile --
86
 *
87
 *      Make a TclFile from a channel.
88
 *
89
 * Results:
90
 *      Returns a new TclFile or NULL on failure.
91
 *
92
 * Side effects:
93
 *      None.
94
 *
95
 *----------------------------------------------------------------------
96
 */
97
 
98
TclFile
99
TclpMakeFile(channel, direction)
100
    Tcl_Channel channel;        /* Channel to get file from. */
101
    int direction;              /* Either TCL_READABLE or TCL_WRITABLE. */
102
{
103
    ClientData data;
104
 
105
    if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data)
106
            == TCL_OK) {
107
        return MakeFile((int)data);
108
    } else {
109
        return (TclFile) NULL;
110
    }
111
}
112
 
113
/*
114
 *----------------------------------------------------------------------
115
 *
116
 * TclpOpenFile --
117
 *
118
 *      Open a file for use in a pipeline.
119
 *
120
 * Results:
121
 *      Returns a new TclFile handle or NULL on failure.
122
 *
123
 * Side effects:
124
 *      May cause a file to be created on the file system.
125
 *
126
 *----------------------------------------------------------------------
127
 */
128
 
129
TclFile
130
TclpOpenFile(fname, mode)
131
    char *fname;                        /* The name of the file to open. */
132
    int mode;                           /* In what mode to open the file? */
133
{
134
    int fd;
135
 
136
    fd = open(fname, mode, 0666);
137
    if (fd != -1) {
138
        fcntl(fd, F_SETFD, FD_CLOEXEC);
139
 
140
        /*
141
         * If the file is being opened for writing, seek to the end
142
         * so we can append to any data already in the file.
143
         */
144
 
145
        if (mode & O_WRONLY) {
146
            lseek(fd, 0, SEEK_END);
147
        }
148
 
149
        /*
150
         * Increment the fd so it can't be 0, which would conflict with
151
         * the NULL return for errors.
152
         */
153
 
154
        return MakeFile(fd);
155
    }
156
    return NULL;
157
}
158
 
159
/*
160
 *----------------------------------------------------------------------
161
 *
162
 * TclpCreateTempFile --
163
 *
164
 *      This function creates a temporary file initialized with an
165
 *      optional string, and returns a file handle with the file pointer
166
 *      at the beginning of the file.
167
 *
168
 * Results:
169
 *      A handle to a file.
170
 *
171
 * Side effects:
172
 *      None.
173
 *
174
 *----------------------------------------------------------------------
175
 */
176
 
177
TclFile
178
TclpCreateTempFile(contents, namePtr)
179
    char *contents;             /* String to write into temp file, or NULL. */
180
    Tcl_DString *namePtr;       /* If non-NULL, pointer to initialized
181
                                 * DString that is filled with the name of
182
                                 * the temp file that was created. */
183
{
184
    char fileName[L_tmpnam];
185
    TclFile file;
186
    size_t length = (contents == NULL) ? 0 : strlen(contents);
187
 
188
    tmpnam(fileName);
189
    file = TclpOpenFile(fileName, O_RDWR|O_CREAT|O_TRUNC);
190
    unlink(fileName);
191
 
192
    if ((file != NULL) && (length > 0)) {
193
        int fd = GetFd(file);
194
        while (1) {
195
            if (write(fd, contents, length) != -1) {
196
                break;
197
            } else if (errno != EINTR) {
198
                close(fd);
199
                return NULL;
200
            }
201
        }
202
        lseek(fd, 0, SEEK_SET);
203
    }
204
    if (namePtr != NULL) {
205
        Tcl_DStringAppend(namePtr, fileName, -1);
206
    }
207
    return file;
208
}
209
 
210
/*
211
 *----------------------------------------------------------------------
212
 *
213
 * TclpCreatePipe --
214
 *
215
 *      Creates a pipe - simply calls the pipe() function.
216
 *
217
 * Results:
218
 *      Returns 1 on success, 0 on failure.
219
 *
220
 * Side effects:
221
 *      Creates a pipe.
222
 *
223
 *----------------------------------------------------------------------
224
 */
225
 
226
int
227
TclpCreatePipe(readPipe, writePipe)
228
    TclFile *readPipe;          /* Location to store file handle for
229
                                 * read side of pipe. */
230
    TclFile *writePipe;         /* Location to store file handle for
231
                                 * write side of pipe. */
232
{
233
    int pipeIds[2];
234
 
235
    if (pipe(pipeIds) != 0) {
236
        return 0;
237
    }
238
 
239
    fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
240
    fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
241
 
242
    *readPipe = MakeFile(pipeIds[0]);
243
    *writePipe = MakeFile(pipeIds[1]);
244
    return 1;
245
}
246
 
247
/*
248
 *----------------------------------------------------------------------
249
 *
250
 * TclpCloseFile --
251
 *
252
 *      Implements a mechanism to close a UNIX file.
253
 *
254
 * Results:
255
 *      Returns 0 on success, or -1 on error, setting errno.
256
 *
257
 * Side effects:
258
 *      The file is closed.
259
 *
260
 *----------------------------------------------------------------------
261
 */
262
 
263
int
264
TclpCloseFile(file)
265
    TclFile file;       /* The file to close. */
266
{
267
    int fd = GetFd(file);
268
 
269
    /*
270
     * Refuse to close the fds for stdin, stdout and stderr.
271
     */
272
 
273
    if ((fd == 0) || (fd == 1) || (fd == 2)) {
274
        return 0;
275
    }
276
 
277
    Tcl_DeleteFileHandler(fd);
278
    return close(fd);
279
}
280
 
281
/*
282
 *----------------------------------------------------------------------
283
 *
284
 * TclpCreateProcess --
285
 *
286
 *      Create a child process that has the specified files as its
287
 *      standard input, output, and error.  The child process runs
288
 *      asynchronously and runs with the same environment variables
289
 *      as the creating process.
290
 *
291
 *      The path is searched to find the specified executable.
292
 *
293
 * Results:
294
 *      The return value is TCL_ERROR and an error message is left in
295
 *      interp->result if there was a problem creating the child
296
 *      process.  Otherwise, the return value is TCL_OK and *pidPtr is
297
 *      filled with the process id of the child process.
298
 *
299
 * Side effects:
300
 *      A process is created.
301
 *
302
 *----------------------------------------------------------------------
303
 */
304
 
305
    /* ARGSUSED */
306
int
307
TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
308
        pidPtr)
309
    Tcl_Interp *interp;         /* Interpreter in which to leave errors that
310
                                 * occurred when creating the child process.
311
                                 * Error messages from the child process
312
                                 * itself are sent to errorFile. */
313
    int argc;                   /* Number of arguments in following array. */
314
    char **argv;                /* Array of argument strings.  argv[0]
315
                                 * contains the name of the executable
316
                                 * converted to native format (using the
317
                                 * Tcl_TranslateFileName call).  Additional
318
                                 * arguments have not been converted. */
319
    TclFile inputFile;          /* If non-NULL, gives the file to use as
320
                                 * input for the child process.  If inputFile
321
                                 * file is not readable or is NULL, the child
322
                                 * will receive no standard input. */
323
    TclFile outputFile;         /* If non-NULL, gives the file that
324
                                 * receives output from the child process.  If
325
                                 * outputFile file is not writeable or is
326
                                 * NULL, output from the child will be
327
                                 * discarded. */
328
    TclFile errorFile;          /* If non-NULL, gives the file that
329
                                 * receives errors from the child process.  If
330
                                 * errorFile file is not writeable or is NULL,
331
                                 * errors from the child will be discarded.
332
                                 * errorFile may be the same as outputFile. */
333
    Tcl_Pid *pidPtr;            /* If this procedure is successful, pidPtr
334
                                 * is filled with the process id of the child
335
                                 * process. */
336
{
337
    TclFile errPipeIn, errPipeOut;
338
    int joinThisError, count, status, fd;
339
    char errSpace[200];
340
    int pid;
341
 
342
    errPipeIn = NULL;
343
    errPipeOut = NULL;
344
    pid = -1;
345
 
346
    /*
347
     * Create a pipe that the child can use to return error
348
     * information if anything goes wrong.
349
     */
350
 
351
    if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
352
        Tcl_AppendResult(interp, "couldn't create pipe: ",
353
                Tcl_PosixError(interp), (char *) NULL);
354
        goto error;
355
    }
356
 
357
    joinThisError = (errorFile == outputFile);
358
    pid = vfork();
359
    if (pid == 0) {
360
        fd = GetFd(errPipeOut);
361
 
362
        /*
363
         * Set up stdio file handles for the child process.
364
         */
365
 
366
        if (!SetupStdFile(inputFile, TCL_STDIN)
367
                || !SetupStdFile(outputFile, TCL_STDOUT)
368
                || (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
369
                || (joinThisError &&
370
                        ((dup2(1,2) == -1) ||
371
                         (fcntl(2, F_SETFD, 0) != 0)))) {
372
            sprintf(errSpace,
373
                    "%dforked process couldn't set up input/output: ",
374
                    errno);
375
            write(fd, errSpace, (size_t) strlen(errSpace));
376
            _exit(1);
377
        }
378
 
379
        /*
380
         * Close the input side of the error pipe.
381
         */
382
 
383
        RestoreSignals();
384
        execvp(argv[0], &argv[0]);
385
        sprintf(errSpace, "%dcouldn't execute \"%.150s\": ", errno,
386
                argv[0]);
387
        write(fd, errSpace, (size_t) strlen(errSpace));
388
        _exit(1);
389
    }
390
    if (pid == -1) {
391
        Tcl_AppendResult(interp, "couldn't fork child process: ",
392
                Tcl_PosixError(interp), (char *) NULL);
393
        goto error;
394
    }
395
 
396
    /*
397
     * Read back from the error pipe to see if the child started
398
     * up OK.  The info in the pipe (if any) consists of a decimal
399
     * errno value followed by an error message.
400
     */
401
 
402
    TclpCloseFile(errPipeOut);
403
    errPipeOut = NULL;
404
 
405
    fd = GetFd(errPipeIn);
406
    count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
407
    if (count > 0) {
408
        char *end;
409
        errSpace[count] = 0;
410
        errno = strtol(errSpace, &end, 10);
411
        Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
412
                (char *) NULL);
413
        goto error;
414
    }
415
 
416
    TclpCloseFile(errPipeIn);
417
    *pidPtr = (Tcl_Pid) pid;
418
    return TCL_OK;
419
 
420
    error:
421
    if (pid != -1) {
422
        /*
423
         * Reap the child process now if an error occurred during its
424
         * startup.
425
         */
426
 
427
        Tcl_WaitPid((Tcl_Pid) pid, &status, WNOHANG);
428
    }
429
 
430
    if (errPipeIn) {
431
        TclpCloseFile(errPipeIn);
432
    }
433
    if (errPipeOut) {
434
        TclpCloseFile(errPipeOut);
435
    }
436
    return TCL_ERROR;
437
}
438
 
439
/*
440
 *----------------------------------------------------------------------
441
 *
442
 * RestoreSignals --
443
 *
444
 *      This procedure is invoked in a forked child process just before
445
 *      exec-ing a new program to restore all signals to their default
446
 *      settings.
447
 *
448
 * Results:
449
 *      None.
450
 *
451
 * Side effects:
452
 *      Signal settings get changed.
453
 *
454
 *----------------------------------------------------------------------
455
 */
456
 
457
static void
458
RestoreSignals()
459
{
460
#ifdef SIGABRT
461
    signal(SIGABRT, SIG_DFL);
462
#endif
463
#ifdef SIGALRM
464
    signal(SIGALRM, SIG_DFL);
465
#endif
466
#ifdef SIGFPE
467
    signal(SIGFPE, SIG_DFL);
468
#endif
469
#ifdef SIGHUP
470
    signal(SIGHUP, SIG_DFL);
471
#endif
472
#ifdef SIGILL
473
    signal(SIGILL, SIG_DFL);
474
#endif
475
#ifdef SIGINT
476
    signal(SIGINT, SIG_DFL);
477
#endif
478
#ifdef SIGPIPE
479
    signal(SIGPIPE, SIG_DFL);
480
#endif
481
#ifdef SIGQUIT
482
    signal(SIGQUIT, SIG_DFL);
483
#endif
484
#ifdef SIGSEGV
485
    signal(SIGSEGV, SIG_DFL);
486
#endif
487
#ifdef SIGTERM
488
    signal(SIGTERM, SIG_DFL);
489
#endif
490
#ifdef SIGUSR1
491
    signal(SIGUSR1, SIG_DFL);
492
#endif
493
#ifdef SIGUSR2
494
    signal(SIGUSR2, SIG_DFL);
495
#endif
496
#ifdef SIGCHLD
497
    signal(SIGCHLD, SIG_DFL);
498
#endif
499
#ifdef SIGCONT
500
    signal(SIGCONT, SIG_DFL);
501
#endif
502
#ifdef SIGTSTP
503
    signal(SIGTSTP, SIG_DFL);
504
#endif
505
#ifdef SIGTTIN
506
    signal(SIGTTIN, SIG_DFL);
507
#endif
508
#ifdef SIGTTOU
509
    signal(SIGTTOU, SIG_DFL);
510
#endif
511
}
512
 
513
/*
514
 *----------------------------------------------------------------------
515
 *
516
 * SetupStdFile --
517
 *
518
 *      Set up stdio file handles for the child process, using the
519
 *      current standard channels if no other files are specified.
520
 *      If no standard channel is defined, or if no file is associated
521
 *      with the channel, then the corresponding standard fd is closed.
522
 *
523
 * Results:
524
 *      Returns 1 on success, or 0 on failure.
525
 *
526
 * Side effects:
527
 *      Replaces stdio fds.
528
 *
529
 *----------------------------------------------------------------------
530
 */
531
 
532
static int
533
SetupStdFile(file, type)
534
    TclFile file;               /* File to dup, or NULL. */
535
    int type;                   /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
536
{
537
    Tcl_Channel channel;
538
    int fd;
539
    int targetFd = 0;            /* Initializations here needed only to */
540
    int direction = 0;           /* prevent warnings about using uninitialized
541
                                 * variables. */
542
 
543
    switch (type) {
544
        case TCL_STDIN:
545
            targetFd = 0;
546
            direction = TCL_READABLE;
547
            break;
548
        case TCL_STDOUT:
549
            targetFd = 1;
550
            direction = TCL_WRITABLE;
551
            break;
552
        case TCL_STDERR:
553
            targetFd = 2;
554
            direction = TCL_WRITABLE;
555
            break;
556
    }
557
 
558
    if (!file) {
559
        channel = Tcl_GetStdChannel(type);
560
        if (channel) {
561
            file = TclpMakeFile(channel, direction);
562
        }
563
    }
564
    if (file) {
565
        fd = GetFd(file);
566
        if (fd != targetFd) {
567
            if (dup2(fd, targetFd) == -1) {
568
                return 0;
569
            }
570
 
571
            /*
572
             * Must clear the close-on-exec flag for the target FD, since
573
             * some systems (e.g. Ultrix) do not clear the CLOEXEC flag on
574
             * the target FD.
575
             */
576
 
577
            fcntl(targetFd, F_SETFD, 0);
578
        } else {
579
            int result;
580
 
581
            /*
582
             * Since we aren't dup'ing the file, we need to explicitly clear
583
             * the close-on-exec flag.
584
             */
585
 
586
            result = fcntl(fd, F_SETFD, 0);
587
        }
588
    } else {
589
        close(targetFd);
590
    }
591
    return 1;
592
}
593
 
594
/*
595
 *----------------------------------------------------------------------
596
 *
597
 * TclpCreateCommandChannel --
598
 *
599
 *      This function is called by the generic IO level to perform
600
 *      the platform specific channel initialization for a command
601
 *      channel.
602
 *
603
 * Results:
604
 *      Returns a new channel or NULL on failure.
605
 *
606
 * Side effects:
607
 *      Allocates a new channel.
608
 *
609
 *----------------------------------------------------------------------
610
 */
611
 
612
Tcl_Channel
613
TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
614
    TclFile readFile;           /* If non-null, gives the file for reading. */
615
    TclFile writeFile;          /* If non-null, gives the file for writing. */
616
    TclFile errorFile;          /* If non-null, gives the file where errors
617
                                 * can be read. */
618
    int numPids;                /* The number of pids in the pid array. */
619
    Tcl_Pid *pidPtr;            /* An array of process identifiers.
620
                                 * Allocated by the caller, freed when
621
                                 * the channel is closed or the processes
622
                                 * are detached (in a background exec). */
623
{
624
    char channelName[20];
625
    int channelId;
626
    PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
627
    int mode;
628
 
629
    statePtr->inFile = readFile;
630
    statePtr->outFile = writeFile;
631
    statePtr->errorFile = errorFile;
632
    statePtr->numPids = numPids;
633
    statePtr->pidPtr = pidPtr;
634
    statePtr->isNonBlocking = 0;
635
 
636
    mode = 0;
637
    if (readFile) {
638
        mode |= TCL_READABLE;
639
    }
640
    if (writeFile) {
641
        mode |= TCL_WRITABLE;
642
    }
643
 
644
    /*
645
     * Use one of the fds associated with the channel as the
646
     * channel id.
647
     */
648
 
649
    if (readFile) {
650
        channelId = GetFd(readFile);
651
    } else if (writeFile) {
652
        channelId = GetFd(writeFile);
653
    } else if (errorFile) {
654
        channelId = GetFd(errorFile);
655
    } else {
656
        channelId = 0;
657
    }
658
 
659
    /*
660
     * For backward compatibility with previous versions of Tcl, we
661
     * use "file%d" as the base name for pipes even though it would
662
     * be more natural to use "pipe%d".
663
     */
664
 
665
    sprintf(channelName, "file%d", channelId);
666
    statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
667
            (ClientData) statePtr, mode);
668
    return statePtr->channel;
669
}
670
 
671
/*
672
 *----------------------------------------------------------------------
673
 *
674
 * TclGetAndDetachPids --
675
 *
676
 *      This procedure is invoked in the generic implementation of a
677
 *      background "exec" (An exec when invoked with a terminating "&")
678
 *      to store a list of the PIDs for processes in a command pipeline
679
 *      in interp->result and to detach the processes.
680
 *
681
 * Results:
682
 *      None.
683
 *
684
 * Side effects:
685
 *      Modifies interp->result. Detaches processes.
686
 *
687
 *----------------------------------------------------------------------
688
 */
689
 
690
void
691
TclGetAndDetachPids(interp, chan)
692
    Tcl_Interp *interp;
693
    Tcl_Channel chan;
694
{
695
    PipeState *pipePtr;
696
    Tcl_ChannelType *chanTypePtr;
697
    int i;
698
    char buf[20];
699
 
700
    /*
701
     * Punt if the channel is not a command channel.
702
     */
703
 
704
    chanTypePtr = Tcl_GetChannelType(chan);
705
    if (chanTypePtr != &pipeChannelType) {
706
        return;
707
    }
708
 
709
    pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
710
    for (i = 0; i < pipePtr->numPids; i++) {
711
        sprintf(buf, "%ld", TclpGetPid(pipePtr->pidPtr[i]));
712
        Tcl_AppendElement(interp, buf);
713
        Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
714
    }
715
    if (pipePtr->numPids > 0) {
716
        ckfree((char *) pipePtr->pidPtr);
717
        pipePtr->numPids = 0;
718
    }
719
}
720
 
721
/*
722
 *----------------------------------------------------------------------
723
 *
724
 * PipeBlockModeProc --
725
 *
726
 *      Helper procedure to set blocking and nonblocking modes on a
727
 *      pipe based channel. Invoked by generic IO level code.
728
 *
729
 * Results:
730
 *      0 if successful, errno when failed.
731
 *
732
 * Side effects:
733
 *      Sets the device into blocking or non-blocking mode.
734
 *
735
 *----------------------------------------------------------------------
736
 */
737
 
738
        /* ARGSUSED */
739
static int
740
PipeBlockModeProc(instanceData, mode)
741
    ClientData instanceData;            /* Pipe state. */
742
    int mode;                           /* The mode to set. Can be one of
743
                                         * TCL_MODE_BLOCKING or
744
                                         * TCL_MODE_NONBLOCKING. */
745
{
746
    PipeState *psPtr = (PipeState *) instanceData;
747
    int curStatus;
748
    int fd;
749
 
750
#ifndef USE_FIONBIO    
751
    if (psPtr->inFile) {
752
        fd = GetFd(psPtr->inFile);
753
        curStatus = fcntl(fd, F_GETFL);
754
        if (mode == TCL_MODE_BLOCKING) {
755
            curStatus &= (~(O_NONBLOCK));
756
        } else {
757
            curStatus |= O_NONBLOCK;
758
        }
759
        if (fcntl(fd, F_SETFL, curStatus) < 0) {
760
            return errno;
761
        }
762
        curStatus = fcntl(fd, F_GETFL);
763
    }
764
    if (psPtr->outFile) {
765
        fd = GetFd(psPtr->outFile);
766
        curStatus = fcntl(fd, F_GETFL);
767
        if (mode == TCL_MODE_BLOCKING) {
768
            curStatus &= (~(O_NONBLOCK));
769
        } else {
770
            curStatus |= O_NONBLOCK;
771
        }
772
        if (fcntl(fd, F_SETFL, curStatus) < 0) {
773
            return errno;
774
        }
775
    }
776
#endif  /* !FIONBIO */
777
 
778
#ifdef  USE_FIONBIO
779
    if (psPtr->inFile) {
780
        fd = GetFd(psPtr->inFile);
781
        if (mode == TCL_MODE_BLOCKING) {
782
            curStatus = 0;
783
        } else {
784
            curStatus = 1;
785
        }
786
        if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
787
            return errno;
788
        }
789
    }
790
    if (psPtr->outFile != NULL) {
791
        fd = GetFd(psPtr->outFile);
792
        if (mode == TCL_MODE_BLOCKING) {
793
            curStatus = 0;
794
        } else {
795
            curStatus = 1;
796
        }
797
        if (ioctl(fd, (int) FIONBIO,  &curStatus) < 0) {
798
            return errno;
799
        }
800
    }
801
#endif  /* USE_FIONBIO */
802
 
803
    return 0;
804
}
805
 
806
/*
807
 *----------------------------------------------------------------------
808
 *
809
 * PipeCloseProc --
810
 *
811
 *      This procedure is invoked by the generic IO level to perform
812
 *      channel-type-specific cleanup when a command pipeline channel
813
 *      is closed.
814
 *
815
 * Results:
816
 *      0 on success, errno otherwise.
817
 *
818
 * Side effects:
819
 *      Closes the command pipeline channel.
820
 *
821
 *----------------------------------------------------------------------
822
 */
823
 
824
        /* ARGSUSED */
825
static int
826
PipeCloseProc(instanceData, interp)
827
    ClientData instanceData;    /* The pipe to close. */
828
    Tcl_Interp *interp;         /* For error reporting. */
829
{
830
    PipeState *pipePtr;
831
    Tcl_Channel errChan;
832
    int errorCode, result;
833
 
834
    errorCode = 0;
835
    result = 0;
836
    pipePtr = (PipeState *) instanceData;
837
    if (pipePtr->inFile) {
838
        if (TclpCloseFile(pipePtr->inFile) < 0) {
839
            errorCode = errno;
840
        }
841
    }
842
    if (pipePtr->outFile) {
843
        if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
844
            errorCode = errno;
845
        }
846
    }
847
 
848
    if (pipePtr->isNonBlocking || TclInExit()) {
849
 
850
        /*
851
         * If the channel is non-blocking or Tcl is being cleaned up, just
852
         * detach the children PIDs, reap them (important if we are in a
853
         * dynamic load module), and discard the errorFile.
854
         */
855
 
856
        Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
857
        Tcl_ReapDetachedProcs();
858
 
859
        if (pipePtr->errorFile) {
860
            TclpCloseFile(pipePtr->errorFile);
861
        }
862
    } else {
863
 
864
        /*
865
         * Wrap the error file into a channel and give it to the cleanup
866
         * routine.
867
         */
868
 
869
        if (pipePtr->errorFile) {
870
            errChan = Tcl_MakeFileChannel(
871
                (ClientData) GetFd(pipePtr->errorFile), TCL_READABLE);
872
        } else {
873
            errChan = NULL;
874
        }
875
        result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
876
                errChan);
877
    }
878
 
879
    if (pipePtr->numPids != 0) {
880
        ckfree((char *) pipePtr->pidPtr);
881
    }
882
    ckfree((char *) pipePtr);
883
    if (errorCode == 0) {
884
        return result;
885
    }
886
    return errorCode;
887
}
888
 
889
/*
890
 *----------------------------------------------------------------------
891
 *
892
 * PipeInputProc --
893
 *
894
 *      This procedure is invoked from the generic IO level to read
895
 *      input from a command pipeline based channel.
896
 *
897
 * Results:
898
 *      The number of bytes read is returned or -1 on error. An output
899
 *      argument contains a POSIX error code if an error occurs, or zero.
900
 *
901
 * Side effects:
902
 *      Reads input from the input device of the channel.
903
 *
904
 *----------------------------------------------------------------------
905
 */
906
 
907
static int
908
PipeInputProc(instanceData, buf, toRead, errorCodePtr)
909
    ClientData instanceData;            /* Pipe state. */
910
    char *buf;                          /* Where to store data read. */
911
    int toRead;                         /* How much space is available
912
                                         * in the buffer? */
913
    int *errorCodePtr;                  /* Where to store error code. */
914
{
915
    PipeState *psPtr = (PipeState *) instanceData;
916
    int bytesRead;                      /* How many bytes were actually
917
                                         * read from the input device? */
918
 
919
    *errorCodePtr = 0;
920
 
921
    /*
922
     * Assume there is always enough input available. This will block
923
     * appropriately, and read will unblock as soon as a short read is
924
     * possible, if the channel is in blocking mode. If the channel is
925
     * nonblocking, the read will never block.
926
     */
927
 
928
    bytesRead = read(GetFd(psPtr->inFile), buf, (size_t) toRead);
929
    if (bytesRead > -1) {
930
        return bytesRead;
931
    }
932
    *errorCodePtr = errno;
933
    return -1;
934
}
935
 
936
/*
937
 *----------------------------------------------------------------------
938
 *
939
 * PipeOutputProc--
940
 *
941
 *      This procedure is invoked from the generic IO level to write
942
 *      output to a command pipeline based channel.
943
 *
944
 * Results:
945
 *      The number of bytes written is returned or -1 on error. An
946
 *      output argument contains a POSIX error code if an error occurred,
947
 *      or zero.
948
 *
949
 * Side effects:
950
 *      Writes output on the output device of the channel.
951
 *
952
 *----------------------------------------------------------------------
953
 */
954
 
955
static int
956
PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)
957
    ClientData instanceData;            /* Pipe state. */
958
    char *buf;                          /* The data buffer. */
959
    int toWrite;                        /* How many bytes to write? */
960
    int *errorCodePtr;                  /* Where to store error code. */
961
{
962
    PipeState *psPtr = (PipeState *) instanceData;
963
    int written;
964
 
965
    *errorCodePtr = 0;
966
    written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
967
    if (written > -1) {
968
        return written;
969
    }
970
    *errorCodePtr = errno;
971
    return -1;
972
}
973
 
974
/*
975
 *----------------------------------------------------------------------
976
 *
977
 * PipeWatchProc --
978
 *
979
 *      Initialize the notifier to watch the fds from this channel.
980
 *
981
 * Results:
982
 *      None.
983
 *
984
 * Side effects:
985
 *      Sets up the notifier so that a future event on the channel will
986
 *      be seen by Tcl.
987
 *
988
 *----------------------------------------------------------------------
989
 */
990
 
991
static void
992
PipeWatchProc(instanceData, mask)
993
    ClientData instanceData;            /* The pipe state. */
994
    int mask;                           /* Events of interest; an OR-ed
995
                                         * combination of TCL_READABLE,
996
                                         * TCL_WRITABEL and TCL_EXCEPTION. */
997
{
998
    PipeState *psPtr = (PipeState *) instanceData;
999
    int newmask;
1000
 
1001
    if (psPtr->inFile) {
1002
        newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
1003
        if (newmask) {
1004
            Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
1005
                    (Tcl_FileProc *) Tcl_NotifyChannel,
1006
                    (ClientData) psPtr->channel);
1007
        } else {
1008
            Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
1009
        }
1010
    }
1011
    if (psPtr->outFile) {
1012
        newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
1013
        if (newmask) {
1014
            Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
1015
                    (Tcl_FileProc *) Tcl_NotifyChannel,
1016
                    (ClientData) psPtr->channel);
1017
        } else {
1018
            Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
1019
        }
1020
    }
1021
}
1022
 
1023
/*
1024
 *----------------------------------------------------------------------
1025
 *
1026
 * PipeGetHandleProc --
1027
 *
1028
 *      Called from Tcl_GetChannelHandle to retrieve OS handles from
1029
 *      inside a command pipeline based channel.
1030
 *
1031
 * Results:
1032
 *      Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
1033
 *      there is no handle for the specified direction.
1034
 *
1035
 * Side effects:
1036
 *      None.
1037
 *
1038
 *----------------------------------------------------------------------
1039
 */
1040
 
1041
static int
1042
PipeGetHandleProc(instanceData, direction, handlePtr)
1043
    ClientData instanceData;    /* The pipe state. */
1044
    int direction;              /* TCL_READABLE or TCL_WRITABLE */
1045
    ClientData *handlePtr;      /* Where to store the handle.  */
1046
{
1047
    PipeState *psPtr = (PipeState *) instanceData;
1048
 
1049
    if (direction == TCL_READABLE && psPtr->inFile) {
1050
        *handlePtr = (ClientData) GetFd(psPtr->inFile);
1051
        return TCL_OK;
1052
    }
1053
    if (direction == TCL_WRITABLE && psPtr->outFile) {
1054
        *handlePtr = (ClientData) GetFd(psPtr->outFile);
1055
        return TCL_OK;
1056
    }
1057
    return TCL_ERROR;
1058
}
1059
 
1060
/*
1061
 *----------------------------------------------------------------------
1062
 *
1063
 * Tcl_WaitPid --
1064
 *
1065
 *      Implements the waitpid system call on Unix systems.
1066
 *
1067
 * Results:
1068
 *      Result of calling waitpid.
1069
 *
1070
 * Side effects:
1071
 *      Waits for a process to terminate.
1072
 *
1073
 *----------------------------------------------------------------------
1074
 */
1075
 
1076
Tcl_Pid
1077
Tcl_WaitPid(pid, statPtr, options)
1078
    Tcl_Pid pid;
1079
    int *statPtr;
1080
    int options;
1081
{
1082
    int result;
1083
    pid_t real_pid;
1084
 
1085
    real_pid = (pid_t) pid;
1086
    while (1) {
1087
        result = (int) waitpid(real_pid, statPtr, options);
1088
        if ((result != -1) || (errno != EINTR)) {
1089
            return (Tcl_Pid) result;
1090
        }
1091
    }
1092
}
1093
 
1094
/*
1095
 *----------------------------------------------------------------------
1096
 *
1097
 * Tcl_PidObjCmd --
1098
 *
1099
 *      This procedure is invoked to process the "pid" Tcl command.
1100
 *      See the user documentation for details on what it does.
1101
 *
1102
 * Results:
1103
 *      A standard Tcl result.
1104
 *
1105
 * Side effects:
1106
 *      See the user documentation.
1107
 *
1108
 *----------------------------------------------------------------------
1109
 */
1110
 
1111
        /* ARGSUSED */
1112
int
1113
Tcl_PidObjCmd(dummy, interp, objc, objv)
1114
    ClientData dummy;           /* Not used. */
1115
    Tcl_Interp *interp;         /* Current interpreter. */
1116
    int objc;                   /* Number of arguments. */
1117
    Tcl_Obj *CONST *objv;       /* Argument strings. */
1118
{
1119
    Tcl_Channel chan;
1120
    Tcl_ChannelType *chanTypePtr;
1121
    PipeState *pipePtr;
1122
    int i;
1123
    Tcl_Obj *resultPtr, *longObjPtr;
1124
 
1125
    if (objc > 2) {
1126
        Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
1127
        return TCL_ERROR;
1128
    }
1129
    if (objc == 1) {
1130
        Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid());
1131
    } else {
1132
        chan = Tcl_GetChannel(interp, Tcl_GetStringFromObj(objv[1], NULL),
1133
                NULL);
1134
        if (chan == (Tcl_Channel) NULL) {
1135
            return TCL_ERROR;
1136
        }
1137
        chanTypePtr = Tcl_GetChannelType(chan);
1138
        if (chanTypePtr != &pipeChannelType) {
1139
            return TCL_OK;
1140
        }
1141
        pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
1142
        resultPtr = Tcl_GetObjResult(interp);
1143
        for (i = 0; i < pipePtr->numPids; i++) {
1144
            longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
1145
            Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
1146
        }
1147
    }
1148
    return TCL_OK;
1149
}

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.