VirtualBox

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

Last change on this file since 31942 was 30093, checked in by vboxsync, 15 years ago

IPRT: Removed VERR_LOGON_FAILURE in favor of VERR_AUTHENTICATION_FAILURE. Cleaned up nits in process-win.cpp and adding some missing function docs. Hope it compiles...

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 38.8 KB
Line 
1/* $Id: process-win.cpp 30093 2010-06-08 14:30:17Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2010 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
33#include <Userenv.h>
34#include <Windows.h>
35#include <tlhelp32.h>
36#include <process.h>
37#include <errno.h>
38
39#include <iprt/process.h>
40#include "internal/iprt.h"
41
42#include <iprt/assert.h>
43#include <iprt/critsect.h>
44#include <iprt/file.h>
45#include <iprt/err.h>
46#include <iprt/env.h>
47#include <iprt/getopt.h>
48#include <iprt/initterm.h>
49#include <iprt/ldr.h>
50#include <iprt/mem.h>
51#include <iprt/once.h>
52#include <iprt/pipe.h>
53#include <iprt/string.h>
54#include <iprt/socket.h>
55
56
57/*******************************************************************************
58* Structures and Typedefs *
59*******************************************************************************/
60typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
61 LPCWSTR,
62 LPCWSTR,
63 DWORD,
64 LPCWSTR,
65 LPWSTR,
66 DWORD,
67 LPVOID,
68 LPCWSTR,
69 LPSTARTUPINFOW,
70 LPPROCESS_INFORMATION);
71typedef FNCREATEPROCESSWITHLOGON *PFNCREATEPROCESSWITHLOGON;
72
73typedef DWORD WINAPI FNWTSGETACTIVECONSOLESESSIONID();
74typedef FNWTSGETACTIVECONSOLESESSIONID *PFNWTSGETACTIVECONSOLESESSIONID;
75
76typedef HANDLE WINAPI FNCREATETOOLHELP32SNAPSHOT(DWORD, DWORD);
77typedef FNCREATETOOLHELP32SNAPSHOT *PFNCREATETOOLHELP32SNAPSHOT;
78
79typedef BOOL WINAPI FNPROCESS32FIRST(HANDLE, LPPROCESSENTRY32);
80typedef FNPROCESS32FIRST *PFNPROCESS32FIRST;
81
82typedef BOOL WINAPI FNPROCESS32NEXT(HANDLE, LPPROCESSENTRY32);
83typedef FNPROCESS32NEXT *PFNPROCESS32NEXT;
84
85typedef BOOL WINAPI FNENUMPROCESSES(DWORD*, DWORD, DWORD*);
86typedef FNENUMPROCESSES *PFNENUMPROCESSES;
87
88typedef DWORD FNGETMODULEBASENAME(HANDLE, HMODULE, LPTSTR, DWORD);
89typedef FNGETMODULEBASENAME *PFNGETMODULEBASENAME;
90
91
92/*******************************************************************************
93* Global Variables *
94*******************************************************************************/
95/** Init once structure. */
96static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
97/** Critical section protecting the process array. */
98static RTCRITSECT g_CritSect;
99/** The number of processes in the array. */
100static uint32_t g_cProcesses;
101/** The current allocation size. */
102static uint32_t g_cProcessesAlloc;
103/** Array containing the live or non-reaped child processes. */
104static struct RTPROCWINENTRY
105{
106 /** The process ID. */
107 ULONG_PTR pid;
108 /** The process handle. */
109 HANDLE hProcess;
110} *g_paProcesses;
111
112
113/**
114 * Clean up the globals.
115 *
116 * @param enmReason Ignored.
117 * @param iStatus Ignored.
118 * @param pvUser Ignored.
119 */
120static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
121{
122 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
123
124 RTCritSectDelete(&g_CritSect);
125
126 size_t i = g_cProcesses;
127 while (i-- > 0)
128 {
129 CloseHandle(g_paProcesses[i].hProcess);
130 g_paProcesses[i].hProcess = NULL;
131 }
132 RTMemFree(g_paProcesses);
133
134 g_paProcesses = NULL;
135 g_cProcesses = 0;
136 g_cProcessesAlloc = 0;
137}
138
139
140/**
141 * Initialize the globals.
142 *
143 * @returns IPRT status code.
144 * @param pvUser1 Ignored.
145 * @param pvUser2 Ignored.
146 */
147static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
148{
149 NOREF(pvUser1); NOREF(pvUser2);
150
151 g_cProcesses = 0;
152 g_cProcessesAlloc = 0;
153 g_paProcesses = NULL;
154 int rc = RTCritSectInit(&g_CritSect);
155 if (RT_SUCCESS(rc))
156 {
157 /** @todo init once, terminate once - this is a generic thing which should
158 * have some kind of static and simpler setup! */
159 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
160 if (RT_SUCCESS(rc))
161 return rc;
162 RTCritSectDelete(&g_CritSect);
163 }
164 return rc;
165}
166
167
168/**
169 * Gets the process handle for a process from g_paProcesses.
170 *
171 * @returns Process handle if found, NULL if not.
172 * @param pid The process to remove (pid).
173 */
174static HANDLE rtProcWinFindPid(RTPROCESS pid)
175{
176 HANDLE hProcess = NULL;
177
178 RTCritSectEnter(&g_CritSect);
179 uint32_t i = g_cProcesses;
180 while (i-- > 0)
181 if (g_paProcesses[i].pid == pid)
182 {
183 hProcess = g_paProcesses[i].hProcess;
184 break;
185 }
186 RTCritSectLeave(&g_CritSect);
187
188 return hProcess;
189}
190
191
192/**
193 * Removes a process from g_paProcesses.
194 *
195 * @param pid The process to remove (pid).
196 */
197static void rtProcWinRemovePid(RTPROCESS pid)
198{
199 RTCritSectEnter(&g_CritSect);
200 uint32_t i = g_cProcesses;
201 while (i-- > 0)
202 if (g_paProcesses[i].pid == pid)
203 {
204 g_cProcesses--;
205 uint32_t cToMove = g_cProcesses - i;
206 if (cToMove)
207 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
208 break;
209 }
210 RTCritSectLeave(&g_CritSect);
211}
212
213
214/**
215 * Adds a process to g_paProcesses.
216 *
217 * @returns IPRT status code.
218 * @param pid The process id.
219 * @param hProcess The process handle.
220 */
221static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
222{
223 RTCritSectEnter(&g_CritSect);
224
225 uint32_t i = g_cProcesses;
226 if (i >= g_cProcessesAlloc)
227 {
228 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
229 if (RT_UNLIKELY(!pvNew))
230 {
231 RTCritSectLeave(&g_CritSect);
232 return VERR_NO_MEMORY;
233 }
234 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
235 g_cProcessesAlloc = i + 16;
236 }
237
238 g_paProcesses[i].pid = pid;
239 g_paProcesses[i].hProcess = hProcess;
240 g_cProcesses = i + 1;
241
242 RTCritSectLeave(&g_CritSect);
243 return VINF_SUCCESS;
244}
245
246
247RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
248{
249 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
250 NULL, NULL, NULL, /* standard handles */
251 NULL /*pszAsUser*/, NULL /* pszPassword*/,
252 pProcess);
253}
254
255
256/**
257 * Get the process token (not the process handle like the name might indicate)
258 * of the process indicated by @a dwPID if the @a pSID matches.
259 *
260 * @returns IPRT status code.
261 *
262 * @param dwPID The process identifier.
263 * @param pSID The secure identifier of the user.
264 * @param phToken Where to return the token handle - duplicate,
265 * caller closes it on success.
266 */
267static int rtProcGetProcessHandle(DWORD dwPID, PSID pSID, PHANDLE phToken)
268{
269 AssertPtr(pSID);
270 AssertPtr(phToken);
271
272 DWORD dwErr;
273 BOOL fRc;
274 bool fFound = false;
275 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPID);
276 if (hProc != NULL)
277 {
278 HANDLE hTokenProc;
279 fRc = OpenProcessToken(hProc,
280 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE
281 | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
282 &hTokenProc);
283 if (fRc)
284 {
285 DWORD dwSize = 0;
286 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
287 if (!fRc)
288 dwErr = GetLastError();
289 if ( !fRc
290 && dwErr == ERROR_INSUFFICIENT_BUFFER
291 && dwSize > 0)
292 {
293 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
294 AssertPtrReturn(pTokenUser, VERR_NO_MEMORY);
295 RT_ZERO(*pTokenUser);
296 if ( GetTokenInformation(hTokenProc,
297 TokenUser,
298 (LPVOID)pTokenUser,
299 dwSize,
300 &dwSize))
301 {
302 if ( IsValidSid(pTokenUser->User.Sid)
303 && EqualSid(pTokenUser->User.Sid, pSID))
304 {
305 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
306 NULL, SecurityIdentification, TokenPrimary, phToken))
307 {
308 /*
309 * So we found the process instance which belongs to the user we want to
310 * to run our new process under. This duplicated token will be used for
311 * the actual CreateProcessAsUserW() call then.
312 */
313 fFound = true;
314 }
315 else
316 dwErr = GetLastError();
317 }
318 }
319 else
320 dwErr = GetLastError();
321 RTMemFree(pTokenUser);
322 }
323 else
324 dwErr = GetLastError();
325 CloseHandle(hTokenProc);
326 }
327 else
328 dwErr = GetLastError();
329 CloseHandle(hProc);
330 }
331 else
332 dwErr = GetLastError();
333 if (fFound)
334 return VINF_SUCCESS;
335 if (dwErr != NO_ERROR)
336 return RTErrConvertFromWin32(dwErr);
337 return VERR_NOT_FOUND; /* No error occured, but we didn't find the right process. */
338}
339
340
341/**
342 * Finds a one of the processes in @a papszNames running with user @a pSID and
343 * returns a duplicate handle to its token.
344 *
345 * @returns Success indicator.
346 * @param papszNames The process candidates, in prioritized order.
347 * @param pSID The secure identifier of the user.
348 * @param phToken Where to return the token handle - duplicate,
349 * caller closes it on success.
350 */
351static bool rtProcFindProcessByName(const char * const *papszNames, PSID pSID, PHANDLE phToken)
352{
353 AssertPtr(papszNames);
354 AssertPtr(pSID);
355 AssertPtr(phToken);
356
357 DWORD dwErr = NO_ERROR;
358 bool fFound = false;
359
360 /*
361 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
362 * and reliable. Fallback to EnumProcess on NT4.
363 */
364 RTLDRMOD hKernel32;
365 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
366 if (RT_SUCCESS(rc))
367 {
368 PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot;
369 rc = RTLdrGetSymbol(hKernel32, "CreateToolhelp32Snapshot", (void**)&pfnCreateToolhelp32Snapshot);
370 if (RT_SUCCESS(rc))
371 {
372 PFNPROCESS32FIRST pfnProcess32First;
373 rc = RTLdrGetSymbol(hKernel32, "Process32First", (void**)&pfnProcess32First);
374 if (RT_SUCCESS(rc))
375 {
376 PFNPROCESS32NEXT pfnProcess32Next;
377 rc = RTLdrGetSymbol(hKernel32, "Process32Next", (void**)&pfnProcess32Next);
378 if (RT_SUCCESS(rc))
379 {
380 HANDLE hSnap = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
381 if (hSnap != INVALID_HANDLE_VALUE)
382 {
383 for (size_t i = 0; papszNames[i] && !fFound; i++)
384 {
385 PROCESSENTRY32 procEntry;
386 procEntry.dwSize = sizeof(PROCESSENTRY32);
387 if (pfnProcess32First(hSnap, &procEntry))
388 {
389 do
390 {
391 if ( _stricmp(procEntry.szExeFile, papszNames[i]) == 0
392 && RT_SUCCESS(rtProcGetProcessHandle(procEntry.th32ProcessID, pSID, phToken)))
393 {
394 fFound = true;
395 break;
396 }
397 } while (pfnProcess32Next(hSnap, &procEntry));
398 }
399 else /* Process32First */
400 dwErr = GetLastError();
401 if (FAILED(dwErr))
402 break;
403 }
404 CloseHandle(hSnap);
405 }
406 else /* hSnap == INVALID_HANDLE_VALUE */
407 dwErr = GetLastError();
408 }
409 }
410 }
411 else /* CreateToolhelp32Snapshot / Toolhelp32 API not available. */
412 {
413 /*
414 * NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
415 * part of the OS) in order to get a lookup. If we don't have this DLL
416 * we are not able to get a token and therefore no UI will be visible.
417 */
418 RTLDRMOD hPSAPI;
419 int rc = RTLdrLoad("PSAPI.dll", &hPSAPI);
420 if (RT_SUCCESS(rc))
421 {
422 PFNENUMPROCESSES pfnEnumProcesses;
423 rc = RTLdrGetSymbol(hPSAPI, "EnumProcesses", (void**)&pfnEnumProcesses);
424 if (RT_SUCCESS(rc))
425 {
426 PFNGETMODULEBASENAME pfnGetModuleBaseName;
427 rc = RTLdrGetSymbol(hPSAPI, "GetModuleBaseName", (void**)&pfnGetModuleBaseName);
428 if (RT_SUCCESS(rc))
429 {
430 /** @todo Retry if pBytesReturned equals cbBytes! */
431 DWORD adwPIDs[4096]; /* Should be sufficient for now. */
432 DWORD cbBytes = 0;
433 if (pfnEnumProcesses(adwPIDs, sizeof(adwPIDs), &cbBytes))
434 {
435 for (size_t i = 0; papszNames[i] && !fFound; i++)
436 {
437 for (DWORD dwIdx = 0; dwIdx < cbBytes/sizeof(DWORD) && !fFound; dwIdx++)
438 {
439 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
440 FALSE, adwPIDs[dwIdx]);
441 if (hProc)
442 {
443 char *pszProcName = NULL;
444 DWORD dwSize = 128;
445 do
446 {
447 RTMemRealloc(pszProcName, dwSize);
448 if (pfnGetModuleBaseName(hProc, 0, pszProcName, dwSize) == dwSize)
449 dwSize += 128;
450 if (dwSize > _4K) /* Play safe. */
451 break;
452 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
453
454 if (pszProcName)
455 {
456 if ( _stricmp(pszProcName, papszNames[i]) == 0
457 && RT_SUCCESS(rtProcGetProcessHandle(adwPIDs[dwIdx], pSID, phToken)))
458 {
459 fFound = true;
460 }
461 }
462 if (pszProcName)
463 RTStrFree(pszProcName);
464 CloseHandle(hProc);
465 }
466 }
467 }
468 }
469 else
470 dwErr = GetLastError();
471 }
472 }
473 }
474 }
475 RTLdrClose(hKernel32);
476 }
477 Assert(dwErr == NO_ERROR);
478 return fFound;
479}
480
481
482static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
483 PRTUTF16 pwszzBlock, DWORD dwCreationFlags,
484 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
485{
486 int rc = VINF_SUCCESS;
487 BOOL fRc = FALSE;
488 DWORD dwErr = NO_ERROR;
489
490 /*
491 * If we run as a service CreateProcessWithLogon will fail,
492 * so don't even try it (because of Local System context).
493 */
494 if (!(fFlags & RTPROC_FLAGS_SERVICE))
495 {
496 RTLDRMOD hAdvAPI32;
497 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
498 if (RT_SUCCESS(rc))
499 {
500 /*
501 * This may fail on too old (NT4) platforms or if the calling process
502 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
503 * platforms (however, this works on W2K!).
504 */
505 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
506 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
507 if (RT_SUCCESS(rc))
508 {
509 fRc = pfnCreateProcessWithLogonW(pwszUser,
510 NULL, /* lpDomain*/
511 pwszPassword,
512 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
513 pwszExec,
514 pwszCmdLine,
515 dwCreationFlags,
516 pwszzBlock,
517 NULL, /* pCurrentDirectory */
518 pStartupInfo,
519 pProcInfo);
520 if (!fRc)
521 dwErr = GetLastError();
522 }
523 RTLdrClose(hAdvAPI32);
524 }
525 }
526
527 /*
528 * Did the API call above fail because we're running on a too old OS (NT4) or
529 * we're running as a Windows service?
530 */
531 if ( RT_FAILURE(rc)
532 || (fFlags & RTPROC_FLAGS_SERVICE))
533 {
534 /*
535 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
536 * we have to do the following:
537 * - Check the credentials supplied and get the user SID.
538 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
539 * user. This of course is only possible if that user is logged in (over
540 * physical console or terminal services).
541 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
542 * use it in order to allow the newly started process acess the user's
543 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
544 * process (but run it without UI).
545 *
546 * The following restrictions apply:
547 * - A process only can show its UI when the user the process should run
548 * under is logged in (has a desktop).
549 * - We do not want to display a process of user A run on the desktop
550 * of user B on multi session systems.
551 *
552 * The following rights are needed in order to use LogonUserW and
553 * CreateProcessAsUserW, so the local policy has to be modified to:
554 * - SE_TCB_NAME = Act as part of the operating system
555 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
556 * - SE_INCREASE_QUOTA_NAME
557 *
558 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
559 */
560 PHANDLE phToken = NULL;
561 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
562 fRc = LogonUserW(pwszUser,
563 /*
564 * Because we have to deal with http://support.microsoft.com/kb/245683
565 * for NULL domain names when running on NT4 here, pass an empty string if so.
566 * However, passing FQDNs should work!
567 */
568 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
569 ? L"" /* NT4 and older. */
570 : NULL, /* Windows 2000 and up. */
571 pwszPassword,
572 LOGON32_LOGON_INTERACTIVE,
573 LOGON32_PROVIDER_DEFAULT,
574 &hTokenLogon);
575
576 bool fFound = false;
577 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
578 if (fRc)
579 {
580 if (fFlags & RTPROC_FLAGS_SERVICE)
581 {
582 DWORD cbName = 0; /* Must be zero to query size! */
583 DWORD cbDomain = 0;
584 SID_NAME_USE sidNameUse = SidTypeUser;
585 fRc = LookupAccountNameW(NULL,
586 pwszUser,
587 NULL,
588 &cbName,
589 NULL,
590 &cbDomain,
591 &sidNameUse);
592 if (!fRc)
593 dwErr = GetLastError();
594 if ( !fRc
595 && dwErr == ERROR_INSUFFICIENT_BUFFER
596 && cbName > 0)
597 {
598 dwErr = NO_ERROR;
599
600 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
601 AssertPtrReturn(pSID, VERR_NO_MEMORY);
602
603 /** @todo No way to allocate a PRTUTF16 directly? */
604 PRTUTF16 pwszDomain = NULL;
605 if (cbDomain > 0)
606 {
607 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
608 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
609 }
610
611 /* Note: Also supports FQDNs! */
612 if ( LookupAccountNameW(NULL, /* lpSystemName */
613 pwszUser,
614 pSID,
615 &cbName,
616 pwszDomain,
617 &cbDomain,
618 &sidNameUse)
619 && IsValidSid(pSID))
620 {
621 /* Array of process names we want to look for. */
622 static const char * const s_papszProcNames[] =
623 {
624#ifdef VBOX /* The explorer entry is a fallback in case GA aren't installed. */
625 { "VBoxTray.exe" },
626#endif
627 { "explorer.exe" },
628 NULL
629 };
630 fFound = rtProcFindProcessByName(s_papszProcNames, pSID, &hTokenUserDesktop);
631 }
632 else
633 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
634 RTMemFree(pSID);
635 if (pwszDomain != NULL)
636 RTUtf16Free(pwszDomain);
637 }
638 }
639 else /* !RTPROC_FLAGS_SERVICE */
640 {
641 /* Nothing to do here right now. */
642 }
643
644 /*
645 * If we didn't find a matching VBoxTray, just use the token we got
646 * above from LogonUserW(). This enables us to at least run processes with
647 * desktop interaction without UI.
648 */
649 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
650
651 /*
652 * Useful KB articles:
653 * http://support.microsoft.com/kb/165194/
654 * http://support.microsoft.com/kb/184802/
655 * http://support.microsoft.com/kb/327618/
656 */
657 fRc = CreateProcessAsUserW(*phToken,
658 pwszExec,
659 pwszCmdLine,
660 NULL, /* pProcessAttributes */
661 NULL, /* pThreadAttributes */
662 TRUE, /* fInheritHandles */
663 dwCreationFlags,
664 pwszzBlock,
665 NULL, /* pCurrentDirectory */
666 pStartupInfo,
667 pProcInfo);
668 if (fRc)
669 dwErr = NO_ERROR;
670 else
671 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
672
673 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
674 CloseHandle(hTokenUserDesktop);
675 CloseHandle(hTokenLogon);
676 }
677 else
678 dwErr = GetLastError(); /* LogonUserW() failed. */
679 }
680
681 if (dwErr != NO_ERROR)
682 {
683 /*
684 * Map some important or much used Windows error codes
685 * to our error codes.
686 */
687 switch (dwErr)
688 {
689 case ERROR_NOACCESS:
690 case ERROR_PRIVILEGE_NOT_HELD:
691 rc = VERR_PERMISSION_DENIED;
692 break;
693
694 case ERROR_PASSWORD_EXPIRED:
695 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
696 rc = VERR_AUTHENTICATION_FAILURE;
697 break;
698
699 default:
700 /* Could trigger a debug assertion! */
701 rc = RTErrConvertFromWin32(dwErr);
702 break;
703 }
704 }
705 else
706 rc = VINF_SUCCESS;
707 return rc;
708}
709
710RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
711 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
712 const char *pszPassword, PRTPROCESS phProcess)
713{
714 /*
715 * Input validation
716 */
717 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
718 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
719 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
720 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
721 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
722 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
723 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
724 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
725 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
726 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
727 /** @todo search the PATH (add flag for this). */
728
729 /*
730 * Initialize the globals.
731 */
732 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
733 AssertRCReturn(rc, rc);
734
735 /*
736 * Get the file descriptors for the handles we've been passed.
737 *
738 * It seems there is no point in trying to convince a child process's CRT
739 * that any of the standard file handles is non-TEXT. So, we don't...
740 */
741 STARTUPINFOW StartupInfo;
742 RT_ZERO(StartupInfo);
743 StartupInfo.cb = sizeof(StartupInfo);
744 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
745#if 1 /* The CRT should keep the standard handles up to date. */
746 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
747 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
748 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
749#else
750 StartupInfo.hStdInput = _get_osfhandle(0);
751 StartupInfo.hStdOutput = _get_osfhandle(1);
752 StartupInfo.hStdError = _get_osfhandle(2);
753#endif
754 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
755 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
756 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
757 for (int i = 0; i < 3; i++)
758 {
759 if (paHandles[i])
760 {
761 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
762 switch (paHandles[i]->enmType)
763 {
764 case RTHANDLETYPE_FILE:
765 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
766 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
767 : INVALID_HANDLE_VALUE;
768 break;
769
770 case RTHANDLETYPE_PIPE:
771 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
772 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
773 : INVALID_HANDLE_VALUE;
774 break;
775
776 case RTHANDLETYPE_SOCKET:
777 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
778 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
779 : INVALID_HANDLE_VALUE;
780 break;
781
782 default:
783 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
784 }
785
786 /* Get the inheritability of the handle. */
787 if (*aphStds[i] != INVALID_HANDLE_VALUE)
788 {
789 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
790 {
791 rc = RTErrConvertFromWin32(GetLastError());
792 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
793 }
794 }
795 }
796 }
797
798 /*
799 * Set the inheritability any handles we're handing the child.
800 */
801 rc = VINF_SUCCESS;
802 for (int i = 0; i < 3; i++)
803 if ( (afInhStds[i] != 0xffffffff)
804 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
805 {
806 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
807 {
808 rc = RTErrConvertFromWin32(GetLastError());
809 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
810 }
811 }
812
813 /*
814 * Create the environment block, command line and convert the executable
815 * name.
816 */
817 PRTUTF16 pwszzBlock;
818 if (RT_SUCCESS(rc))
819 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
820 if (RT_SUCCESS(rc))
821 {
822 PRTUTF16 pwszCmdLine;
823 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
824 if (RT_SUCCESS(rc))
825 {
826 PRTUTF16 pwszExec;
827 rc = RTStrToUtf16(pszExec, &pwszExec);
828 if (RT_SUCCESS(rc))
829 {
830 /*
831 * Get going...
832 */
833 PROCESS_INFORMATION ProcInfo;
834 RT_ZERO(ProcInfo);
835 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
836 if (fFlags & RTPROC_FLAGS_DETACHED)
837 dwCreationFlags |= DETACHED_PROCESS;
838
839 /*
840 * Only use the normal CreateProcess stuff if we have no user name
841 * and we are not running from a (Windows) service. Otherwise use
842 * the more advanced version in rtProcCreateAsUserHlp().
843 */
844 if ( pszAsUser == NULL
845 && !(fFlags & RTPROC_FLAGS_SERVICE))
846 {
847 if (CreateProcessW(pwszExec,
848 pwszCmdLine,
849 NULL, /* pProcessAttributes */
850 NULL, /* pThreadAttributes */
851 TRUE, /* fInheritHandles */
852 dwCreationFlags,
853 pwszzBlock,
854 NULL, /* pCurrentDirectory */
855 &StartupInfo,
856 &ProcInfo))
857 rc = VINF_SUCCESS;
858 else
859 rc = RTErrConvertFromWin32(GetLastError());
860 }
861 else
862 {
863 /*
864 * Convert the additional parameters and use a helper
865 * function to do the actual work.
866 */
867 PRTUTF16 pwszUser;
868 rc = RTStrToUtf16(pszAsUser, &pwszUser);
869 if (RT_SUCCESS(rc))
870 {
871 PRTUTF16 pwszPassword;
872 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
873 if (RT_SUCCESS(rc))
874 {
875 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
876 pwszExec, pwszCmdLine, pwszzBlock, dwCreationFlags,
877 &StartupInfo, &ProcInfo, fFlags);
878
879 RTUtf16Free(pwszPassword);
880 }
881 RTUtf16Free(pwszUser);
882 }
883 }
884 if (RT_SUCCESS(rc))
885 {
886 CloseHandle(ProcInfo.hThread);
887 if (phProcess)
888 {
889 /*
890 * Add the process to the child process list so
891 * RTProcWait can reuse and close the process handle.
892 */
893 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
894 *phProcess = ProcInfo.dwProcessId;
895 }
896 else
897 CloseHandle(ProcInfo.hProcess);
898 rc = VINF_SUCCESS;
899 }
900 RTUtf16Free(pwszExec);
901 }
902 RTUtf16Free(pwszCmdLine);
903 }
904 RTEnvFreeUtf16Block(pwszzBlock);
905 }
906
907 /* Undo any handle inherit changes. */
908 for (int i = 0; i < 3; i++)
909 if ( (afInhStds[i] != 0xffffffff)
910 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
911 {
912 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
913 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
914 }
915
916 return rc;
917}
918
919
920
921RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
922{
923 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
924 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
925 AssertRCReturn(rc, rc);
926
927 /*
928 * Try find the process among the ones we've spawned, otherwise, attempt
929 * opening the specified process.
930 */
931 HANDLE hProcess = rtProcWinFindPid(Process);
932 if (hProcess == NULL)
933 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
934 if (hProcess != NULL)
935 {
936 /*
937 * Wait for it to terminate.
938 */
939 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
940 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
941 while (WaitRc == WAIT_IO_COMPLETION)
942 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
943 switch (WaitRc)
944 {
945 /*
946 * It has terminated.
947 */
948 case WAIT_OBJECT_0:
949 {
950 DWORD dwExitCode;
951 if (GetExitCodeProcess(hProcess, &dwExitCode))
952 {
953 /** @todo the exit code can be special statuses. */
954 if (pProcStatus)
955 {
956 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
957 pProcStatus->iStatus = (int)dwExitCode;
958 }
959 rtProcWinRemovePid(Process);
960 return VINF_SUCCESS;
961 }
962 break;
963 }
964
965 /*
966 * It hasn't terminated just yet.
967 */
968 case WAIT_TIMEOUT:
969 return VERR_PROCESS_RUNNING;
970
971 /*
972 * Something went wrong...
973 */
974 case WAIT_FAILED:
975 break;
976 case WAIT_ABANDONED:
977 AssertFailed();
978 return VERR_GENERAL_FAILURE;
979 default:
980 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
981 return VERR_GENERAL_FAILURE;
982 }
983 }
984 DWORD dwErr = GetLastError();
985 if (dwErr == ERROR_INVALID_PARAMETER)
986 return VERR_PROCESS_NOT_FOUND;
987 return RTErrConvertFromWin32(dwErr);
988}
989
990
991RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
992{
993 /** @todo this isn't quite right. */
994 return RTProcWait(Process, fFlags, pProcStatus);
995}
996
997
998RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
999{
1000 int rc = VINF_SUCCESS;
1001 HANDLE hProcess = rtProcWinFindPid(Process);
1002 if (hProcess != NULL)
1003 {
1004 if (!TerminateProcess(hProcess, 127))
1005 rc = RTErrConvertFromWin32(GetLastError());
1006 }
1007 else
1008 {
1009 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
1010 if (hProcess != NULL)
1011 {
1012 BOOL fRc = TerminateProcess(hProcess, 127);
1013 DWORD dwErr = GetLastError();
1014 CloseHandle(hProcess);
1015 if (!fRc)
1016 rc = RTErrConvertFromWin32(dwErr);
1017 }
1018 }
1019 return rc;
1020}
1021
1022
1023RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
1024{
1025 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
1026 DWORD_PTR dwSystemAffinityMask;
1027
1028 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
1029 Assert(fRc);
1030
1031 return dwProcessAffinityMask;
1032}
1033
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