VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/win/process-win.cpp@ 37448

Last change on this file since 37448 was 37448, checked in by vboxsync, 14 years ago

process-creation/*: Adjusted assertions for RTPROC_FLAGS_SAME_CONTRACT (Solaris only) and RTPROC_FLAGS_NO_PROFILE (Windows only).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 49.6 KB
Line 
1/* $Id: process-win.cpp 37448 2011-06-14 16:06:06Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*******************************************************************************
29* Header Files *
30*******************************************************************************/
31#define LOG_GROUP RTLOGGROUP_PROCESS
32#include <iprt/asm.h> /* hack */
33
34#include <Userenv.h>
35#include <Windows.h>
36#include <tlhelp32.h>
37#include <process.h>
38#include <errno.h>
39#include <Strsafe.h>
40
41#include <iprt/process.h>
42#include "internal/iprt.h"
43
44#include <iprt/assert.h>
45#include <iprt/critsect.h>
46#include <iprt/file.h>
47#include <iprt/err.h>
48#include <iprt/env.h>
49#include <iprt/getopt.h>
50#include <iprt/initterm.h>
51#include <iprt/ldr.h>
52#include <iprt/mem.h>
53#include <iprt/once.h>
54#include <iprt/pipe.h>
55#include <iprt/string.h>
56#include <iprt/socket.h>
57
58
59/*******************************************************************************
60* Structures and Typedefs *
61*******************************************************************************/
62typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
63 LPCWSTR,
64 LPCWSTR,
65 DWORD,
66 LPCWSTR,
67 LPWSTR,
68 DWORD,
69 LPVOID,
70 LPCWSTR,
71 LPSTARTUPINFOW,
72 LPPROCESS_INFORMATION);
73typedef FNCREATEPROCESSWITHLOGON *PFNCREATEPROCESSWITHLOGON;
74
75typedef DWORD WINAPI FNWTSGETACTIVECONSOLESESSIONID();
76typedef FNWTSGETACTIVECONSOLESESSIONID *PFNWTSGETACTIVECONSOLESESSIONID;
77
78typedef HANDLE WINAPI FNCREATETOOLHELP32SNAPSHOT(DWORD, DWORD);
79typedef FNCREATETOOLHELP32SNAPSHOT *PFNCREATETOOLHELP32SNAPSHOT;
80
81typedef BOOL WINAPI FNPROCESS32FIRST(HANDLE, LPPROCESSENTRY32);
82typedef FNPROCESS32FIRST *PFNPROCESS32FIRST;
83
84typedef BOOL WINAPI FNPROCESS32NEXT(HANDLE, LPPROCESSENTRY32);
85typedef FNPROCESS32NEXT *PFNPROCESS32NEXT;
86
87typedef BOOL WINAPI FNENUMPROCESSES(DWORD*, DWORD, DWORD*);
88typedef FNENUMPROCESSES *PFNENUMPROCESSES;
89
90typedef DWORD FNGETMODULEBASENAME(HANDLE, HMODULE, LPTSTR, DWORD);
91typedef FNGETMODULEBASENAME *PFNGETMODULEBASENAME;
92
93typedef BOOL WINAPI FNCREATEENVIRONMENTBLOCK(LPVOID *, HANDLE, BOOL);
94typedef FNCREATEENVIRONMENTBLOCK *PFNCREATEENVIRONMENTBLOCK;
95
96typedef BOOL WINAPI FNPFNDESTROYENVIRONMENTBLOCK(LPVOID);
97typedef FNPFNDESTROYENVIRONMENTBLOCK *PFNPFNDESTROYENVIRONMENTBLOCK;
98
99typedef BOOL WINAPI FNLOADUSERPROFILEW(HANDLE, LPPROFILEINFOW);
100typedef FNLOADUSERPROFILEW *PFNLOADUSERPROFILEW;
101
102typedef BOOL WINAPI FNUNLOADUSERPROFILE(HANDLE, HANDLE);
103typedef FNUNLOADUSERPROFILE *PFNUNLOADUSERPROFILE;
104
105
106/*******************************************************************************
107* Global Variables *
108*******************************************************************************/
109/** Init once structure. */
110static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
111/** Critical section protecting the process array. */
112static RTCRITSECT g_CritSect;
113/** The number of processes in the array. */
114static uint32_t g_cProcesses;
115/** The current allocation size. */
116static uint32_t g_cProcessesAlloc;
117/** Array containing the live or non-reaped child processes. */
118static struct RTPROCWINENTRY
119{
120 /** The process ID. */
121 ULONG_PTR pid;
122 /** The process handle. */
123 HANDLE hProcess;
124} *g_paProcesses;
125
126
127/**
128 * Clean up the globals.
129 *
130 * @param enmReason Ignored.
131 * @param iStatus Ignored.
132 * @param pvUser Ignored.
133 */
134static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
135{
136 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
137
138 RTCritSectDelete(&g_CritSect);
139
140 size_t i = g_cProcesses;
141 while (i-- > 0)
142 {
143 CloseHandle(g_paProcesses[i].hProcess);
144 g_paProcesses[i].hProcess = NULL;
145 }
146 RTMemFree(g_paProcesses);
147
148 g_paProcesses = NULL;
149 g_cProcesses = 0;
150 g_cProcessesAlloc = 0;
151}
152
153
154/**
155 * Initialize the globals.
156 *
157 * @returns IPRT status code.
158 * @param pvUser1 Ignored.
159 * @param pvUser2 Ignored.
160 */
161static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
162{
163 NOREF(pvUser1); NOREF(pvUser2);
164
165 g_cProcesses = 0;
166 g_cProcessesAlloc = 0;
167 g_paProcesses = NULL;
168 int rc = RTCritSectInit(&g_CritSect);
169 if (RT_SUCCESS(rc))
170 {
171 /** @todo init once, terminate once - this is a generic thing which should
172 * have some kind of static and simpler setup! */
173 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
174 if (RT_SUCCESS(rc))
175 return rc;
176 RTCritSectDelete(&g_CritSect);
177 }
178 return rc;
179}
180
181
182/**
183 * Gets the process handle for a process from g_paProcesses.
184 *
185 * @returns Process handle if found, NULL if not.
186 * @param pid The process to remove (pid).
187 */
188static HANDLE rtProcWinFindPid(RTPROCESS pid)
189{
190 HANDLE hProcess = NULL;
191
192 RTCritSectEnter(&g_CritSect);
193 uint32_t i = g_cProcesses;
194 while (i-- > 0)
195 if (g_paProcesses[i].pid == pid)
196 {
197 hProcess = g_paProcesses[i].hProcess;
198 break;
199 }
200 RTCritSectLeave(&g_CritSect);
201
202 return hProcess;
203}
204
205
206/**
207 * Removes a process from g_paProcesses and closes the process handle.
208 *
209 * @param pid The process to remove (pid).
210 */
211static void rtProcWinRemovePid(RTPROCESS pid)
212{
213 RTCritSectEnter(&g_CritSect);
214 uint32_t i = g_cProcesses;
215 while (i-- > 0)
216 if (g_paProcesses[i].pid == pid)
217 {
218 HANDLE hProcess = g_paProcesses[i].hProcess;
219
220 g_cProcesses--;
221 uint32_t cToMove = g_cProcesses - i;
222 if (cToMove)
223 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
224
225 RTCritSectLeave(&g_CritSect);
226 CloseHandle(hProcess);
227 return;
228 }
229 RTCritSectLeave(&g_CritSect);
230}
231
232
233/**
234 * Adds a process to g_paProcesses.
235 *
236 * @returns IPRT status code.
237 * @param pid The process id.
238 * @param hProcess The process handle.
239 */
240static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
241{
242 RTCritSectEnter(&g_CritSect);
243
244 uint32_t i = g_cProcesses;
245 if (i >= g_cProcessesAlloc)
246 {
247 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
248 if (RT_UNLIKELY(!pvNew))
249 {
250 RTCritSectLeave(&g_CritSect);
251 return VERR_NO_MEMORY;
252 }
253 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
254 g_cProcessesAlloc = i + 16;
255 }
256
257 g_paProcesses[i].pid = pid;
258 g_paProcesses[i].hProcess = hProcess;
259 g_cProcesses = i + 1;
260
261 RTCritSectLeave(&g_CritSect);
262 return VINF_SUCCESS;
263}
264
265
266RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
267{
268 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
269 NULL, NULL, NULL, /* standard handles */
270 NULL /*pszAsUser*/, NULL /* pszPassword*/,
271 pProcess);
272}
273
274
275/**
276 * Map some important or much used Windows error codes
277 * to our error codes.
278 *
279 * @return Mapped IPRT status code.
280 * @param dwError Windows error code to map to IPRT code.
281 */
282static int rtProcMapErrorCodes(DWORD dwError)
283{
284 int rc;
285 switch (dwError)
286 {
287 case ERROR_NOACCESS:
288 case ERROR_PRIVILEGE_NOT_HELD:
289 rc = VERR_PERMISSION_DENIED;
290 break;
291
292 case ERROR_PASSWORD_EXPIRED:
293 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
294 case ERROR_PASSWORD_RESTRICTION:
295 rc = VERR_AUTHENTICATION_FAILURE;
296 break;
297
298 default:
299 /* Could trigger a debug assertion! */
300 rc = RTErrConvertFromWin32(dwError);
301 break;
302 }
303 return rc;
304}
305
306
307/**
308 * Get the process token (not the process handle like the name might indicate)
309 * of the process indicated by @a dwPID if the @a pSID matches.
310 *
311 * @returns IPRT status code.
312 *
313 * @param dwPID The process identifier.
314 * @param pSID The secure identifier of the user.
315 * @param phToken Where to return the token handle - duplicate,
316 * caller closes it on success.
317 */
318static int rtProcGetProcessHandle(DWORD dwPID, PSID pSID, PHANDLE phToken)
319{
320 AssertPtr(pSID);
321 AssertPtr(phToken);
322
323 DWORD dwErr;
324 BOOL fRc;
325 bool fFound = false;
326 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPID);
327 if (hProc != NULL)
328 {
329 HANDLE hTokenProc;
330 fRc = OpenProcessToken(hProc,
331 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
332 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
333 &hTokenProc);
334 if (fRc)
335 {
336 DWORD dwSize = 0;
337 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
338 if (!fRc)
339 dwErr = GetLastError();
340 if ( !fRc
341 && dwErr == ERROR_INSUFFICIENT_BUFFER
342 && dwSize > 0)
343 {
344 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
345 AssertPtrReturn(pTokenUser, VERR_NO_MEMORY);
346 RT_ZERO(*pTokenUser);
347 if ( GetTokenInformation(hTokenProc,
348 TokenUser,
349 (LPVOID)pTokenUser,
350 dwSize,
351 &dwSize))
352 {
353 if ( IsValidSid(pTokenUser->User.Sid)
354 && EqualSid(pTokenUser->User.Sid, pSID))
355 {
356 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
357 NULL, SecurityIdentification, TokenPrimary, phToken))
358 {
359 /*
360 * So we found the process instance which belongs to the user we want to
361 * to run our new process under. This duplicated token will be used for
362 * the actual CreateProcessAsUserW() call then.
363 */
364 fFound = true;
365 }
366 else
367 dwErr = GetLastError();
368 }
369 }
370 else
371 dwErr = GetLastError();
372 RTMemFree(pTokenUser);
373 }
374 else
375 dwErr = GetLastError();
376 CloseHandle(hTokenProc);
377 }
378 else
379 dwErr = GetLastError();
380 CloseHandle(hProc);
381 }
382 else
383 dwErr = GetLastError();
384 if (fFound)
385 return VINF_SUCCESS;
386 if (dwErr != NO_ERROR)
387 return RTErrConvertFromWin32(dwErr);
388 return VERR_NOT_FOUND; /* No error occurred, but we didn't find the right process. */
389}
390
391
392/**
393 * Finds a one of the processes in @a papszNames running with user @a pSID and
394 * returns a duplicate handle to its token.
395 *
396 * @returns Success indicator.
397 * @param papszNames The process candidates, in prioritized order.
398 * @param pSID The secure identifier of the user.
399 * @param phToken Where to return the token handle - duplicate,
400 * caller closes it on success.
401 */
402static bool rtProcFindProcessByName(const char * const *papszNames, PSID pSID, PHANDLE phToken)
403{
404 AssertPtr(papszNames);
405 AssertPtr(pSID);
406 AssertPtr(phToken);
407
408 DWORD dwErr = NO_ERROR;
409 bool fFound = false;
410
411 /*
412 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
413 * and reliable. Fallback to EnumProcess on NT4.
414 */
415 RTLDRMOD hKernel32;
416 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
417 if (RT_SUCCESS(rc))
418 {
419 PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot;
420 rc = RTLdrGetSymbol(hKernel32, "CreateToolhelp32Snapshot", (void**)&pfnCreateToolhelp32Snapshot);
421 if (RT_SUCCESS(rc))
422 {
423 PFNPROCESS32FIRST pfnProcess32First;
424 rc = RTLdrGetSymbol(hKernel32, "Process32First", (void**)&pfnProcess32First);
425 if (RT_SUCCESS(rc))
426 {
427 PFNPROCESS32NEXT pfnProcess32Next;
428 rc = RTLdrGetSymbol(hKernel32, "Process32Next", (void**)&pfnProcess32Next);
429 if (RT_SUCCESS(rc))
430 {
431 HANDLE hSnap = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
432 if (hSnap != INVALID_HANDLE_VALUE)
433 {
434 for (size_t i = 0; papszNames[i] && !fFound; i++)
435 {
436 PROCESSENTRY32 procEntry;
437 procEntry.dwSize = sizeof(PROCESSENTRY32);
438 if (pfnProcess32First(hSnap, &procEntry))
439 {
440 do
441 {
442 if ( _stricmp(procEntry.szExeFile, papszNames[i]) == 0
443 && RT_SUCCESS(rtProcGetProcessHandle(procEntry.th32ProcessID, pSID, phToken)))
444 {
445 fFound = true;
446 break;
447 }
448 } while (pfnProcess32Next(hSnap, &procEntry));
449 }
450 else /* Process32First */
451 dwErr = GetLastError();
452 if (FAILED(dwErr))
453 break;
454 }
455 CloseHandle(hSnap);
456 }
457 else /* hSnap == INVALID_HANDLE_VALUE */
458 dwErr = GetLastError();
459 }
460 }
461 }
462 else /* CreateToolhelp32Snapshot / Toolhelp32 API not available. */
463 {
464 /*
465 * NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
466 * part of the OS) in order to get a lookup. If we don't have this DLL
467 * we are not able to get a token and therefore no UI will be visible.
468 */
469 RTLDRMOD hPSAPI;
470 int rc = RTLdrLoad("PSAPI.dll", &hPSAPI);
471 if (RT_SUCCESS(rc))
472 {
473 PFNENUMPROCESSES pfnEnumProcesses;
474 rc = RTLdrGetSymbol(hPSAPI, "EnumProcesses", (void**)&pfnEnumProcesses);
475 if (RT_SUCCESS(rc))
476 {
477 PFNGETMODULEBASENAME pfnGetModuleBaseName;
478 rc = RTLdrGetSymbol(hPSAPI, "GetModuleBaseName", (void**)&pfnGetModuleBaseName);
479 if (RT_SUCCESS(rc))
480 {
481 /** @todo Retry if pBytesReturned equals cbBytes! */
482 DWORD adwPIDs[4096]; /* Should be sufficient for now. */
483 DWORD cbBytes = 0;
484 if (pfnEnumProcesses(adwPIDs, sizeof(adwPIDs), &cbBytes))
485 {
486 for (size_t i = 0; papszNames[i] && !fFound; i++)
487 {
488 for (DWORD dwIdx = 0; dwIdx < cbBytes/sizeof(DWORD) && !fFound; dwIdx++)
489 {
490 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
491 FALSE, adwPIDs[dwIdx]);
492 if (hProc)
493 {
494 char *pszProcName = NULL;
495 DWORD dwSize = 128;
496 do
497 {
498 RTMemRealloc(pszProcName, dwSize);
499 if (pfnGetModuleBaseName(hProc, 0, pszProcName, dwSize) == dwSize)
500 dwSize += 128;
501 if (dwSize > _4K) /* Play safe. */
502 break;
503 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
504
505 if (pszProcName)
506 {
507 if ( _stricmp(pszProcName, papszNames[i]) == 0
508 && RT_SUCCESS(rtProcGetProcessHandle(adwPIDs[dwIdx], pSID, phToken)))
509 {
510 fFound = true;
511 }
512 }
513 if (pszProcName)
514 RTStrFree(pszProcName);
515 CloseHandle(hProc);
516 }
517 }
518 }
519 }
520 else
521 dwErr = GetLastError();
522 }
523 }
524 }
525 }
526 RTLdrClose(hKernel32);
527 }
528 Assert(dwErr == NO_ERROR);
529 return fFound;
530}
531
532
533/**
534 * Logs on a specified user and returns its primary token.
535 *
536 * @return int
537 *
538 * @param pwszUser User name.
539 * @param pwszPassword Password.
540 * @param pwszDomain Domain (not used at the moment).
541 * @param phToken Pointer to store the logon token.
542 */
543static int rtProcUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, HANDLE *phToken)
544{
545 /** @todo Add domain support! */
546 BOOL fRc = LogonUserW(pwszUser,
547 /*
548 * Because we have to deal with http://support.microsoft.com/kb/245683
549 * for NULL domain names when running on NT4 here, pass an empty string if so.
550 * However, passing FQDNs should work!
551 */
552 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
553 ? L"" /* NT4 and older. */
554 : NULL, /* Windows 2000 and up. */
555 pwszPassword,
556 LOGON32_LOGON_INTERACTIVE,
557 LOGON32_PROVIDER_DEFAULT,
558 phToken);
559 if (!fRc)
560 return rtProcMapErrorCodes(GetLastError());
561 return VINF_SUCCESS;
562}
563
564
565/**
566 * Logs off a user, specified by the given token.
567 *
568 * @param hToken The token (=user) to log off.
569 */
570static void rtProcUserLogoff(HANDLE hToken)
571{
572 CloseHandle(hToken);
573}
574
575
576/**
577 * Creates an environment block out of a handed in Unicode and RTENV block.
578 * The RTENV block can overwrite entries already present in the Unicode block.
579 *
580 * @return IPRT status code.
581 *
582 * @param pvBlock Unicode block (array) of environment entries; name=value
583 * @param hEnv Handle of an existing RTENV block to use.
584 * @param ppwszBlock Pointer to the final output.
585 */
586static int rtProcEnvironmentCreateInternal(VOID *pvBlock, RTENV hEnv, PRTUTF16 *ppwszBlock)
587{
588 int rc = VINF_SUCCESS;
589 RTENV hEnvTemp;
590 rc = RTEnvClone(&hEnvTemp, hEnv);
591 if (RT_SUCCESS(rc))
592 {
593 PRTUTF16 pBlock = (PRTUTF16)pvBlock;
594 while ( pBlock
595 && pBlock != '\0'
596 && RT_SUCCESS(rc))
597 {
598 char *pszEntry;
599 rc = RTUtf16ToUtf8(pBlock, &pszEntry);
600 if (RT_SUCCESS(rc))
601 {
602 /* Don't overwrite values which we already have set to a custom value
603 * specified in hEnv ... */
604 if (!RTEnvExistEx(hEnv, pszEntry))
605 rc = RTEnvPutEx(hEnvTemp, pszEntry);
606 RTStrFree(pszEntry);
607 }
608
609 size_t l;
610 /* 32k should be the maximum the environment block can have on Windows. */
611 if (FAILED(StringCbLengthW((LPCWSTR)pBlock, _32K * sizeof(RTUTF16), &l)))
612 break;
613 pBlock += l / sizeof(RTUTF16);
614 if (pBlock[1] == '\0') /* Did we reach the double zero termination (\0\0)? */
615 break;
616 pBlock++; /* Skip zero termination of current string and advance to next string ... */
617 }
618
619 if (RT_SUCCESS(rc))
620 rc = RTEnvQueryUtf16Block(hEnvTemp, ppwszBlock);
621 RTEnvDestroy(hEnvTemp);
622 }
623 return rc;
624}
625
626
627/**
628 * Builds up the environment block for a specified user (identified by a token),
629 * whereas hEnv is an additional set of environment variables which overwrite existing
630 * values of the user profile. ppwszBlock needs to be destroyed after usage
631 * calling rtProcEnvironmentDestroy().
632 *
633 * @return IPRT status code.
634 *
635 * @param hToken Token of the user to use.
636 * @param hEnv Own environment block to extend/overwrite the profile's data with.
637 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
638 */
639static int rtProcEnvironmentCreateFromToken(HANDLE hToken, RTENV hEnv, PRTUTF16 *ppwszBlock)
640{
641 RTLDRMOD hUserenv;
642 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
643 if (RT_SUCCESS(rc))
644 {
645 PFNCREATEENVIRONMENTBLOCK pfnCreateEnvironmentBlock;
646 rc = RTLdrGetSymbol(hUserenv, "CreateEnvironmentBlock", (void**)&pfnCreateEnvironmentBlock);
647 if (RT_SUCCESS(rc))
648 {
649 PFNPFNDESTROYENVIRONMENTBLOCK pfnDestroyEnvironmentBlock;
650 rc = RTLdrGetSymbol(hUserenv, "DestroyEnvironmentBlock", (void**)&pfnDestroyEnvironmentBlock);
651 if (RT_SUCCESS(rc))
652 {
653 LPVOID pEnvBlockProfile = NULL;
654 if (pfnCreateEnvironmentBlock(&pEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
655 {
656 rc = rtProcEnvironmentCreateInternal(pEnvBlockProfile, hEnv, ppwszBlock);
657 pfnDestroyEnvironmentBlock(pEnvBlockProfile);
658 }
659 else
660 rc = RTErrConvertFromWin32(GetLastError());
661 }
662 }
663 RTLdrClose(hUserenv);
664 }
665 /* If we don't have the Userenv-API for whatever reason or something with the
666 * native environment block failed, try to return at least our own environment block. */
667 if (RT_FAILURE(rc))
668 rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock);
669 return rc;
670}
671
672
673/**
674 * Builds up the environment block for a specified user (identified by user name, password
675 * and domain), whereas hEnv is an additional set of environment variables which overwrite
676 * existing values of the user profile. ppwszBlock needs to be destroyed after usage
677 * calling rtProcEnvironmentDestroy().
678 *
679 * @return IPRT status code.
680 *
681 * @param pwszUser User name.
682 * @param pwszPassword Password.
683 * @param pwszDomain Domain.
684 * @param hEnv Own environment block to extend/overwrite the profile's data with.
685 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
686 */
687static int rtProcEnvironmentCreateFromAccount(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
688 RTENV hEnv, PRTUTF16 *ppwszBlock)
689{
690 HANDLE hToken;
691 int rc = rtProcUserLogon(pwszUser, pwszPassword, pwszDomain, &hToken);
692 if (RT_SUCCESS(rc))
693 {
694 rc = rtProcEnvironmentCreateFromToken(hToken, hEnv, ppwszBlock);
695 rtProcUserLogoff(hToken);
696 }
697 return rc;
698}
699
700
701/**
702 * Destroys an environment block formerly created by rtProcEnvironmentCreateInternal(),
703 * rtProcEnvironmentCreateFromToken() or rtProcEnvironmentCreateFromAccount().
704 *
705 * @param ppwszBlock Environment block to destroy.
706 */
707static void rtProcEnvironmentDestroy(PRTUTF16 ppwszBlock)
708{
709 RTEnvFreeUtf16Block(ppwszBlock);
710}
711
712
713static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
714 RTENV hEnv, DWORD dwCreationFlags,
715 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
716{
717 int rc = VINF_SUCCESS;
718 BOOL fRc = FALSE;
719 DWORD dwErr = NO_ERROR;
720
721 /*
722 * If we run as a service CreateProcessWithLogon will fail,
723 * so don't even try it (because of Local System context).
724 */
725 if (!(fFlags & RTPROC_FLAGS_SERVICE))
726 {
727 RTLDRMOD hAdvAPI32;
728 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
729 if (RT_SUCCESS(rc))
730 {
731 /*
732 * This may fail on too old (NT4) platforms or if the calling process
733 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
734 * platforms (however, this works on W2K!).
735 */
736 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
737 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
738 if (RT_SUCCESS(rc))
739 {
740 PRTUTF16 pwszzBlock;
741 rc = rtProcEnvironmentCreateFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
742 hEnv, &pwszzBlock);
743 if (RT_SUCCESS(rc))
744 {
745 fRc = pfnCreateProcessWithLogonW(pwszUser,
746 NULL, /* lpDomain*/
747 pwszPassword,
748 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
749 pwszExec,
750 pwszCmdLine,
751 dwCreationFlags,
752 pwszzBlock,
753 NULL, /* pCurrentDirectory */
754 pStartupInfo,
755 pProcInfo);
756 if (!fRc)
757 dwErr = GetLastError();
758 rtProcEnvironmentDestroy(pwszzBlock);
759 }
760 }
761 RTLdrClose(hAdvAPI32);
762 }
763 }
764
765 /*
766 * Did the API call above fail because we're running on a too old OS (NT4) or
767 * we're running as a Windows service?
768 */
769 if ( RT_FAILURE(rc)
770 || (fFlags & RTPROC_FLAGS_SERVICE))
771 {
772 /*
773 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
774 * we have to do the following:
775 * - Check the credentials supplied and get the user SID.
776 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
777 * user. This of course is only possible if that user is logged in (over
778 * physical console or terminal services).
779 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
780 * use it in order to allow the newly started process to access the user's
781 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
782 * process (but run it without UI).
783 *
784 * The following restrictions apply:
785 * - A process only can show its UI when the user the process should run
786 * under is logged in (has a desktop).
787 * - We do not want to display a process of user A run on the desktop
788 * of user B on multi session systems.
789 *
790 * The following rights are needed in order to use LogonUserW and
791 * CreateProcessAsUserW, so the local policy has to be modified to:
792 * - SE_TCB_NAME = Act as part of the operating system
793 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
794 * - SE_INCREASE_QUOTA_NAME
795 *
796 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
797 */
798 PHANDLE phToken = NULL;
799 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
800 rc = rtProcUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
801 if (RT_SUCCESS(rc))
802 {
803 bool fFound = false;
804 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
805
806 if (fFlags & RTPROC_FLAGS_SERVICE)
807 {
808 DWORD cbName = 0; /* Must be zero to query size! */
809 DWORD cbDomain = 0;
810 SID_NAME_USE sidNameUse = SidTypeUser;
811 fRc = LookupAccountNameW(NULL,
812 pwszUser,
813 NULL,
814 &cbName,
815 NULL,
816 &cbDomain,
817 &sidNameUse);
818 if (!fRc)
819 dwErr = GetLastError();
820 if ( !fRc
821 && dwErr == ERROR_INSUFFICIENT_BUFFER
822 && cbName > 0)
823 {
824 dwErr = NO_ERROR;
825
826 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
827 AssertPtrReturn(pSID, VERR_NO_MEMORY);
828
829 /** @todo No way to allocate a PRTUTF16 directly? */
830 PRTUTF16 pwszDomain = NULL;
831 if (cbDomain > 0)
832 {
833 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
834 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
835 }
836
837 /* Note: Also supports FQDNs! */
838 if ( LookupAccountNameW(NULL, /* lpSystemName */
839 pwszUser,
840 pSID,
841 &cbName,
842 pwszDomain,
843 &cbDomain,
844 &sidNameUse)
845 && IsValidSid(pSID))
846 {
847 /* Array of process names we want to look for. */
848 static const char * const s_papszProcNames[] =
849 {
850#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
851 { "VBoxTray.exe" },
852#endif
853 { "explorer.exe" },
854 NULL
855 };
856 fFound = rtProcFindProcessByName(s_papszProcNames, pSID, &hTokenUserDesktop);
857 }
858 else
859 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
860 RTMemFree(pSID);
861 if (pwszDomain != NULL)
862 RTUtf16Free(pwszDomain);
863 }
864 }
865 else /* !RTPROC_FLAGS_SERVICE */
866 {
867 /* Nothing to do here right now. */
868 }
869
870 /** @todo Hmm, this function already is too big! We need to split
871 * it up into several small parts. */
872
873 /* If we got an error due to account lookup/loading above, don't
874 * continue here. */
875 if (dwErr == NO_ERROR)
876 {
877 /*
878 * If we didn't find a matching VBoxTray, just use the token we got
879 * above from LogonUserW(). This enables us to at least run processes with
880 * desktop interaction without UI.
881 */
882 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
883 RTLDRMOD hUserenv;
884 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
885 if (RT_SUCCESS(rc))
886 {
887 PFNLOADUSERPROFILEW pfnLoadUserProfileW;
888 rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
889 if (RT_SUCCESS(rc))
890 {
891 PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
892 rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
893 if (RT_SUCCESS(rc))
894 {
895 PROFILEINFOW profileInfo;
896 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
897 {
898 ZeroMemory(&profileInfo, sizeof(profileInfo));
899 profileInfo.dwSize = sizeof(profileInfo);
900 profileInfo.lpUserName = pwszUser;
901 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
902
903 if (!pfnLoadUserProfileW(*phToken, &profileInfo))
904 dwErr = GetLastError();
905 }
906
907 if (dwErr == NO_ERROR)
908 {
909 PRTUTF16 pwszzBlock;
910 rc = rtProcEnvironmentCreateFromToken(*phToken, hEnv, &pwszzBlock);
911 if (RT_SUCCESS(rc))
912 {
913 /*
914 * Useful KB articles:
915 * http://support.microsoft.com/kb/165194/
916 * http://support.microsoft.com/kb/184802/
917 * http://support.microsoft.com/kb/327618/
918 */
919 fRc = CreateProcessAsUserW(*phToken,
920 pwszExec,
921 pwszCmdLine,
922 NULL, /* pProcessAttributes */
923 NULL, /* pThreadAttributes */
924 TRUE, /* fInheritHandles */
925 dwCreationFlags,
926 pwszzBlock,
927 NULL, /* pCurrentDirectory */
928 pStartupInfo,
929 pProcInfo);
930 if (fRc)
931 dwErr = NO_ERROR;
932 else
933 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
934 rtProcEnvironmentDestroy(pwszzBlock);
935 }
936 else
937 dwErr = rc;
938
939 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
940 pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
941 }
942 }
943 }
944 RTLdrClose(hUserenv);
945 } /* Userenv.dll found/loaded? */
946 } /* Account lookup succeeded? */
947 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
948 CloseHandle(hTokenUserDesktop);
949 rtProcUserLogoff(hTokenLogon);
950 }
951 }
952
953 if ( RT_SUCCESS(rc)
954 && dwErr != NO_ERROR)
955 {
956 rc = rtProcMapErrorCodes(dwErr);
957 }
958 return rc;
959}
960
961RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
962 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
963 const char *pszPassword, PRTPROCESS phProcess)
964{
965 /*
966 * Input validation
967 */
968 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
969 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
970 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_SAME_CONTRACT | RTPROC_FLAGS_NO_PROFILE)), VERR_INVALID_PARAMETER);
971 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
972 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
973 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
974 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
975 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
976 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
977 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
978 /** @todo search the PATH (add flag for this). */
979
980 /*
981 * Initialize the globals.
982 */
983 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
984 AssertRCReturn(rc, rc);
985
986 /*
987 * Get the file descriptors for the handles we've been passed.
988 *
989 * It seems there is no point in trying to convince a child process's CRT
990 * that any of the standard file handles is non-TEXT. So, we don't...
991 */
992 STARTUPINFOW StartupInfo;
993 RT_ZERO(StartupInfo);
994 StartupInfo.cb = sizeof(StartupInfo);
995 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
996#if 1 /* The CRT should keep the standard handles up to date. */
997 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
998 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
999 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1000#else
1001 StartupInfo.hStdInput = _get_osfhandle(0);
1002 StartupInfo.hStdOutput = _get_osfhandle(1);
1003 StartupInfo.hStdError = _get_osfhandle(2);
1004#endif
1005 /* If we want to have a hidden process (e.g. not visible to
1006 * to the user) use the STARTUPINFO flags. */
1007 if (fFlags & RTPROC_FLAGS_HIDDEN)
1008 {
1009 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
1010 StartupInfo.wShowWindow = SW_HIDE;
1011 }
1012
1013 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
1014 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
1015 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
1016 for (int i = 0; i < 3; i++)
1017 {
1018 if (paHandles[i])
1019 {
1020 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
1021 switch (paHandles[i]->enmType)
1022 {
1023 case RTHANDLETYPE_FILE:
1024 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
1025 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
1026 : INVALID_HANDLE_VALUE;
1027 break;
1028
1029 case RTHANDLETYPE_PIPE:
1030 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
1031 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
1032 : INVALID_HANDLE_VALUE;
1033 break;
1034
1035 case RTHANDLETYPE_SOCKET:
1036 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
1037 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
1038 : INVALID_HANDLE_VALUE;
1039 break;
1040
1041 default:
1042 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1043 }
1044
1045 /* Get the inheritability of the handle. */
1046 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1047 {
1048 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1049 {
1050 rc = RTErrConvertFromWin32(GetLastError());
1051 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1052 }
1053 }
1054 }
1055 }
1056
1057 /*
1058 * Set the inheritability any handles we're handing the child.
1059 */
1060 rc = VINF_SUCCESS;
1061 for (int i = 0; i < 3; i++)
1062 if ( (afInhStds[i] != 0xffffffff)
1063 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1064 {
1065 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1066 {
1067 rc = RTErrConvertFromWin32(GetLastError());
1068 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1069 }
1070 }
1071
1072 /*
1073 * Create the environment block, command line and convert the executable
1074 * name.
1075 */
1076 PRTUTF16 pwszzBlock;
1077 if (RT_SUCCESS(rc))
1078 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
1079 if (RT_SUCCESS(rc))
1080 {
1081 PRTUTF16 pwszCmdLine;
1082 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1083 if (RT_SUCCESS(rc))
1084 {
1085 PRTUTF16 pwszExec;
1086 rc = RTStrToUtf16(pszExec, &pwszExec);
1087 if (RT_SUCCESS(rc))
1088 {
1089 /*
1090 * Get going...
1091 */
1092 PROCESS_INFORMATION ProcInfo;
1093 RT_ZERO(ProcInfo);
1094 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1095 if (fFlags & RTPROC_FLAGS_DETACHED)
1096 dwCreationFlags |= DETACHED_PROCESS;
1097
1098 /*
1099 * Only use the normal CreateProcess stuff if we have no user name
1100 * and we are not running from a (Windows) service. Otherwise use
1101 * the more advanced version in rtProcCreateAsUserHlp().
1102 */
1103 if ( pszAsUser == NULL
1104 && !(fFlags & RTPROC_FLAGS_SERVICE))
1105 {
1106 if (CreateProcessW(pwszExec,
1107 pwszCmdLine,
1108 NULL, /* pProcessAttributes */
1109 NULL, /* pThreadAttributes */
1110 TRUE, /* fInheritHandles */
1111 dwCreationFlags,
1112 pwszzBlock,
1113 NULL, /* pCurrentDirectory */
1114 &StartupInfo,
1115 &ProcInfo))
1116 rc = VINF_SUCCESS;
1117 else
1118 rc = RTErrConvertFromWin32(GetLastError());
1119 }
1120 else
1121 {
1122 /*
1123 * Convert the additional parameters and use a helper
1124 * function to do the actual work.
1125 */
1126 PRTUTF16 pwszUser;
1127 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1128 if (RT_SUCCESS(rc))
1129 {
1130 PRTUTF16 pwszPassword;
1131 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1132 if (RT_SUCCESS(rc))
1133 {
1134 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
1135 pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1136 &StartupInfo, &ProcInfo, fFlags);
1137
1138 RTUtf16Free(pwszPassword);
1139 }
1140 RTUtf16Free(pwszUser);
1141 }
1142 }
1143 if (RT_SUCCESS(rc))
1144 {
1145 CloseHandle(ProcInfo.hThread);
1146 if (phProcess)
1147 {
1148 /*
1149 * Add the process to the child process list so
1150 * RTProcWait can reuse and close the process handle.
1151 */
1152 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1153 *phProcess = ProcInfo.dwProcessId;
1154 }
1155 else
1156 CloseHandle(ProcInfo.hProcess);
1157 rc = VINF_SUCCESS;
1158 }
1159 RTUtf16Free(pwszExec);
1160 }
1161 RTUtf16Free(pwszCmdLine);
1162 }
1163 RTEnvFreeUtf16Block(pwszzBlock);
1164 }
1165
1166 /* Undo any handle inherit changes. */
1167 for (int i = 0; i < 3; i++)
1168 if ( (afInhStds[i] != 0xffffffff)
1169 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1170 {
1171 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1172 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1173 }
1174
1175 return rc;
1176}
1177
1178
1179
1180RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1181{
1182 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1183 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1184 AssertRCReturn(rc, rc);
1185
1186 /*
1187 * Try find the process among the ones we've spawned, otherwise, attempt
1188 * opening the specified process.
1189 */
1190 HANDLE hOpenedProc = NULL;
1191 HANDLE hProcess = rtProcWinFindPid(Process);
1192 if (hProcess == NULL)
1193 {
1194 hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1195 if (hProcess == NULL)
1196 {
1197 DWORD dwErr = GetLastError();
1198 if (dwErr == ERROR_INVALID_PARAMETER)
1199 return VERR_PROCESS_NOT_FOUND;
1200 return RTErrConvertFromWin32(dwErr);
1201 }
1202 }
1203
1204 /*
1205 * Wait for it to terminate.
1206 */
1207 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1208 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1209 while (WaitRc == WAIT_IO_COMPLETION)
1210 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1211 switch (WaitRc)
1212 {
1213 /*
1214 * It has terminated.
1215 */
1216 case WAIT_OBJECT_0:
1217 {
1218 DWORD dwExitCode;
1219 if (GetExitCodeProcess(hProcess, &dwExitCode))
1220 {
1221 /** @todo the exit code can be special statuses. */
1222 if (pProcStatus)
1223 {
1224 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1225 pProcStatus->iStatus = (int)dwExitCode;
1226 }
1227 if (hOpenedProc == NULL)
1228 rtProcWinRemovePid(Process);
1229 rc = VINF_SUCCESS;
1230 }
1231 else
1232 rc = RTErrConvertFromWin32(GetLastError());
1233 break;
1234 }
1235
1236 /*
1237 * It hasn't terminated just yet.
1238 */
1239 case WAIT_TIMEOUT:
1240 rc = VERR_PROCESS_RUNNING;
1241 break;
1242
1243 /*
1244 * Something went wrong...
1245 */
1246 case WAIT_FAILED:
1247 rc = RTErrConvertFromWin32(GetLastError());
1248 break;
1249
1250 case WAIT_ABANDONED:
1251 AssertFailed();
1252 rc = VERR_GENERAL_FAILURE;
1253 break;
1254
1255 default:
1256 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1257 rc = VERR_GENERAL_FAILURE;
1258 break;
1259 }
1260
1261 if (hOpenedProc != NULL)
1262 CloseHandle(hOpenedProc);
1263 return rc;
1264}
1265
1266
1267RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1268{
1269 /** @todo this isn't quite right. */
1270 return RTProcWait(Process, fFlags, pProcStatus);
1271}
1272
1273
1274RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1275{
1276 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1277 AssertRCReturn(rc, rc);
1278
1279 /*
1280 * Try find the process among the ones we've spawned, otherwise, attempt
1281 * opening the specified process.
1282 */
1283 HANDLE hProcess = rtProcWinFindPid(Process);
1284 if (hProcess != NULL)
1285 {
1286 if (!TerminateProcess(hProcess, 127))
1287 rc = RTErrConvertFromWin32(GetLastError());
1288 }
1289 else
1290 {
1291 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1292 if (hProcess != NULL)
1293 {
1294 BOOL fRc = TerminateProcess(hProcess, 127);
1295 DWORD dwErr = GetLastError();
1296 CloseHandle(hProcess);
1297 if (!fRc)
1298 rc = RTErrConvertFromWin32(dwErr);
1299 }
1300 }
1301 return rc;
1302}
1303
1304
1305RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1306{
1307 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1308 DWORD_PTR dwSystemAffinityMask;
1309
1310 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1311 Assert(fRc);
1312
1313 return dwProcessAffinityMask;
1314}
1315
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette