VirtualBox

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

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

updates

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