VirtualBox

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

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

iprt: Added RTPipeFromNative, implemented the windows version and added tests.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 22.1 KB
Line 
1/* $Id: process-win.cpp 27613 2010-03-23 01:12:54Z vboxsync $ */
2/** @file
3 * IPRT - Process, Windows.
4 */
5
6/*
7 * Copyright (C) 2006-2010 Sun Microsystems, Inc.
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 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
27 * Clara, CA 95054 USA or visit http://www.sun.com if you need
28 * additional information or have any questions.
29 */
30
31
32/*******************************************************************************
33* Header Files *
34*******************************************************************************/
35#define LOG_GROUP RTLOGGROUP_PROCESS
36
37#include <Userenv.h>
38#include <Windows.h>
39#include <process.h>
40#include <errno.h>
41
42#include <iprt/process.h>
43#include "internal/iprt.h"
44
45#include <iprt/assert.h>
46#include <iprt/critsect.h>
47#include <iprt/file.h>
48#include <iprt/err.h>
49#include <iprt/env.h>
50#include <iprt/getopt.h>
51#include <iprt/initterm.h>
52#include <iprt/ldr.h>
53#include <iprt/mem.h>
54#include <iprt/once.h>
55#include <iprt/pipe.h>
56#include <iprt/string.h>
57#include <iprt/socket.h>
58
59
60/*******************************************************************************
61* Structures and Typedefs *
62*******************************************************************************/
63typedef WINADVAPI BOOL WINAPI FNCREATEPROCESSWITHLOGON(LPCWSTR,
64 LPCWSTR,
65 LPCWSTR,
66 DWORD,
67 LPCWSTR,
68 LPWSTR,
69 DWORD,
70 LPVOID,
71 LPCWSTR,
72 LPSTARTUPINFOW,
73 LPPROCESS_INFORMATION);
74typedef FNCREATEPROCESSWITHLOGON *PFNCREATEPROCESSWITHLOGON;
75
76
77/*******************************************************************************
78* Global Variables *
79*******************************************************************************/
80/** Init once structure. */
81static RTONCE g_rtProcWinInitOnce = RTONCE_INITIALIZER;
82/** Critical section protecting the process array. */
83static RTCRITSECT g_CritSect;
84/** The number of processes in the array. */
85static uint32_t g_cProcesses;
86/** The current allocation size. */
87static uint32_t g_cProcessesAlloc;
88/** Array containing the live or non-reaped child processes. */
89static struct RTPROCWINENTRY
90{
91 /** The process ID. */
92 ULONG_PTR pid;
93 /** The process handle. */
94 HANDLE hProcess;
95} *g_paProcesses;
96
97
98/**
99 * Clean up the globals.
100 *
101 * @param enmReason Ignored.
102 * @param iStatus Ignored.
103 * @param pvUser Ignored.
104 */
105static DECLCALLBACK(void) rtProcWinTerm(RTTERMREASON enmReason, int32_t iStatus, void *pvUser)
106{
107 NOREF(pvUser); NOREF(iStatus); NOREF(enmReason);
108
109 RTCritSectDelete(&g_CritSect);
110
111 size_t i = g_cProcesses;
112 while (i-- > 0)
113 {
114 CloseHandle(g_paProcesses[i].hProcess);
115 g_paProcesses[i].hProcess = NULL;
116 }
117 RTMemFree(g_paProcesses);
118
119 g_paProcesses = NULL;
120 g_cProcesses = 0;
121 g_cProcessesAlloc = 0;
122}
123
124
125/**
126 * Initialize the globals.
127 *
128 * @returns IPRT status code.
129 * @param pvUser1 Ignored.
130 * @param pvUser2 Ignored.
131 */
132static DECLCALLBACK(int32_t) rtProcWinInitOnce(void *pvUser1, void *pvUser2)
133{
134 NOREF(pvUser1); NOREF(pvUser2);
135
136 g_cProcesses = 0;
137 g_cProcessesAlloc = 0;
138 g_paProcesses = NULL;
139 int rc = RTCritSectInit(&g_CritSect);
140 if (RT_SUCCESS(rc))
141 {
142 /** @todo init once, terminate once - this is a generic thing which should
143 * have some kind of static and simpler setup! */
144 rc = RTTermRegisterCallback(rtProcWinTerm, NULL);
145 if (RT_SUCCESS(rc))
146 return rc;
147 RTCritSectDelete(&g_CritSect);
148 }
149 return rc;
150}
151
152
153/**
154 * Gets the process handle for a process from g_paProcesses.
155 *
156 * @returns Process handle if found, NULL if not.
157 * @param pid The process to remove (pid).
158 */
159static HANDLE rtProcWinFindPid(RTPROCESS pid)
160{
161 HANDLE hProcess = NULL;
162
163 RTCritSectEnter(&g_CritSect);
164 uint32_t i = g_cProcesses;
165 while (i-- > 0)
166 if (g_paProcesses[i].pid == pid)
167 {
168 hProcess = g_paProcesses[i].hProcess;
169 break;
170 }
171 RTCritSectLeave(&g_CritSect);
172
173 return hProcess;
174}
175
176
177/**
178 * Removes a process from g_paProcesses.
179 *
180 * @param pid The process to remove (pid).
181 */
182static void rtProcWinRemovePid(RTPROCESS pid)
183{
184 RTCritSectEnter(&g_CritSect);
185 uint32_t i = g_cProcesses;
186 while (i-- > 0)
187 if (g_paProcesses[i].pid == pid)
188 {
189 g_cProcesses--;
190 uint32_t cToMove = g_cProcesses - i;
191 if (cToMove)
192 memmove(&g_paProcesses[i], &g_paProcesses[i + 1], cToMove * sizeof(g_paProcesses[0]));
193 break;
194 }
195 RTCritSectLeave(&g_CritSect);
196}
197
198
199/**
200 * Adds a process to g_paProcesses.
201 *
202 * @returns IPRT status code.
203 * @param pid The process id.
204 * @param hProcess The process handle.
205 */
206static int rtProcWinAddPid(RTPROCESS pid, HANDLE hProcess)
207{
208 RTCritSectEnter(&g_CritSect);
209
210 uint32_t i = g_cProcesses;
211 if (i >= g_cProcessesAlloc)
212 {
213 void *pvNew = RTMemRealloc(g_paProcesses, (i + 16) * sizeof(g_paProcesses[0]));
214 if (RT_UNLIKELY(!pvNew))
215 {
216 RTCritSectLeave(&g_CritSect);
217 return VERR_NO_MEMORY;
218 }
219 g_paProcesses = (struct RTPROCWINENTRY *)pvNew;
220 g_cProcessesAlloc = i + 16;
221 }
222
223 g_paProcesses[i].pid = pid;
224 g_paProcesses[i].hProcess = hProcess;
225 g_cProcesses = i + 1;
226
227 RTCritSectLeave(&g_CritSect);
228 return VINF_SUCCESS;
229}
230
231
232RTR3DECL(int) RTProcCreate(const char *pszExec, const char * const *papszArgs, RTENV Env, unsigned fFlags, PRTPROCESS pProcess)
233{
234 return RTProcCreateEx(pszExec, papszArgs, Env, fFlags,
235 NULL, NULL, NULL, /* standard handles */
236 NULL /*pszAsUser*/, NULL /* pszPassword*/,
237 pProcess);
238}
239
240
241static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
242 PRTUTF16 pwszzBlock, STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo)
243{
244 /** @todo On NT4 we need to enable the SeTcbPrivilege to act as part of the operating system. Otherwise
245 * we will get error 1314 (priviledge not held) as a response. */
246
247 /*
248 * The following rights are needed in order to use LogonUserW and
249 * CreateProcessAsUserW, so the local policy has to be modified to:
250 * - SE_TCB_NAME = Act as part of the operating system
251 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
252 * - SE_INCREASE_QUOTA_NAME
253 *
254 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
255 */
256 int rc;
257 DWORD dwErr = NO_ERROR;
258 HANDLE hToken = INVALID_HANDLE_VALUE;
259 BOOL fRc = LogonUserW(pwszUser,
260 NULL, /* lpDomain */
261 pwszPassword,
262 LOGON32_LOGON_INTERACTIVE,
263 LOGON32_PROVIDER_DEFAULT,
264 &hToken);
265 if (fRc)
266 {
267 fRc = CreateProcessAsUserW(hToken,
268 pwszExec,
269 pwszCmdLine,
270 NULL, /* pProcessAttributes */
271 NULL, /* pThreadAttributes */
272 TRUE, /* fInheritHandles */
273 CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
274 pwszzBlock,
275 NULL, /* pCurrentDirectory */
276 pStartupInfo,
277 pProcInfo);
278 if (!fRc)
279 dwErr = GetLastError();
280 CloseHandle(hToken);
281 }
282 else
283 dwErr = GetLastError();
284
285#ifndef IPRT_TARGET_NT4
286 /*
287 * If we don't hold enough priviledges to spawn a new process with
288 * different credentials we have to use CreateProcessWithLogonW here. This
289 * API is W2K+ and uses a system service for spawning the process.
290 */
291 /** @todo Use fFlags to either use this feature or just fail. */
292 if (dwErr == ERROR_PRIVILEGE_NOT_HELD)
293 {
294 RTLDRMOD hAdvAPI32;
295 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
296 if (RT_SUCCESS(rc))
297 {
298 /* This may fail on too old (NT4) platforms. */
299 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
300 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
301 if (RT_SUCCESS(rc))
302 {
303 fRc = pfnCreateProcessWithLogonW(pwszUser,
304 NULL, /* lpDomain*/
305 pwszPassword,
306 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
307 pwszExec,
308 pwszCmdLine,
309 CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
310 pwszzBlock,
311 NULL, /* pCurrentDirectory */
312 pStartupInfo,
313 pProcInfo);
314 if (fRc)
315 dwErr = NO_ERROR;
316 else
317 dwErr = GetLastError();
318 }
319 RTLdrClose(hAdvAPI32);
320 }
321 }
322#endif
323
324 if (dwErr != NO_ERROR)
325 rc = RTErrConvertFromWin32(dwErr);
326 else
327 rc = VINF_SUCCESS;
328 return rc;
329}
330
331RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
332 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
333 const char *pszPassword, PRTPROCESS phProcess)
334{
335 /*
336 * Input validation
337 */
338 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
339 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
340 AssertReturn(!(fFlags & ~RTPROC_FLAGS_DAEMONIZE), VERR_INVALID_PARAMETER);
341 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
342 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
343 /** @todo search the PATH (add flag for this). */
344
345 /*
346 * Initialize the globals.
347 */
348 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
349 AssertRCReturn(rc, rc);
350
351 /*
352 * Get the file descriptors for the handles we've been passed.
353 *
354 * It seems there is no point in trying to convince a child process's CRT
355 * that any of the standard file handles is non-TEXT. So, we don't...
356 */
357 STARTUPINFOW StartupInfo;
358 RT_ZERO(StartupInfo);
359 StartupInfo.cb = sizeof(StartupInfo);
360 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
361#if 1 /* The CRT should keep the standard handles up to date. */
362 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
363 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
364 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
365#else
366 StartupInfo.hStdInput = _get_osfhandle(0);
367 StartupInfo.hStdOutput = _get_osfhandle(1);
368 StartupInfo.hStdError = _get_osfhandle(2);
369#endif
370 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
371 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
372 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
373 for (int i = 0; i < 3; i++)
374 {
375 if (paHandles[i])
376 {
377 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
378 switch (paHandles[i]->enmType)
379 {
380 case RTHANDLETYPE_FILE:
381 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
382 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
383 : INVALID_HANDLE_VALUE;
384 break;
385
386 case RTHANDLETYPE_PIPE:
387 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
388 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
389 : INVALID_HANDLE_VALUE;
390 break;
391
392 case RTHANDLETYPE_SOCKET:
393 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
394 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
395 : INVALID_HANDLE_VALUE;
396 break;
397
398 default:
399 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
400 }
401
402 /* Get the inheritability of the handle. */
403 if (*aphStds[i] != INVALID_HANDLE_VALUE)
404 {
405 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
406 {
407 rc = RTErrConvertFromWin32(GetLastError());
408 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
409 }
410 }
411 }
412 }
413
414 /*
415 * Set the inheritability any handles we're handing the child.
416 */
417 rc = VINF_SUCCESS;
418 for (int i = 0; i < 3; i++)
419 if ( (afInhStds[i] != 0xffffffff)
420 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
421 {
422 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
423 {
424 rc = RTErrConvertFromWin32(GetLastError());
425 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
426 }
427 }
428
429 /*
430 * Create the environment block, command line and convert the executable
431 * name.
432 */
433 PRTUTF16 pwszzBlock;
434 if (RT_SUCCESS(rc))
435 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
436 if (RT_SUCCESS(rc))
437 {
438 PRTUTF16 pwszCmdLine;
439 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
440 if (RT_SUCCESS(rc))
441 {
442 PRTUTF16 pwszExec;
443 rc = RTStrToUtf16(pszExec, &pwszExec);
444 if (RT_SUCCESS(rc))
445 {
446 /*
447 * Get going...
448 */
449 PROCESS_INFORMATION ProcInfo;
450 RT_ZERO(ProcInfo);
451 if (pszAsUser == NULL)
452 {
453 if (CreateProcessW(pwszExec,
454 pwszCmdLine,
455 NULL, /* pProcessAttributes */
456 NULL, /* pThreadAttributes */
457 TRUE, /* fInheritHandles */
458 CREATE_UNICODE_ENVIRONMENT, /* dwCreationFlags */
459 pwszzBlock,
460 NULL, /* pCurrentDirectory */
461 &StartupInfo,
462 &ProcInfo))
463 rc = VINF_SUCCESS;
464 else
465 rc = RTErrConvertFromWin32(GetLastError());
466 }
467 else
468 {
469 /*
470 * Convert the additional parameters and use a helper
471 * function to do the actual work.
472 */
473 PRTUTF16 pwszUser;
474 rc = RTStrToUtf16(pszAsUser, &pwszUser);
475 if (RT_SUCCESS(rc))
476 {
477 PRTUTF16 pwszPassword;
478 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
479 if (RT_SUCCESS(rc))
480 {
481 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
482 pwszExec, pwszCmdLine, pwszzBlock,
483 &StartupInfo, &ProcInfo);
484
485 RTUtf16Free(pwszPassword);
486 }
487 RTUtf16Free(pwszUser);
488 }
489 }
490 if (RT_SUCCESS(rc))
491 {
492 CloseHandle(ProcInfo.hThread);
493 if (phProcess)
494 {
495 /*
496 * Add the process to the child process list so
497 * RTProcWait can reuse and close the process handle.
498 */
499 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
500 *phProcess = ProcInfo.dwProcessId;
501 }
502 else
503 CloseHandle(ProcInfo.hProcess);
504 rc = VINF_SUCCESS;
505 }
506 RTUtf16Free(pwszExec);
507 }
508 RTUtf16Free(pwszCmdLine);
509 }
510 RTEnvFreeUtf16Block(pwszzBlock);
511 }
512
513 /* Undo any handle inherit changes. */
514 for (int i = 0; i < 3; i++)
515 if ( (afInhStds[i] != 0xffffffff)
516 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
517 {
518 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
519 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
520 }
521
522 return rc;
523}
524
525
526
527RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
528{
529 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
530 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
531 AssertRCReturn(rc, rc);
532
533 /*
534 * Try find the process among the ones we've spawned, otherwise, attempt
535 * opening the specified process.
536 */
537 HANDLE hProcess = rtProcWinFindPid(Process);
538 if (hProcess == NULL)
539 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
540 if (hProcess != NULL)
541 {
542 /*
543 * Wait for it to terminate.
544 */
545 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
546 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
547 while (WaitRc == WAIT_IO_COMPLETION)
548 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
549 switch (WaitRc)
550 {
551 /*
552 * It has terminated.
553 */
554 case WAIT_OBJECT_0:
555 {
556 DWORD dwExitCode;
557 if (GetExitCodeProcess(hProcess, &dwExitCode))
558 {
559 /** @todo the exit code can be special statuses. */
560 if (pProcStatus)
561 {
562 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
563 pProcStatus->iStatus = (int)dwExitCode;
564 }
565 rtProcWinRemovePid(Process);
566 return VINF_SUCCESS;
567 }
568 break;
569 }
570
571 /*
572 * It hasn't terminated just yet.
573 */
574 case WAIT_TIMEOUT:
575 return VERR_PROCESS_RUNNING;
576
577 /*
578 * Something went wrong...
579 */
580 case WAIT_FAILED:
581 break;
582 case WAIT_ABANDONED:
583 AssertFailed();
584 return VERR_GENERAL_FAILURE;
585 default:
586 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
587 return VERR_GENERAL_FAILURE;
588 }
589 }
590 DWORD dwErr = GetLastError();
591 if (dwErr == ERROR_INVALID_PARAMETER)
592 return VERR_PROCESS_NOT_FOUND;
593 return RTErrConvertFromWin32(dwErr);
594}
595
596
597RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
598{
599 /** @todo this isn't quite right. */
600 return RTProcWait(Process, fFlags, pProcStatus);
601}
602
603
604RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
605{
606 int rc = VINF_SUCCESS;
607 HANDLE hProcess = rtProcWinFindPid(Process);
608 if (hProcess != NULL)
609 {
610 if (!TerminateProcess(hProcess, 127))
611 rc = RTErrConvertFromWin32(GetLastError());
612 }
613 else
614 {
615 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
616 if (hProcess != NULL)
617 {
618 BOOL fRc = TerminateProcess(hProcess, 127);
619 DWORD dwErr = GetLastError();
620 CloseHandle(hProcess);
621 if (!fRc)
622 rc = RTErrConvertFromWin32(dwErr);
623 }
624 }
625 return rc;
626}
627
628
629RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
630{
631 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
632 DWORD_PTR dwSystemAffinityMask;
633
634 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
635 Assert(fRc);
636
637 return dwProcessAffinityMask;
638}
639
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