VirtualBox

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

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

Runtime: Whitespace and svn:keyword cleanups by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 52.7 KB
Line 
1/* $Id: process-win.cpp 48935 2013-10-07 21:19:37Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2013 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 RTENV block.
616 * The RTENV block can overwrite entries already present in the Unicode block.
617 *
618 * @return IPRT status code.
619 *
620 * @param pvBlock Unicode block (array) of environment entries; name=value
621 * @param hEnv Handle of an existing RTENV block to use.
622 * @param ppwszBlock Pointer to the final output.
623 */
624static int rtProcWinEnvironmentCreateInternal(VOID *pvBlock, RTENV hEnv, PRTUTF16 *ppwszBlock)
625{
626 RTENV hEnvTemp;
627 int rc = RTEnvClone(&hEnvTemp, hEnv);
628 if (RT_SUCCESS(rc))
629 {
630 PCRTUTF16 pwch = (PCRTUTF16)pvBlock;
631 while ( pwch
632 && RT_SUCCESS(rc))
633 {
634 if (*pwch)
635 {
636 char *pszEntry;
637 rc = RTUtf16ToUtf8(pwch, &pszEntry);
638 if (RT_SUCCESS(rc))
639 {
640 /* Don't overwrite values which we already have set to a custom value
641 * specified in hEnv ... */
642 if (!RTEnvExistEx(hEnv, pszEntry))
643 rc = RTEnvPutEx(hEnvTemp, pszEntry);
644 RTStrFree(pszEntry);
645 }
646 }
647 pwch += RTUtf16Len(pwch) + 1;
648 if (*pwch)
649 break;
650 }
651
652 if (RT_SUCCESS(rc))
653 rc = RTEnvQueryUtf16Block(hEnvTemp, ppwszBlock);
654 RTEnvDestroy(hEnvTemp);
655 }
656 return rc;
657}
658
659
660/**
661 * Creates the environment block using Userenv.dll.
662 *
663 * Builds up the environment block for a specified user (identified by a token),
664 * whereas hEnv is an additional set of environment variables which overwrite existing
665 * values of the user profile. ppwszBlock needs to be destroyed after usage
666 * calling rtProcWinDestoryEnv().
667 *
668 * @return IPRT status code.
669 *
670 * @param hToken Token of the user to use.
671 * @param hEnv Own environment block to extend/overwrite the profile's data with.
672 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
673 */
674static int rtProcWinCreateEnvFromToken(HANDLE hToken, RTENV hEnv, PRTUTF16 *ppwszBlock)
675{
676 RTLDRMOD hUserenv;
677 int rc = RTLdrLoadSystem("Userenv.dll", true /*fNoUnload*/, &hUserenv);
678 if (RT_SUCCESS(rc))
679 {
680 PFNCREATEENVIRONMENTBLOCK pfnCreateEnvironmentBlock;
681 rc = RTLdrGetSymbol(hUserenv, "CreateEnvironmentBlock", (void**)&pfnCreateEnvironmentBlock);
682 if (RT_SUCCESS(rc))
683 {
684 PFNPFNDESTROYENVIRONMENTBLOCK pfnDestroyEnvironmentBlock;
685 rc = RTLdrGetSymbol(hUserenv, "DestroyEnvironmentBlock", (void**)&pfnDestroyEnvironmentBlock);
686 if (RT_SUCCESS(rc))
687 {
688 LPVOID pvEnvBlockProfile = NULL;
689 if (pfnCreateEnvironmentBlock(&pvEnvBlockProfile, hToken, FALSE /* Don't inherit from parent. */))
690 {
691 rc = rtProcWinEnvironmentCreateInternal(pvEnvBlockProfile, hEnv, ppwszBlock);
692 pfnDestroyEnvironmentBlock(pvEnvBlockProfile);
693 }
694 else
695 rc = RTErrConvertFromWin32(GetLastError());
696 }
697 }
698 RTLdrClose(hUserenv);
699 }
700
701 /* If we don't have the Userenv-API for whatever reason or something with the
702 * native environment block failed, try to return at least our own environment block. */
703 /** @todo this probably isn't a great idea if CreateEnvironmentBlock fails. */
704 if (RT_FAILURE(rc))
705 rc = RTEnvQueryUtf16Block(hEnv, ppwszBlock);
706 return rc;
707}
708
709
710/**
711 * Builds up the environment block for a specified user (identified by user name, password
712 * and domain), whereas hEnv is an additional set of environment variables which overwrite
713 * existing values of the user profile. ppwszBlock needs to be destroyed after usage
714 * calling rtProcWinDestoryEnv().
715 *
716 * @return IPRT status code.
717 *
718 * @param pwszUser User name.
719 * @param pwszPassword Password.
720 * @param pwszDomain Domain.
721 * @param hEnv Own environment block to extend/overwrite the profile's data with.
722 * @param ppwszBlock Pointer to a pointer of the final UTF16 environment block.
723 */
724static int rtProcWinCreateEnvFromAccount(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszDomain,
725 RTENV hEnv, PRTUTF16 *ppwszBlock)
726{
727 HANDLE hToken;
728 int rc = rtProcWinUserLogon(pwszUser, pwszPassword, pwszDomain, &hToken);
729 if (RT_SUCCESS(rc))
730 {
731 rc = rtProcWinCreateEnvFromToken(hToken, hEnv, ppwszBlock);
732 rtProcWinUserLogoff(hToken);
733 }
734 return rc;
735}
736
737
738/**
739 * Destroys an environment block formerly created by rtProcWinEnvironmentCreateInternal(),
740 * rtProcWinCreateEnvFromToken() or rtProcWinCreateEnvFromAccount().
741 *
742 * @param ppwszBlock Environment block to destroy.
743 */
744static void rtProcWinDestroyEnv(PRTUTF16 ppwszBlock)
745{
746 RTEnvFreeUtf16Block(ppwszBlock);
747}
748
749
750/**
751 * Method \#2.
752 */
753static int rtProcWinCreateAsUser2(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
754 RTENV hEnv, DWORD dwCreationFlags,
755 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
756{
757 /*
758 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
759 * we have to do the following:
760 * - Check the credentials supplied and get the user SID.
761 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
762 * user. This of course is only possible if that user is logged in (over
763 * physical console or terminal services).
764 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
765 * use it in order to allow the newly started process to access the user's
766 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
767 * process (but run it without UI).
768 *
769 * The following restrictions apply:
770 * - A process only can show its UI when the user the process should run
771 * under is logged in (has a desktop).
772 * - We do not want to display a process of user A run on the desktop
773 * of user B on multi session systems.
774 *
775 * The following rights are needed in order to use LogonUserW and
776 * CreateProcessAsUserW, so the local policy has to be modified to:
777 * - SE_TCB_NAME = Act as part of the operating system
778 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
779 * - SE_INCREASE_QUOTA_NAME
780 *
781 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
782 */
783 DWORD dwErr = NO_ERROR;
784 PHANDLE phToken = NULL;
785 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
786 int rc = rtProcWinUserLogon(pwszUser, pwszPassword, NULL /* Domain */, &hTokenLogon);
787 if (RT_SUCCESS(rc))
788 {
789 DWORD fRc;
790 bool fFound = false;
791 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
792
793 if (fFlags & RTPROC_FLAGS_SERVICE)
794 {
795 DWORD cbSid = 0; /* Must be zero to query size! */
796 DWORD cchDomain = 0;
797 SID_NAME_USE sidNameUse = SidTypeUser;
798 fRc = LookupAccountNameW(NULL,
799 pwszUser,
800 NULL,
801 &cbSid,
802 NULL,
803 &cchDomain,
804 &sidNameUse);
805 if (!fRc)
806 dwErr = GetLastError();
807 if ( !fRc
808 && dwErr == ERROR_INSUFFICIENT_BUFFER
809 && cbSid > 0)
810 {
811 dwErr = NO_ERROR;
812
813 PSID pSid = (PSID)RTMemAlloc(cbSid * sizeof(wchar_t)); /** @todo r=bird: What's the relationship between wchar_t and PSID? */
814 AssertPtrReturn(pSid, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory... */
815
816 PRTUTF16 pwszDomain = NULL;
817 if (cchDomain > 0)
818 {
819 pwszDomain = (PRTUTF16)RTMemAlloc(cchDomain * sizeof(RTUTF16));
820 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY); /** @todo r=bird: Leaking token handles when we're out of memory... */
821 }
822
823 /* Note: Also supports FQDNs! */
824 if ( LookupAccountNameW(NULL, /* lpSystemName */
825 pwszUser,
826 pSid,
827 &cbSid,
828 pwszDomain,
829 &cchDomain,
830 &sidNameUse)
831 && IsValidSid(pSid))
832 {
833 /* Array of process names we want to look for. */
834 static const char * const s_papszProcNames[] =
835 {
836#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
837 { "VBoxTray.exe" },
838#endif
839 { "explorer.exe" },
840 NULL
841 };
842 fFound = rtProcWinFindTokenByProcess(s_papszProcNames, pSid, &hTokenUserDesktop);
843 }
844 else
845 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
846 RTMemFree(pSid);
847 RTMemFree(pwszDomain);
848 }
849 }
850 else /* !RTPROC_FLAGS_SERVICE */
851 {
852 /* Nothing to do here right now. */
853 }
854
855 /** @todo Hmm, this function already is too big! We need to split
856 * it up into several small parts. */
857
858 /* If we got an error due to account lookup/loading above, don't
859 * continue here. */
860 if (dwErr == NO_ERROR)
861 {
862 /*
863 * If we didn't find a matching VBoxTray, just use the token we got
864 * above from LogonUserW(). This enables us to at least run processes with
865 * desktop interaction without UI.
866 */
867 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
868 RTLDRMOD hUserenv;
869 int rc = RTLdrLoadSystem("Userenv.dll", true /*fNoUnload*/, &hUserenv);
870 if (RT_SUCCESS(rc))
871 {
872 PFNLOADUSERPROFILEW pfnLoadUserProfileW;
873 rc = RTLdrGetSymbol(hUserenv, "LoadUserProfileW", (void**)&pfnLoadUserProfileW);
874 if (RT_SUCCESS(rc))
875 {
876 PFNUNLOADUSERPROFILE pfnUnloadUserProfile;
877 rc = RTLdrGetSymbol(hUserenv, "UnloadUserProfile", (void**)&pfnUnloadUserProfile);
878 if (RT_SUCCESS(rc))
879 {
880 PROFILEINFOW profileInfo;
881 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
882 {
883 RT_ZERO(profileInfo);
884 profileInfo.dwSize = sizeof(profileInfo);
885 profileInfo.lpUserName = pwszUser;
886 profileInfo.dwFlags = PI_NOUI; /* Prevents the display of profile error messages. */
887
888 if (!pfnLoadUserProfileW(*phToken, &profileInfo))
889 dwErr = GetLastError();
890 }
891
892 if (dwErr == NO_ERROR)
893 {
894 PRTUTF16 pwszzBlock;
895 rc = rtProcWinCreateEnvFromToken(*phToken, hEnv, &pwszzBlock);
896 if (RT_SUCCESS(rc))
897 {
898 /*
899 * Useful KB articles:
900 * http://support.microsoft.com/kb/165194/
901 * http://support.microsoft.com/kb/184802/
902 * http://support.microsoft.com/kb/327618/
903 */
904 fRc = CreateProcessAsUserW(*phToken,
905 pwszExec,
906 pwszCmdLine,
907 NULL, /* pProcessAttributes */
908 NULL, /* pThreadAttributes */
909 TRUE, /* fInheritHandles */
910 dwCreationFlags,
911 pwszzBlock,
912 NULL, /* pCurrentDirectory */
913 pStartupInfo,
914 pProcInfo);
915 if (fRc)
916 dwErr = NO_ERROR;
917 else
918 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
919 rtProcWinDestroyEnv(pwszzBlock);
920 }
921
922 if (!(fFlags & RTPROC_FLAGS_NO_PROFILE))
923 {
924 fRc = pfnUnloadUserProfile(*phToken, profileInfo.hProfile);
925#ifdef RT_STRICT
926 if (!fRc)
927 {
928 DWORD dwErr2 = GetLastError();
929 AssertMsgFailed(("Unloading user profile failed with error %u (%#x) - Are all handles closed? (dwErr=%u)",
930 dwErr2, dwErr2, dwErr));
931 }
932#endif
933 }
934 }
935 }
936 }
937 RTLdrClose(hUserenv);
938 } /* Userenv.dll found/loaded? */
939 } /* Account lookup succeeded? */
940 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
941 CloseHandle(hTokenUserDesktop);
942 rtProcWinUserLogoff(hTokenLogon);
943 }
944
945 if ( RT_SUCCESS(rc)
946 && dwErr != NO_ERROR)
947 {
948 rc = rtProcWinMapErrorCodes(dwErr);
949 if (rc == VERR_UNRESOLVED_ERROR)
950 LogRelFunc(("dwErr=%u (%#x), rc=%Rrc\n", dwErr, dwErr, rc));
951 }
952 return rc;
953}
954
955
956/**
957 * Method \#1.
958 *
959 * This may fail on too old (NT4) platforms or if the calling process
960 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
961 * platforms (however, this works on W2K!).
962 */
963static int rtProcWinCreateAsUser1(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
964 RTENV hEnv, DWORD dwCreationFlags,
965 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
966{
967 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
968 pfnCreateProcessWithLogonW = (PFNCREATEPROCESSWITHLOGON)RTLdrGetSystemSymbol("Advapi32.dll", "CreateProcessWithLogonW");
969 if (pfnCreateProcessWithLogonW)
970 return VERR_SYMBOL_NOT_FOUND;
971
972 PRTUTF16 pwszzBlock;
973 int rc = rtProcWinCreateEnvFromAccount(pwszUser, pwszPassword, NULL /* Domain */,
974 hEnv, &pwszzBlock);
975 if (RT_SUCCESS(rc))
976 {
977 BOOL fRc = pfnCreateProcessWithLogonW(pwszUser,
978 NULL, /* lpDomain*/
979 pwszPassword,
980 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
981 pwszExec,
982 pwszCmdLine,
983 dwCreationFlags,
984 pwszzBlock,
985 NULL, /* pCurrentDirectory */
986 pStartupInfo,
987 pProcInfo);
988 if (!fRc)
989 {
990 DWORD dwErr = GetLastError();
991 rc = rtProcWinMapErrorCodes(dwErr);
992 if (rc == VERR_UNRESOLVED_ERROR)
993 LogRelFunc(("pfnCreateProcessWithLogonW (%p) failed: dwErr=%u (%#x), rc=%Rrc\n",
994 pfnCreateProcessWithLogonW, dwErr, dwErr, rc));
995 }
996 rtProcWinDestroyEnv(pwszzBlock);
997 }
998 return rc;
999}
1000
1001
1002static int rtProcWinCreateAsUser(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
1003 RTENV hEnv, DWORD dwCreationFlags,
1004 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
1005{
1006 /*
1007 * If we run as a service CreateProcessWithLogon will fail,
1008 * so don't even try it (because of Local System context).
1009 */
1010 int rc = VERR_TRY_AGAIN;
1011 if (!(fFlags & RTPROC_FLAGS_SERVICE))
1012 rc = rtProcWinCreateAsUser1(pwszUser, pwszPassword, pwszExec, pwszCmdLine, hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags);
1013
1014 /*
1015 * Did the API call above fail because we're running on a too old OS (NT4) or
1016 * we're running as a Windows service?
1017 */
1018 if (RT_FAILURE(rc))
1019 rc = rtProcWinCreateAsUser2(pwszUser, pwszPassword, pwszExec, pwszCmdLine, hEnv, dwCreationFlags, pStartupInfo, pProcInfo, fFlags);
1020
1021 return rc;
1022}
1023
1024
1025/**
1026 * RTPathTraverseList callback used by RTProcCreateEx to locate the executable.
1027 */
1028static DECLCALLBACK(int) rtPathFindExec(char const *pchPath, size_t cchPath, void *pvUser1, void *pvUser2)
1029{
1030 const char *pszExec = (const char *)pvUser1;
1031 char *pszRealExec = (char *)pvUser2;
1032 int rc = RTPathJoinEx(pszRealExec, RTPATH_MAX, pchPath, cchPath, pszExec, RTSTR_MAX);
1033 if (RT_FAILURE(rc))
1034 return rc;
1035 if (RTFileExists(pszRealExec))
1036 return VINF_SUCCESS;
1037 return VERR_TRY_AGAIN;
1038}
1039
1040
1041RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
1042 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
1043 const char *pszPassword, PRTPROCESS phProcess)
1044{
1045 /*
1046 * Input validation
1047 */
1048 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
1049 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
1050 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_HIDDEN | RTPROC_FLAGS_SERVICE | RTPROC_FLAGS_SAME_CONTRACT
1051 | RTPROC_FLAGS_NO_PROFILE | RTPROC_FLAGS_NO_WINDOW | RTPROC_FLAGS_SEARCH_PATH)),
1052 VERR_INVALID_PARAMETER);
1053 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
1054 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
1055 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
1056 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
1057 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
1058 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
1059 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
1060 /** @todo search the PATH (add flag for this). */
1061
1062 /*
1063 * Initialize the globals.
1064 */
1065 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1066 AssertRCReturn(rc, rc);
1067
1068 /*
1069 * Resolve the executable name via the PATH if requested.
1070 */
1071 char szRealExec[RTPATH_MAX];
1072 if ( (fFlags & RTPROC_FLAGS_SEARCH_PATH)
1073 && !RTPathHavePath(pszExec)
1074 && !RTPathExists(pszExec) )
1075 {
1076 /* search */
1077 char *pszPath;
1078 if (RTEnvExistEx(hEnv, "PATH"))
1079 pszPath = RTEnvDupEx(hEnv, "PATH");
1080 else
1081 pszPath = RTEnvDupEx(hEnv, "Path");
1082 rc = RTPathTraverseList(pszPath, ';', rtPathFindExec, (void *)pszExec, &szRealExec[0]);
1083 RTStrFree(pszPath);
1084 if (RT_FAILURE(rc))
1085 return rc == VERR_END_OF_STRING ? VERR_FILE_NOT_FOUND : rc;
1086 pszExec = szRealExec;
1087 }
1088
1089 /*
1090 * Get the file descriptors for the handles we've been passed.
1091 *
1092 * It seems there is no point in trying to convince a child process's CRT
1093 * that any of the standard file handles is non-TEXT. So, we don't...
1094 */
1095 STARTUPINFOW StartupInfo;
1096 RT_ZERO(StartupInfo);
1097 StartupInfo.cb = sizeof(StartupInfo);
1098 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
1099#if 1 /* The CRT should keep the standard handles up to date. */
1100 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
1101 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
1102 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
1103#else
1104 StartupInfo.hStdInput = _get_osfhandle(0);
1105 StartupInfo.hStdOutput = _get_osfhandle(1);
1106 StartupInfo.hStdError = _get_osfhandle(2);
1107#endif
1108 /* If we want to have a hidden process (e.g. not visible to
1109 * to the user) use the STARTUPINFO flags. */
1110 if (fFlags & RTPROC_FLAGS_HIDDEN)
1111 {
1112 StartupInfo.dwFlags |= STARTF_USESHOWWINDOW;
1113 StartupInfo.wShowWindow = SW_HIDE;
1114 }
1115
1116 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
1117 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
1118 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
1119 for (int i = 0; i < 3; i++)
1120 {
1121 if (paHandles[i])
1122 {
1123 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
1124 switch (paHandles[i]->enmType)
1125 {
1126 case RTHANDLETYPE_FILE:
1127 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
1128 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
1129 : INVALID_HANDLE_VALUE;
1130 break;
1131
1132 case RTHANDLETYPE_PIPE:
1133 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
1134 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
1135 : INVALID_HANDLE_VALUE;
1136 break;
1137
1138 case RTHANDLETYPE_SOCKET:
1139 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
1140 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
1141 : INVALID_HANDLE_VALUE;
1142 break;
1143
1144 default:
1145 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
1146 }
1147
1148 /* Get the inheritability of the handle. */
1149 if (*aphStds[i] != INVALID_HANDLE_VALUE)
1150 {
1151 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
1152 {
1153 rc = RTErrConvertFromWin32(GetLastError());
1154 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
1155 }
1156 }
1157 }
1158 }
1159
1160 /*
1161 * Set the inheritability any handles we're handing the child.
1162 */
1163 rc = VINF_SUCCESS;
1164 for (int i = 0; i < 3; i++)
1165 if ( (afInhStds[i] != 0xffffffff)
1166 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1167 {
1168 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
1169 {
1170 rc = RTErrConvertFromWin32(GetLastError());
1171 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
1172 }
1173 }
1174
1175 /*
1176 * Create the environment block, command line and convert the executable
1177 * name.
1178 */
1179 PRTUTF16 pwszzBlock;
1180 if (RT_SUCCESS(rc))
1181 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
1182 if (RT_SUCCESS(rc))
1183 {
1184 PRTUTF16 pwszCmdLine;
1185 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
1186 if (RT_SUCCESS(rc))
1187 {
1188 PRTUTF16 pwszExec;
1189 rc = RTStrToUtf16(pszExec, &pwszExec);
1190 if (RT_SUCCESS(rc))
1191 {
1192 /*
1193 * Get going...
1194 */
1195 PROCESS_INFORMATION ProcInfo;
1196 RT_ZERO(ProcInfo);
1197 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
1198 if (fFlags & RTPROC_FLAGS_DETACHED)
1199 dwCreationFlags |= DETACHED_PROCESS;
1200 if (fFlags & RTPROC_FLAGS_NO_WINDOW)
1201 dwCreationFlags |= CREATE_NO_WINDOW;
1202
1203 /*
1204 * Only use the normal CreateProcess stuff if we have no user name
1205 * and we are not running from a (Windows) service. Otherwise use
1206 * the more advanced version in rtProcWinCreateAsUser().
1207 */
1208 if ( pszAsUser == NULL
1209 && !(fFlags & RTPROC_FLAGS_SERVICE))
1210 {
1211 if (CreateProcessW(pwszExec,
1212 pwszCmdLine,
1213 NULL, /* pProcessAttributes */
1214 NULL, /* pThreadAttributes */
1215 TRUE, /* fInheritHandles */
1216 dwCreationFlags,
1217 pwszzBlock,
1218 NULL, /* pCurrentDirectory */
1219 &StartupInfo,
1220 &ProcInfo))
1221 rc = VINF_SUCCESS;
1222 else
1223 rc = RTErrConvertFromWin32(GetLastError());
1224 }
1225 else
1226 {
1227 /*
1228 * Convert the additional parameters and use a helper
1229 * function to do the actual work.
1230 */
1231 PRTUTF16 pwszUser;
1232 rc = RTStrToUtf16(pszAsUser, &pwszUser);
1233 if (RT_SUCCESS(rc))
1234 {
1235 PRTUTF16 pwszPassword;
1236 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
1237 if (RT_SUCCESS(rc))
1238 {
1239 rc = rtProcWinCreateAsUser(pwszUser, pwszPassword,
1240 pwszExec, pwszCmdLine, hEnv, dwCreationFlags,
1241 &StartupInfo, &ProcInfo, fFlags);
1242
1243 RTUtf16Free(pwszPassword);
1244 }
1245 RTUtf16Free(pwszUser);
1246 }
1247 }
1248 if (RT_SUCCESS(rc))
1249 {
1250 CloseHandle(ProcInfo.hThread);
1251 if (phProcess)
1252 {
1253 /*
1254 * Add the process to the child process list so
1255 * RTProcWait can reuse and close the process handle.
1256 */
1257 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
1258 *phProcess = ProcInfo.dwProcessId;
1259 }
1260 else
1261 CloseHandle(ProcInfo.hProcess);
1262 rc = VINF_SUCCESS;
1263 }
1264 RTUtf16Free(pwszExec);
1265 }
1266 RTUtf16Free(pwszCmdLine);
1267 }
1268 RTEnvFreeUtf16Block(pwszzBlock);
1269 }
1270
1271 /* Undo any handle inherit changes. */
1272 for (int i = 0; i < 3; i++)
1273 if ( (afInhStds[i] != 0xffffffff)
1274 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
1275 {
1276 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
1277 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
1278 }
1279
1280 return rc;
1281}
1282
1283
1284
1285RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1286{
1287 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
1288 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1289 AssertRCReturn(rc, rc);
1290
1291 /*
1292 * Try find the process among the ones we've spawned, otherwise, attempt
1293 * opening the specified process.
1294 */
1295 HANDLE hOpenedProc = NULL;
1296 HANDLE hProcess = rtProcWinFindPid(Process);
1297 if (hProcess == NULL)
1298 {
1299 hProcess = hOpenedProc = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
1300 if (hProcess == NULL)
1301 {
1302 DWORD dwErr = GetLastError();
1303 if (dwErr == ERROR_INVALID_PARAMETER)
1304 return VERR_PROCESS_NOT_FOUND;
1305 return RTErrConvertFromWin32(dwErr);
1306 }
1307 }
1308
1309 /*
1310 * Wait for it to terminate.
1311 */
1312 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
1313 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1314 while (WaitRc == WAIT_IO_COMPLETION)
1315 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
1316 switch (WaitRc)
1317 {
1318 /*
1319 * It has terminated.
1320 */
1321 case WAIT_OBJECT_0:
1322 {
1323 DWORD dwExitCode;
1324 if (GetExitCodeProcess(hProcess, &dwExitCode))
1325 {
1326 /** @todo the exit code can be special statuses. */
1327 if (pProcStatus)
1328 {
1329 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
1330 pProcStatus->iStatus = (int)dwExitCode;
1331 }
1332 if (hOpenedProc == NULL)
1333 rtProcWinRemovePid(Process);
1334 rc = VINF_SUCCESS;
1335 }
1336 else
1337 rc = RTErrConvertFromWin32(GetLastError());
1338 break;
1339 }
1340
1341 /*
1342 * It hasn't terminated just yet.
1343 */
1344 case WAIT_TIMEOUT:
1345 rc = VERR_PROCESS_RUNNING;
1346 break;
1347
1348 /*
1349 * Something went wrong...
1350 */
1351 case WAIT_FAILED:
1352 rc = RTErrConvertFromWin32(GetLastError());
1353 break;
1354
1355 case WAIT_ABANDONED:
1356 AssertFailed();
1357 rc = VERR_GENERAL_FAILURE;
1358 break;
1359
1360 default:
1361 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
1362 rc = VERR_GENERAL_FAILURE;
1363 break;
1364 }
1365
1366 if (hOpenedProc != NULL)
1367 CloseHandle(hOpenedProc);
1368 return rc;
1369}
1370
1371
1372RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
1373{
1374 /** @todo this isn't quite right. */
1375 return RTProcWait(Process, fFlags, pProcStatus);
1376}
1377
1378
1379RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
1380{
1381 if (Process == NIL_RTPROCESS)
1382 return VINF_SUCCESS;
1383
1384 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL);
1385 AssertRCReturn(rc, rc);
1386
1387 /*
1388 * Try find the process among the ones we've spawned, otherwise, attempt
1389 * opening the specified process.
1390 */
1391 HANDLE hProcess = rtProcWinFindPid(Process);
1392 if (hProcess != NULL)
1393 {
1394 if (!TerminateProcess(hProcess, 127))
1395 rc = RTErrConvertFromWin32(GetLastError());
1396 }
1397 else
1398 {
1399 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1400 if (hProcess != NULL)
1401 {
1402 BOOL fRc = TerminateProcess(hProcess, 127);
1403 DWORD dwErr = GetLastError();
1404 CloseHandle(hProcess);
1405 if (!fRc)
1406 rc = RTErrConvertFromWin32(dwErr);
1407 }
1408 }
1409 return rc;
1410}
1411
1412
1413RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1414{
1415 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1416 DWORD_PTR dwSystemAffinityMask;
1417
1418 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1419 Assert(fRc);
1420
1421 return dwProcessAffinityMask;
1422}
1423
1424
1425RTR3DECL(int) RTProcQueryUsername(RTPROCESS hProcess, char *pszUser, size_t cbUser,
1426 size_t *pcbUser)
1427{
1428 AssertReturn( (pszUser && cbUser > 0)
1429 || (!pszUser && !cbUser), VERR_INVALID_PARAMETER);
1430
1431 if (hProcess != RTProcSelf())
1432 return VERR_NOT_SUPPORTED;
1433
1434 RTUTF16 awszUserName[UNLEN + 1];
1435 DWORD cchUserName = UNLEN + 1;
1436
1437 if (!GetUserNameW(&awszUserName[0], &cchUserName))
1438 return RTErrConvertFromWin32(GetLastError());
1439
1440 char *pszUserName = NULL;
1441 int rc = RTUtf16ToUtf8(awszUserName, &pszUserName);
1442 if (RT_SUCCESS(rc))
1443 {
1444 size_t cbUserName = strlen(pszUserName) + 1;
1445
1446 if (pcbUser)
1447 *pcbUser = cbUserName;
1448
1449 if (cbUserName > cbUser)
1450 rc = VERR_BUFFER_OVERFLOW;
1451 else
1452 {
1453 memcpy(pszUser, pszUserName, cbUserName);
1454 rc = VINF_SUCCESS;
1455 }
1456
1457 RTStrFree(pszUserName);
1458 }
1459
1460 return rc;
1461}
1462
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