VirtualBox

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

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

RTProcWait/win: build fix

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