VirtualBox

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

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

Typo.

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