VirtualBox

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

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

Extended RTOnce with termination cleanups. (Changes existing structures and functions.)

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette