VirtualBox

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

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

IPRT/process-win: More generic way for starting processes from a service (no NT4 yet).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 33.7 KB
Line 
1/* $Id: process-win.cpp 29716 2010-05-21 08:31:19Z 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 BOOL WINAPI FNWTSQUERYUSERTOKEN(ULONG, PHANDLE);
77typedef FNWTSQUERYUSERTOKEN *PFNWTSQUERYUSERTOKEN;
78
79
80/*******************************************************************************
81* Global Variables *
82*******************************************************************************/
83/** Init once structure. */
84static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
85/** Critical section protecting the process array. */
86static RTCRITSECT g_CritSect;
87/** The number of processes in the array. */
88static uint32_t g_cProcesses;
89/** The current allocation size. */
90static uint32_t g_cProcessesAlloc;
91/** Array containing the live or non-reaped child processes. */
92static struct RTPROCWINENTRY
93{
94 /** The process ID. */
95 ULONG_PTR pid;
96 /** The process handle. */
97 HANDLE hProcess;
98} *g_paProcesses;
99
100
101/**
102 * Clean up the globals.
103 *
104 * @param enmReason Ignored.
105 * @param iStatus Ignored.
106 * @param pvUser Ignored.
107 */
108static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
109{
110 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
111
112 RTCritSectDelete(&g_CritSect);
113
114 size_t i = g_cProcesses;
115 while (i-- > 0)
116 {
117 CloseHandle(g_paProcesses[i].hProcess);
118 g_paProcesses[i].hProcess = NULL;
119 }
120 RTMemFree(g_paProcesses);
121
122 g_paProcesses = NULL;
123 g_cProcesses = 0;
124 g_cProcessesAlloc = 0;
125}
126
127
128/**
129 * Initialize the globals.
130 *
131 * @returns IPRT status code.
132 * @param pvUser1 Ignored.
133 * @param pvUser2 Ignored.
134 */
135static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
136{
137 NOREF(pvUser1); NOREF(pvUser2);
138
139 g_cProcesses = 0;
140 g_cProcessesAlloc = 0;
141 g_paProcesses = NULL;
142 int rc = RTCritSectInit(&g_CritSect);
143 if (RT_SUCCESS(rc))
144 {
145 /** @todo init once, terminate once - this is a generic thing which should
146 * have some kind of static and simpler setup! */
147 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
148 if (RT_SUCCESS(rc))
149 return rc;
150 RTCritSectDelete(&g_CritSect);
151 }
152 return rc;
153}
154
155
156/**
157 * Gets the process handle for a process from g_paProcesses.
158 *
159 * @returns Process handle if found, NULL if not.
160 * @param pid The process to remove (pid).
161 */
162static HANDLE rtProcWinFindPid(RTPROCESS pid)
163{
164 HANDLE hProcess = NULL;
165
166 RTCritSectEnter(&g_CritSect);
167 uint32_t i = g_cProcesses;
168 while (i-- > 0)
169 if (g_paProcesses[i].pid == pid)
170 {
171 hProcess = g_paProcesses[i].hProcess;
172 break;
173 }
174 RTCritSectLeave(&g_CritSect);
175
176 return hProcess;
177}
178
179
180/**
181 * Removes a process from g_paProcesses.
182 *
183 * @param pid The process to remove (pid).
184 */
185static void rtProcWinRemovePid(RTPROCESS pid)
186{
187 RTCritSectEnter(&g_CritSect);
188 uint32_t i = g_cProcesses;
189 while (i-- > 0)
190 if (g_paProcesses[i].pid == pid)
191 {
192 g_cProcesses--;
193 uint32_t cToMove = g_cProcesses - i;
194 if (cToMove)
195 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
196 break;
197 }
198 RTCritSectLeave(&g_CritSect);
199}
200
201
202/**
203 * Adds a process to g_paProcesses.
204 *
205 * @returns IPRT status code.
206 * @param pid The process id.
207 * @param hProcess The process handle.
208 */
209static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
210{
211 RTCritSectEnter(&g_CritSect);
212
213 uint32_t i = g_cProcesses;
214 if (i >= g_cProcessesAlloc)
215 {
216 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
217 if (RT_UNLIKELY(!pvNew))
218 {
219 RTCritSectLeave(&g_CritSect);
220 return VERR_NO_MEMORY;
221 }
222 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
223 g_cProcessesAlloc = i + 16;
224 }
225
226 g_paProcesses[i].pid = pid;
227 g_paProcesses[i].hProcess = hProcess;
228 g_cProcesses = i + 1;
229
230 RTCritSectLeave(&g_CritSect);
231 return VINF_SUCCESS;
232}
233
234
235RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
236{
237 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
238 NULL, NULL, NULL, /* standard handles */
239 NULL /*pszAsUser*/, NULL /* pszPassword*/,
240 pProcess);
241}
242
243
244static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
245 PRTUTF16 pwszzBlock, DWORD dwCreationFlags,
246 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
247{
248 int rc = VINF_SUCCESS;
249 BOOL fRc = FALSE;
250 DWORD dwErr = NO_ERROR;
251
252 /*
253 * If we run as a service CreateProcessWithLogon will fail,
254 * so don't even try it (because of Local System context).
255 */
256 if (!(fFlags & RTPROC_FLAGS_SERVICE))
257 {
258 RTLDRMOD hAdvAPI32;
259 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
260 if (RT_SUCCESS(rc))
261 {
262 /*
263 * This may fail on too old (NT4) platforms or if the calling process
264 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
265 * platforms (however, this works on W2K!).
266 */
267 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
268 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
269 if (RT_SUCCESS(rc))
270 {
271 fRc = pfnCreateProcessWithLogonW(pwszUser,
272 NULL, /* lpDomain*/
273 pwszPassword,
274 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
275 pwszExec,
276 pwszCmdLine,
277 dwCreationFlags,
278 pwszzBlock,
279 NULL, /* pCurrentDirectory */
280 pStartupInfo,
281 pProcInfo);
282 if (!fRc)
283 dwErr = GetLastError();
284 }
285 RTLdrClose(hAdvAPI32);
286 }
287 }
288
289 /*
290 * Get the session ID.
291 */
292 DWORD dwSessionID = 0; /* On W2K the session ID is always 0 (does not have fast user switching). */
293 RTLDRMOD hKernel32;
294 rc = RTLdrLoad("Kernel32.dll", &hKernel32);
295 if (RT_SUCCESS(rc))
296 {
297 PFNWTSGETACTIVECONSOLESESSIONID pfnWTSGetActiveConsoleSessionId;
298 rc = RTLdrGetSymbol(hKernel32, "WTSGetActiveConsoleSessionId", (void **)&pfnWTSGetActiveConsoleSessionId);
299 if (RT_SUCCESS(rc))
300 {
301 /*
302 * Console session means the session which the physical keyboard and mouse
303 * is connected to. Due to FUS (fast user switching) starting with Windows XP
304 * this can be a different session than 0.
305 */
306 dwSessionID = pfnWTSGetActiveConsoleSessionId(); /* Get active console session ID. */
307 }
308 RTLdrClose(hKernel32);
309 }
310
311 /*
312 * Did the API call above fail because we're running on a too old OS (NT4) or
313 * we're running as a Windows service?
314 */
315 if ( RT_FAILURE(rc)
316 || (fFlags & RTPROC_FLAGS_SERVICE))
317 {
318 /*
319 * First we have to validate the credentials. If they're valid we can
320 * proceed. This is important when running as a service which then looks up
321 * the current session the user is logged on in order to start the actual
322 * process there.
323 *
324 * The following rights are needed in order to use LogonUserW and
325 * CreateProcessAsUserW, so the local policy has to be modified to:
326 * - SE_TCB_NAME = Act as part of the operating system
327 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
328 * - SE_INCREASE_QUOTA_NAME
329 *
330 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
331 *
332 ** @todo Deal with http://support.microsoft.com/kb/245683 for NULL domain names
333 * on NT4 (ignored here by now). Passing FQDNs should work!
334 */
335 PHANDLE phToken = NULL;
336 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
337 fRc = LogonUserW(pwszUser,
338 NULL,
339 pwszPassword,
340 LOGON32_LOGON_INTERACTIVE,
341 LOGON32_PROVIDER_DEFAULT,
342 &hTokenLogon);
343
344 BOOL bFound = FALSE;
345 HANDLE hTokenVBoxTray = INVALID_HANDLE_VALUE;
346 if (fRc)
347 {
348 if (fFlags & RTPROC_FLAGS_SERVICE)
349 {
350 DWORD cbName = 0; /* Must be zero to query size! */
351 DWORD cbDomain = 0;
352 SID_NAME_USE sidNameUse = SidTypeUser;
353 fRc = LookupAccountNameW(NULL,
354 pwszUser,
355 NULL,
356 &cbName,
357 NULL,
358 &cbDomain,
359 &sidNameUse);
360 if (!fRc)
361 dwErr = GetLastError();
362 if ( !fRc
363 && dwErr == ERROR_INSUFFICIENT_BUFFER
364 && cbName > 0)
365 {
366 dwErr = NO_ERROR;
367
368 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
369 AssertPtrReturn(pSID, VERR_NO_MEMORY);
370
371 /** @todo No way to allocate a PRTUTF16 directly? */
372 char *pszDomainUtf8 = NULL;
373 PRTUTF16 pwszDomain = NULL;
374 if (cbDomain > 0)
375 {
376 pszDomainUtf8 = RTStrAlloc(cbDomain);
377 rc = RTStrToUtf16(pszDomainUtf8, &pwszDomain);
378 AssertRCReturn(rc, rc);
379 RTStrFree(pszDomainUtf8);
380 }
381
382 /* Note: Also supports FQDNs! */
383 if ( LookupAccountNameW(NULL, /* lpSystemName */
384 pwszUser,
385 pSID,
386 &cbName,
387 pwszDomain,
388 &cbDomain,
389 &sidNameUse)
390 && IsValidSid(pSID))
391 {
392 HANDLE hSnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
393 if (hSnap != INVALID_HANDLE_VALUE)
394 {
395 PROCESSENTRY32 procEntry;
396 procEntry.dwSize = sizeof(PROCESSENTRY32);
397 if (Process32First(hSnap, &procEntry))
398 {
399 DWORD dwVBoxTrayPID = 0;
400 DWORD dwCurSession = 0;
401 do
402 {
403 if ( _stricmp(procEntry.szExeFile, "VBoxTray.exe") == 0
404 && ProcessIdToSessionId(procEntry.th32ProcessID, &dwCurSession))
405 {
406 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, procEntry.th32ProcessID);
407 if (hProc != NULL)
408 {
409 HANDLE hTokenProc;
410 fRc = OpenProcessToken(hProc,
411 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE |
412 TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
413 &hTokenProc);
414 if (fRc)
415 {
416 DWORD dwSize = 0;
417 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
418 if (!fRc)
419 dwErr = GetLastError();
420 if ( !fRc
421 && dwErr == ERROR_INSUFFICIENT_BUFFER
422 && dwSize > 0)
423 {
424 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
425 AssertPtrBreak(pTokenUser);
426 RT_ZERO(*pTokenUser);
427 if ( GetTokenInformation(hTokenProc,
428 TokenUser,
429 (LPVOID)pTokenUser,
430 dwSize,
431 &dwSize)
432 && IsValidSid(pTokenUser->User.Sid)
433 && EqualSid(pTokenUser->User.Sid, pSID))
434 {
435 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
436 NULL, SecurityIdentification, TokenPrimary, &hTokenVBoxTray))
437 {
438 /*
439 * So we found a VBoxTray instance which belongs to the user we want to
440 * to run our new process under. This duplicated token will be used for
441 * the actual CreateProcessAsUserW() call then.
442 */
443 bFound = TRUE;
444 }
445 else
446 dwErr = GetLastError();
447 }
448 else
449 dwErr = GetLastError();
450 RTMemFree(pTokenUser);
451 }
452 else
453 dwErr = GetLastError();
454 CloseHandle(hTokenProc);
455 }
456 else
457 dwErr = GetLastError();
458 CloseHandle(hProc);
459 }
460 else
461 dwErr = GetLastError();
462 }
463 } while (Process32Next(hSnap, &procEntry) && !bFound);
464 }
465 else /* Process32First */
466 dwErr = GetLastError();
467 CloseHandle(hSnap);
468 }
469 else /* hSnap =! INVALID_HANDLE_VALUE */
470 dwErr = GetLastError();
471 }
472 else
473 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
474 RTMemFree(pSID);
475 if (pwszDomain != NULL)
476 RTUtf16Free(pwszDomain);
477 }
478 }
479 else /* !RTPROC_FLAGS_SERVICE */
480 {
481 /* Nothing to do here right now. */
482 }
483
484 /*
485 * If we didn't find a matching VBoxTray, just use the token we got
486 * above from LogonUserW(). This enables us to at least run processes with
487 * desktop interaction without UI.
488 */
489 phToken = bFound ? &hTokenVBoxTray : &hTokenLogon;
490
491 /*
492 * Useful KB articles:
493 * http://support.microsoft.com/kb/165194/
494 * http://support.microsoft.com/kb/184802/
495 * http://support.microsoft.com/kb/327618/
496 */
497 fRc = CreateProcessAsUserW(*phToken,
498 pwszExec,
499 pwszCmdLine,
500 NULL, /* pProcessAttributes */
501 NULL, /* pThreadAttributes */
502 TRUE, /* fInheritHandles */
503 dwCreationFlags,
504 pwszzBlock,
505 NULL, /* pCurrentDirectory */
506 pStartupInfo,
507 pProcInfo);
508 if (fRc)
509 dwErr = NO_ERROR;
510 else
511 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
512
513 if (hTokenVBoxTray != INVALID_HANDLE_VALUE)
514 CloseHandle(hTokenVBoxTray);
515 CloseHandle(hTokenLogon);
516 }
517 else
518 dwErr = GetLastError(); /* LogonUserW() failed. */
519 }
520
521 if (dwErr != NO_ERROR)
522 {
523 /*
524 * Map some important or much used Windows error codes
525 * to our error codes.
526 */
527 switch (dwErr)
528 {
529 case ERROR_NOACCESS:
530 case ERROR_PRIVILEGE_NOT_HELD:
531 rc = VERR_PERMISSION_DENIED;
532 break;
533
534 case ERROR_PASSWORD_EXPIRED:
535 case ERROR_ACCOUNT_RESTRICTION:
536 rc = VERR_LOGON_FAILURE;
537 break;
538
539 default:
540 /* Could trigger a debug assertion! */
541 rc = RTErrConvertFromWin32(dwErr);
542 break;
543 }
544 }
545 else
546 rc = VINF_SUCCESS;
547 return rc;
548}
549
550RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
551 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
552 const char *pszPassword, PRTPROCESS phProcess)
553{
554 /*
555 * Input validation
556 */
557 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
558 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
559 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
560 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
561 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
562 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
563 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
564 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
565 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
566 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
567 /** @todo search the PATH (add flag for this). */
568
569 /*
570 * Initialize the globals.
571 */
572 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
573 AssertRCReturn(rc, rc);
574
575 /*
576 * Get the file descriptors for the handles we've been passed.
577 *
578 * It seems there is no point in trying to convince a child process's CRT
579 * that any of the standard file handles is non-TEXT. So, we don't...
580 */
581 STARTUPINFOW StartupInfo;
582 RT_ZERO(StartupInfo);
583 StartupInfo.cb = sizeof(StartupInfo);
584 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
585#if 1 /* The CRT should keep the standard handles up to date. */
586 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
587 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
588 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
589#else
590 StartupInfo.hStdInput = _get_osfhandle(0);
591 StartupInfo.hStdOutput = _get_osfhandle(1);
592 StartupInfo.hStdError = _get_osfhandle(2);
593#endif
594 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
595 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
596 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
597 for (int i = 0; i < 3; i++)
598 {
599 if (paHandles[i])
600 {
601 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
602 switch (paHandles[i]->enmType)
603 {
604 case RTHANDLETYPE_FILE:
605 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
606 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
607 : INVALID_HANDLE_VALUE;
608 break;
609
610 case RTHANDLETYPE_PIPE:
611 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
612 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
613 : INVALID_HANDLE_VALUE;
614 break;
615
616 case RTHANDLETYPE_SOCKET:
617 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
618 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
619 : INVALID_HANDLE_VALUE;
620 break;
621
622 default:
623 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
624 }
625
626 /* Get the inheritability of the handle. */
627 if (*aphStds[i] != INVALID_HANDLE_VALUE)
628 {
629 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
630 {
631 rc = RTErrConvertFromWin32(GetLastError());
632 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
633 }
634 }
635 }
636 }
637
638 /*
639 * Set the inheritability any handles we're handing the child.
640 */
641 rc = VINF_SUCCESS;
642 for (int i = 0; i < 3; i++)
643 if ( (afInhStds[i] != 0xffffffff)
644 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
645 {
646 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
647 {
648 rc = RTErrConvertFromWin32(GetLastError());
649 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
650 }
651 }
652
653 /*
654 * Create the environment block, command line and convert the executable
655 * name.
656 */
657 PRTUTF16 pwszzBlock;
658 if (RT_SUCCESS(rc))
659 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
660 if (RT_SUCCESS(rc))
661 {
662 PRTUTF16 pwszCmdLine;
663 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
664 if (RT_SUCCESS(rc))
665 {
666 PRTUTF16 pwszExec;
667 rc = RTStrToUtf16(pszExec, &pwszExec);
668 if (RT_SUCCESS(rc))
669 {
670 /*
671 * Get going...
672 */
673 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
674 if (fFlags & RTPROC_FLAGS_DETACHED)
675 dwCreationFlags |= DETACHED_PROCESS;
676
677 PROCESS_INFORMATION ProcInfo;
678 RT_ZERO(ProcInfo);
679
680 /*
681 * Only use the normal CreateProcess stuff if we have no user name
682 * and we are not running from a (Windows) service. Otherwise use
683 * the more advanced version in rtProcCreateAsUserHlp().
684 */
685 if ( pszAsUser == NULL
686 && !(fFlags & RTPROC_FLAGS_SERVICE))
687 {
688 if (CreateProcessW(pwszExec,
689 pwszCmdLine,
690 NULL, /* pProcessAttributes */
691 NULL, /* pThreadAttributes */
692 TRUE, /* fInheritHandles */
693 dwCreationFlags,
694 pwszzBlock,
695 NULL, /* pCurrentDirectory */
696 &StartupInfo,
697 &ProcInfo))
698 rc = VINF_SUCCESS;
699 else
700 rc = RTErrConvertFromWin32(GetLastError());
701 }
702 else
703 {
704 /*
705 * Convert the additional parameters and use a helper
706 * function to do the actual work.
707 */
708 PRTUTF16 pwszUser;
709 rc = RTStrToUtf16(pszAsUser, &pwszUser);
710 if (RT_SUCCESS(rc))
711 {
712 PRTUTF16 pwszPassword;
713 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
714 if (RT_SUCCESS(rc))
715 {
716 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
717 pwszExec, pwszCmdLine, pwszzBlock, dwCreationFlags,
718 &StartupInfo, &ProcInfo, fFlags);
719
720 RTUtf16Free(pwszPassword);
721 }
722 RTUtf16Free(pwszUser);
723 }
724 }
725 if (RT_SUCCESS(rc))
726 {
727 CloseHandle(ProcInfo.hThread);
728 if (phProcess)
729 {
730 /*
731 * Add the process to the child process list so
732 * RTProcWait can reuse and close the process handle.
733 */
734 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
735 *phProcess = ProcInfo.dwProcessId;
736 }
737 else
738 CloseHandle(ProcInfo.hProcess);
739 rc = VINF_SUCCESS;
740 }
741 RTUtf16Free(pwszExec);
742 }
743 RTUtf16Free(pwszCmdLine);
744 }
745 RTEnvFreeUtf16Block(pwszzBlock);
746 }
747
748 /* Undo any handle inherit changes. */
749 for (int i = 0; i < 3; i++)
750 if ( (afInhStds[i] != 0xffffffff)
751 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
752 {
753 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
754 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
755 }
756
757 return rc;
758}
759
760
761
762RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
763{
764 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
765 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
766 AssertRCReturn(rc, rc);
767
768 /*
769 * Try find the process among the ones we've spawned, otherwise, attempt
770 * opening the specified process.
771 */
772 HANDLE hProcess = rtProcWinFindPid(Process);
773 if (hProcess == NULL)
774 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
775 if (hProcess != NULL)
776 {
777 /*
778 * Wait for it to terminate.
779 */
780 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
781 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
782 while (WaitRc == WAIT_IO_COMPLETION)
783 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
784 switch (WaitRc)
785 {
786 /*
787 * It has terminated.
788 */
789 case WAIT_OBJECT_0:
790 {
791 DWORD dwExitCode;
792 if (GetExitCodeProcess(hProcess, &dwExitCode))
793 {
794 /** @todo the exit code can be special statuses. */
795 if (pProcStatus)
796 {
797 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
798 pProcStatus->iStatus = (int)dwExitCode;
799 }
800 rtProcWinRemovePid(Process);
801 return VINF_SUCCESS;
802 }
803 break;
804 }
805
806 /*
807 * It hasn't terminated just yet.
808 */
809 case WAIT_TIMEOUT:
810 return VERR_PROCESS_RUNNING;
811
812 /*
813 * Something went wrong...
814 */
815 case WAIT_FAILED:
816 break;
817 case WAIT_ABANDONED:
818 AssertFailed();
819 return VERR_GENERAL_FAILURE;
820 default:
821 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
822 return VERR_GENERAL_FAILURE;
823 }
824 }
825 DWORD dwErr = GetLastError();
826 if (dwErr == ERROR_INVALID_PARAMETER)
827 return VERR_PROCESS_NOT_FOUND;
828 return RTErrConvertFromWin32(dwErr);
829}
830
831
832RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
833{
834 /** @todo this isn't quite right. */
835 return RTProcWait(Process, fFlags, pProcStatus);
836}
837
838
839RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
840{
841 int rc = VINF_SUCCESS;
842 HANDLE hProcess = rtProcWinFindPid(Process);
843 if (hProcess != NULL)
844 {
845 if (!TerminateProcess(hProcess, 127))
846 rc = RTErrConvertFromWin32(GetLastError());
847 }
848 else
849 {
850 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
851 if (hProcess != NULL)
852 {
853 BOOL fRc = TerminateProcess(hProcess, 127);
854 DWORD dwErr = GetLastError();
855 CloseHandle(hProcess);
856 if (!fRc)
857 rc = RTErrConvertFromWin32(dwErr);
858 }
859 }
860 return rc;
861}
862
863
864RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
865{
866 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
867 DWORD_PTR dwSystemAffinityMask;
868
869 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
870 Assert(fRc);
871
872 return dwProcessAffinityMask;
873}
874
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