VirtualBox

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

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

IPRT: Added RTPROC_FLAGS_SERVICE for code path handling when used with a (Windows) service, added first support for session 0 isolation on Windows Vista+.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 27.3 KB
Line 
1/* $Id: process-win.cpp 29289 2010-05-10 09:38:30Z 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 <process.h>
36#include <errno.h>
37
38#include <iprt/process.h>
39#include "internal/iprt.h"
40
41#include <iprt/assert.h>
42#include <iprt/critsect.h>
43#include <iprt/file.h>
44#include <iprt/err.h>
45#include <iprt/env.h>
46#include <iprt/getopt.h>
47#include <iprt/initterm.h>
48#include <iprt/ldr.h>
49#include <iprt/mem.h>
50#include <iprt/once.h>
51#include <iprt/pipe.h>
52#include <iprt/string.h>
53#include <iprt/socket.h>
54
55
56/*******************************************************************************
57* Structures and Typedefs *
58*******************************************************************************/
59typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
60 LPCWSTR,
61 LPCWSTR,
62 DWORD,
63 LPCWSTR,
64 LPWSTR,
65 DWORD,
66 LPVOID,
67 LPCWSTR,
68 LPSTARTUPINFOW,
69 LPPROCESS_INFORMATION);
70typedef FNCREATEPROCESSWITHLOGON *PFNCREATEPROCESSWITHLOGON;
71
72typedef DWORD WINAPI FNWTSGETACTIVECONSOLESESSIONID();
73typedef FNWTSGETACTIVECONSOLESESSIONID *PFNWTSGETACTIVECONSOLESESSIONID;
74
75typedef BOOL WINAPI FNWTSQUERYUSERTOKEN(ULONG, PHANDLE);
76typedef FNWTSQUERYUSERTOKEN *PFNWTSQUERYUSERTOKEN;
77
78
79/*******************************************************************************
80* Global Variables *
81*******************************************************************************/
82/** Init once structure. */
83static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
84/** Critical section protecting the process array. */
85static RTCRITSECT g_CritSect;
86/** The number of processes in the array. */
87static uint32_t g_cProcesses;
88/** The current allocation size. */
89static uint32_t g_cProcessesAlloc;
90/** Array containing the live or non-reaped child processes. */
91static struct RTPROCWINENTRY
92{
93 /** The process ID. */
94 ULONG_PTR pid;
95 /** The process handle. */
96 HANDLE hProcess;
97} *g_paProcesses;
98
99
100/**
101 * Clean up the globals.
102 *
103 * @param enmReason Ignored.
104 * @param iStatus Ignored.
105 * @param pvUser Ignored.
106 */
107static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
108{
109 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
110
111 RTCritSectDelete(&g_CritSect);
112
113 size_t i = g_cProcesses;
114 while (i-- > 0)
115 {
116 CloseHandle(g_paProcesses[i].hProcess);
117 g_paProcesses[i].hProcess = NULL;
118 }
119 RTMemFree(g_paProcesses);
120
121 g_paProcesses = NULL;
122 g_cProcesses = 0;
123 g_cProcessesAlloc = 0;
124}
125
126
127/**
128 * Initialize the globals.
129 *
130 * @returns IPRT status code.
131 * @param pvUser1 Ignored.
132 * @param pvUser2 Ignored.
133 */
134static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
135{
136 NOREF(pvUser1); NOREF(pvUser2);
137
138 g_cProcesses = 0;
139 g_cProcessesAlloc = 0;
140 g_paProcesses = NULL;
141 int rc = RTCritSectInit(&g_CritSect);
142 if (RT_SUCCESS(rc))
143 {
144 /** @todo init once, terminate once - this is a generic thing which should
145 * have some kind of static and simpler setup! */
146 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
147 if (RT_SUCCESS(rc))
148 return rc;
149 RTCritSectDelete(&g_CritSect);
150 }
151 return rc;
152}
153
154
155/**
156 * Gets the process handle for a process from g_paProcesses.
157 *
158 * @returns Process handle if found, NULL if not.
159 * @param pid The process to remove (pid).
160 */
161static HANDLE rtProcWinFindPid(RTPROCESS pid)
162{
163 HANDLE hProcess = NULL;
164
165 RTCritSectEnter(&g_CritSect);
166 uint32_t i = g_cProcesses;
167 while (i-- > 0)
168 if (g_paProcesses[i].pid == pid)
169 {
170 hProcess = g_paProcesses[i].hProcess;
171 break;
172 }
173 RTCritSectLeave(&g_CritSect);
174
175 return hProcess;
176}
177
178
179/**
180 * Removes a process from g_paProcesses.
181 *
182 * @param pid The process to remove (pid).
183 */
184static void rtProcWinRemovePid(RTPROCESS pid)
185{
186 RTCritSectEnter(&g_CritSect);
187 uint32_t i = g_cProcesses;
188 while (i-- > 0)
189 if (g_paProcesses[i].pid == pid)
190 {
191 g_cProcesses--;
192 uint32_t cToMove = g_cProcesses - i;
193 if (cToMove)
194 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
195 break;
196 }
197 RTCritSectLeave(&g_CritSect);
198}
199
200
201/**
202 * Adds a process to g_paProcesses.
203 *
204 * @returns IPRT status code.
205 * @param pid The process id.
206 * @param hProcess The process handle.
207 */
208static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
209{
210 RTCritSectEnter(&g_CritSect);
211
212 uint32_t i = g_cProcesses;
213 if (i >= g_cProcessesAlloc)
214 {
215 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
216 if (RT_UNLIKELY(!pvNew))
217 {
218 RTCritSectLeave(&g_CritSect);
219 return VERR_NO_MEMORY;
220 }
221 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
222 g_cProcessesAlloc = i + 16;
223 }
224
225 g_paProcesses[i].pid = pid;
226 g_paProcesses[i].hProcess = hProcess;
227 g_cProcesses = i + 1;
228
229 RTCritSectLeave(&g_CritSect);
230 return VINF_SUCCESS;
231}
232
233
234RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
235{
236 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
237 NULL, NULL, NULL, /* standard handles */
238 NULL /*pszAsUser*/, NULL /* pszPassword*/,
239 pProcess);
240}
241
242
243static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
244 PRTUTF16 pwszzBlock, DWORD dwCreationFlags,
245 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
246{
247 HANDLE hToken = INVALID_HANDLE_VALUE;
248 BOOL fRc = FALSE;
249#ifndef IPRT_TARGET_NT4
250 /*
251 * Get the session ID.
252 */
253 DWORD dwSessionID = 0; /* On W2K the session ID is always 0 (does not have fast user switching). */
254 RTLDRMOD hKernel32;
255 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
256 if (RT_SUCCESS(rc))
257 {
258 PFNWTSGETACTIVECONSOLESESSIONID pfnWTSGetActiveConsoleSessionId;
259 rc = RTLdrGetSymbol(hKernel32, "WTSGetActiveConsoleSessionId", (void **)&pfnWTSGetActiveConsoleSessionId);
260 if (RT_SUCCESS(rc))
261 {
262 /*
263 * Console session means the session which the physical keyboard and mouse
264 * is connected to. Due to FUS (fast user switching) starting with Windows XP
265 * this can be a different session than 0.
266 */
267 dwSessionID = pfnWTSGetActiveConsoleSessionId(); /* Get active console session ID. */
268 }
269 RTLdrClose(hKernel32);
270 }
271
272 if (fFlags & RTPROC_FLAGS_SERVICE)
273 {
274 /*
275 * Get the current user token.
276 */
277 RTLDRMOD hWtsAPI32;
278 rc = RTLdrLoad("Wtsapi32.dll", &hWtsAPI32);
279 if (RT_SUCCESS(rc))
280 {
281 /* Note that WTSQueryUserToken() only is available on Windows XP and up! */
282 PFNWTSQUERYUSERTOKEN pfnWTSQueryUserToken;
283 rc = RTLdrGetSymbol(hWtsAPI32, "WTSQueryUserToken", (void **)&pfnWTSQueryUserToken);
284 if (RT_SUCCESS(rc))
285 fRc = pfnWTSQueryUserToken(dwSessionID, &hToken);
286 RTLdrClose(hWtsAPI32);
287 }
288 }
289#endif /* !IPRT_TARGET_NT4 */
290
291 DWORD dwErr = NO_ERROR;
292 /*
293 * If not run by a service, use the normal LogonUserW function in order
294 * to run the child process with different credentials using the returned
295 * primary token.
296 */
297 if (!(fFlags & RTPROC_FLAGS_SERVICE))
298 {
299 /*
300 * The following rights are needed in order to use LogonUserW and
301 * CreateProcessAsUserW, so the local policy has to be modified to:
302 * - SE_TCB_NAME = Act as part of the operating system
303 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
304 * - SE_INCREASE_QUOTA_NAME
305 *
306 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
307 */
308 fRc = LogonUserW(pwszUser,
309 NULL, /* lpDomain */
310 pwszPassword,
311 LOGON32_LOGON_INTERACTIVE,
312 LOGON32_PROVIDER_DEFAULT,
313 &hToken);
314 }
315
316 if (fRc)
317 {
318#ifndef IPRT_TARGET_NT4
319 HANDLE hDuplicatedToken = INVALID_HANDLE_VALUE;
320 HANDLE hProcessToken = INVALID_HANDLE_VALUE;
321
322 /*
323 * If used by a service, open this service process and adjust
324 * privileges by enabling the SE_TCB_NAME right to act as part
325 * of the operating system.
326 */
327 if (fFlags & RTPROC_FLAGS_SERVICE)
328 {
329 TOKEN_PRIVILEGES privToken, privTokenOld;
330 DWORD dwTokenSize;
331
332 fRc = OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hProcessToken);
333 if ( fRc
334 && LookupPrivilegeValue(NULL, SE_TCB_NAME, &privToken.Privileges[0].Luid))
335 {
336 privToken.PrivilegeCount = 1;
337 privToken.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
338 fRc = AdjustTokenPrivileges(hProcessToken, FALSE, &privToken, sizeof(privToken), &privTokenOld, &dwTokenSize);
339 if (!fRc)
340 dwErr = GetLastError();
341 }
342 }
343
344 if ( dwErr == ERROR_SUCCESS
345 || dwErr == ERROR_NOT_ALL_ASSIGNED) /* The AdjustTokenPrivileges() did not succeed, but this isn't fatal. */
346 {
347 fRc = DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary, &hDuplicatedToken);
348#if 0
349 /*
350 * Assign the current session ID (> 0 on Windows Vista and up) to the primary token we just duplicated.
351 ** @todo This does not work yet, needs more investigation! */
352 */
353 if (fRc)
354 {
355 fRc = SetTokenInformation(hDuplicatedToken, TokenSessionId, &dwSessionID, sizeof(DWORD));
356 if (!fRc)
357 dwErr = GetLastError();
358 }
359 else
360 dwErr = GetLastError();
361#endif
362 }
363
364#endif /* !IPRT_TARGET_NT4 */
365
366 /** @todo On NT4 we need to enable the SeTcbPrivilege to act as part of the operating system. Otherwise
367 * we will get error 1314 (priviledge not held) as a response. */
368
369 /* Hopefully having a valid token now! */
370 if ( fRc
371#ifdef IPRT_TARGET_NT4
372 && hToken != INVALID_HANDLE_VALUE
373#else
374 && hDuplicatedToken != INVALID_HANDLE_VALUE
375#endif /* IPRT_TARGET_NT4 */
376 )
377 {
378 fRc = CreateProcessAsUserW(
379#ifdef IPRT_TARGET_NT4
380 hToken,
381#else
382 hDuplicatedToken,
383#endif /* !IPRT_TARGET_NT4 */
384 pwszExec,
385 pwszCmdLine,
386 NULL, /* pProcessAttributes */
387 NULL, /* pThreadAttributes */
388 TRUE, /* fInheritHandles */
389 dwCreationFlags,
390 pwszzBlock,
391 NULL, /* pCurrentDirectory */
392 pStartupInfo,
393 pProcInfo);
394 if (!fRc)
395 dwErr = GetLastError();
396#ifndef IPRT_TARGET_NT4
397 CloseHandle(hDuplicatedToken);
398#endif /* !IPRT_TARGET_NT4 */
399 }
400 CloseHandle(hToken);
401#ifndef IPRT_TARGET_NT4
402 if (fFlags & RTPROC_FLAGS_SERVICE)
403 {
404 /** @todo Drop SE_TCB_NAME priviledge before closing the process handle! */
405 if (hProcessToken != INVALID_HANDLE_VALUE)
406 CloseHandle(hProcessToken);
407 }
408#endif /* !IPRT_TARGET_NT4 */
409 }
410 else
411 dwErr = GetLastError();
412
413#ifndef IPRT_TARGET_NT4
414 /*
415 * If we don't hold enough priviledges to spawn a new process with
416 * different credentials we have to use CreateProcessWithLogonW here. This
417 * API is W2K+ and uses a system service for spawning the process.
418 */
419 /** @todo Use fFlags to either use this feature or just fail. */
420 if (dwErr == ERROR_PRIVILEGE_NOT_HELD)
421 {
422 RTLDRMOD hAdvAPI32;
423 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
424 if (RT_SUCCESS(rc))
425 {
426 /* This may fail on too old (NT4) platforms. */
427 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
428 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
429 if (RT_SUCCESS(rc))
430 {
431 fRc = pfnCreateProcessWithLogonW(pwszUser,
432 NULL, /* lpDomain*/
433 pwszPassword,
434 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
435 pwszExec,
436 pwszCmdLine,
437 dwCreationFlags,
438 pwszzBlock,
439 NULL, /* pCurrentDirectory */
440 pStartupInfo,
441 pProcInfo);
442 if (fRc)
443 dwErr = NO_ERROR;
444 else
445 dwErr = GetLastError();
446 }
447 RTLdrClose(hAdvAPI32);
448 }
449 }
450#endif /* !IPRT_TARGET_NT4 */
451
452 if (dwErr != NO_ERROR)
453 rc = RTErrConvertFromWin32(dwErr);
454 else
455 rc = VINF_SUCCESS;
456 return rc;
457}
458
459RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
460 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
461 const char *pszPassword, PRTPROCESS phProcess)
462{
463 /*
464 * Input validation
465 */
466 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
467 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
468 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
469 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
470 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
471 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
472 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
473 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
474 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
475 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
476 /** @todo search the PATH (add flag for this). */
477
478 /*
479 * Initialize the globals.
480 */
481 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
482 AssertRCReturn(rc, rc);
483
484 /*
485 * Get the file descriptors for the handles we've been passed.
486 *
487 * It seems there is no point in trying to convince a child process's CRT
488 * that any of the standard file handles is non-TEXT. So, we don't...
489 */
490 STARTUPINFOW StartupInfo;
491 RT_ZERO(StartupInfo);
492 StartupInfo.cb = sizeof(StartupInfo);
493 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
494#if 1 /* The CRT should keep the standard handles up to date. */
495 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
496 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
497 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
498#else
499 StartupInfo.hStdInput = _get_osfhandle(0);
500 StartupInfo.hStdOutput = _get_osfhandle(1);
501 StartupInfo.hStdError = _get_osfhandle(2);
502#endif
503 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
504 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
505 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
506 for (int i = 0; i < 3; i++)
507 {
508 if (paHandles[i])
509 {
510 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
511 switch (paHandles[i]->enmType)
512 {
513 case RTHANDLETYPE_FILE:
514 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
515 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
516 : INVALID_HANDLE_VALUE;
517 break;
518
519 case RTHANDLETYPE_PIPE:
520 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
521 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
522 : INVALID_HANDLE_VALUE;
523 break;
524
525 case RTHANDLETYPE_SOCKET:
526 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
527 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
528 : INVALID_HANDLE_VALUE;
529 break;
530
531 default:
532 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
533 }
534
535 /* Get the inheritability of the handle. */
536 if (*aphStds[i] != INVALID_HANDLE_VALUE)
537 {
538 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
539 {
540 rc = RTErrConvertFromWin32(GetLastError());
541 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
542 }
543 }
544 }
545 }
546
547 /*
548 * Set the inheritability any handles we're handing the child.
549 */
550 rc = VINF_SUCCESS;
551 for (int i = 0; i < 3; i++)
552 if ( (afInhStds[i] != 0xffffffff)
553 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
554 {
555 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
556 {
557 rc = RTErrConvertFromWin32(GetLastError());
558 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
559 }
560 }
561
562 /*
563 * Create the environment block, command line and convert the executable
564 * name.
565 */
566 PRTUTF16 pwszzBlock;
567 if (RT_SUCCESS(rc))
568 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
569 if (RT_SUCCESS(rc))
570 {
571 PRTUTF16 pwszCmdLine;
572 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
573 if (RT_SUCCESS(rc))
574 {
575 PRTUTF16 pwszExec;
576 rc = RTStrToUtf16(pszExec, &pwszExec);
577 if (RT_SUCCESS(rc))
578 {
579 /*
580 * Get going...
581 */
582 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
583 if (fFlags & RTPROC_FLAGS_DETACHED)
584 dwCreationFlags |= DETACHED_PROCESS;
585
586 PROCESS_INFORMATION ProcInfo;
587 RT_ZERO(ProcInfo);
588 if (pszAsUser == NULL)
589 {
590 if (CreateProcessW(pwszExec,
591 pwszCmdLine,
592 NULL, /* pProcessAttributes */
593 NULL, /* pThreadAttributes */
594 TRUE, /* fInheritHandles */
595 dwCreationFlags,
596 pwszzBlock,
597 NULL, /* pCurrentDirectory */
598 &StartupInfo,
599 &ProcInfo))
600 rc = VINF_SUCCESS;
601 else
602 rc = RTErrConvertFromWin32(GetLastError());
603 }
604 else
605 {
606 /*
607 * Convert the additional parameters and use a helper
608 * function to do the actual work.
609 */
610 PRTUTF16 pwszUser;
611 rc = RTStrToUtf16(pszAsUser, &pwszUser);
612 if (RT_SUCCESS(rc))
613 {
614 PRTUTF16 pwszPassword;
615 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
616 if (RT_SUCCESS(rc))
617 {
618 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
619 pwszExec, pwszCmdLine, pwszzBlock, dwCreationFlags,
620 &StartupInfo, &ProcInfo, fFlags);
621
622 RTUtf16Free(pwszPassword);
623 }
624 RTUtf16Free(pwszUser);
625 }
626 }
627 if (RT_SUCCESS(rc))
628 {
629 CloseHandle(ProcInfo.hThread);
630 if (phProcess)
631 {
632 /*
633 * Add the process to the child process list so
634 * RTProcWait can reuse and close the process handle.
635 */
636 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
637 *phProcess = ProcInfo.dwProcessId;
638 }
639 else
640 CloseHandle(ProcInfo.hProcess);
641 rc = VINF_SUCCESS;
642 }
643 RTUtf16Free(pwszExec);
644 }
645 RTUtf16Free(pwszCmdLine);
646 }
647 RTEnvFreeUtf16Block(pwszzBlock);
648 }
649
650 /* Undo any handle inherit changes. */
651 for (int i = 0; i < 3; i++)
652 if ( (afInhStds[i] != 0xffffffff)
653 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
654 {
655 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
656 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
657 }
658
659 return rc;
660}
661
662
663
664RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
665{
666 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
667 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
668 AssertRCReturn(rc, rc);
669
670 /*
671 * Try find the process among the ones we've spawned, otherwise, attempt
672 * opening the specified process.
673 */
674 HANDLE hProcess = rtProcWinFindPid(Process);
675 if (hProcess == NULL)
676 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
677 if (hProcess != NULL)
678 {
679 /*
680 * Wait for it to terminate.
681 */
682 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
683 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
684 while (WaitRc == WAIT_IO_COMPLETION)
685 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
686 switch (WaitRc)
687 {
688 /*
689 * It has terminated.
690 */
691 case WAIT_OBJECT_0:
692 {
693 DWORD dwExitCode;
694 if (GetExitCodeProcess(hProcess, &dwExitCode))
695 {
696 /** @todo the exit code can be special statuses. */
697 if (pProcStatus)
698 {
699 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
700 pProcStatus->iStatus = (int)dwExitCode;
701 }
702 rtProcWinRemovePid(Process);
703 return VINF_SUCCESS;
704 }
705 break;
706 }
707
708 /*
709 * It hasn't terminated just yet.
710 */
711 case WAIT_TIMEOUT:
712 return VERR_PROCESS_RUNNING;
713
714 /*
715 * Something went wrong...
716 */
717 case WAIT_FAILED:
718 break;
719 case WAIT_ABANDONED:
720 AssertFailed();
721 return VERR_GENERAL_FAILURE;
722 default:
723 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
724 return VERR_GENERAL_FAILURE;
725 }
726 }
727 DWORD dwErr = GetLastError();
728 if (dwErr == ERROR_INVALID_PARAMETER)
729 return VERR_PROCESS_NOT_FOUND;
730 return RTErrConvertFromWin32(dwErr);
731}
732
733
734RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
735{
736 /** @todo this isn't quite right. */
737 return RTProcWait(Process, fFlags, pProcStatus);
738}
739
740
741RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
742{
743 int rc = VINF_SUCCESS;
744 HANDLE hProcess = rtProcWinFindPid(Process);
745 if (hProcess != NULL)
746 {
747 if (!TerminateProcess(hProcess, 127))
748 rc = RTErrConvertFromWin32(GetLastError());
749 }
750 else
751 {
752 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
753 if (hProcess != NULL)
754 {
755 BOOL fRc = TerminateProcess(hProcess, 127);
756 DWORD dwErr = GetLastError();
757 CloseHandle(hProcess);
758 if (!fRc)
759 rc = RTErrConvertFromWin32(dwErr);
760 }
761 }
762 return rc;
763}
764
765
766RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
767{
768 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
769 DWORD_PTR dwSystemAffinityMask;
770
771 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
772 Assert(fRc);
773
774 return dwProcessAffinityMask;
775}
776
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