VirtualBox

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

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

IPRT/process-win: Not needed; rc already contains the right error code.

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