VirtualBox

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

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

Runtime/Process: Missing changes, relax the parameter checks for RTProcQueryUsername

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