VirtualBox

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

Last change on this file since 52627 was 50745, checked in by vboxsync, 11 years ago

IPRT/process-win: Fixed overwriting of existing environment variables in hEnv block, added a flag to specify mandatory overwriting.

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