ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/radiance/ray/src/common/win_process.c
Revision: 3.4
Committed: Tue Oct 21 19:19:28 2003 UTC (20 years, 6 months ago) by schorsch
Content type: text/plain
Branch: MAIN
CVS Tags: rad3R6
Changes since 3.3: +11 -4 lines
Log Message:
Various platform compatibility fixes.

File Contents

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