ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_process.c
Revision: 3.1
Committed: Thu Jun 26 00:58:09 2003 UTC (20 years, 10 months ago) by schorsch
Content type: text/plain
Branch: MAIN
Log Message:
Abstracted process and path handling for Windows.
Renamed FLOAT to RREAL because of conflict on Windows.
Added conditional compiles for some signal handlers.

File Contents

# User Rev Content
1 schorsch 3.1 #ifndef lint
2     static char RCSid[]="$Id$";
3     #endif
4     /*
5     * Routines to communicate with separate process via dual pipes.
6     * Windows version.
7     *
8     * External symbols declared in standard.h
9     */
10    
11     #include "copyright.h"
12    
13     #include <stdio.h>
14     #define STRICT
15     #include <windows.h> /* typedefs */
16     #include <io.h> /* _open_osfhandle */
17     #include <fcntl.h> /* _O_XXX */
18    
19     #include "standard.h"
20     #include "rtprocess.h"
21    
22    
23     /* Looks like some Windows versions use negative PIDs.
24     Let's just hope they either make them *all* negative, or none. */
25     static int system_uses_negative_pids = 0;
26    
27    
28     /*
29     Safely terminate a process by creating a remote thread
30     in the process that calls ExitProcess.
31     As presented by Andrew Tucker in Windows Developer Magazine.
32     */
33     #ifndef OBSOLETE_WINDOWS /* won't work on Win 9X/ME/CE. */
34     BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
35     {
36     DWORD dwTID, dwCode, dwErr = 0;
37     HANDLE hProcessDup = INVALID_HANDLE_VALUE;
38     HANDLE hRT = NULL;
39     HINSTANCE hKernel = GetModuleHandle("Kernel32");
40     BOOL bSuccess = FALSE;
41    
42     BOOL bDup = DuplicateHandle(GetCurrentProcess(),
43     hProcess,
44     GetCurrentProcess(),
45     &hProcessDup,
46     PROCESS_ALL_ACCESS,
47     FALSE,
48     0);
49     /* Detect the special case where the process is already dead... */
50     if ( GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
51     (dwCode == STILL_ACTIVE) ) {
52     FARPROC pfnExitProc;
53     pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
54     hRT = CreateRemoteThread((bDup) ? hProcessDup : hProcess,
55     NULL,
56     0,
57     (LPTHREAD_START_ROUTINE)pfnExitProc,
58     (PVOID)uExitCode, 0, &dwTID);
59     if ( hRT == NULL ) dwErr = GetLastError();
60     } else {
61     dwErr = ERROR_PROCESS_ABORTED;
62     }
63     if ( hRT ) {
64     /* Must wait process to terminate to guarantee that it has exited... */
65     WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
66     CloseHandle(hRT);
67     bSuccess = TRUE;
68     }
69     if ( bDup ) CloseHandle(hProcessDup);
70     if ( !bSuccess ) SetLastError(dwErr);
71     return bSuccess;
72     }
73     #endif
74    
75    
76     static int
77     start_process(SUBPROC *proc, char *cmdstr)
78     {
79     BOOL res;
80     int Pflags = 0;
81     SECURITY_ATTRIBUTES SAttrs;
82     STARTUPINFO SInfo;
83     PROCESS_INFORMATION PInfo;
84     /* welcome to the world of resource handles */
85     HANDLE hToChildRead = NULL;
86     HANDLE hToChildWrite = NULL;
87     HANDLE hFromChildRead = NULL;
88     HANDLE hFromChildWrite = NULL;
89     HANDLE hRead = NULL, hWrite = NULL;
90     HANDLE hStdIn, hStdOut, hStdErr;
91     HANDLE hCurProc;
92    
93     /* get process and standard stream handles */
94     hCurProc = GetCurrentProcess();
95     hStdIn = GetStdHandle(STD_INPUT_HANDLE);
96     hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
97     hStdErr = GetStdHandle(STD_ERROR_HANDLE);
98    
99     /* the remote pipe handles must be inheritable */
100     SAttrs.bInheritHandle = 1;
101     SAttrs.lpSecurityDescriptor = NULL;
102     SAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
103    
104     /* make pipe, assign to stdout */
105     /* we'll check for errors after CreateProcess()...*/
106     res = CreatePipe(&hFromChildRead, &hFromChildWrite, &SAttrs, 0);
107     res = SetStdHandle(STD_OUTPUT_HANDLE, hFromChildWrite);
108     /* create non-inheritable dup of local end */
109     res = DuplicateHandle(hCurProc, hFromChildRead, hCurProc, &hRead,
110     0, FALSE, DUPLICATE_SAME_ACCESS);
111     CloseHandle(hFromChildRead); hFromChildRead = NULL;
112    
113     res = CreatePipe(&hToChildRead, &hToChildWrite, &SAttrs, 0);
114     res = SetStdHandle(STD_INPUT_HANDLE, hToChildRead);
115     res = DuplicateHandle(hCurProc, hToChildWrite, hCurProc, &hWrite,
116     0, FALSE, DUPLICATE_SAME_ACCESS);
117     CloseHandle(hToChildWrite); hToChildWrite = NULL;
118    
119     CloseHandle(hCurProc); hCurProc = NULL;
120    
121     /* do some bookkeeping for Windows... */
122     SInfo.cb = sizeof(STARTUPINFO);
123     SInfo.lpReserved = NULL;
124     SInfo.lpDesktop = NULL;
125     SInfo.lpTitle = NULL;
126     SInfo.cbReserved2 = 0;
127     SInfo.lpReserved2 = NULL;
128     /* don't open a console automatically, pass handles */
129     SInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
130     SInfo.wShowWindow = SW_HIDE;
131     SInfo.hStdInput = hToChildRead;
132     SInfo.hStdOutput = hFromChildWrite;
133     SInfo.hStdError = hStdErr; /* reuse original stderr */
134    
135     res = CreateProcess(NULL, /* command name */
136     cmdstr, /* full command line */
137     NULL, /* default process attributes */
138     NULL, /* default security attributes */
139     1, /* inherit handles (pass doesn't work reliably) */
140     Pflags, /* process flags */
141     NULL, /* no new environment */
142     NULL, /* stay in current directory */
143     &SInfo,
144     &PInfo
145     );
146     /* reset stdin/stdout in any case */
147     SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
148     SetStdHandle(STD_INPUT_HANDLE, hStdIn);
149     /* Oops... */
150     if(res == 0) {
151     char es[128];
152     _snprintf(es, sizeof(es),
153     "Error creating process (%d)\n", GetLastError());
154     eputs(es);
155     goto error;
156     }
157     /* close stuff we don't need */
158     CloseHandle(PInfo.hThread);
159     CloseHandle(hFromChildWrite); hFromChildWrite = NULL;
160     CloseHandle(hToChildRead); hToChildRead = NULL;
161     /* get the file descriptors */
162     proc->r = _open_osfhandle((long)hRead, _O_RDONLY);
163     proc->w = _open_osfhandle((long)hWrite, _O_APPEND);
164     proc->pid = PInfo.dwProcessId;
165     proc->running = 1;
166     CloseHandle(hCurProc);
167     /* Windows doesn't tell us the actual buffer size */
168     return PIPE_BUF;
169    
170     error: /* cleanup */
171     if(PInfo.hThread) CloseHandle(PInfo.hThread);
172     if(hToChildRead) CloseHandle(hToChildRead);
173     if(hToChildWrite) CloseHandle(hToChildWrite);
174     if(hFromChildRead) CloseHandle(hFromChildRead);
175     if(hFromChildWrite) CloseHandle(hFromChildWrite);
176     if(hRead) CloseHandle(hRead);
177     if(hWrite) CloseHandle(hWrite);
178     if(hCurProc) CloseHandle(hCurProc);
179     proc->running = 0;
180     return 0;
181     /* There... Are we happy now? */
182     }
183    
184    
185     static int /* copied size or -1 on error */
186     wordncopy( /* copy (quoted) src to dest. */
187    
188     char * dest,
189     char * src,
190     int dlen,
191     int insert_space, /* prepend a space */
192     int force_dq /* turn 'src' into "dest" (for Win command line) */
193     )
194     {
195     int slen;
196     int pos = 0;
197    
198     slen = strlen(src);
199     if (insert_space) {
200     if (1 >= dlen) return -1;
201     dest[pos++] = ' ';
202     }
203     if (strpbrk(src, " \f\n\r\t\v")) {
204     if (force_dq && src[0] == '\'' && src[slen-1] == '\'') {
205     if (slen + pos + 1 > dlen) return -1;
206     dest[pos++] = '"';
207     strncpy(dest + pos, src + 1, slen -2);
208     pos += slen - 2;
209     dest[pos++] = '"';
210     } else if (src[0] == '"' && src[slen-1] == '"') {
211     if (slen + pos + 1 > dlen) return -1;
212     strncpy(dest + pos, src, slen);
213     pos += slen;
214     } else {
215     if (slen + pos + 3 > dlen) return -1;
216     dest[pos++] = '"';
217     strncpy(dest + pos, src, slen);
218     pos += slen;
219     dest[pos++] = '"';
220     }
221     } else {
222     if (slen + pos + 1 > dlen) return -1;
223     strncpy(dest + pos, src, slen);
224     pos += slen;
225     }
226     dest[pos] = '\0';
227     return pos;
228     }
229    
230    
231    
232     static char *
233     quoted_cmdline( /* compose command line for StartProcess() as static string */
234    
235     char *cmdpath, /* full path to executable */
236     char *sl[] /* list of arguments */
237     )
238     {
239     static char *cmdstr;
240     static int clen;
241     char *newcs;
242     int newlen, pos, res, i;
243    
244     newlen = strlen(cmdpath) + 3; /* allow two quotes plus the final \0 */
245     for (i = 0; sl[i] != NULL; i++) {
246     newlen += strlen(sl[i]) + 3; /* allow two quotes and a space */
247     }
248     if (cmdstr == NULL) {
249     cmdstr = (char *) malloc(newlen);
250     if (cmdstr == NULL) return NULL;
251     } else if (newlen > clen) {
252     newcs = (char *) realloc(cmdstr, newlen);
253     if (newcs == NULL) return NULL;
254     cmdstr = newcs;
255     }
256     clen = newlen;
257     pos = wordncopy(cmdstr, cmdpath, clen, 0, 1);
258     if (pos < 0) return NULL;
259     for (i = 0; sl[i] != NULL; i++) {
260     res = wordncopy(cmdstr + pos, sl[i], clen - pos, 1, 1);
261     if (res < 0) return NULL;
262     pos += res;
263     }
264     return cmdstr;
265     }
266    
267    
268     int
269     open_process(SUBPROC *proc, char *av[])
270     {
271     char *cmdpath;
272     char *cmdstr;
273     int res;
274    
275     proc->running = 0;
276     cmdpath = getpath(av[0], getenv("PATH"), X_OK);
277     cmdstr = quoted_cmdline(cmdpath, av);
278     if (cmdstr == NULL) { return 0; }
279     return start_process(proc, cmdstr);
280     }
281    
282    
283     int
284     close_process(SUBPROC *proc) {
285     int icres, ocres;
286     DWORD pid;
287     HANDLE hProc;
288    
289     ocres = close(proc->w);
290     icres = close(proc->r);
291     pid = proc->pid;
292     if(ocres != 0 || icres != 0) {
293     hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid);
294     /* something went wrong: enforce infanticide */
295     /* other than that, it looks like we want to ignore errors here */
296     if (proc->running) {
297     if(hProc != NULL) {
298     #ifdef OBSOLETE_WINDOWS
299     #define KILL_TIMEOUT 10 * 1000 /* milliseconds */
300     /* it might have some windows open... */
301     EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)pid);
302     if(WaitForSingleObject(hProc, KILL_TIMEOUT)!=WAIT_OBJECT_0) {
303     /* No way to avoid dangling DLLs here. */
304     TerminateProcess(hProc, 0);
305     }
306     #else
307     SafeTerminateProcess(hProc, 0);
308     #endif
309     /* WaitForSingleObject(hProc, 0); */
310     /* not much use to wait on Windows */
311     CloseHandle(hProc);
312     }
313     }
314     }
315     proc->running = 0;
316     return 0; /* XXX we need to figure out more here... */
317     }
318    
319    
320     #ifdef TEST_MODULE
321     int
322     main( int argc, char **argv )
323     {
324     SUBPROC proc;
325     FILE *inf, *outf;
326     int res;
327     char ret[1024];
328     char *command[]= {"word", "gappy word", "\"quoted words\"", "'squoted words'", NULL};
329    
330     res = open_process(&proc, command)
331     if (res == 0) {
332     printf("open_process() failed with return value 0\n");
333     return -1;
334     }
335     printf("process opened with return value: %d, pid: %d, r: %d, w: %d\n",
336     res, proc.pid, proc.r, proc.w);
337     inf = fdopen(proc.r, "rb");
338     outf = fdopen(proc.w, "wb");
339     fprintf(outf,"0 0 0 0 1 0\n");
340     fflush(outf);
341     fgets(ret, sizeof(ret), inf);
342     printf("%s\n",ret);
343     close_process(&proc);
344     }
345     #endif
346