VirtualBox

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

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

IPRT/process-win: Added ERROR_FILE_CORRUPT translation.

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