sl@0
|
1 |
/*
|
sl@0
|
2 |
* tclUnixPipe.c --
|
sl@0
|
3 |
*
|
sl@0
|
4 |
* This file implements the UNIX-specific exec pipeline functions,
|
sl@0
|
5 |
* the "pipe" channel driver, and the "pid" Tcl command.
|
sl@0
|
6 |
*
|
sl@0
|
7 |
* Copyright (c) 1991-1994 The Regents of the University of California.
|
sl@0
|
8 |
* Copyright (c) 1994-1997 Sun Microsystems, Inc.
|
sl@0
|
9 |
* Portions Copyright (c) 2007-2008 Nokia Corporation and/or its subsidiaries. All rights reserved.
|
sl@0
|
10 |
*
|
sl@0
|
11 |
* See the file "license.terms" for information on usage and redistribution
|
sl@0
|
12 |
* of this file, and for a DISCLAIMER OF ALL WARRANTIES.
|
sl@0
|
13 |
*
|
sl@0
|
14 |
* RCS: @(#) $Id: tclUnixPipe.c,v 1.23.2.7 2006/08/02 20:04:40 das Exp $
|
sl@0
|
15 |
*/
|
sl@0
|
16 |
|
sl@0
|
17 |
#include "tclInt.h"
|
sl@0
|
18 |
#include "tclPort.h"
|
sl@0
|
19 |
|
sl@0
|
20 |
#if defined(__SYMBIAN32__)
|
sl@0
|
21 |
#include "tclSymbianGlobals.h"
|
sl@0
|
22 |
#include <spawn.h>
|
sl@0
|
23 |
#define ADDPARAMTOCHILD 4
|
sl@0
|
24 |
#endif
|
sl@0
|
25 |
|
sl@0
|
26 |
void TclPrint1(const char* aFmt, const char* aStr);
|
sl@0
|
27 |
void TclPrint2(const char* aFmt, const char* aStr, int aNum);
|
sl@0
|
28 |
void TclPrint3(const char* aFmt);
|
sl@0
|
29 |
|
sl@0
|
30 |
|
sl@0
|
31 |
#ifdef USE_VFORK
|
sl@0
|
32 |
#define fork vfork
|
sl@0
|
33 |
#endif
|
sl@0
|
34 |
|
sl@0
|
35 |
/*
|
sl@0
|
36 |
* The following macros convert between TclFile's and fd's. The conversion
|
sl@0
|
37 |
* simple involves shifting fd's up by one to ensure that no valid fd is ever
|
sl@0
|
38 |
* the same as NULL.
|
sl@0
|
39 |
*/
|
sl@0
|
40 |
|
sl@0
|
41 |
#define MakeFile(fd) ((TclFile)(((int)fd)+1))
|
sl@0
|
42 |
#define GetFd(file) (((int)file)-1)
|
sl@0
|
43 |
/*
|
sl@0
|
44 |
* This structure describes per-instance state of a pipe based channel.
|
sl@0
|
45 |
*/
|
sl@0
|
46 |
|
sl@0
|
47 |
typedef struct PipeState {
|
sl@0
|
48 |
Tcl_Channel channel;/* Channel associated with this file. */
|
sl@0
|
49 |
TclFile inFile; /* Output from pipe. */
|
sl@0
|
50 |
TclFile outFile; /* Input to pipe. */
|
sl@0
|
51 |
TclFile errorFile; /* Error output from pipe. */
|
sl@0
|
52 |
int numPids; /* How many processes are attached to this pipe? */
|
sl@0
|
53 |
Tcl_Pid *pidPtr; /* The process IDs themselves. Allocated by
|
sl@0
|
54 |
* the creator of the pipe. */
|
sl@0
|
55 |
int isNonBlocking; /* Nonzero when the pipe is in nonblocking mode.
|
sl@0
|
56 |
* Used to decide whether to wait for the children
|
sl@0
|
57 |
* at close time. */
|
sl@0
|
58 |
} PipeState;
|
sl@0
|
59 |
|
sl@0
|
60 |
/*
|
sl@0
|
61 |
* Declarations for local procedures defined in this file:
|
sl@0
|
62 |
*/
|
sl@0
|
63 |
|
sl@0
|
64 |
static int PipeBlockModeProc _ANSI_ARGS_((ClientData instanceData,
|
sl@0
|
65 |
int mode));
|
sl@0
|
66 |
static int PipeCloseProc _ANSI_ARGS_((ClientData instanceData,
|
sl@0
|
67 |
Tcl_Interp *interp));
|
sl@0
|
68 |
static int PipeGetHandleProc _ANSI_ARGS_((ClientData instanceData,
|
sl@0
|
69 |
int direction, ClientData *handlePtr));
|
sl@0
|
70 |
static int PipeInputProc _ANSI_ARGS_((ClientData instanceData,
|
sl@0
|
71 |
char *buf, int toRead, int *errorCode));
|
sl@0
|
72 |
static int PipeOutputProc _ANSI_ARGS_((
|
sl@0
|
73 |
ClientData instanceData, CONST char *buf, int toWrite,
|
sl@0
|
74 |
int *errorCode));
|
sl@0
|
75 |
static void PipeWatchProc _ANSI_ARGS_((ClientData instanceData, int mask));
|
sl@0
|
76 |
static void RestoreSignals _ANSI_ARGS_((void));
|
sl@0
|
77 |
#ifndef __SYMBIAN32__
|
sl@0
|
78 |
static int SetupStdFile _ANSI_ARGS_((TclFile file, int type));
|
sl@0
|
79 |
#endif
|
sl@0
|
80 |
/*
|
sl@0
|
81 |
* This structure describes the channel type structure for command pipe
|
sl@0
|
82 |
* based IO:
|
sl@0
|
83 |
*/
|
sl@0
|
84 |
|
sl@0
|
85 |
static Tcl_ChannelType pipeChannelType = {
|
sl@0
|
86 |
"pipe", /* Type name. */
|
sl@0
|
87 |
TCL_CHANNEL_VERSION_4, /* v4 channel */
|
sl@0
|
88 |
PipeCloseProc, /* Close proc. */
|
sl@0
|
89 |
PipeInputProc, /* Input proc. */
|
sl@0
|
90 |
PipeOutputProc, /* Output proc. */
|
sl@0
|
91 |
NULL, /* Seek proc. */
|
sl@0
|
92 |
NULL, /* Set option proc. */
|
sl@0
|
93 |
NULL, /* Get option proc. */
|
sl@0
|
94 |
PipeWatchProc, /* Initialize notifier. */
|
sl@0
|
95 |
PipeGetHandleProc, /* Get OS handles out of channel. */
|
sl@0
|
96 |
NULL, /* close2proc. */
|
sl@0
|
97 |
PipeBlockModeProc, /* Set blocking or non-blocking mode.*/
|
sl@0
|
98 |
NULL, /* flush proc. */
|
sl@0
|
99 |
NULL, /* handler proc. */
|
sl@0
|
100 |
NULL, /* wide seek proc */
|
sl@0
|
101 |
NULL, /* thread action proc */
|
sl@0
|
102 |
};
|
sl@0
|
103 |
|
sl@0
|
104 |
/*
|
sl@0
|
105 |
*----------------------------------------------------------------------
|
sl@0
|
106 |
*
|
sl@0
|
107 |
* TclpMakeFile --
|
sl@0
|
108 |
*
|
sl@0
|
109 |
* Make a TclFile from a channel.
|
sl@0
|
110 |
*
|
sl@0
|
111 |
* Results:
|
sl@0
|
112 |
* Returns a new TclFile or NULL on failure.
|
sl@0
|
113 |
*
|
sl@0
|
114 |
* Side effects:
|
sl@0
|
115 |
* None.
|
sl@0
|
116 |
*
|
sl@0
|
117 |
*----------------------------------------------------------------------
|
sl@0
|
118 |
*/
|
sl@0
|
119 |
|
sl@0
|
120 |
TclFile
|
sl@0
|
121 |
TclpMakeFile(channel, direction)
|
sl@0
|
122 |
Tcl_Channel channel; /* Channel to get file from. */
|
sl@0
|
123 |
int direction; /* Either TCL_READABLE or TCL_WRITABLE. */
|
sl@0
|
124 |
{
|
sl@0
|
125 |
ClientData data;
|
sl@0
|
126 |
|
sl@0
|
127 |
if (Tcl_GetChannelHandle(channel, direction, (ClientData *) &data)
|
sl@0
|
128 |
== TCL_OK) {
|
sl@0
|
129 |
return MakeFile((int)data);
|
sl@0
|
130 |
} else {
|
sl@0
|
131 |
return (TclFile) NULL;
|
sl@0
|
132 |
}
|
sl@0
|
133 |
}
|
sl@0
|
134 |
|
sl@0
|
135 |
/*
|
sl@0
|
136 |
*----------------------------------------------------------------------
|
sl@0
|
137 |
*
|
sl@0
|
138 |
* TclpOpenFile --
|
sl@0
|
139 |
*
|
sl@0
|
140 |
* Open a file for use in a pipeline.
|
sl@0
|
141 |
*
|
sl@0
|
142 |
* Results:
|
sl@0
|
143 |
* Returns a new TclFile handle or NULL on failure.
|
sl@0
|
144 |
*
|
sl@0
|
145 |
* Side effects:
|
sl@0
|
146 |
* May cause a file to be created on the file system.
|
sl@0
|
147 |
*
|
sl@0
|
148 |
*----------------------------------------------------------------------
|
sl@0
|
149 |
*/
|
sl@0
|
150 |
|
sl@0
|
151 |
TclFile
|
sl@0
|
152 |
TclpOpenFile(fname, mode)
|
sl@0
|
153 |
CONST char *fname; /* The name of the file to open. */
|
sl@0
|
154 |
int mode; /* In what mode to open the file? */
|
sl@0
|
155 |
{
|
sl@0
|
156 |
int fd;
|
sl@0
|
157 |
CONST char *native;
|
sl@0
|
158 |
Tcl_DString ds;
|
sl@0
|
159 |
|
sl@0
|
160 |
native = Tcl_UtfToExternalDString(NULL, fname, -1, &ds);
|
sl@0
|
161 |
fd = TclOSopen(native, mode, 0666); /* INTL: Native. */
|
sl@0
|
162 |
Tcl_DStringFree(&ds);
|
sl@0
|
163 |
if (fd != -1) {
|
sl@0
|
164 |
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
sl@0
|
165 |
|
sl@0
|
166 |
/*
|
sl@0
|
167 |
* If the file is being opened for writing, seek to the end
|
sl@0
|
168 |
* so we can append to any data already in the file.
|
sl@0
|
169 |
*/
|
sl@0
|
170 |
|
sl@0
|
171 |
if ((mode & O_WRONLY) && !(mode & O_APPEND)) {
|
sl@0
|
172 |
TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_END);
|
sl@0
|
173 |
}
|
sl@0
|
174 |
|
sl@0
|
175 |
/*
|
sl@0
|
176 |
* Increment the fd so it can't be 0, which would conflict with
|
sl@0
|
177 |
* the NULL return for errors.
|
sl@0
|
178 |
*/
|
sl@0
|
179 |
|
sl@0
|
180 |
return MakeFile(fd);
|
sl@0
|
181 |
}
|
sl@0
|
182 |
return NULL;
|
sl@0
|
183 |
}
|
sl@0
|
184 |
|
sl@0
|
185 |
/*
|
sl@0
|
186 |
*----------------------------------------------------------------------
|
sl@0
|
187 |
*
|
sl@0
|
188 |
* TclpCreateTempFile --
|
sl@0
|
189 |
*
|
sl@0
|
190 |
* This function creates a temporary file initialized with an
|
sl@0
|
191 |
* optional string, and returns a file handle with the file pointer
|
sl@0
|
192 |
* at the beginning of the file.
|
sl@0
|
193 |
*
|
sl@0
|
194 |
* Results:
|
sl@0
|
195 |
* A handle to a file.
|
sl@0
|
196 |
*
|
sl@0
|
197 |
* Side effects:
|
sl@0
|
198 |
* None.
|
sl@0
|
199 |
*
|
sl@0
|
200 |
*----------------------------------------------------------------------
|
sl@0
|
201 |
*/
|
sl@0
|
202 |
|
sl@0
|
203 |
TclFile
|
sl@0
|
204 |
TclpCreateTempFile(contents)
|
sl@0
|
205 |
CONST char *contents; /* String to write into temp file, or NULL. */
|
sl@0
|
206 |
{
|
sl@0
|
207 |
char fileName[L_tmpnam + 9];
|
sl@0
|
208 |
CONST char *native;
|
sl@0
|
209 |
Tcl_DString dstring;
|
sl@0
|
210 |
int fd;
|
sl@0
|
211 |
|
sl@0
|
212 |
/*
|
sl@0
|
213 |
* We should also check against making more then TMP_MAX of these.
|
sl@0
|
214 |
*/
|
sl@0
|
215 |
|
sl@0
|
216 |
// Symbian doesn't have a default temp directory, so we use cwd (root of private data cage)
|
sl@0
|
217 |
#ifdef __SYMBIAN32__
|
sl@0
|
218 |
if (getcwd(fileName, L_tmpnam) == NULL)
|
sl@0
|
219 |
{
|
sl@0
|
220 |
return NULL;
|
sl@0
|
221 |
}
|
sl@0
|
222 |
/* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */
|
sl@0
|
223 |
fileName[0] = 'c';
|
sl@0
|
224 |
#else
|
sl@0
|
225 |
strcpy(fileName, P_tmpdir); /* INTL: Native. */
|
sl@0
|
226 |
#endif
|
sl@0
|
227 |
if (fileName[strlen(fileName) - 1] != '/') {
|
sl@0
|
228 |
#ifdef __SYMBIAN32__
|
sl@0
|
229 |
strcat(fileName, "\\");
|
sl@0
|
230 |
#else
|
sl@0
|
231 |
strcat(fileName, "/"); /* INTL: Native. */
|
sl@0
|
232 |
#endif
|
sl@0
|
233 |
}
|
sl@0
|
234 |
strcat(fileName, "tclXXXXXX");
|
sl@0
|
235 |
fd = mkstemp(fileName); /* INTL: Native. */
|
sl@0
|
236 |
if (fd == -1) {
|
sl@0
|
237 |
return NULL;
|
sl@0
|
238 |
}
|
sl@0
|
239 |
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
sl@0
|
240 |
#ifdef __SYMBIAN32__
|
sl@0
|
241 |
strcpy(tmpFileName, fileName);
|
sl@0
|
242 |
#endif
|
sl@0
|
243 |
unlink(fileName); /* INTL: Native. */
|
sl@0
|
244 |
|
sl@0
|
245 |
if (contents != NULL) {
|
sl@0
|
246 |
native = Tcl_UtfToExternalDString(NULL, contents, -1, &dstring);
|
sl@0
|
247 |
if (write(fd, native, strlen(native)) == -1) {
|
sl@0
|
248 |
close(fd);
|
sl@0
|
249 |
Tcl_DStringFree(&dstring);
|
sl@0
|
250 |
return NULL;
|
sl@0
|
251 |
}
|
sl@0
|
252 |
Tcl_DStringFree(&dstring);
|
sl@0
|
253 |
TclOSseek(fd, (Tcl_SeekOffset) 0, SEEK_SET);
|
sl@0
|
254 |
}
|
sl@0
|
255 |
return MakeFile(fd);
|
sl@0
|
256 |
}
|
sl@0
|
257 |
|
sl@0
|
258 |
/*
|
sl@0
|
259 |
*----------------------------------------------------------------------
|
sl@0
|
260 |
*
|
sl@0
|
261 |
* TclpTempFileName --
|
sl@0
|
262 |
*
|
sl@0
|
263 |
* This function returns unique filename.
|
sl@0
|
264 |
*
|
sl@0
|
265 |
* Results:
|
sl@0
|
266 |
* Returns a valid Tcl_Obj* with refCount 0, or NULL on failure.
|
sl@0
|
267 |
*
|
sl@0
|
268 |
* Side effects:
|
sl@0
|
269 |
* None.
|
sl@0
|
270 |
*
|
sl@0
|
271 |
*----------------------------------------------------------------------
|
sl@0
|
272 |
*/
|
sl@0
|
273 |
|
sl@0
|
274 |
Tcl_Obj*
|
sl@0
|
275 |
TclpTempFileName()
|
sl@0
|
276 |
{
|
sl@0
|
277 |
char fileName[L_tmpnam + 9];
|
sl@0
|
278 |
Tcl_Obj *result = NULL;
|
sl@0
|
279 |
int fd;
|
sl@0
|
280 |
|
sl@0
|
281 |
/*
|
sl@0
|
282 |
* We should also check against making more then TMP_MAX of these.
|
sl@0
|
283 |
*/
|
sl@0
|
284 |
|
sl@0
|
285 |
// Symbian doesn't have a default temp directory, so we use cwd (root of private data cage)
|
sl@0
|
286 |
#ifdef __SYMBIAN32__
|
sl@0
|
287 |
if (getcwd(fileName, L_tmpnam) == NULL)
|
sl@0
|
288 |
{
|
sl@0
|
289 |
return NULL;
|
sl@0
|
290 |
}
|
sl@0
|
291 |
/* TODO - the line bellow is a temporary patch. The defect number is: DEF116621. */
|
sl@0
|
292 |
fileName[0] = 'c';
|
sl@0
|
293 |
#else
|
sl@0
|
294 |
strcpy(fileName, P_tmpdir); /* INTL: Native. */
|
sl@0
|
295 |
#endif
|
sl@0
|
296 |
if (fileName[strlen(fileName) - 1] != '/') {
|
sl@0
|
297 |
#ifdef __SYMBIAN32__
|
sl@0
|
298 |
strcat(fileName, "\\");
|
sl@0
|
299 |
#else
|
sl@0
|
300 |
strcat(fileName, "/"); /* INTL: Native. */
|
sl@0
|
301 |
#endif
|
sl@0
|
302 |
}
|
sl@0
|
303 |
strcat(fileName, "tclXXXXXX");
|
sl@0
|
304 |
fd = mkstemp(fileName); /* INTL: Native. */
|
sl@0
|
305 |
if (fd == -1) {
|
sl@0
|
306 |
return NULL;
|
sl@0
|
307 |
}
|
sl@0
|
308 |
fcntl(fd, F_SETFD, FD_CLOEXEC);
|
sl@0
|
309 |
unlink(fileName); /* INTL: Native. */
|
sl@0
|
310 |
|
sl@0
|
311 |
result = TclpNativeToNormalized((ClientData) fileName);
|
sl@0
|
312 |
close (fd);
|
sl@0
|
313 |
return result;
|
sl@0
|
314 |
}
|
sl@0
|
315 |
|
sl@0
|
316 |
/*
|
sl@0
|
317 |
*----------------------------------------------------------------------
|
sl@0
|
318 |
*
|
sl@0
|
319 |
* TclpCreatePipe --
|
sl@0
|
320 |
*
|
sl@0
|
321 |
* Creates a pipe - simply calls the pipe() function.
|
sl@0
|
322 |
*
|
sl@0
|
323 |
* Results:
|
sl@0
|
324 |
* Returns 1 on success, 0 on failure.
|
sl@0
|
325 |
*
|
sl@0
|
326 |
* Side effects:
|
sl@0
|
327 |
* Creates a pipe.
|
sl@0
|
328 |
*
|
sl@0
|
329 |
*----------------------------------------------------------------------
|
sl@0
|
330 |
*/
|
sl@0
|
331 |
|
sl@0
|
332 |
int
|
sl@0
|
333 |
TclpCreatePipe(readPipe, writePipe)
|
sl@0
|
334 |
TclFile *readPipe; /* Location to store file handle for
|
sl@0
|
335 |
* read side of pipe. */
|
sl@0
|
336 |
TclFile *writePipe; /* Location to store file handle for
|
sl@0
|
337 |
* write side of pipe. */
|
sl@0
|
338 |
{
|
sl@0
|
339 |
int pipeIds[2];
|
sl@0
|
340 |
|
sl@0
|
341 |
if (pipe(pipeIds) != 0) {
|
sl@0
|
342 |
return 0;
|
sl@0
|
343 |
}
|
sl@0
|
344 |
|
sl@0
|
345 |
fcntl(pipeIds[0], F_SETFD, FD_CLOEXEC);
|
sl@0
|
346 |
fcntl(pipeIds[1], F_SETFD, FD_CLOEXEC);
|
sl@0
|
347 |
|
sl@0
|
348 |
*readPipe = MakeFile(pipeIds[0]);
|
sl@0
|
349 |
*writePipe = MakeFile(pipeIds[1]);
|
sl@0
|
350 |
return 1;
|
sl@0
|
351 |
}
|
sl@0
|
352 |
|
sl@0
|
353 |
/*
|
sl@0
|
354 |
*----------------------------------------------------------------------
|
sl@0
|
355 |
*
|
sl@0
|
356 |
* TclpCloseFile --
|
sl@0
|
357 |
*
|
sl@0
|
358 |
* Implements a mechanism to close a UNIX file.
|
sl@0
|
359 |
*
|
sl@0
|
360 |
* Results:
|
sl@0
|
361 |
* Returns 0 on success, or -1 on error, setting errno.
|
sl@0
|
362 |
*
|
sl@0
|
363 |
* Side effects:
|
sl@0
|
364 |
* The file is closed.
|
sl@0
|
365 |
*
|
sl@0
|
366 |
*----------------------------------------------------------------------
|
sl@0
|
367 |
*/
|
sl@0
|
368 |
|
sl@0
|
369 |
int
|
sl@0
|
370 |
TclpCloseFile(file)
|
sl@0
|
371 |
TclFile file; /* The file to close. */
|
sl@0
|
372 |
{
|
sl@0
|
373 |
int fd = GetFd(file);
|
sl@0
|
374 |
|
sl@0
|
375 |
/*
|
sl@0
|
376 |
* Refuse to close the fds for stdin, stdout and stderr.
|
sl@0
|
377 |
*/
|
sl@0
|
378 |
|
sl@0
|
379 |
if ((fd == 0) || (fd == 1) || (fd == 2)) {
|
sl@0
|
380 |
return 0;
|
sl@0
|
381 |
}
|
sl@0
|
382 |
|
sl@0
|
383 |
Tcl_DeleteFileHandler(fd);
|
sl@0
|
384 |
return close(fd);
|
sl@0
|
385 |
}
|
sl@0
|
386 |
|
sl@0
|
387 |
/*
|
sl@0
|
388 |
*---------------------------------------------------------------------------
|
sl@0
|
389 |
*
|
sl@0
|
390 |
* TclpCreateProcess --
|
sl@0
|
391 |
*
|
sl@0
|
392 |
* Create a child process that has the specified files as its
|
sl@0
|
393 |
* standard input, output, and error. The child process runs
|
sl@0
|
394 |
* asynchronously and runs with the same environment variables
|
sl@0
|
395 |
* as the creating process.
|
sl@0
|
396 |
*
|
sl@0
|
397 |
* The path is searched to find the specified executable.
|
sl@0
|
398 |
*
|
sl@0
|
399 |
* Results:
|
sl@0
|
400 |
* The return value is TCL_ERROR and an error message is left in
|
sl@0
|
401 |
* the interp's result if there was a problem creating the child
|
sl@0
|
402 |
* process. Otherwise, the return value is TCL_OK and *pidPtr is
|
sl@0
|
403 |
* filled with the process id of the child process.
|
sl@0
|
404 |
*
|
sl@0
|
405 |
* Side effects:
|
sl@0
|
406 |
* A process is created.
|
sl@0
|
407 |
*
|
sl@0
|
408 |
*---------------------------------------------------------------------------
|
sl@0
|
409 |
*/
|
sl@0
|
410 |
|
sl@0
|
411 |
/* ARGSUSED */
|
sl@0
|
412 |
int
|
sl@0
|
413 |
TclpCreateProcess(interp, argc, argv, inputFile, outputFile, errorFile,
|
sl@0
|
414 |
pidPtr)
|
sl@0
|
415 |
Tcl_Interp *interp; /* Interpreter in which to leave errors that
|
sl@0
|
416 |
* occurred when creating the child process.
|
sl@0
|
417 |
* Error messages from the child process
|
sl@0
|
418 |
* itself are sent to errorFile. */
|
sl@0
|
419 |
int argc; /* Number of arguments in following array. */
|
sl@0
|
420 |
CONST char **argv; /* Array of argument strings in UTF-8.
|
sl@0
|
421 |
* argv[0] contains the name of the executable
|
sl@0
|
422 |
* translated using Tcl_TranslateFileName
|
sl@0
|
423 |
* call). Additional arguments have not been
|
sl@0
|
424 |
* converted. */
|
sl@0
|
425 |
TclFile inputFile; /* If non-NULL, gives the file to use as
|
sl@0
|
426 |
* input for the child process. If inputFile
|
sl@0
|
427 |
* file is not readable or is NULL, the child
|
sl@0
|
428 |
* will receive no standard input. */
|
sl@0
|
429 |
TclFile outputFile; /* If non-NULL, gives the file that
|
sl@0
|
430 |
* receives output from the child process. If
|
sl@0
|
431 |
* outputFile file is not writeable or is
|
sl@0
|
432 |
* NULL, output from the child will be
|
sl@0
|
433 |
* discarded. */
|
sl@0
|
434 |
TclFile errorFile; /* If non-NULL, gives the file that
|
sl@0
|
435 |
* receives errors from the child process. If
|
sl@0
|
436 |
* errorFile file is not writeable or is NULL,
|
sl@0
|
437 |
* errors from the child will be discarded.
|
sl@0
|
438 |
* errorFile may be the same as outputFile. */
|
sl@0
|
439 |
Tcl_Pid *pidPtr; /* If this procedure is successful, pidPtr
|
sl@0
|
440 |
* is filled with the process id of the child
|
sl@0
|
441 |
* process. */
|
sl@0
|
442 |
{
|
sl@0
|
443 |
TclFile errPipeIn, errPipeOut;
|
sl@0
|
444 |
int count, status, fd;
|
sl@0
|
445 |
char errSpace[200 + TCL_INTEGER_SPACE];
|
sl@0
|
446 |
Tcl_DString *dsArray;
|
sl@0
|
447 |
char **newArgv = 0;
|
sl@0
|
448 |
int pid, i;
|
sl@0
|
449 |
#ifdef __SYMBIAN32__
|
sl@0
|
450 |
int fd1;
|
sl@0
|
451 |
int RetVal;
|
sl@0
|
452 |
char buf[256];
|
sl@0
|
453 |
int fifoResult;
|
sl@0
|
454 |
struct timeval tv;
|
sl@0
|
455 |
fd_set readfds;
|
sl@0
|
456 |
#endif
|
sl@0
|
457 |
|
sl@0
|
458 |
errPipeIn = NULL;
|
sl@0
|
459 |
errPipeOut = NULL;
|
sl@0
|
460 |
pid = -1;
|
sl@0
|
461 |
|
sl@0
|
462 |
/*
|
sl@0
|
463 |
* Create a pipe that the child can use to return error
|
sl@0
|
464 |
* information if anything goes wrong.
|
sl@0
|
465 |
*/
|
sl@0
|
466 |
#ifdef __SYMBIAN32__
|
sl@0
|
467 |
// change the communication between parent and child process, it just report the failure of child process creation
|
sl@0
|
468 |
|
sl@0
|
469 |
tmpnam(fifoFileName);
|
sl@0
|
470 |
fifoResult = mkfifo(fifoFileName,S_IXGRP);
|
sl@0
|
471 |
if(fifoResult == -1)
|
sl@0
|
472 |
{
|
sl@0
|
473 |
//fifo creation failure.
|
sl@0
|
474 |
fprintf(stderr,"\n*** failure mkfifo errno is %d***\n",errno);
|
sl@0
|
475 |
goto error;
|
sl@0
|
476 |
}
|
sl@0
|
477 |
else
|
sl@0
|
478 |
{
|
sl@0
|
479 |
int ReadFifoFd = open(fifoFileName,O_RDONLY | O_NONBLOCK);
|
sl@0
|
480 |
|
sl@0
|
481 |
if(ReadFifoFd == -1)
|
sl@0
|
482 |
{
|
sl@0
|
483 |
//Failed to open the Fifo
|
sl@0
|
484 |
printf("\n*** failure Fifo Open ***\n");
|
sl@0
|
485 |
goto error;
|
sl@0
|
486 |
}
|
sl@0
|
487 |
else
|
sl@0
|
488 |
{
|
sl@0
|
489 |
errPipeIn = MakeFile(ReadFifoFd);
|
sl@0
|
490 |
}
|
sl@0
|
491 |
}
|
sl@0
|
492 |
|
sl@0
|
493 |
//set the stdin,stdout,stderr and pipeid to the child process. the fds are passed to the posix_spawn() in argv
|
sl@0
|
494 |
argc = argc + ADDPARAMTOCHILD;
|
sl@0
|
495 |
|
sl@0
|
496 |
#else
|
sl@0
|
497 |
if (TclpCreatePipe(&errPipeIn, &errPipeOut) == 0) {
|
sl@0
|
498 |
Tcl_AppendResult(interp, "couldn't create pipe: ",
|
sl@0
|
499 |
Tcl_PosixError(interp), (char *) NULL);
|
sl@0
|
500 |
goto error;
|
sl@0
|
501 |
}
|
sl@0
|
502 |
#endif
|
sl@0
|
503 |
|
sl@0
|
504 |
/*
|
sl@0
|
505 |
* We need to allocate and convert this before the fork
|
sl@0
|
506 |
* so it is properly deallocated later
|
sl@0
|
507 |
*/
|
sl@0
|
508 |
dsArray = (Tcl_DString *) ckalloc(argc * sizeof(Tcl_DString));
|
sl@0
|
509 |
newArgv = (char **) ckalloc((argc+1) * sizeof(char *));
|
sl@0
|
510 |
newArgv[argc] = NULL;
|
sl@0
|
511 |
#ifdef __SYMBIAN32__
|
sl@0
|
512 |
for (i = 0; i < (argc-ADDPARAMTOCHILD); i++) {
|
sl@0
|
513 |
#else
|
sl@0
|
514 |
for (i = 0; i < argc; i++) {
|
sl@0
|
515 |
#endif
|
sl@0
|
516 |
newArgv[i] = Tcl_UtfToExternalDString(NULL, argv[i], -1, &dsArray[i]);
|
sl@0
|
517 |
}
|
sl@0
|
518 |
|
sl@0
|
519 |
#ifdef __SYMBIAN32__
|
sl@0
|
520 |
fprintf(stderr,"\r\n");
|
sl@0
|
521 |
//set the stdin,stdout,stderr to the child process. the fds pass to the posix_spawn() in argv
|
sl@0
|
522 |
for (i = argc-ADDPARAMTOCHILD; i < argc; i++)
|
sl@0
|
523 |
{
|
sl@0
|
524 |
if(i == (argc-ADDPARAMTOCHILD))
|
sl@0
|
525 |
{
|
sl@0
|
526 |
strcpy(buf,fifoFileName);
|
sl@0
|
527 |
fprintf(stderr,"fifoFileName is %s\r\n", fifoFileName);
|
sl@0
|
528 |
}
|
sl@0
|
529 |
else if(i == (argc-ADDPARAMTOCHILD+1))
|
sl@0
|
530 |
{
|
sl@0
|
531 |
if (inputFile)
|
sl@0
|
532 |
{
|
sl@0
|
533 |
strcpy(buf,inFileName);
|
sl@0
|
534 |
}
|
sl@0
|
535 |
else
|
sl@0
|
536 |
{
|
sl@0
|
537 |
strcpy(buf,"STD");
|
sl@0
|
538 |
}
|
sl@0
|
539 |
fprintf(stderr, "inFileName is %s\r\n", inFileName);
|
sl@0
|
540 |
}
|
sl@0
|
541 |
else if(i == (argc-ADDPARAMTOCHILD+2))
|
sl@0
|
542 |
{
|
sl@0
|
543 |
if (outputFile)
|
sl@0
|
544 |
{
|
sl@0
|
545 |
strcpy(buf,outFileName);
|
sl@0
|
546 |
}
|
sl@0
|
547 |
else
|
sl@0
|
548 |
{
|
sl@0
|
549 |
strcpy(buf,"STD");
|
sl@0
|
550 |
}
|
sl@0
|
551 |
fprintf(stderr, "outFileName is %s\r\n", outFileName);
|
sl@0
|
552 |
}
|
sl@0
|
553 |
else if(i == (argc-ADDPARAMTOCHILD+3))
|
sl@0
|
554 |
{
|
sl@0
|
555 |
if (errorFile)
|
sl@0
|
556 |
{
|
sl@0
|
557 |
strcpy(buf,errFileName);
|
sl@0
|
558 |
}
|
sl@0
|
559 |
else
|
sl@0
|
560 |
{
|
sl@0
|
561 |
strcpy(buf,"STD");
|
sl@0
|
562 |
}
|
sl@0
|
563 |
fprintf(stderr, "errFileName is %s\r\n", errFileName);
|
sl@0
|
564 |
}
|
sl@0
|
565 |
|
sl@0
|
566 |
newArgv[i] = Tcl_UtfToExternalDString(NULL, buf, -1, &dsArray[i]);
|
sl@0
|
567 |
}
|
sl@0
|
568 |
#endif
|
sl@0
|
569 |
#ifdef USE_VFORK
|
sl@0
|
570 |
/*
|
sl@0
|
571 |
* After vfork(), do not call code in the child that changes global state,
|
sl@0
|
572 |
* because it is using the parent's memory space at that point and writes
|
sl@0
|
573 |
* might corrupt the parent: so ensure standard channels are initialized in
|
sl@0
|
574 |
* the parent, otherwise SetupStdFile() might initialize them in the child.
|
sl@0
|
575 |
*/
|
sl@0
|
576 |
if (!inputFile) {
|
sl@0
|
577 |
Tcl_GetStdChannel(TCL_STDIN);
|
sl@0
|
578 |
}
|
sl@0
|
579 |
if (!outputFile) {
|
sl@0
|
580 |
Tcl_GetStdChannel(TCL_STDOUT);
|
sl@0
|
581 |
}
|
sl@0
|
582 |
if (!errorFile) {
|
sl@0
|
583 |
Tcl_GetStdChannel(TCL_STDERR);
|
sl@0
|
584 |
}
|
sl@0
|
585 |
#endif
|
sl@0
|
586 |
TclPrint1(" == TclpCreateProcess(), posix_spawn(), process %S\r\n", newArgv[0]);
|
sl@0
|
587 |
#ifdef __SYMBIAN32__
|
sl@0
|
588 |
// change fork() using posix_spawn()
|
sl@0
|
589 |
if (argc > 1)
|
sl@0
|
590 |
{
|
sl@0
|
591 |
RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,newArgv,NULL);
|
sl@0
|
592 |
}
|
sl@0
|
593 |
else
|
sl@0
|
594 |
{
|
sl@0
|
595 |
RetVal= posix_spawn(&pid,newArgv[0],NULL,NULL,NULL,NULL);
|
sl@0
|
596 |
}
|
sl@0
|
597 |
if (RetVal != 0)
|
sl@0
|
598 |
{
|
sl@0
|
599 |
pid = -1; //if error, the value of pid is unspecified. ensure we still report the error.
|
sl@0
|
600 |
}
|
sl@0
|
601 |
#else
|
sl@0
|
602 |
pid = fork();
|
sl@0
|
603 |
if (pid == 0) {
|
sl@0
|
604 |
int joinThisError = errorFile && (errorFile == outputFile);
|
sl@0
|
605 |
|
sl@0
|
606 |
fd = GetFd(errPipeOut);
|
sl@0
|
607 |
|
sl@0
|
608 |
/*
|
sl@0
|
609 |
* Set up stdio file handles for the child process.
|
sl@0
|
610 |
*/
|
sl@0
|
611 |
|
sl@0
|
612 |
if (!SetupStdFile(inputFile, TCL_STDIN)
|
sl@0
|
613 |
|| !SetupStdFile(outputFile, TCL_STDOUT)
|
sl@0
|
614 |
|| (!joinThisError && !SetupStdFile(errorFile, TCL_STDERR))
|
sl@0
|
615 |
|| (joinThisError &&
|
sl@0
|
616 |
((dup2(1,2) == -1) ||
|
sl@0
|
617 |
(fcntl(2, F_SETFD, 0) != 0)))) {
|
sl@0
|
618 |
sprintf(errSpace, "forked process couldn't set up input/output, error: %d\r\n", errno);
|
sl@0
|
619 |
write(fd, errSpace, (size_t) strlen(errSpace));
|
sl@0
|
620 |
_exit(1);
|
sl@0
|
621 |
}
|
sl@0
|
622 |
|
sl@0
|
623 |
/*
|
sl@0
|
624 |
* Close the input side of the error pipe.
|
sl@0
|
625 |
*/
|
sl@0
|
626 |
|
sl@0
|
627 |
RestoreSignals();
|
sl@0
|
628 |
execvp(newArgv[0], newArgv); /* INTL: Native. */
|
sl@0
|
629 |
sprintf(errSpace, "couldn't execute \"%.150s\", error: %d\r\n", argv[0], errno);
|
sl@0
|
630 |
write(fd, errSpace, (size_t) strlen(errSpace));
|
sl@0
|
631 |
_exit(1);
|
sl@0
|
632 |
}
|
sl@0
|
633 |
#endif
|
sl@0
|
634 |
TclPrint2(" == TclpCreateProcess(), posix_spawn(), process %S, pid %d\r\n", newArgv[0], (int)pid);
|
sl@0
|
635 |
|
sl@0
|
636 |
//
|
sl@0
|
637 |
// Free the mem we used for the fork
|
sl@0
|
638 |
//
|
sl@0
|
639 |
for (i = 0; i < argc; i++) {
|
sl@0
|
640 |
Tcl_DStringFree(&dsArray[i]);
|
sl@0
|
641 |
}
|
sl@0
|
642 |
ckfree((char *) dsArray);
|
sl@0
|
643 |
ckfree((char *) newArgv);
|
sl@0
|
644 |
|
sl@0
|
645 |
if (pid == -1) {
|
sl@0
|
646 |
#ifdef __SYMBIAN32__
|
sl@0
|
647 |
Tcl_AppendResult(interp, "couldn't posix_spawn child process: ",
|
sl@0
|
648 |
#else
|
sl@0
|
649 |
Tcl_AppendResult(interp, "couldn't fork child process: ",
|
sl@0
|
650 |
#endif
|
sl@0
|
651 |
Tcl_PosixError(interp), (char *) NULL);
|
sl@0
|
652 |
goto error;
|
sl@0
|
653 |
}
|
sl@0
|
654 |
|
sl@0
|
655 |
/*
|
sl@0
|
656 |
* Read back from the error pipe to see if the child started
|
sl@0
|
657 |
* up OK. The info in the pipe (if any) consists of a decimal
|
sl@0
|
658 |
* errno value followed by an error message.
|
sl@0
|
659 |
*/
|
sl@0
|
660 |
//*
|
sl@0
|
661 |
#ifdef __SYMBIAN32__
|
sl@0
|
662 |
TclpCloseFile(errPipeOut);
|
sl@0
|
663 |
errPipeOut = NULL;
|
sl@0
|
664 |
#endif
|
sl@0
|
665 |
fd = GetFd(errPipeIn);
|
sl@0
|
666 |
#ifdef __SYMBIAN32__
|
sl@0
|
667 |
TclPrint3(" == TclpCreateProcess(), select()\r\n");
|
sl@0
|
668 |
tv.tv_sec = 3;
|
sl@0
|
669 |
tv.tv_usec = 0;
|
sl@0
|
670 |
|
sl@0
|
671 |
FD_ZERO(&readfds);
|
sl@0
|
672 |
FD_SET(fd, &readfds);
|
sl@0
|
673 |
// Use select to wait for child process
|
sl@0
|
674 |
i = select(fd+1, &readfds, NULL, NULL,&tv);
|
sl@0
|
675 |
if ( i != -1 && FD_ISSET(fd, &readfds))
|
sl@0
|
676 |
{
|
sl@0
|
677 |
count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
|
sl@0
|
678 |
if (count > 0) {
|
sl@0
|
679 |
char *end;
|
sl@0
|
680 |
errSpace[count] = 0;
|
sl@0
|
681 |
errno = strtol(errSpace, &end, 10);
|
sl@0
|
682 |
if (errno != 0)
|
sl@0
|
683 |
{
|
sl@0
|
684 |
Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
|
sl@0
|
685 |
(char *) NULL);
|
sl@0
|
686 |
goto error;
|
sl@0
|
687 |
}
|
sl@0
|
688 |
}
|
sl@0
|
689 |
}
|
sl@0
|
690 |
else
|
sl@0
|
691 |
{
|
sl@0
|
692 |
// Process not started properly
|
sl@0
|
693 |
Tcl_AppendResult(interp, "couldn't read error info from child process: ",
|
sl@0
|
694 |
Tcl_PosixError(interp), (char *) NULL);
|
sl@0
|
695 |
goto error;
|
sl@0
|
696 |
}
|
sl@0
|
697 |
#else
|
sl@0
|
698 |
count = read(fd, errSpace, (size_t) (sizeof(errSpace) - 1));
|
sl@0
|
699 |
if (count > 0) {
|
sl@0
|
700 |
char *end;
|
sl@0
|
701 |
errSpace[count] = 0;
|
sl@0
|
702 |
errno = strtol(errSpace, &end, 10);
|
sl@0
|
703 |
Tcl_AppendResult(interp, end, Tcl_PosixError(interp),
|
sl@0
|
704 |
(char *) NULL);
|
sl@0
|
705 |
goto error;
|
sl@0
|
706 |
}
|
sl@0
|
707 |
#endif
|
sl@0
|
708 |
TclpCloseFile(errPipeIn);
|
sl@0
|
709 |
#ifdef __SYMBIAN32__
|
sl@0
|
710 |
unlink(fifoFileName);
|
sl@0
|
711 |
#endif
|
sl@0
|
712 |
*pidPtr = (Tcl_Pid) pid;
|
sl@0
|
713 |
TclPrint3(" == TclpCreateProcess(), TCL_OK\r\n");
|
sl@0
|
714 |
return TCL_OK;
|
sl@0
|
715 |
|
sl@0
|
716 |
error:
|
sl@0
|
717 |
TclPrint2(" == TclpCreateProcess(), TCL_ERROR,%S errno %d\r\n", "", errno);
|
sl@0
|
718 |
if (pid != -1) {
|
sl@0
|
719 |
/*
|
sl@0
|
720 |
* Reap the child process now if an error occurred during its
|
sl@0
|
721 |
* startup. We don't call this with WNOHANG because that can lead to
|
sl@0
|
722 |
* defunct processes on an MP system. We shouldn't have to worry
|
sl@0
|
723 |
* about hanging here, since this is the error case. [Bug: 6148]
|
sl@0
|
724 |
*/
|
sl@0
|
725 |
|
sl@0
|
726 |
Tcl_WaitPid((Tcl_Pid) pid, &status, 0);
|
sl@0
|
727 |
}
|
sl@0
|
728 |
|
sl@0
|
729 |
if (errPipeIn) {
|
sl@0
|
730 |
TclpCloseFile(errPipeIn);
|
sl@0
|
731 |
}
|
sl@0
|
732 |
if (errPipeOut) {
|
sl@0
|
733 |
TclpCloseFile(errPipeOut);
|
sl@0
|
734 |
}
|
sl@0
|
735 |
#ifdef __SYMBIAN32__
|
sl@0
|
736 |
unlink(fifoFileName);
|
sl@0
|
737 |
#endif
|
sl@0
|
738 |
|
sl@0
|
739 |
return TCL_ERROR;
|
sl@0
|
740 |
}
|
sl@0
|
741 |
|
sl@0
|
742 |
/*
|
sl@0
|
743 |
*----------------------------------------------------------------------
|
sl@0
|
744 |
*
|
sl@0
|
745 |
* RestoreSignals --
|
sl@0
|
746 |
*
|
sl@0
|
747 |
* This procedure is invoked in a forked child process just before
|
sl@0
|
748 |
* exec-ing a new program to restore all signals to their default
|
sl@0
|
749 |
* settings.
|
sl@0
|
750 |
*
|
sl@0
|
751 |
* Results:
|
sl@0
|
752 |
* None.
|
sl@0
|
753 |
*
|
sl@0
|
754 |
* Side effects:
|
sl@0
|
755 |
* Signal settings get changed.
|
sl@0
|
756 |
*
|
sl@0
|
757 |
*----------------------------------------------------------------------
|
sl@0
|
758 |
*/
|
sl@0
|
759 |
|
sl@0
|
760 |
static void
|
sl@0
|
761 |
RestoreSignals()
|
sl@0
|
762 |
{
|
sl@0
|
763 |
// Symbian & PIPS don't support signals.
|
sl@0
|
764 |
#ifndef __SYMBIAN32__
|
sl@0
|
765 |
#ifdef SIGABRT
|
sl@0
|
766 |
signal(SIGABRT, SIG_DFL);
|
sl@0
|
767 |
#endif
|
sl@0
|
768 |
#ifdef SIGALRM
|
sl@0
|
769 |
signal(SIGALRM, SIG_DFL);
|
sl@0
|
770 |
#endif
|
sl@0
|
771 |
#ifdef SIGFPE
|
sl@0
|
772 |
signal(SIGFPE, SIG_DFL);
|
sl@0
|
773 |
#endif
|
sl@0
|
774 |
#ifdef SIGHUP
|
sl@0
|
775 |
signal(SIGHUP, SIG_DFL);
|
sl@0
|
776 |
#endif
|
sl@0
|
777 |
#ifdef SIGILL
|
sl@0
|
778 |
signal(SIGILL, SIG_DFL);
|
sl@0
|
779 |
#endif
|
sl@0
|
780 |
#ifdef SIGINT
|
sl@0
|
781 |
signal(SIGINT, SIG_DFL);
|
sl@0
|
782 |
#endif
|
sl@0
|
783 |
#ifdef SIGPIPE
|
sl@0
|
784 |
signal(SIGPIPE, SIG_DFL);
|
sl@0
|
785 |
#endif
|
sl@0
|
786 |
#ifdef SIGQUIT
|
sl@0
|
787 |
signal(SIGQUIT, SIG_DFL);
|
sl@0
|
788 |
#endif
|
sl@0
|
789 |
#ifdef SIGSEGV
|
sl@0
|
790 |
signal(SIGSEGV, SIG_DFL);
|
sl@0
|
791 |
#endif
|
sl@0
|
792 |
#ifdef SIGTERM
|
sl@0
|
793 |
signal(SIGTERM, SIG_DFL);
|
sl@0
|
794 |
#endif
|
sl@0
|
795 |
#ifdef SIGUSR1
|
sl@0
|
796 |
signal(SIGUSR1, SIG_DFL);
|
sl@0
|
797 |
#endif
|
sl@0
|
798 |
#ifdef SIGUSR2
|
sl@0
|
799 |
signal(SIGUSR2, SIG_DFL);
|
sl@0
|
800 |
#endif
|
sl@0
|
801 |
#ifdef SIGCHLD
|
sl@0
|
802 |
signal(SIGCHLD, SIG_DFL);
|
sl@0
|
803 |
#endif
|
sl@0
|
804 |
#ifdef SIGCONT
|
sl@0
|
805 |
signal(SIGCONT, SIG_DFL);
|
sl@0
|
806 |
#endif
|
sl@0
|
807 |
#ifdef SIGTSTP
|
sl@0
|
808 |
signal(SIGTSTP, SIG_DFL);
|
sl@0
|
809 |
#endif
|
sl@0
|
810 |
#ifdef SIGTTIN
|
sl@0
|
811 |
signal(SIGTTIN, SIG_DFL);
|
sl@0
|
812 |
#endif
|
sl@0
|
813 |
#ifdef SIGTTOU
|
sl@0
|
814 |
signal(SIGTTOU, SIG_DFL);
|
sl@0
|
815 |
#endif
|
sl@0
|
816 |
#endif
|
sl@0
|
817 |
}
|
sl@0
|
818 |
|
sl@0
|
819 |
/*
|
sl@0
|
820 |
*----------------------------------------------------------------------
|
sl@0
|
821 |
*
|
sl@0
|
822 |
* SetupStdFile --
|
sl@0
|
823 |
*
|
sl@0
|
824 |
* Set up stdio file handles for the child process, using the
|
sl@0
|
825 |
* current standard channels if no other files are specified.
|
sl@0
|
826 |
* If no standard channel is defined, or if no file is associated
|
sl@0
|
827 |
* with the channel, then the corresponding standard fd is closed.
|
sl@0
|
828 |
*
|
sl@0
|
829 |
* Results:
|
sl@0
|
830 |
* Returns 1 on success, or 0 on failure.
|
sl@0
|
831 |
*
|
sl@0
|
832 |
* Side effects:
|
sl@0
|
833 |
* Replaces stdio fds.
|
sl@0
|
834 |
*
|
sl@0
|
835 |
*----------------------------------------------------------------------
|
sl@0
|
836 |
*/
|
sl@0
|
837 |
#ifdef __SYMBIAN32__
|
sl@0
|
838 |
int
|
sl@0
|
839 |
#else
|
sl@0
|
840 |
static int
|
sl@0
|
841 |
#endif
|
sl@0
|
842 |
SetupStdFile(file, type)
|
sl@0
|
843 |
TclFile file; /* File to dup, or NULL. */
|
sl@0
|
844 |
int type; /* One of TCL_STDIN, TCL_STDOUT, TCL_STDERR */
|
sl@0
|
845 |
{
|
sl@0
|
846 |
Tcl_Channel channel;
|
sl@0
|
847 |
int fd;
|
sl@0
|
848 |
int targetFd = 0; /* Initializations here needed only to */
|
sl@0
|
849 |
int direction = 0; /* prevent warnings about using uninitialized
|
sl@0
|
850 |
* variables. */
|
sl@0
|
851 |
|
sl@0
|
852 |
switch (type) {
|
sl@0
|
853 |
case TCL_STDIN:
|
sl@0
|
854 |
targetFd = 0;
|
sl@0
|
855 |
direction = TCL_READABLE;
|
sl@0
|
856 |
break;
|
sl@0
|
857 |
case TCL_STDOUT:
|
sl@0
|
858 |
targetFd = 1;
|
sl@0
|
859 |
direction = TCL_WRITABLE;
|
sl@0
|
860 |
break;
|
sl@0
|
861 |
case TCL_STDERR:
|
sl@0
|
862 |
targetFd = 2;
|
sl@0
|
863 |
direction = TCL_WRITABLE;
|
sl@0
|
864 |
break;
|
sl@0
|
865 |
}
|
sl@0
|
866 |
|
sl@0
|
867 |
if (!file) {
|
sl@0
|
868 |
channel = Tcl_GetStdChannel(type);
|
sl@0
|
869 |
if (channel) {
|
sl@0
|
870 |
file = TclpMakeFile(channel, direction);
|
sl@0
|
871 |
}
|
sl@0
|
872 |
}
|
sl@0
|
873 |
if (file) {
|
sl@0
|
874 |
fd = GetFd(file);
|
sl@0
|
875 |
if (fd != targetFd) {
|
sl@0
|
876 |
if (dup2(fd, targetFd) == -1) {
|
sl@0
|
877 |
return 0;
|
sl@0
|
878 |
}
|
sl@0
|
879 |
|
sl@0
|
880 |
/*
|
sl@0
|
881 |
* Must clear the close-on-exec flag for the target FD, since
|
sl@0
|
882 |
* some systems (e.g. Ultrix) do not clear the CLOEXEC flag on
|
sl@0
|
883 |
* the target FD.
|
sl@0
|
884 |
*/
|
sl@0
|
885 |
|
sl@0
|
886 |
fcntl(targetFd, F_SETFD, 0);
|
sl@0
|
887 |
} else {
|
sl@0
|
888 |
/*
|
sl@0
|
889 |
* Since we aren't dup'ing the file, we need to explicitly clear
|
sl@0
|
890 |
* the close-on-exec flag.
|
sl@0
|
891 |
*/
|
sl@0
|
892 |
|
sl@0
|
893 |
fcntl(fd, F_SETFD, 0);
|
sl@0
|
894 |
}
|
sl@0
|
895 |
} else {
|
sl@0
|
896 |
close(targetFd);
|
sl@0
|
897 |
}
|
sl@0
|
898 |
return 1;
|
sl@0
|
899 |
}
|
sl@0
|
900 |
|
sl@0
|
901 |
/*
|
sl@0
|
902 |
*----------------------------------------------------------------------
|
sl@0
|
903 |
*
|
sl@0
|
904 |
* TclpCreateCommandChannel --
|
sl@0
|
905 |
*
|
sl@0
|
906 |
* This function is called by the generic IO level to perform
|
sl@0
|
907 |
* the platform specific channel initialization for a command
|
sl@0
|
908 |
* channel.
|
sl@0
|
909 |
*
|
sl@0
|
910 |
* Results:
|
sl@0
|
911 |
* Returns a new channel or NULL on failure.
|
sl@0
|
912 |
*
|
sl@0
|
913 |
* Side effects:
|
sl@0
|
914 |
* Allocates a new channel.
|
sl@0
|
915 |
*
|
sl@0
|
916 |
*----------------------------------------------------------------------
|
sl@0
|
917 |
*/
|
sl@0
|
918 |
|
sl@0
|
919 |
Tcl_Channel
|
sl@0
|
920 |
TclpCreateCommandChannel(readFile, writeFile, errorFile, numPids, pidPtr)
|
sl@0
|
921 |
TclFile readFile; /* If non-null, gives the file for reading. */
|
sl@0
|
922 |
TclFile writeFile; /* If non-null, gives the file for writing. */
|
sl@0
|
923 |
TclFile errorFile; /* If non-null, gives the file where errors
|
sl@0
|
924 |
* can be read. */
|
sl@0
|
925 |
int numPids; /* The number of pids in the pid array. */
|
sl@0
|
926 |
Tcl_Pid *pidPtr; /* An array of process identifiers.
|
sl@0
|
927 |
* Allocated by the caller, freed when
|
sl@0
|
928 |
* the channel is closed or the processes
|
sl@0
|
929 |
* are detached (in a background exec). */
|
sl@0
|
930 |
{
|
sl@0
|
931 |
char channelName[16 + TCL_INTEGER_SPACE];
|
sl@0
|
932 |
int channelId;
|
sl@0
|
933 |
PipeState *statePtr = (PipeState *) ckalloc((unsigned) sizeof(PipeState));
|
sl@0
|
934 |
int mode;
|
sl@0
|
935 |
|
sl@0
|
936 |
statePtr->inFile = readFile;
|
sl@0
|
937 |
statePtr->outFile = writeFile;
|
sl@0
|
938 |
statePtr->errorFile = errorFile;
|
sl@0
|
939 |
statePtr->numPids = numPids;
|
sl@0
|
940 |
statePtr->pidPtr = pidPtr;
|
sl@0
|
941 |
statePtr->isNonBlocking = 0;
|
sl@0
|
942 |
|
sl@0
|
943 |
mode = 0;
|
sl@0
|
944 |
if (readFile) {
|
sl@0
|
945 |
mode |= TCL_READABLE;
|
sl@0
|
946 |
}
|
sl@0
|
947 |
if (writeFile) {
|
sl@0
|
948 |
mode |= TCL_WRITABLE;
|
sl@0
|
949 |
}
|
sl@0
|
950 |
|
sl@0
|
951 |
/*
|
sl@0
|
952 |
* Use one of the fds associated with the channel as the
|
sl@0
|
953 |
* channel id.
|
sl@0
|
954 |
*/
|
sl@0
|
955 |
|
sl@0
|
956 |
if (readFile) {
|
sl@0
|
957 |
channelId = GetFd(readFile);
|
sl@0
|
958 |
} else if (writeFile) {
|
sl@0
|
959 |
channelId = GetFd(writeFile);
|
sl@0
|
960 |
} else if (errorFile) {
|
sl@0
|
961 |
channelId = GetFd(errorFile);
|
sl@0
|
962 |
} else {
|
sl@0
|
963 |
channelId = 0;
|
sl@0
|
964 |
}
|
sl@0
|
965 |
|
sl@0
|
966 |
/*
|
sl@0
|
967 |
* For backward compatibility with previous versions of Tcl, we
|
sl@0
|
968 |
* use "file%d" as the base name for pipes even though it would
|
sl@0
|
969 |
* be more natural to use "pipe%d".
|
sl@0
|
970 |
*/
|
sl@0
|
971 |
|
sl@0
|
972 |
sprintf(channelName, "file%d", channelId);
|
sl@0
|
973 |
statePtr->channel = Tcl_CreateChannel(&pipeChannelType, channelName,
|
sl@0
|
974 |
(ClientData) statePtr, mode);
|
sl@0
|
975 |
return statePtr->channel;
|
sl@0
|
976 |
}
|
sl@0
|
977 |
|
sl@0
|
978 |
/*
|
sl@0
|
979 |
*----------------------------------------------------------------------
|
sl@0
|
980 |
*
|
sl@0
|
981 |
* TclGetAndDetachPids --
|
sl@0
|
982 |
*
|
sl@0
|
983 |
* This procedure is invoked in the generic implementation of a
|
sl@0
|
984 |
* background "exec" (An exec when invoked with a terminating "&")
|
sl@0
|
985 |
* to store a list of the PIDs for processes in a command pipeline
|
sl@0
|
986 |
* in the interp's result and to detach the processes.
|
sl@0
|
987 |
*
|
sl@0
|
988 |
* Results:
|
sl@0
|
989 |
* None.
|
sl@0
|
990 |
*
|
sl@0
|
991 |
* Side effects:
|
sl@0
|
992 |
* Modifies the interp's result. Detaches processes.
|
sl@0
|
993 |
*
|
sl@0
|
994 |
*----------------------------------------------------------------------
|
sl@0
|
995 |
*/
|
sl@0
|
996 |
|
sl@0
|
997 |
void
|
sl@0
|
998 |
TclGetAndDetachPids(interp, chan)
|
sl@0
|
999 |
Tcl_Interp *interp;
|
sl@0
|
1000 |
Tcl_Channel chan;
|
sl@0
|
1001 |
{
|
sl@0
|
1002 |
PipeState *pipePtr;
|
sl@0
|
1003 |
Tcl_ChannelType *chanTypePtr;
|
sl@0
|
1004 |
int i;
|
sl@0
|
1005 |
char buf[TCL_INTEGER_SPACE];
|
sl@0
|
1006 |
|
sl@0
|
1007 |
/*
|
sl@0
|
1008 |
* Punt if the channel is not a command channel.
|
sl@0
|
1009 |
*/
|
sl@0
|
1010 |
|
sl@0
|
1011 |
chanTypePtr = Tcl_GetChannelType(chan);
|
sl@0
|
1012 |
if (chanTypePtr != &pipeChannelType) {
|
sl@0
|
1013 |
return;
|
sl@0
|
1014 |
}
|
sl@0
|
1015 |
|
sl@0
|
1016 |
pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
|
sl@0
|
1017 |
for (i = 0; i < pipePtr->numPids; i++) {
|
sl@0
|
1018 |
TclFormatInt(buf, (long) TclpGetPid(pipePtr->pidPtr[i]));
|
sl@0
|
1019 |
Tcl_AppendElement(interp, buf);
|
sl@0
|
1020 |
Tcl_DetachPids(1, &(pipePtr->pidPtr[i]));
|
sl@0
|
1021 |
}
|
sl@0
|
1022 |
if (pipePtr->numPids > 0) {
|
sl@0
|
1023 |
ckfree((char *) pipePtr->pidPtr);
|
sl@0
|
1024 |
pipePtr->numPids = 0;
|
sl@0
|
1025 |
}
|
sl@0
|
1026 |
}
|
sl@0
|
1027 |
|
sl@0
|
1028 |
/*
|
sl@0
|
1029 |
*----------------------------------------------------------------------
|
sl@0
|
1030 |
*
|
sl@0
|
1031 |
* PipeBlockModeProc --
|
sl@0
|
1032 |
*
|
sl@0
|
1033 |
* Helper procedure to set blocking and nonblocking modes on a
|
sl@0
|
1034 |
* pipe based channel. Invoked by generic IO level code.
|
sl@0
|
1035 |
*
|
sl@0
|
1036 |
* Results:
|
sl@0
|
1037 |
* 0 if successful, errno when failed.
|
sl@0
|
1038 |
*
|
sl@0
|
1039 |
* Side effects:
|
sl@0
|
1040 |
* Sets the device into blocking or non-blocking mode.
|
sl@0
|
1041 |
*
|
sl@0
|
1042 |
*----------------------------------------------------------------------
|
sl@0
|
1043 |
*/
|
sl@0
|
1044 |
|
sl@0
|
1045 |
/* ARGSUSED */
|
sl@0
|
1046 |
static int
|
sl@0
|
1047 |
PipeBlockModeProc(instanceData, mode)
|
sl@0
|
1048 |
ClientData instanceData; /* Pipe state. */
|
sl@0
|
1049 |
int mode; /* The mode to set. Can be one of
|
sl@0
|
1050 |
* TCL_MODE_BLOCKING or
|
sl@0
|
1051 |
* TCL_MODE_NONBLOCKING. */
|
sl@0
|
1052 |
{
|
sl@0
|
1053 |
PipeState *psPtr = (PipeState *) instanceData;
|
sl@0
|
1054 |
int curStatus;
|
sl@0
|
1055 |
int fd;
|
sl@0
|
1056 |
|
sl@0
|
1057 |
#ifndef USE_FIONBIO
|
sl@0
|
1058 |
if (psPtr->inFile) {
|
sl@0
|
1059 |
fd = GetFd(psPtr->inFile);
|
sl@0
|
1060 |
curStatus = fcntl(fd, F_GETFL);
|
sl@0
|
1061 |
if (mode == TCL_MODE_BLOCKING) {
|
sl@0
|
1062 |
curStatus &= (~(O_NONBLOCK));
|
sl@0
|
1063 |
} else {
|
sl@0
|
1064 |
curStatus |= O_NONBLOCK;
|
sl@0
|
1065 |
}
|
sl@0
|
1066 |
if (fcntl(fd, F_SETFL, curStatus) < 0) {
|
sl@0
|
1067 |
return errno;
|
sl@0
|
1068 |
}
|
sl@0
|
1069 |
}
|
sl@0
|
1070 |
if (psPtr->outFile) {
|
sl@0
|
1071 |
fd = GetFd(psPtr->outFile);
|
sl@0
|
1072 |
curStatus = fcntl(fd, F_GETFL);
|
sl@0
|
1073 |
if (mode == TCL_MODE_BLOCKING) {
|
sl@0
|
1074 |
curStatus &= (~(O_NONBLOCK));
|
sl@0
|
1075 |
} else {
|
sl@0
|
1076 |
curStatus |= O_NONBLOCK;
|
sl@0
|
1077 |
}
|
sl@0
|
1078 |
if (fcntl(fd, F_SETFL, curStatus) < 0) {
|
sl@0
|
1079 |
return errno;
|
sl@0
|
1080 |
}
|
sl@0
|
1081 |
}
|
sl@0
|
1082 |
#endif /* !FIONBIO */
|
sl@0
|
1083 |
|
sl@0
|
1084 |
#ifdef USE_FIONBIO
|
sl@0
|
1085 |
if (psPtr->inFile) {
|
sl@0
|
1086 |
fd = GetFd(psPtr->inFile);
|
sl@0
|
1087 |
if (mode == TCL_MODE_BLOCKING) {
|
sl@0
|
1088 |
curStatus = 0;
|
sl@0
|
1089 |
} else {
|
sl@0
|
1090 |
curStatus = 1;
|
sl@0
|
1091 |
}
|
sl@0
|
1092 |
if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
|
sl@0
|
1093 |
return errno;
|
sl@0
|
1094 |
}
|
sl@0
|
1095 |
}
|
sl@0
|
1096 |
if (psPtr->outFile != NULL) {
|
sl@0
|
1097 |
fd = GetFd(psPtr->outFile);
|
sl@0
|
1098 |
if (mode == TCL_MODE_BLOCKING) {
|
sl@0
|
1099 |
curStatus = 0;
|
sl@0
|
1100 |
} else {
|
sl@0
|
1101 |
curStatus = 1;
|
sl@0
|
1102 |
}
|
sl@0
|
1103 |
if (ioctl(fd, (int) FIONBIO, &curStatus) < 0) {
|
sl@0
|
1104 |
return errno;
|
sl@0
|
1105 |
}
|
sl@0
|
1106 |
}
|
sl@0
|
1107 |
#endif /* USE_FIONBIO */
|
sl@0
|
1108 |
|
sl@0
|
1109 |
psPtr->isNonBlocking = (mode == TCL_MODE_NONBLOCKING);
|
sl@0
|
1110 |
|
sl@0
|
1111 |
return 0;
|
sl@0
|
1112 |
}
|
sl@0
|
1113 |
|
sl@0
|
1114 |
/*
|
sl@0
|
1115 |
*----------------------------------------------------------------------
|
sl@0
|
1116 |
*
|
sl@0
|
1117 |
* PipeCloseProc --
|
sl@0
|
1118 |
*
|
sl@0
|
1119 |
* This procedure is invoked by the generic IO level to perform
|
sl@0
|
1120 |
* channel-type-specific cleanup when a command pipeline channel
|
sl@0
|
1121 |
* is closed.
|
sl@0
|
1122 |
*
|
sl@0
|
1123 |
* Results:
|
sl@0
|
1124 |
* 0 on success, errno otherwise.
|
sl@0
|
1125 |
*
|
sl@0
|
1126 |
* Side effects:
|
sl@0
|
1127 |
* Closes the command pipeline channel.
|
sl@0
|
1128 |
*
|
sl@0
|
1129 |
*----------------------------------------------------------------------
|
sl@0
|
1130 |
*/
|
sl@0
|
1131 |
|
sl@0
|
1132 |
/* ARGSUSED */
|
sl@0
|
1133 |
static int
|
sl@0
|
1134 |
PipeCloseProc(instanceData, interp)
|
sl@0
|
1135 |
ClientData instanceData; /* The pipe to close. */
|
sl@0
|
1136 |
Tcl_Interp *interp; /* For error reporting. */
|
sl@0
|
1137 |
{
|
sl@0
|
1138 |
PipeState *pipePtr;
|
sl@0
|
1139 |
Tcl_Channel errChan;
|
sl@0
|
1140 |
int errorCode, result;
|
sl@0
|
1141 |
|
sl@0
|
1142 |
errorCode = 0;
|
sl@0
|
1143 |
result = 0;
|
sl@0
|
1144 |
pipePtr = (PipeState *) instanceData;
|
sl@0
|
1145 |
if (pipePtr->inFile) {
|
sl@0
|
1146 |
if (TclpCloseFile(pipePtr->inFile) < 0) {
|
sl@0
|
1147 |
errorCode = errno;
|
sl@0
|
1148 |
}
|
sl@0
|
1149 |
}
|
sl@0
|
1150 |
if (pipePtr->outFile) {
|
sl@0
|
1151 |
if ((TclpCloseFile(pipePtr->outFile) < 0) && (errorCode == 0)) {
|
sl@0
|
1152 |
errorCode = errno;
|
sl@0
|
1153 |
}
|
sl@0
|
1154 |
}
|
sl@0
|
1155 |
|
sl@0
|
1156 |
if (pipePtr->isNonBlocking || TclInExit()) {
|
sl@0
|
1157 |
|
sl@0
|
1158 |
/*
|
sl@0
|
1159 |
* If the channel is non-blocking or Tcl is being cleaned up, just
|
sl@0
|
1160 |
* detach the children PIDs, reap them (important if we are in a
|
sl@0
|
1161 |
* dynamic load module), and discard the errorFile.
|
sl@0
|
1162 |
*/
|
sl@0
|
1163 |
|
sl@0
|
1164 |
Tcl_DetachPids(pipePtr->numPids, pipePtr->pidPtr);
|
sl@0
|
1165 |
Tcl_ReapDetachedProcs();
|
sl@0
|
1166 |
|
sl@0
|
1167 |
if (pipePtr->errorFile) {
|
sl@0
|
1168 |
TclpCloseFile(pipePtr->errorFile);
|
sl@0
|
1169 |
}
|
sl@0
|
1170 |
} else {
|
sl@0
|
1171 |
|
sl@0
|
1172 |
/*
|
sl@0
|
1173 |
* Wrap the error file into a channel and give it to the cleanup
|
sl@0
|
1174 |
* routine.
|
sl@0
|
1175 |
*/
|
sl@0
|
1176 |
|
sl@0
|
1177 |
if (pipePtr->errorFile) {
|
sl@0
|
1178 |
errChan = Tcl_MakeFileChannel(
|
sl@0
|
1179 |
(ClientData) GetFd(pipePtr->errorFile), TCL_READABLE);
|
sl@0
|
1180 |
} else {
|
sl@0
|
1181 |
errChan = NULL;
|
sl@0
|
1182 |
}
|
sl@0
|
1183 |
result = TclCleanupChildren(interp, pipePtr->numPids, pipePtr->pidPtr,
|
sl@0
|
1184 |
errChan);
|
sl@0
|
1185 |
}
|
sl@0
|
1186 |
|
sl@0
|
1187 |
if (pipePtr->numPids != 0) {
|
sl@0
|
1188 |
ckfree((char *) pipePtr->pidPtr);
|
sl@0
|
1189 |
}
|
sl@0
|
1190 |
ckfree((char *) pipePtr);
|
sl@0
|
1191 |
if (errorCode == 0) {
|
sl@0
|
1192 |
return result;
|
sl@0
|
1193 |
}
|
sl@0
|
1194 |
return errorCode;
|
sl@0
|
1195 |
}
|
sl@0
|
1196 |
|
sl@0
|
1197 |
/*
|
sl@0
|
1198 |
*----------------------------------------------------------------------
|
sl@0
|
1199 |
*
|
sl@0
|
1200 |
* PipeInputProc --
|
sl@0
|
1201 |
*
|
sl@0
|
1202 |
* This procedure is invoked from the generic IO level to read
|
sl@0
|
1203 |
* input from a command pipeline based channel.
|
sl@0
|
1204 |
*
|
sl@0
|
1205 |
* Results:
|
sl@0
|
1206 |
* The number of bytes read is returned or -1 on error. An output
|
sl@0
|
1207 |
* argument contains a POSIX error code if an error occurs, or zero.
|
sl@0
|
1208 |
*
|
sl@0
|
1209 |
* Side effects:
|
sl@0
|
1210 |
* Reads input from the input device of the channel.
|
sl@0
|
1211 |
*
|
sl@0
|
1212 |
*----------------------------------------------------------------------
|
sl@0
|
1213 |
*/
|
sl@0
|
1214 |
|
sl@0
|
1215 |
static int
|
sl@0
|
1216 |
PipeInputProc(instanceData, buf, toRead, errorCodePtr)
|
sl@0
|
1217 |
ClientData instanceData; /* Pipe state. */
|
sl@0
|
1218 |
char *buf; /* Where to store data read. */
|
sl@0
|
1219 |
int toRead; /* How much space is available
|
sl@0
|
1220 |
* in the buffer? */
|
sl@0
|
1221 |
int *errorCodePtr; /* Where to store error code. */
|
sl@0
|
1222 |
{
|
sl@0
|
1223 |
PipeState *psPtr = (PipeState *) instanceData;
|
sl@0
|
1224 |
int bytesRead; /* How many bytes were actually
|
sl@0
|
1225 |
* read from the input device? */
|
sl@0
|
1226 |
|
sl@0
|
1227 |
*errorCodePtr = 0;
|
sl@0
|
1228 |
|
sl@0
|
1229 |
/*
|
sl@0
|
1230 |
* Assume there is always enough input available. This will block
|
sl@0
|
1231 |
* appropriately, and read will unblock as soon as a short read is
|
sl@0
|
1232 |
* possible, if the channel is in blocking mode. If the channel is
|
sl@0
|
1233 |
* nonblocking, the read will never block.
|
sl@0
|
1234 |
* Some OSes can throw an interrupt error, for which we should
|
sl@0
|
1235 |
* immediately retry. [Bug #415131]
|
sl@0
|
1236 |
*/
|
sl@0
|
1237 |
|
sl@0
|
1238 |
do {
|
sl@0
|
1239 |
bytesRead = read (GetFd(psPtr->inFile), buf, (size_t) toRead);
|
sl@0
|
1240 |
} while ((bytesRead < 0) && (errno == EINTR));
|
sl@0
|
1241 |
|
sl@0
|
1242 |
if (bytesRead < 0) {
|
sl@0
|
1243 |
*errorCodePtr = errno;
|
sl@0
|
1244 |
return -1;
|
sl@0
|
1245 |
} else {
|
sl@0
|
1246 |
return bytesRead;
|
sl@0
|
1247 |
}
|
sl@0
|
1248 |
}
|
sl@0
|
1249 |
|
sl@0
|
1250 |
/*
|
sl@0
|
1251 |
*----------------------------------------------------------------------
|
sl@0
|
1252 |
*
|
sl@0
|
1253 |
* PipeOutputProc--
|
sl@0
|
1254 |
*
|
sl@0
|
1255 |
* This procedure is invoked from the generic IO level to write
|
sl@0
|
1256 |
* output to a command pipeline based channel.
|
sl@0
|
1257 |
*
|
sl@0
|
1258 |
* Results:
|
sl@0
|
1259 |
* The number of bytes written is returned or -1 on error. An
|
sl@0
|
1260 |
* output argument contains a POSIX error code if an error occurred,
|
sl@0
|
1261 |
* or zero.
|
sl@0
|
1262 |
*
|
sl@0
|
1263 |
* Side effects:
|
sl@0
|
1264 |
* Writes output on the output device of the channel.
|
sl@0
|
1265 |
*
|
sl@0
|
1266 |
*----------------------------------------------------------------------
|
sl@0
|
1267 |
*/
|
sl@0
|
1268 |
|
sl@0
|
1269 |
static int
|
sl@0
|
1270 |
PipeOutputProc(instanceData, buf, toWrite, errorCodePtr)
|
sl@0
|
1271 |
ClientData instanceData; /* Pipe state. */
|
sl@0
|
1272 |
CONST char *buf; /* The data buffer. */
|
sl@0
|
1273 |
int toWrite; /* How many bytes to write? */
|
sl@0
|
1274 |
int *errorCodePtr; /* Where to store error code. */
|
sl@0
|
1275 |
{
|
sl@0
|
1276 |
PipeState *psPtr = (PipeState *) instanceData;
|
sl@0
|
1277 |
int written;
|
sl@0
|
1278 |
|
sl@0
|
1279 |
*errorCodePtr = 0;
|
sl@0
|
1280 |
|
sl@0
|
1281 |
/*
|
sl@0
|
1282 |
* Some OSes can throw an interrupt error, for which we should
|
sl@0
|
1283 |
* immediately retry. [Bug #415131]
|
sl@0
|
1284 |
*/
|
sl@0
|
1285 |
|
sl@0
|
1286 |
do {
|
sl@0
|
1287 |
written = write(GetFd(psPtr->outFile), buf, (size_t) toWrite);
|
sl@0
|
1288 |
} while ((written < 0) && (errno == EINTR));
|
sl@0
|
1289 |
|
sl@0
|
1290 |
if (written < 0) {
|
sl@0
|
1291 |
*errorCodePtr = errno;
|
sl@0
|
1292 |
return -1;
|
sl@0
|
1293 |
} else {
|
sl@0
|
1294 |
return written;
|
sl@0
|
1295 |
}
|
sl@0
|
1296 |
}
|
sl@0
|
1297 |
|
sl@0
|
1298 |
/*
|
sl@0
|
1299 |
*----------------------------------------------------------------------
|
sl@0
|
1300 |
*
|
sl@0
|
1301 |
* PipeWatchProc --
|
sl@0
|
1302 |
*
|
sl@0
|
1303 |
* Initialize the notifier to watch the fds from this channel.
|
sl@0
|
1304 |
*
|
sl@0
|
1305 |
* Results:
|
sl@0
|
1306 |
* None.
|
sl@0
|
1307 |
*
|
sl@0
|
1308 |
* Side effects:
|
sl@0
|
1309 |
* Sets up the notifier so that a future event on the channel will
|
sl@0
|
1310 |
* be seen by Tcl.
|
sl@0
|
1311 |
*
|
sl@0
|
1312 |
*----------------------------------------------------------------------
|
sl@0
|
1313 |
*/
|
sl@0
|
1314 |
|
sl@0
|
1315 |
static void
|
sl@0
|
1316 |
PipeWatchProc(instanceData, mask)
|
sl@0
|
1317 |
ClientData instanceData; /* The pipe state. */
|
sl@0
|
1318 |
int mask; /* Events of interest; an OR-ed
|
sl@0
|
1319 |
* combination of TCL_READABLE,
|
sl@0
|
1320 |
* TCL_WRITABEL and TCL_EXCEPTION. */
|
sl@0
|
1321 |
{
|
sl@0
|
1322 |
PipeState *psPtr = (PipeState *) instanceData;
|
sl@0
|
1323 |
int newmask;
|
sl@0
|
1324 |
|
sl@0
|
1325 |
if (psPtr->inFile) {
|
sl@0
|
1326 |
newmask = mask & (TCL_READABLE | TCL_EXCEPTION);
|
sl@0
|
1327 |
if (newmask) {
|
sl@0
|
1328 |
Tcl_CreateFileHandler(GetFd(psPtr->inFile), mask,
|
sl@0
|
1329 |
(Tcl_FileProc *) Tcl_NotifyChannel,
|
sl@0
|
1330 |
(ClientData) psPtr->channel);
|
sl@0
|
1331 |
} else {
|
sl@0
|
1332 |
Tcl_DeleteFileHandler(GetFd(psPtr->inFile));
|
sl@0
|
1333 |
}
|
sl@0
|
1334 |
}
|
sl@0
|
1335 |
if (psPtr->outFile) {
|
sl@0
|
1336 |
newmask = mask & (TCL_WRITABLE | TCL_EXCEPTION);
|
sl@0
|
1337 |
if (newmask) {
|
sl@0
|
1338 |
Tcl_CreateFileHandler(GetFd(psPtr->outFile), mask,
|
sl@0
|
1339 |
(Tcl_FileProc *) Tcl_NotifyChannel,
|
sl@0
|
1340 |
(ClientData) psPtr->channel);
|
sl@0
|
1341 |
} else {
|
sl@0
|
1342 |
Tcl_DeleteFileHandler(GetFd(psPtr->outFile));
|
sl@0
|
1343 |
}
|
sl@0
|
1344 |
}
|
sl@0
|
1345 |
}
|
sl@0
|
1346 |
|
sl@0
|
1347 |
/*
|
sl@0
|
1348 |
*----------------------------------------------------------------------
|
sl@0
|
1349 |
*
|
sl@0
|
1350 |
* PipeGetHandleProc --
|
sl@0
|
1351 |
*
|
sl@0
|
1352 |
* Called from Tcl_GetChannelHandle to retrieve OS handles from
|
sl@0
|
1353 |
* inside a command pipeline based channel.
|
sl@0
|
1354 |
*
|
sl@0
|
1355 |
* Results:
|
sl@0
|
1356 |
* Returns TCL_OK with the fd in handlePtr, or TCL_ERROR if
|
sl@0
|
1357 |
* there is no handle for the specified direction.
|
sl@0
|
1358 |
*
|
sl@0
|
1359 |
* Side effects:
|
sl@0
|
1360 |
* None.
|
sl@0
|
1361 |
*
|
sl@0
|
1362 |
*----------------------------------------------------------------------
|
sl@0
|
1363 |
*/
|
sl@0
|
1364 |
|
sl@0
|
1365 |
static int
|
sl@0
|
1366 |
PipeGetHandleProc(instanceData, direction, handlePtr)
|
sl@0
|
1367 |
ClientData instanceData; /* The pipe state. */
|
sl@0
|
1368 |
int direction; /* TCL_READABLE or TCL_WRITABLE */
|
sl@0
|
1369 |
ClientData *handlePtr; /* Where to store the handle. */
|
sl@0
|
1370 |
{
|
sl@0
|
1371 |
PipeState *psPtr = (PipeState *) instanceData;
|
sl@0
|
1372 |
|
sl@0
|
1373 |
if (direction == TCL_READABLE && psPtr->inFile) {
|
sl@0
|
1374 |
*handlePtr = (ClientData) GetFd(psPtr->inFile);
|
sl@0
|
1375 |
return TCL_OK;
|
sl@0
|
1376 |
}
|
sl@0
|
1377 |
if (direction == TCL_WRITABLE && psPtr->outFile) {
|
sl@0
|
1378 |
*handlePtr = (ClientData) GetFd(psPtr->outFile);
|
sl@0
|
1379 |
return TCL_OK;
|
sl@0
|
1380 |
}
|
sl@0
|
1381 |
return TCL_ERROR;
|
sl@0
|
1382 |
}
|
sl@0
|
1383 |
|
sl@0
|
1384 |
/*
|
sl@0
|
1385 |
*----------------------------------------------------------------------
|
sl@0
|
1386 |
*
|
sl@0
|
1387 |
* Tcl_WaitPid --
|
sl@0
|
1388 |
*
|
sl@0
|
1389 |
* Implements the waitpid system call on Unix systems.
|
sl@0
|
1390 |
*
|
sl@0
|
1391 |
* Results:
|
sl@0
|
1392 |
* Result of calling waitpid.
|
sl@0
|
1393 |
*
|
sl@0
|
1394 |
* Side effects:
|
sl@0
|
1395 |
* Waits for a process to terminate.
|
sl@0
|
1396 |
*
|
sl@0
|
1397 |
*----------------------------------------------------------------------
|
sl@0
|
1398 |
*/
|
sl@0
|
1399 |
|
sl@0
|
1400 |
EXPORT_C Tcl_Pid
|
sl@0
|
1401 |
Tcl_WaitPid(pid, statPtr, options)
|
sl@0
|
1402 |
Tcl_Pid pid;
|
sl@0
|
1403 |
int *statPtr;
|
sl@0
|
1404 |
int options;
|
sl@0
|
1405 |
{
|
sl@0
|
1406 |
int result;
|
sl@0
|
1407 |
pid_t real_pid;
|
sl@0
|
1408 |
|
sl@0
|
1409 |
real_pid = (pid_t) pid;
|
sl@0
|
1410 |
while (1) {
|
sl@0
|
1411 |
result = (int) waitpid(real_pid, statPtr, options);
|
sl@0
|
1412 |
if ((result != -1) || (errno != EINTR)) {
|
sl@0
|
1413 |
return (Tcl_Pid) result;
|
sl@0
|
1414 |
}
|
sl@0
|
1415 |
}
|
sl@0
|
1416 |
}
|
sl@0
|
1417 |
|
sl@0
|
1418 |
/*
|
sl@0
|
1419 |
*----------------------------------------------------------------------
|
sl@0
|
1420 |
*
|
sl@0
|
1421 |
* Tcl_PidObjCmd --
|
sl@0
|
1422 |
*
|
sl@0
|
1423 |
* This procedure is invoked to process the "pid" Tcl command.
|
sl@0
|
1424 |
* See the user documentation for details on what it does.
|
sl@0
|
1425 |
*
|
sl@0
|
1426 |
* Results:
|
sl@0
|
1427 |
* A standard Tcl result.
|
sl@0
|
1428 |
*
|
sl@0
|
1429 |
* Side effects:
|
sl@0
|
1430 |
* See the user documentation.
|
sl@0
|
1431 |
*
|
sl@0
|
1432 |
*----------------------------------------------------------------------
|
sl@0
|
1433 |
*/
|
sl@0
|
1434 |
|
sl@0
|
1435 |
/* ARGSUSED */
|
sl@0
|
1436 |
int
|
sl@0
|
1437 |
Tcl_PidObjCmd(dummy, interp, objc, objv)
|
sl@0
|
1438 |
ClientData dummy; /* Not used. */
|
sl@0
|
1439 |
Tcl_Interp *interp; /* Current interpreter. */
|
sl@0
|
1440 |
int objc; /* Number of arguments. */
|
sl@0
|
1441 |
Tcl_Obj *CONST *objv; /* Argument strings. */
|
sl@0
|
1442 |
{
|
sl@0
|
1443 |
Tcl_Channel chan;
|
sl@0
|
1444 |
Tcl_ChannelType *chanTypePtr;
|
sl@0
|
1445 |
PipeState *pipePtr;
|
sl@0
|
1446 |
int i;
|
sl@0
|
1447 |
Tcl_Obj *resultPtr, *longObjPtr;
|
sl@0
|
1448 |
|
sl@0
|
1449 |
if (objc > 2) {
|
sl@0
|
1450 |
Tcl_WrongNumArgs(interp, 1, objv, "?channelId?");
|
sl@0
|
1451 |
return TCL_ERROR;
|
sl@0
|
1452 |
}
|
sl@0
|
1453 |
if (objc == 1) {
|
sl@0
|
1454 |
Tcl_SetLongObj(Tcl_GetObjResult(interp), (long) getpid());
|
sl@0
|
1455 |
} else {
|
sl@0
|
1456 |
chan = Tcl_GetChannel(interp, Tcl_GetString(objv[1]), NULL);
|
sl@0
|
1457 |
if (chan == (Tcl_Channel) NULL) {
|
sl@0
|
1458 |
return TCL_ERROR;
|
sl@0
|
1459 |
}
|
sl@0
|
1460 |
chanTypePtr = Tcl_GetChannelType(chan);
|
sl@0
|
1461 |
if (chanTypePtr != &pipeChannelType) {
|
sl@0
|
1462 |
return TCL_OK;
|
sl@0
|
1463 |
}
|
sl@0
|
1464 |
pipePtr = (PipeState *) Tcl_GetChannelInstanceData(chan);
|
sl@0
|
1465 |
resultPtr = Tcl_GetObjResult(interp);
|
sl@0
|
1466 |
for (i = 0; i < pipePtr->numPids; i++) {
|
sl@0
|
1467 |
longObjPtr = Tcl_NewLongObj((long) TclpGetPid(pipePtr->pidPtr[i]));
|
sl@0
|
1468 |
Tcl_ListObjAppendElement(NULL, resultPtr, longObjPtr);
|
sl@0
|
1469 |
}
|
sl@0
|
1470 |
}
|
sl@0
|
1471 |
return TCL_OK;
|
sl@0
|
1472 |
}
|
sl@0
|
1473 |
|
sl@0
|
1474 |
/*
|
sl@0
|
1475 |
*----------------------------------------------------------------------
|
sl@0
|
1476 |
*
|
sl@0
|
1477 |
* TclpFinalizePipes --
|
sl@0
|
1478 |
*
|
sl@0
|
1479 |
* Cleans up the pipe subsystem from Tcl_FinalizeThread
|
sl@0
|
1480 |
*
|
sl@0
|
1481 |
* Results:
|
sl@0
|
1482 |
* None.
|
sl@0
|
1483 |
*
|
sl@0
|
1484 |
* This procedure carries out no operation on Unix.
|
sl@0
|
1485 |
*
|
sl@0
|
1486 |
*----------------------------------------------------------------------
|
sl@0
|
1487 |
*/
|
sl@0
|
1488 |
|
sl@0
|
1489 |
void
|
sl@0
|
1490 |
TclpFinalizePipes()
|
sl@0
|
1491 |
{
|
sl@0
|
1492 |
}
|
sl@0
|
1493 |
|