VirtualBox

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

Last change on this file since 39842 was 39842, checked in by vboxsync, 13 years ago

IPRT/process-win: Fixed handle leak on legacy APIs; check rc when unloading user profile to detect non-closed handles more quickly.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 50.5 KB
Line 
1/* $Id: process-win.cpp 39842 2012-01-23 18:21:18Z 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 RTLdrClose(hPSAPI);
525 }
526 }
527 RTLdrClose(hKernel32);
528 }
529 Assert(dwErr == NO_ERROR);
530 return fFound;
531}
532
533
534/**
535 * Logs on a specified user and returns its primary token.
536 *
537 * @return int
538 *
539 * @param pwszUser User name.
540 * @param pwszPassword Password.
541 * @param pwszDomain Domain (not used at the moment).
542 * @param phToken Pointer to store the logon token.
543 */
544static int rtProcUserLogon(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain, HANDLE *phToken)
545{
546 /** @todo Add domain support! */
547 BOOL fRc = LogonUserW(pwszUser,
548 /*
549 * Because we have to deal with http://support.microsoft.com/kb/245683
550 * for NULL domain names when running on NT4 here, pass an empty string if so.
551 * However, passing FQDNs should work!
552 */
553 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
554 ? L"" /* NT4 and older. */
555 : NULL, /* Windows 2000 and up. */
556 pwszPassword,
557 LOGON32_LOGON_INTERACTIVE,
558 LOGON32_PROVIDER_DEFAULT,
559 phToken);
560 if (!fRc)
561 return rtProcMapErrorCodes(GetLastError());
562 return VINF_SUCCESS;
563}
564
565
566/**
567 * Logs off a user, specified by the given token.
568 *
569 * @param hToken The token (=user) to log off.
570 */
571static void rtProcUserLogoff(HANDLE hToken)
572{
573 CloseHandle(hToken);
574}
575
576
577/**
578 * Creates an environment block out of a handed in Unicode and RTENV block.
579 * The RTENV block can overwrite entries already present in the Unicode block.
580 *
581 * @return IPRT status code.
582 *
583 * @param pvBlock Unicode block (array) of environment entries; name=value
584 * @param hEnv Handle of an existing RTENV block to use.
585 * @param ppwszBlock Pointer to the final output.
586 */
587static int rtProcEnvironmentCreateInternal(VOID *pvBlock, RTENV hEnv, PRTUTF16 *ppwszBlock)
588{
589 int rc = VINF_SUCCESS;
590 RTENV hEnvTemp;
591 rc = RTEnvClone(&hEnvTemp, hEnv);
592 if (RT_SUCCESS(rc))
593 {
594 PRTUTF16 pBlock = (PRTUTF16)pvBlock;
595 while ( pBlock
596 && pBlock != '\0'
597 && RT_SUCCESS(rc))
598 {
599 char *pszEntry;
600 rc = RTUtf16ToUtf8(pBlock, &pszEntry);
601 if (RT_SUCCESS(rc))
602 {
603 /* Don't overwrite values which we already have set to a custom value
604 * specified in hEnv ... */
605 if (!RTEnvExistEx(hEnv, pszEntry))
606 rc = RTEnvPutEx(hEnvTemp, pszEntry);
607 RTStrFree(pszEntry);
608 }
609
610 size_t l;
611 /* 32k should be the maximum the environment block can have on Windows. */
612 if (FAILED(StringCbLengthW((LPCWSTR)pBlock, _32K * sizeof(RTUTF16), &l)))
613 break;
614 pBlock += l / sizeof(RTUTF16);
615 if (pBlock[1] == '\0') /* Did we reach the double zero termination (\0\0)? */
616 break;
617 pBlock++; /* Skip zero termination of current string and advance to next string ... */
618 }
619
620 if (RT_SUCCESS(rc))
621 rc = RTEnvQueryUtf16Block(hEnvTemp, ppwszBlock);
622 RTEnvDestroy(hEnvTemp);
623 }
624 return rc;
625}
626
627
628/**
629 * Builds up the environment block for a specified user (identified by a token),
630 * whereas hEnv is an additional set of environment variables which overwrite existing
631 * values of the user profile. ppwszBlock needs to be destroyed after usage
632 * calling rtProcEnvironmentDestroy().
633 *
634 * @return IPRT status code.
635 *
636 * @param hToken Token of the user to use.
637 * @param hEnv Own environment block to extend/overwrite the profile's data with.
638 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
639 */
640static int rtProcEnvironmentCreateFromToken(HANDLE hToken, RTENV hEnv, PRTUTF16 *ppwszBlock)
641{
642 RTLDRMOD hUserenv;
643 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
644 if (RT_SUCCESS(rc))
645 {
646 PFNCREATEENVIRONMENTBLOCK pfnCreateEnvironmentBlock;
647 rc = RTLdrGetSymbol(hUserenv, "CreateEnvironmentBlock", (void**)&pfnCreateEnvironmentBlock);
648 if (RT_SUCCESS(rc))
649 {
650 PFNPFNDESTROYENVIRONMENTBLOCK pfnDestroyEnvironmentBlock;
651 rc = RTLdrGetSymbol(hUserenv, "DestroyEnvironmentBlock", (void**)&pfnDestroyEnvironmentBlock);
652 if (RT_SUCCESS(rc))
653 {
654 LPVOID pEnvBlockProfile = NULL;
655 if (pfnCreateEnvironmentBlock(&pEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
656 {
657 rc = rtProcEnvironmentCreateInternal(pEnvBlockProfile, hEnv, ppwszBlock);
658 pfnDestroyEnvironmentBlock(pEnvBlockProfile);
659 }
660 else
661 rc = RTErrConvertFromWin32(GetLastError());
662 }
663 }
664 RTLdrClose(hUserenv);
665 }
666 /* If we don't have the Userenv-API for whatever reason or something with the
667 * native environment block failed, try to return at least our own environment block. */
668 if (RT_FAILURE(rc))
669 rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock);
670 return rc;
671}
672
673
674/**
675 * Builds up the environment block for a specified user (identified by user name, password
676 * and domain), whereas hEnv is an additional set of environment variables which overwrite
677 * existing values of the user profile. ppwszBlock needs to be destroyed after usage
678 * calling rtProcEnvironmentDestroy().
679 *
680 * @return IPRT status code.
681 *
682 * @param pwszUser User name.
683 * @param pwszPassword Password.
684 * @param pwszDomain Domain.
685 * @param hEnv Own environment block to extend/overwrite the profile's data with.
686 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
687 */
688static int rtProcEnvironmentCreateFromAccount(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
689 RTENV hEnv, PRTUTF16 *ppwszBlock)
690{
691 HANDLE hToken;
692 int rc = rtProcUserLogon(pwszUser, pwszPassword, pwszDomain, &hToken);
693 if (RT_SUCCESS(rc))
694 {
695 rc = rtProcEnvironmentCreateFromToken(hToken, hEnv, ppwszBlock);
696 rtProcUserLogoff(hToken);
697 }
698 return rc;
699}
700
701
702/**
703 * Destroys an environment block formerly created by rtProcEnvironmentCreateInternal(),
704 * rtProcEnvironmentCreateFromToken() or rtProcEnvironmentCreateFromAccount().
705 *
706 * @param ppwszBlock Environment block to destroy.
707 */
708static void rtProcEnvironmentDestroy(PRTUTF16 ppwszBlock)
709{
710 RTEnvFreeUtf16Block(ppwszBlock);
711}
712
713
714static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
715 RTENV hEnv, DWORD dwCreationFlags,
716 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
717{
718 int rc = VINF_SUCCESS;
719 BOOL fRc = FALSE;
720 DWORD dwErr = NO_ERROR;
721
722 /*
723 * If we run as a service CreateProcessWithLogon will fail,
724 * so don't even try it (because of Local System context).
725 */
726 if (!(fFlags & RTPROC_FLAGS_SERVICE))
727 {
728 RTLDRMOD hAdvAPI32;
729 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
730 if (RT_SUCCESS(rc))
731 {
732 /*
733 * This may fail on too old (NT4) platforms or if the calling process
734 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
735 * platforms (however, this works on W2K!).
736 */
737 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
738 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
739 if (RT_SUCCESS(rc))
740 {
741 PRTUTF16 pwszzBlock;
742 rc = rtProcEnvironmentCreateFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
743 hEnv, &pwszzBlock);
744 if (RT_SUCCESS(rc))
745 {
746 fRc = pfnCreateProcessWithLogonW(pwszUser,
747 NULL, /* lpDomain*/
748 pwszPassword,
749 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
750 pwszExec,
751 pwszCmdLine,
752 dwCreationFlags,
753 pwszzBlock,
754 NULL, /* pCurrentDirectory */
755 pStartupInfo,
756 pProcInfo);
757 if (!fRc)
758 dwErr = GetLastError();
759 rtProcEnvironmentDestroy(pwszzBlock);
760 }
761 }
762 RTLdrClose(hAdvAPI32);
763 }
764 }
765
766 /*
767 * Did the API call above fail because we're running on a too old OS (NT4) or
768 * we're running as a Windows service?
769 */
770 if ( RT_FAILURE(rc)
771 || (fFlags & RTPROC_FLAGS_SERVICE))
772 {
773 /*
774 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
775 * we have to do the following:
776 * - Check the credentials supplied and get the user SID.
777 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
778 * user. This of course is only possible if that user is logged in (over
779 * physical console or terminal services).
780 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
781 * use it in order to allow the newly started process to access the user's
782 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
783 * process (but run it without UI).
784 *
785 * The following restrictions apply:
786 * - A process only can show its UI when the user the process should run
787 * under is logged in (has a desktop).
788 * - We do not want to display a process of user A run on the desktop
789 * of user B on multi session systems.
790 *
791 * The following rights are needed in order to use LogonUserW and
792 * CreateProcessAsUserW, so the local policy has to be modified to:
793 * - SE_TCB_NAME = Act as part of the operating system
794 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
795 * - SE_INCREASE_QUOTA_NAME
796 *
797 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
798 */
799 PHANDLE phToken = NULL;
800 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
801 rc = rtProcUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
802 if (RT_SUCCESS(rc))
803 {
804 bool fFound = false;
805 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
806
807 if (fFlags & RTPROC_FLAGS_SERVICE)
808 {
809 DWORD cbName = 0; /* Must be zero to query size! */
810 DWORD cbDomain = 0;
811 SID_NAME_USE sidNameUse = SidTypeUser;
812 fRc = LookupAccountNameW(NULL,
813 pwszUser,
814 NULL,
815 &cbName,
816 NULL,
817 &cbDomain,
818 &sidNameUse);
819 if (!fRc)
820 dwErr = GetLastError();
821 if ( !fRc
822 && dwErr == ERROR_INSUFFICIENT_BUFFER
823 && cbName > 0)
824 {
825 dwErr = NO_ERROR;
826
827 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
828 AssertPtrReturn(pSID, VERR_NO_MEMORY);
829
830 /** @todo No way to allocate a PRTUTF16 directly? */
831 PRTUTF16 pwszDomain = NULL;
832 if (cbDomain > 0)
833 {
834 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
835 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
836 }
837
838 /* Note: Also supports FQDNs! */
839 if ( LookupAccountNameW(NULL, /* lpSystemName */
840 pwszUser,
841 pSID,
842 &cbName,
843 pwszDomain,
844 &cbDomain,
845 &sidNameUse)
846 && IsValidSid(pSID))
847 {
848 /* Array of process names we want to look for. */
849 static const char * const s_papszProcNames[] =
850 {
851#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
852 { "VBoxTray.exe" },
853#endif
854 { "explorer.exe" },
855 NULL
856 };
857 fFound = rtProcFindProcessByName(s_papszProcNames, pSID, &hTokenUserDesktop);
858 }
859 else
860 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
861 RTMemFree(pSID);
862 if (pwszDomain != NULL)
863 RTUtf16Free(pwszDomain);
864 }
865 }
866 else /* !RTPROC_FLAGS_SERVICE */
867 {
868 /* Nothing to do here right now. */
869 }
870
871 /** @todo Hmm, this function already is too big! We need to split
872 * it up into several small parts. */
873
874 /* If we got an error due to account lookup/loading above, don't
875 * continue here. */
876 if (dwErr == NO_ERROR)
877 {
878 /*
879 * If we didn't find a matching VBoxTray, just use the token we got
880 * above from LogonUserW(). This enables us to at least run processes with
881 * desktop interaction without UI.
882 */
883 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
884 RTLDRMOD hUserenv;
885 int rc = RTLdrLoad("Userenv.dll", &hUserenv);
886 if (RT_SUCCESS(rc))
887 {
888 PFNLOADUSERPROFILEW pfnLoadUserProfileW;
889 rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
890 if (RT_SUCCESS(rc))
891 {
892 PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
893 rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
894 if (RT_SUCCESS(rc))
895 {
896 PROFILEINFOW profileInfo;
897 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
898 {
899 ZeroMemory(&profileInfo, sizeof(profileInfo));
900 profileInfo.dwSize = sizeof(profileInfo);
901 profileInfo.lpUserName = pwszUser;
902 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
903
904 if (!pfnLoadUserProfileW(*phToken, &profileInfo))
905 dwErr = GetLastError();
906 }
907
908 if (dwErr == NO_ERROR)
909 {
910 PRTUTF16 pwszzBlock;
911 rc = rtProcEnvironmentCreateFromToken(*phToken, hEnv, &pwszzBlock);
912 if (RT_SUCCESS(rc))
913 {
914 /*
915 * Useful KB articles:
916 * http://support.microsoft.com/kb/165194/
917 * http://support.microsoft.com/kb/184802/
918 * http://support.microsoft.com/kb/327618/
919 */
920 fRc = CreateProcessAsUserW(*phToken,
921 pwszExec,
922 pwszCmdLine,
923 NULL, /* pProcessAttributes */
924 NULL, /* pThreadAttributes */
925 TRUE, /* fInheritHandles */
926 dwCreationFlags,
927 pwszzBlock,
928 NULL, /* pCurrentDirectory */
929 pStartupInfo,
930 pProcInfo);
931 if (fRc)
932 dwErr = NO_ERROR;
933 else
934 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
935 rtProcEnvironmentDestroy(pwszzBlock);
936 }
937 else
938 dwErr = rc;
939
940 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
941 {
942 fRc = pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
943 if (!fRc)
944 {
945 /* In case there were some handles left open, we want to know about
946 * that -- can be tricky to debug later! */
947 DWORD dwErr2 = GetLastError();
948 AssertMsgFailed(("Unloading user profile failed with error %ld", dwErr2));
949 if (dwErr == NO_ERROR)
950 dwErr = dwErr2;
951 }
952 }
953 }
954 }
955 }
956 RTLdrClose(hUserenv);
957 } /* Userenv.dll found/loaded? */
958 } /* Account lookup succeeded? */
959 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
960 CloseHandle(hTokenUserDesktop);
961 rtProcUserLogoff(hTokenLogon);
962 }
963 }
964
965 if ( RT_SUCCESS(rc)
966 && dwErr != NO_ERROR)
967 {
968 rc = rtProcMapErrorCodes(dwErr);
969 }
970 return rc;
971}
972
973RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
974 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
975 const char *pszPassword, PRTPROCESS phProcess)
976{
977 /*
978 * Input validation
979 */
980 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
981 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
982 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_SAME_CONTRACT | RTPROC_FLAGS_NO_PROFILE | RTPROC_FLAGS_NO_WINDOW)), VERR_INVALID_PARAMETER);
983 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
984 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
985 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
986 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
987 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
988 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
989 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
990 /** @todo search the PATH (add flag for this). */
991
992 /*
993 * Initialize the globals.
994 */
995 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
996 AssertRCReturn(rc, rc);
997
998 /*
999 * Get the file descriptors for the handles we've been passed.
1000 *
1001 * It seems there is no point in trying to convince a child process's CRT
1002 * that any of the standard file handles is non-TEXT. So, we don't...
1003 */
1004 STARTUPINFOW StartupInfo;
1005 RT_ZERO(StartupInfo);
1006 StartupInfo.cb = sizeof(StartupInfo);
1007 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1008#if 1 /* The CRT should keep the standard handles up to date. */
1009 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1010 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1011 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1012#else
1013 StartupInfo.hStdInput = _get_osfhandle(0);
1014 StartupInfo.hStdOutput = _get_osfhandle(1);
1015 StartupInfo.hStdError = _get_osfhandle(2);
1016#endif
1017 /* If we want to have a hidden process (e.g. not visible to
1018 * to the user) use the STARTUPINFO flags. */
1019 if (fFlags & RTPROC_FLAGS_HIDDEN)
1020 {
1021 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
1022 StartupInfo.wShowWindow = SW_HIDE;
1023 }
1024
1025 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
1026 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
1027 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
1028 for (int i = 0; i < 3; i++)
1029 {
1030 if (paHandles[i])
1031 {
1032 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
1033 switch (paHandles[i]->enmType)
1034 {
1035 case RTHANDLETYPE_FILE:
1036 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
1037 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
1038 : INVALID_HANDLE_VALUE;
1039 break;
1040
1041 case RTHANDLETYPE_PIPE:
1042 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
1043 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
1044 : INVALID_HANDLE_VALUE;
1045 break;
1046
1047 case RTHANDLETYPE_SOCKET:
1048 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
1049 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
1050 : INVALID_HANDLE_VALUE;
1051 break;
1052
1053 default:
1054 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1055 }
1056
1057 /* Get the inheritability of the handle. */
1058 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1059 {
1060 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1061 {
1062 rc = RTErrConvertFromWin32(GetLastError());
1063 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1064 }
1065 }
1066 }
1067 }
1068
1069 /*
1070 * Set the inheritability any handles we're handing the child.
1071 */
1072 rc = VINF_SUCCESS;
1073 for (int i = 0; i < 3; i++)
1074 if ( (afInhStds[i] != 0xffffffff)
1075 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1076 {
1077 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1078 {
1079 rc = RTErrConvertFromWin32(GetLastError());
1080 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1081 }
1082 }
1083
1084 /*
1085 * Create the environment block, command line and convert the executable
1086 * name.
1087 */
1088 PRTUTF16 pwszzBlock;
1089 if (RT_SUCCESS(rc))
1090 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
1091 if (RT_SUCCESS(rc))
1092 {
1093 PRTUTF16 pwszCmdLine;
1094 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1095 if (RT_SUCCESS(rc))
1096 {
1097 PRTUTF16 pwszExec;
1098 rc = RTStrToUtf16(pszExec, &pwszExec);
1099 if (RT_SUCCESS(rc))
1100 {
1101 /*
1102 * Get going...
1103 */
1104 PROCESS_INFORMATION ProcInfo;
1105 RT_ZERO(ProcInfo);
1106 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1107 if (fFlags & RTPROC_FLAGS_DETACHED)
1108 dwCreationFlags |= DETACHED_PROCESS;
1109 if (fFlags & RTPROC_FLAGS_NO_WINDOW)
1110 dwCreationFlags |= CREATE_NO_WINDOW;
1111
1112 /*
1113 * Only use the normal CreateProcess stuff if we have no user name
1114 * and we are not running from a (Windows) service. Otherwise use
1115 * the more advanced version in rtProcCreateAsUserHlp().
1116 */
1117 if ( pszAsUser == NULL
1118 && !(fFlags & RTPROC_FLAGS_SERVICE))
1119 {
1120 if (CreateProcessW(pwszExec,
1121 pwszCmdLine,
1122 NULL, /* pProcessAttributes */
1123 NULL, /* pThreadAttributes */
1124 TRUE, /* fInheritHandles */
1125 dwCreationFlags,
1126 pwszzBlock,
1127 NULL, /* pCurrentDirectory */
1128 &StartupInfo,
1129 &ProcInfo))
1130 rc = VINF_SUCCESS;
1131 else
1132 rc = RTErrConvertFromWin32(GetLastError());
1133 }
1134 else
1135 {
1136 /*
1137 * Convert the additional parameters and use a helper
1138 * function to do the actual work.
1139 */
1140 PRTUTF16 pwszUser;
1141 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1142 if (RT_SUCCESS(rc))
1143 {
1144 PRTUTF16 pwszPassword;
1145 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1146 if (RT_SUCCESS(rc))
1147 {
1148 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
1149 pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1150 &StartupInfo, &ProcInfo, fFlags);
1151
1152 RTUtf16Free(pwszPassword);
1153 }
1154 RTUtf16Free(pwszUser);
1155 }
1156 }
1157 if (RT_SUCCESS(rc))
1158 {
1159 CloseHandle(ProcInfo.hThread);
1160 if (phProcess)
1161 {
1162 /*
1163 * Add the process to the child process list so
1164 * RTProcWait can reuse and close the process handle.
1165 */
1166 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1167 *phProcess = ProcInfo.dwProcessId;
1168 }
1169 else
1170 CloseHandle(ProcInfo.hProcess);
1171 rc = VINF_SUCCESS;
1172 }
1173 RTUtf16Free(pwszExec);
1174 }
1175 RTUtf16Free(pwszCmdLine);
1176 }
1177 RTEnvFreeUtf16Block(pwszzBlock);
1178 }
1179
1180 /* Undo any handle inherit changes. */
1181 for (int i = 0; i < 3; i++)
1182 if ( (afInhStds[i] != 0xffffffff)
1183 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1184 {
1185 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1186 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1187 }
1188
1189 return rc;
1190}
1191
1192
1193
1194RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1195{
1196 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1197 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1198 AssertRCReturn(rc, rc);
1199
1200 /*
1201 * Try find the process among the ones we've spawned, otherwise, attempt
1202 * opening the specified process.
1203 */
1204 HANDLE hOpenedProc = NULL;
1205 HANDLE hProcess = rtProcWinFindPid(Process);
1206 if (hProcess == NULL)
1207 {
1208 hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1209 if (hProcess == NULL)
1210 {
1211 DWORD dwErr = GetLastError();
1212 if (dwErr == ERROR_INVALID_PARAMETER)
1213 return VERR_PROCESS_NOT_FOUND;
1214 return RTErrConvertFromWin32(dwErr);
1215 }
1216 }
1217
1218 /*
1219 * Wait for it to terminate.
1220 */
1221 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1222 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1223 while (WaitRc == WAIT_IO_COMPLETION)
1224 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1225 switch (WaitRc)
1226 {
1227 /*
1228 * It has terminated.
1229 */
1230 case WAIT_OBJECT_0:
1231 {
1232 DWORD dwExitCode;
1233 if (GetExitCodeProcess(hProcess, &dwExitCode))
1234 {
1235 /** @todo the exit code can be special statuses. */
1236 if (pProcStatus)
1237 {
1238 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1239 pProcStatus->iStatus = (int)dwExitCode;
1240 }
1241 if (hOpenedProc == NULL)
1242 rtProcWinRemovePid(Process);
1243 rc = VINF_SUCCESS;
1244 }
1245 else
1246 rc = RTErrConvertFromWin32(GetLastError());
1247 break;
1248 }
1249
1250 /*
1251 * It hasn't terminated just yet.
1252 */
1253 case WAIT_TIMEOUT:
1254 rc = VERR_PROCESS_RUNNING;
1255 break;
1256
1257 /*
1258 * Something went wrong...
1259 */
1260 case WAIT_FAILED:
1261 rc = RTErrConvertFromWin32(GetLastError());
1262 break;
1263
1264 case WAIT_ABANDONED:
1265 AssertFailed();
1266 rc = VERR_GENERAL_FAILURE;
1267 break;
1268
1269 default:
1270 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1271 rc = VERR_GENERAL_FAILURE;
1272 break;
1273 }
1274
1275 if (hOpenedProc != NULL)
1276 CloseHandle(hOpenedProc);
1277 return rc;
1278}
1279
1280
1281RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1282{
1283 /** @todo this isn't quite right. */
1284 return RTProcWait(Process, fFlags, pProcStatus);
1285}
1286
1287
1288RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1289{
1290 if (Process == NIL_RTPROCESS)
1291 return VINF_SUCCESS;
1292
1293 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
1294 AssertRCReturn(rc, rc);
1295
1296 /*
1297 * Try find the process among the ones we've spawned, otherwise, attempt
1298 * opening the specified process.
1299 */
1300 HANDLE hProcess = rtProcWinFindPid(Process);
1301 if (hProcess != NULL)
1302 {
1303 if (!TerminateProcess(hProcess, 127))
1304 rc = RTErrConvertFromWin32(GetLastError());
1305 }
1306 else
1307 {
1308 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1309 if (hProcess != NULL)
1310 {
1311 BOOL fRc = TerminateProcess(hProcess, 127);
1312 DWORD dwErr = GetLastError();
1313 CloseHandle(hProcess);
1314 if (!fRc)
1315 rc = RTErrConvertFromWin32(dwErr);
1316 }
1317 }
1318 return rc;
1319}
1320
1321
1322RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1323{
1324 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1325 DWORD_PTR dwSystemAffinityMask;
1326
1327 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1328 Assert(fRc);
1329
1330 return dwProcessAffinityMask;
1331}
1332
Note: See TracBrowser for help on using the repository browser.

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