VirtualBox

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

Last change on this file since 46404 was 46404, checked in by vboxsync, 12 years ago

Forward ported r86229, r86230 + r86234 (IPRT/process-win.cpp: If there's an unresolved native error, we want to know it via release logging. Shouldn't happen by default of course. Handle ERROR_ACCOUNT_DISABLED. VERR_AUTHENTICATION_FAILURE -> VERR_ACCOUNT_RESTRICTED).

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