Back to index

radiance  4R0+20100331
win_process.c
Go to the documentation of this file.
00001 #ifndef lint
00002 static char RCSid[]="$Id: win_process.c,v 3.6 2005/09/19 11:30:10 schorsch Exp $";
00003 #endif
00004 /*
00005  * Routines to communicate with separate process via dual pipes.
00006  * Windows version.
00007  *
00008  * External symbols declared in standard.h
00009  */
00010 
00011 #include "copyright.h"
00012 
00013 #include <stdio.h>
00014 #define STRICT
00015 #include <windows.h> /* typedefs */
00016 #include <io.h>      /* _open_osfhandle */
00017 #include <fcntl.h>   /* _O_XXX */
00018 
00019 #include "rterror.h"
00020 #include "rtio.h"
00021 #include "rtprocess.h"
00022 
00023 
00024 int
00025 win_nice(int inc) /* simple nice(2) replacement for Windows */
00026 {
00027        /* We don't have much granularity available: IDLE_PRIORITY_CLASS
00028           will run whenever no other higher priority process is running */
00029        if (inc > 0) {
00030               return (int)!SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);
00031        }
00032        return 0;
00033 }
00034 
00035 
00036 /*
00037     Safely terminate a process by creating a remote thread
00038     in the process that calls ExitProcess.
00039        As presented by Andrew Tucker in Windows Developer Magazine.
00040 */
00041 #ifndef OBSOLETE_WINDOWS  /* won't work on Win 9X/ME/CE. */
00042 BOOL SafeTerminateProcess(HANDLE hProcess, UINT uExitCode)
00043 {
00044        DWORD dwTID, dwCode, dwErr = 0;
00045        HANDLE hProcessDup = INVALID_HANDLE_VALUE;
00046        HANDLE hRT = NULL;
00047        HINSTANCE hKernel = GetModuleHandle("Kernel32");
00048        BOOL bSuccess = FALSE;
00049 
00050        BOOL bDup = DuplicateHandle(GetCurrentProcess(), 
00051                      hProcess, 
00052                      GetCurrentProcess(), 
00053                      &hProcessDup, 
00054                      PROCESS_ALL_ACCESS, 
00055                      FALSE, 
00056                      0);
00057        /* Detect the special case where the process is already dead... */
00058        if ( GetExitCodeProcess((bDup) ? hProcessDup : hProcess, &dwCode) && 
00059                      (dwCode == STILL_ACTIVE) ) {
00060               FARPROC pfnExitProc;
00061               pfnExitProc = GetProcAddress(hKernel, "ExitProcess");
00062               hRT = CreateRemoteThread((bDup) ? hProcessDup : hProcess, 
00063                             NULL, 
00064                             0, 
00065                             (LPTHREAD_START_ROUTINE)pfnExitProc,
00066                             (PVOID)uExitCode, 0, &dwTID);
00067               if ( hRT == NULL ) dwErr = GetLastError();
00068        } else {
00069               dwErr = ERROR_PROCESS_ABORTED;
00070        }
00071        if ( hRT ) {
00072               /* Must wait process to terminate to guarantee that it has exited... */
00073               WaitForSingleObject((bDup) ? hProcessDup : hProcess, INFINITE);
00074               CloseHandle(hRT);
00075               bSuccess = TRUE;
00076        }
00077        if ( bDup ) CloseHandle(hProcessDup);
00078        if ( !bSuccess ) SetLastError(dwErr);
00079        return bSuccess;
00080 }
00081 #endif
00082 
00083 
00084 static int
00085 start_process(SUBPROC *proc, char *cmdstr)
00086 {
00087        BOOL res;
00088        int Pflags = 0;
00089        SECURITY_ATTRIBUTES SAttrs;
00090        STARTUPINFO SInfo;
00091        PROCESS_INFORMATION PInfo;
00092        /* welcome to the world of resource handles */
00093        HANDLE hToChildRead = NULL;
00094        HANDLE hToChildWrite = NULL;
00095        HANDLE hFromChildRead = NULL;
00096        HANDLE hFromChildWrite = NULL;
00097        HANDLE hRead = NULL, hWrite = NULL;
00098        HANDLE hStdIn, hStdOut, hStdErr;
00099        HANDLE hCurProc;
00100 
00101        /* get process and standard stream handles */
00102        hCurProc = GetCurrentProcess();
00103        hStdIn = GetStdHandle(STD_INPUT_HANDLE);
00104        hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
00105        hStdErr = GetStdHandle(STD_ERROR_HANDLE);
00106        
00107        /* the remote pipe handles must be inheritable */
00108        SAttrs.bInheritHandle = 1;
00109        SAttrs.lpSecurityDescriptor = NULL;
00110        SAttrs.nLength = sizeof(SECURITY_ATTRIBUTES);
00111 
00112        /* make pipe, assign to stdout */
00113        /* we'll check for errors after CreateProcess()...*/
00114        res = CreatePipe(&hFromChildRead, &hFromChildWrite, &SAttrs, 0);
00115        res = SetStdHandle(STD_OUTPUT_HANDLE, hFromChildWrite);
00116        /* create non-inheritable dup of local end */
00117        res = DuplicateHandle(hCurProc, hFromChildRead, hCurProc, &hRead,
00118                      0, FALSE, DUPLICATE_SAME_ACCESS);
00119        CloseHandle(hFromChildRead); hFromChildRead = NULL;
00120 
00121        res = CreatePipe(&hToChildRead, &hToChildWrite, &SAttrs, 0);
00122        res = SetStdHandle(STD_INPUT_HANDLE, hToChildRead);
00123        res = DuplicateHandle(hCurProc, hToChildWrite, hCurProc, &hWrite,
00124                      0, FALSE, DUPLICATE_SAME_ACCESS);
00125        CloseHandle(hToChildWrite); hToChildWrite = NULL;
00126 
00127        CloseHandle(hCurProc); hCurProc = NULL;
00128 
00129        /* do some bookkeeping for Windows... */
00130        SInfo.cb = sizeof(STARTUPINFO);
00131        SInfo.lpReserved = NULL;
00132        SInfo.lpDesktop = NULL;
00133        SInfo.lpTitle = NULL;
00134        SInfo.cbReserved2 = 0;
00135        SInfo.lpReserved2 = NULL;
00136        /* don't open a console automatically, pass handles */
00137        SInfo.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
00138        SInfo.wShowWindow = SW_HIDE;
00139        SInfo.hStdInput = hToChildRead;
00140        SInfo.hStdOutput = hFromChildWrite;
00141        SInfo.hStdError = hStdErr; /* reuse original stderr */
00142        
00143        res = CreateProcess(NULL, /* command name */
00144               cmdstr, /* full command line */
00145               NULL,    /* default process attributes */
00146               NULL,    /* default security attributes */
00147               1,       /* inherit handles (pass doesn't work reliably) */
00148               Pflags,  /* process flags */
00149               NULL,    /* no new environment */
00150               NULL,    /* stay in current directory */
00151               &SInfo,
00152               &PInfo
00153               );
00154        /* reset stdin/stdout in any case */
00155        SetStdHandle(STD_OUTPUT_HANDLE, hStdOut);
00156        SetStdHandle(STD_INPUT_HANDLE, hStdIn);
00157        /* Oops... */
00158        if(res == 0) {
00159               char es[128];
00160               _snprintf(es, sizeof(es),
00161                             "Error creating process (%d)\n", GetLastError());
00162               eputs(es);
00163               goto error;
00164        }
00165        /* close stuff we don't need */
00166        CloseHandle(PInfo.hThread);
00167        CloseHandle(hFromChildWrite); hFromChildWrite = NULL;
00168        CloseHandle(hToChildRead); hToChildRead = NULL;
00169        /* get the file descriptors */
00170        proc->r = _open_osfhandle((long)hRead, _O_RDONLY);
00171        proc->w = _open_osfhandle((long)hWrite, _O_APPEND);
00172        proc->pid = PInfo.dwProcessId;
00173        proc->running = 1;
00174        CloseHandle(hCurProc);
00175        /* Windows doesn't tell us the actual buffer size */
00176        return PIPE_BUF;
00177 
00178 error: /* cleanup */
00179        if(PInfo.hThread) CloseHandle(PInfo.hThread);
00180        if(hToChildRead) CloseHandle(hToChildRead);
00181        if(hToChildWrite) CloseHandle(hToChildWrite);
00182        if(hFromChildRead) CloseHandle(hFromChildRead);
00183        if(hFromChildWrite) CloseHandle(hFromChildWrite);
00184        if(hRead) CloseHandle(hRead);
00185        if(hWrite) CloseHandle(hWrite);
00186        if(hCurProc) CloseHandle(hCurProc);
00187        proc->running = 0;
00188        return 0;
00189        /* There... Are we happy now? */
00190 }
00191 
00192 
00193 static int         /* copied size or -1 on error */
00194 wordncopy(         /* copy (quoted) src to dest. */
00195 
00196 char * dest,
00197 char * src,
00198 int dlen,
00199 int insert_space,  /* prepend a space  */
00200 int force_dq       /* turn 'src' into "dest" (for Win command line) */
00201 )
00202 {
00203        int slen;
00204        int pos = 0;
00205 
00206        slen = strlen(src);
00207        if (insert_space) {
00208               if (1 >= dlen) return -1;
00209               dest[pos++] = ' ';
00210        }
00211        if (strpbrk(src, " \f\n\r\t\v")) {
00212               if (force_dq && src[0] == '\'' && src[slen-1] == '\'') {
00213                      if (slen + pos + 1 > dlen) return -1;
00214                      dest[pos++] = '"';
00215                      strncpy(dest + pos, src + 1, slen -2);
00216                      pos += slen - 2;
00217                      dest[pos++] = '"';
00218               } else if (src[0] == '"' && src[slen-1] == '"') {
00219                      if (slen + pos + 1 > dlen) return -1;
00220                      strncpy(dest + pos, src, slen);
00221                      pos += slen;
00222               } else {
00223                      if (slen + pos + 3 > dlen) return -1;
00224                      dest[pos++] = '"';
00225                      strncpy(dest + pos, src, slen);
00226                      pos += slen;
00227                      dest[pos++] = '"';
00228               }
00229        } else {
00230               if (slen + pos + 1 > dlen) return -1;
00231               strncpy(dest + pos, src, slen);
00232               pos += slen;
00233        }
00234        dest[pos] = '\0';
00235        return pos;
00236 }      
00237 
00238 
00239 
00240 static char *
00241 quoted_cmdline(  /* compose command line for StartProcess() as static string */
00242 
00243 char *cmdpath,  /* full path to executable */
00244 char *sl[]       /* list of arguments */
00245 )
00246 {
00247        static char *cmdstr;
00248        static int clen;
00249        char *newcs;
00250        int newlen, pos, res, i;
00251 
00252        newlen = strlen(cmdpath) + 3; /* allow two quotes plus the final \0 */
00253        for (i = 0; sl[i] != NULL; i++) {
00254               newlen += strlen(sl[i]) + 3; /* allow two quotes and a space */
00255        }
00256        if (cmdstr == NULL) {
00257               cmdstr = (char *) malloc(newlen);
00258               if (cmdstr == NULL) return NULL;
00259        } else if (newlen > clen) {
00260               newcs = (char *) realloc(cmdstr, newlen);
00261               if (newcs == NULL) return NULL;
00262               cmdstr = newcs;
00263        }
00264        clen = newlen;
00265        pos = wordncopy(cmdstr, cmdpath, clen, 0, 1);
00266        if (pos < 0) return NULL;
00267        for (i = 0; sl[i] != NULL; i++) {
00268               res = wordncopy(cmdstr + pos, sl[i], clen - pos, 1, 1);
00269               if (res < 0) return NULL;
00270               pos += res;
00271        }
00272        return cmdstr;
00273 }
00274 
00275 
00276 int
00277 open_process(SUBPROC *proc, char *av[])
00278 {
00279        char *cmdpath;
00280        char *cmdstr;
00281 
00282        proc->running = 0;
00283        cmdpath = getpath(av[0], getenv("PATH"), X_OK);
00284        cmdstr = quoted_cmdline(cmdpath, av+1);
00285        if (cmdstr == NULL) { return 0; }
00286        return start_process(proc, cmdstr);
00287 }
00288 
00289 
00290 int win_kill(RT_PID pid, int sig) /* we ignore sig... */
00291 {
00292        HANDLE hProc;
00293 
00294        hProc = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE, FALSE, pid);
00295        /*  it looks like we want to ignore errors here */
00296        if(hProc != NULL) {
00297 #ifdef OBSOLETE_WINDOWS
00298 #define KILL_TIMEOUT 10 * 1000 /* milliseconds */
00299               /* it might have some windows open... */
00300               EnumWindows((WNDENUMPROC)TerminateAppEnum, (LPARAM)pid);
00301               if(WaitForSingleObject(hProc, KILL_TIMEOUT)!=WAIT_OBJECT_0) {
00302                      /* No way to avoid dangling DLLs here. */
00303                      TerminateProcess(hProc, 0);
00304               }
00305 #else
00306               SafeTerminateProcess(hProc, 0);
00307 #endif
00308               /* WaitForSingleObject(hProc, 0); */
00309               /* not much use to wait on Windows */
00310               CloseHandle(hProc);
00311        }
00312        return 0; /* XXX we need to figure out more here... */
00313 }
00314 
00315 
00316 int
00317 close_process(SUBPROC *proc) {
00318        int icres, ocres;
00319        DWORD pid;
00320 
00321        ocres = close(proc->w);
00322        icres = close(proc->r);
00323        pid = proc->pid;
00324        if(ocres != 0 || icres != 0) {
00325               /* something went wrong: enforce infanticide */
00326               /* other than that, it looks like we want to ignore errors here */
00327               if (proc->running) {
00328                      win_kill(pid, 0);
00329               }
00330        }
00331        proc->running = 0;
00332        return 0; /* XXX we need to figure out more here... */
00333 }
00334 
00335 
00336 #ifdef TEST_MODULE
00337 int
00338 main( int argc, char **argv )
00339 {
00340        SUBPROC proc;
00341        FILE *inf, *outf;
00342        int res;
00343        char ret[1024];
00344        char *command[]= {"word", "gappy word", "\"quoted words\"", "'squoted words'", NULL};
00345 
00346     res = open_process(&proc, command)
00347        if (res == 0) {
00348               printf("open_process() failed with return value 0\n");
00349               return -1;
00350        }
00351        printf("process opened with return value: %d, pid: %d,  r: %d,  w: %d\n",
00352                      res, proc.pid, proc.r, proc.w);
00353        inf = fdopen(proc.r, "rb");
00354        outf = fdopen(proc.w, "wb");
00355        fprintf(outf,"0 0 0 0 1 0\n");
00356        fflush(outf);
00357        fgets(ret, sizeof(ret), inf);
00358        printf("%s\n",ret);
00359        close_process(&proc);
00360 }
00361 #endif
00362