ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_process.c
Revision: 3.3
Committed: Mon Jul 14 20:02:29 2003 UTC (20 years, 9 months ago) by schorsch
Content type: text/plain
Branch: MAIN
Changes since 3.2: +3 -2 lines
Log Message:
Moved some more platform dependencies to common header files.
Included a few necessary system headers.

File Contents

# User Rev Content
1 schorsch 3.1 #ifndef lint
2 schorsch 3.3 static char RCSid[]="$Id: win_process.c,v 3.2 2003/07/03 22:41:44 schorsch Exp $";
3 schorsch 3.1 #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 schorsch 3.3 #include "rterror.h"
20     #include "rtio.h"
21 schorsch 3.1 #include "rtprocess.h"
22    
23    
24     /* Looks like some Windows versions use negative PIDs.
25     Let's just hope they either make them *all* negative, or none. */
26     static int system_uses_negative_pids = 0;
27    
28    
29     /*
30     Safely terminate a process by creating a remote thread
31     in the process that calls ExitProcess.
32     As presented by Andrew Tucker in Windows Developer Magazine.
33     */
34     #ifndef OBSOLETE_WINDOWS /* won't work on Win 9X/ME/CE. */
35     BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
36     {
37     DWORD dwTID, dwCode, dwErr = 0;
38     HANDLE hProcessDup = INVALID_HANDLE_VALUE;
39     HANDLE hRT = NULL;
40     HINSTANCE hKernel = GetModuleHandle("Kernel32");
41     BOOL bSuccess = FALSE;
42    
43     BOOL bDup = DuplicateHandle(GetCurrentProcess(),
44     hProcess,
45     GetCurrentProcess(),
46     &hProcessDup,
47     PROCESS_ALL_ACCESS,
48     FALSE,
49     0);
50     /* Detect the special case where the process is already dead... */
51     if ( GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) &&
52     (dwCode == STILL_ACTIVE) ) {
53     FARPROC pfnExitProc;
54     pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
55     hRT = CreateRemoteThread((bDup) ? hProcessDup : hProcess,
56     NULL,
57     0,
58     (LPTHREAD_START_ROUTINE)pfnExitProc,
59     (PVOID)uExitCode, 0, &dwTID);
60     if ( hRT == NULL ) dwErr = GetLastError();
61     } else {
62     dwErr = ERROR_PROCESS_ABORTED;
63     }
64     if ( hRT ) {
65     /* Must wait process to terminate to guarantee that it has exited... */
66     WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
67     CloseHandle(hRT);
68     bSuccess = TRUE;
69     }
70     if ( bDup ) CloseHandle(hProcessDup);
71     if ( !bSuccess ) SetLastError(dwErr);
72     return bSuccess;
73     }
74     #endif
75    
76    
77     static int
78     start_process(SUBPROC *proc, char *cmdstr)
79     {
80     BOOL res;
81     int Pflags = 0;
82     SECURITY_ATTRIBUTES SAttrs;
83     STARTUPINFO SInfo;
84     PROCESS_INFORMATION PInfo;
85     /* welcome to the world of resource handles */
86     HANDLE hToChildRead = NULL;
87     HANDLE hToChildWrite = NULL;
88     HANDLE hFromChildRead = NULL;
89     HANDLE hFromChildWrite = NULL;
90     HANDLE hRead = NULL, hWrite = NULL;
91     HANDLE hStdIn, hStdOut, hStdErr;
92     HANDLE hCurProc;
93    
94     /* get process and standard stream handles */
95     hCurProc = GetCurrentProcess();
96     hStdIn = GetStdHandle(STD_INPUT_HANDLE);
97     hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
98     hStdErr = GetStdHandle(STD_ERROR_HANDLE);
99    
100     /* the remote pipe handles must be inheritable */
101     SAttrs.bInheritHandle = 1;
102     SAttrs.lpSecurityDescriptor = NULL;
103     SAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
104    
105     /* make pipe, assign to stdout */
106     /* we'll check for errors after CreateProcess()...*/
107     res = CreatePipe(&hFromChildRead, &hFromChildWrite, &SAttrs, 0);
108     res = SetStdHandle(STD_OUTPUT_HANDLE, hFromChildWrite);
109     /* create non-inheritable dup of local end */
110     res = DuplicateHandle(hCurProc, hFromChildRead, hCurProc, &hRead,
111     0, FALSE, DUPLICATE_SAME_ACCESS);
112     CloseHandle(hFromChildRead); hFromChildRead = NULL;
113    
114     res = CreatePipe(&hToChildRead, &hToChildWrite, &SAttrs, 0);
115     res = SetStdHandle(STD_INPUT_HANDLE, hToChildRead);
116     res = DuplicateHandle(hCurProc, hToChildWrite, hCurProc, &hWrite,
117     0, FALSE, DUPLICATE_SAME_ACCESS);
118     CloseHandle(hToChildWrite); hToChildWrite = NULL;
119    
120     CloseHandle(hCurProc); hCurProc = NULL;
121    
122     /* do some bookkeeping for Windows... */
123     SInfo.cb = sizeof(STARTUPINFO);
124     SInfo.lpReserved = NULL;
125     SInfo.lpDesktop = NULL;
126     SInfo.lpTitle = NULL;
127     SInfo.cbReserved2 = 0;
128     SInfo.lpReserved2 = NULL;
129     /* don't open a console automatically, pass handles */
130     SInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
131     SInfo.wShowWindow = SW_HIDE;
132     SInfo.hStdInput = hToChildRead;
133     SInfo.hStdOutput = hFromChildWrite;
134     SInfo.hStdError = hStdErr; /* reuse original stderr */
135    
136     res = CreateProcess(NULL, /* command name */
137     cmdstr, /* full command line */
138     NULL, /* default process attributes */
139     NULL, /* default security attributes */
140     1, /* inherit handles (pass doesn't work reliably) */
141     Pflags, /* process flags */
142     NULL, /* no new environment */
143     NULL, /* stay in current directory */
144     &SInfo,
145     &PInfo
146     );
147     /* reset stdin/stdout in any case */
148     SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
149     SetStdHandle(STD_INPUT_HANDLE, hStdIn);
150     /* Oops... */
151     if(res == 0) {
152     char es[128];
153     _snprintf(es, sizeof(es),
154     "Error creating process (%d)\n", GetLastError());
155     eputs(es);
156     goto error;
157     }
158     /* close stuff we don't need */
159     CloseHandle(PInfo.hThread);
160     CloseHandle(hFromChildWrite); hFromChildWrite = NULL;
161     CloseHandle(hToChildRead); hToChildRead = NULL;
162     /* get the file descriptors */
163     proc->r = _open_osfhandle((long)hRead, _O_RDONLY);
164     proc->w = _open_osfhandle((long)hWrite, _O_APPEND);
165     proc->pid = PInfo.dwProcessId;
166     proc->running = 1;
167     CloseHandle(hCurProc);
168     /* Windows doesn't tell us the actual buffer size */
169     return PIPE_BUF;
170    
171     error: /* cleanup */
172     if(PInfo.hThread) CloseHandle(PInfo.hThread);
173     if(hToChildRead) CloseHandle(hToChildRead);
174     if(hToChildWrite) CloseHandle(hToChildWrite);
175     if(hFromChildRead) CloseHandle(hFromChildRead);
176     if(hFromChildWrite) CloseHandle(hFromChildWrite);
177     if(hRead) CloseHandle(hRead);
178     if(hWrite) CloseHandle(hWrite);
179     if(hCurProc) CloseHandle(hCurProc);
180     proc->running = 0;
181     return 0;
182     /* There... Are we happy now? */
183     }
184    
185    
186     static int /* copied size or -1 on error */
187     wordncopy( /* copy (quoted) src to dest. */
188    
189     char * dest,
190     char * src,
191     int dlen,
192     int insert_space, /* prepend a space */
193     int force_dq /* turn 'src' into "dest" (for Win command line) */
194     )
195     {
196     int slen;
197     int pos = 0;
198    
199     slen = strlen(src);
200     if (insert_space) {
201     if (1 >= dlen) return -1;
202     dest[pos++] = ' ';
203     }
204     if (strpbrk(src, " \f\n\r\t\v")) {
205     if (force_dq && src[0] == '\'' && src[slen-1] == '\'') {
206     if (slen + pos + 1 > dlen) return -1;
207     dest[pos++] = '"';
208     strncpy(dest + pos, src + 1, slen -2);
209     pos += slen - 2;
210     dest[pos++] = '"';
211     } else if (src[0] == '"' && src[slen-1] == '"') {
212     if (slen + pos + 1 > dlen) return -1;
213     strncpy(dest + pos, src, slen);
214     pos += slen;
215     } else {
216     if (slen + pos + 3 > dlen) return -1;
217     dest[pos++] = '"';
218     strncpy(dest + pos, src, slen);
219     pos += slen;
220     dest[pos++] = '"';
221     }
222     } else {
223     if (slen + pos + 1 > dlen) return -1;
224     strncpy(dest + pos, src, slen);
225     pos += slen;
226     }
227     dest[pos] = '\0';
228     return pos;
229     }
230    
231    
232    
233     static char *
234     quoted_cmdline( /* compose command line for StartProcess() as static string */
235    
236     char *cmdpath, /* full path to executable */
237     char *sl[] /* list of arguments */
238     )
239     {
240     static char *cmdstr;
241     static int clen;
242     char *newcs;
243     int newlen, pos, res, i;
244    
245     newlen = strlen(cmdpath) + 3; /* allow two quotes plus the final \0 */
246     for (i = 0; sl[i] != NULL; i++) {
247     newlen += strlen(sl[i]) + 3; /* allow two quotes and a space */
248     }
249     if (cmdstr == NULL) {
250     cmdstr = (char *) malloc(newlen);
251     if (cmdstr == NULL) return NULL;
252     } else if (newlen > clen) {
253     newcs = (char *) realloc(cmdstr, newlen);
254     if (newcs == NULL) return NULL;
255     cmdstr = newcs;
256     }
257     clen = newlen;
258     pos = wordncopy(cmdstr, cmdpath, clen, 0, 1);
259     if (pos < 0) return NULL;
260     for (i = 0; sl[i] != NULL; i++) {
261     res = wordncopy(cmdstr + pos, sl[i], clen - pos, 1, 1);
262     if (res < 0) return NULL;
263     pos += res;
264     }
265     return cmdstr;
266     }
267    
268    
269     int
270     open_process(SUBPROC *proc, char *av[])
271     {
272     char *cmdpath;
273     char *cmdstr;
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