VirtualBox

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

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

IPRT/process-win: Only deal with NULL domains on NT4.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 37.1 KB
Line 
1/* $Id: process-win.cpp 29912 2010-05-31 14:36:04Z 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
256static int rtProcGetProcessHandle(DWORD dwPID, PSID pSID, PHANDLE phToken)
257{
258 AssertPtr(pSID);
259 AssertPtr(phToken);
260
261 DWORD dwErr;
262 BOOL fRc;
263 BOOL fFound = FALSE;
264 HANDLE hProc = OpenProcess(MAXIMUM_ALLOWED, TRUE, dwPID);
265 if (hProc != NULL)
266 {
267 HANDLE hTokenProc;
268 fRc = OpenProcessToken(hProc,
269 TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY | TOKEN_DUPLICATE |
270 TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID | TOKEN_READ | TOKEN_WRITE,
271 &hTokenProc);
272 if (fRc)
273 {
274 DWORD dwSize = 0;
275 fRc = GetTokenInformation(hTokenProc, TokenUser, NULL, 0, &dwSize);
276 if (!fRc)
277 dwErr = GetLastError();
278 if ( !fRc
279 && dwErr == ERROR_INSUFFICIENT_BUFFER
280 && dwSize > 0)
281 {
282 PTOKEN_USER pTokenUser = (PTOKEN_USER)RTMemAlloc(dwSize);
283 AssertPtrReturn(pTokenUser, VERR_NO_MEMORY);
284 RT_ZERO(*pTokenUser);
285 if ( GetTokenInformation(hTokenProc,
286 TokenUser,
287 (LPVOID)pTokenUser,
288 dwSize,
289 &dwSize))
290 {
291 if ( IsValidSid(pTokenUser->User.Sid)
292 && EqualSid(pTokenUser->User.Sid, pSID))
293 {
294 if (DuplicateTokenEx(hTokenProc, MAXIMUM_ALLOWED,
295 NULL, SecurityIdentification, TokenPrimary, phToken))
296 {
297 /*
298 * So we found the process instance which belongs to the user we want to
299 * to run our new process under. This duplicated token will be used for
300 * the actual CreateProcessAsUserW() call then.
301 */
302 fFound = TRUE;
303 }
304 else
305 dwErr = GetLastError();
306 }
307 }
308 else
309 dwErr = GetLastError();
310 RTMemFree(pTokenUser);
311 }
312 else
313 dwErr = GetLastError();
314 CloseHandle(hTokenProc);
315 }
316 else
317 dwErr = GetLastError();
318 CloseHandle(hProc);
319 }
320 else
321 dwErr = GetLastError();
322 if (fFound)
323 return VINF_SUCCESS;
324 if (dwErr != NO_ERROR)
325 return RTErrConvertFromWin32(dwErr);
326 return VERR_NOT_FOUND; /* No error occured, but we didn't find the right process. */
327}
328
329
330static BOOL rtProcFindProcessByName(const char *pszName, PSID pSID, PHANDLE phToken)
331{
332 AssertPtr(pszName);
333 AssertPtr(pSID);
334 AssertPtr(phToken);
335
336 DWORD dwErr = NO_ERROR;
337 BOOL fFound = FALSE;
338
339 /*
340 * On modern systems (W2K+) try the Toolhelp32 API first; this is more stable
341 * and reliable. Fallback to EnumProcess on NT4.
342 */
343 RTLDRMOD hKernel32;
344 int rc = RTLdrLoad("Kernel32.dll", &hKernel32);
345 if (RT_SUCCESS(rc))
346 {
347 PFNCREATETOOLHELP32SNAPSHOT pfnCreateToolhelp32Snapshot;
348 rc = RTLdrGetSymbol(hKernel32, "CreateToolhelp32Snapshot", (void**)&pfnCreateToolhelp32Snapshot);
349 if (RT_SUCCESS(rc))
350 {
351 PFNPROCESS32FIRST pfnProcess32First;
352 rc = RTLdrGetSymbol(hKernel32, "Process32First", (void**)&pfnProcess32First);
353 if (RT_SUCCESS(rc))
354 {
355 PFNPROCESS32NEXT pfnProcess32Next;
356 rc = RTLdrGetSymbol(hKernel32, "Process32Next", (void**)&pfnProcess32Next);
357 if (RT_SUCCESS(rc))
358 {
359 HANDLE hSnap = pfnCreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
360 if (hSnap != INVALID_HANDLE_VALUE)
361 {
362 PROCESSENTRY32 procEntry;
363 procEntry.dwSize = sizeof(PROCESSENTRY32);
364 if (pfnProcess32First(hSnap, &procEntry))
365 {
366 do
367 {
368 if ( _stricmp(procEntry.szExeFile, pszName) == 0
369 && RT_SUCCESS(rtProcGetProcessHandle(procEntry.th32ProcessID, pSID, phToken)))
370 {
371 fFound = TRUE;
372 }
373 } while (pfnProcess32Next(hSnap, &procEntry) && !fFound);
374 }
375 else /* Process32First */
376 dwErr = GetLastError();
377 CloseHandle(hSnap);
378 }
379 else /* hSnap =! INVALID_HANDLE_VALUE */
380 dwErr = GetLastError();
381 }
382 }
383 }
384 else /* CreateToolhelp32Snapshot / Toolhelp32 API not available. */
385 {
386 /*
387 * NT4 needs a copy of "PSAPI.dll" (redistributed by Microsoft and not
388 * part of the OS) in order to get a lookup. If we don't have this DLL
389 * we are not able to get a token and therefore no UI will be visible.
390 */
391 RTLDRMOD hPSAPI;
392 int rc = RTLdrLoad("PSAPI.dll", &hPSAPI);
393 if (RT_SUCCESS(rc))
394 {
395 PFNENUMPROCESSES pfnEnumProcesses;
396 rc = RTLdrGetSymbol(hPSAPI, "EnumProcesses", (void**)&pfnEnumProcesses);
397 if (RT_SUCCESS(rc))
398 {
399 PFNGETMODULEBASENAME pfnGetModuleBaseName;
400 rc = RTLdrGetSymbol(hPSAPI, "GetModuleBaseName", (void**)&pfnGetModuleBaseName);
401 if (RT_SUCCESS(rc))
402 {
403 /** @todo Retry if pBytesReturned equals cbBytes! */
404 DWORD dwPIDs[4096]; /* Should be sufficient for now. */
405 DWORD cbBytes = 0;
406 if (pfnEnumProcesses(dwPIDs, sizeof(dwPIDs), &cbBytes))
407 {
408 for (DWORD dwIdx = 0; dwIdx < cbBytes/sizeof(DWORD) && !fFound; dwIdx++)
409 {
410 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
411 FALSE, dwPIDs[dwIdx]);
412 if (hProc)
413 {
414 char *pszProcName = NULL;
415 DWORD dwSize = 128;
416 do
417 {
418 RTMemRealloc(pszProcName, dwSize);
419 if (pfnGetModuleBaseName(hProc, 0, pszProcName, dwSize) == dwSize)
420 dwSize += 128;
421 } while (GetLastError() == ERROR_INSUFFICIENT_BUFFER);
422
423 if (pszProcName)
424 {
425 if ( _stricmp(pszProcName, pszName) == 0
426 && RT_SUCCESS(rtProcGetProcessHandle(dwPIDs[dwIdx], pSID, phToken)))
427 {
428 fFound = TRUE;
429 }
430 }
431 if (pszProcName)
432 RTStrFree(pszProcName);
433 CloseHandle(hProc);
434 }
435 }
436 }
437 else
438 dwErr = GetLastError();
439 }
440 }
441 }
442 }
443 RTLdrClose(hKernel32);
444 }
445 Assert(dwErr == NO_ERROR);
446 return fFound;
447}
448
449
450static int rtProcCreateAsUserHlp(PRTUTF16 pwszUser, PRTUTF16 pwszPassword, PRTUTF16 pwszExec, PRTUTF16 pwszCmdLine,
451 PRTUTF16 pwszzBlock, DWORD dwCreationFlags,
452 STARTUPINFOW *pStartupInfo, PROCESS_INFORMATION *pProcInfo, uint32_t fFlags)
453{
454 int rc = VINF_SUCCESS;
455 BOOL fRc = FALSE;
456 DWORD dwErr = NO_ERROR;
457
458 /*
459 * If we run as a service CreateProcessWithLogon will fail,
460 * so don't even try it (because of Local System context).
461 */
462 if (!(fFlags & RTPROC_FLAGS_SERVICE))
463 {
464 RTLDRMOD hAdvAPI32;
465 rc = RTLdrLoad("Advapi32.dll", &hAdvAPI32);
466 if (RT_SUCCESS(rc))
467 {
468 /*
469 * This may fail on too old (NT4) platforms or if the calling process
470 * is running on a SYSTEM account (like a service, ERROR_ACCESS_DENIED) on newer
471 * platforms (however, this works on W2K!).
472 */
473 PFNCREATEPROCESSWITHLOGON pfnCreateProcessWithLogonW;
474 rc = RTLdrGetSymbol(hAdvAPI32, "CreateProcessWithLogonW", (void**)&pfnCreateProcessWithLogonW);
475 if (RT_SUCCESS(rc))
476 {
477 fRc = pfnCreateProcessWithLogonW(pwszUser,
478 NULL, /* lpDomain*/
479 pwszPassword,
480 1 /*LOGON_WITH_PROFILE*/, /* dwLogonFlags */
481 pwszExec,
482 pwszCmdLine,
483 dwCreationFlags,
484 pwszzBlock,
485 NULL, /* pCurrentDirectory */
486 pStartupInfo,
487 pProcInfo);
488 if (!fRc)
489 dwErr = GetLastError();
490 }
491 RTLdrClose(hAdvAPI32);
492 }
493 }
494
495 /*
496 * Did the API call above fail because we're running on a too old OS (NT4) or
497 * we're running as a Windows service?
498 */
499 if ( RT_FAILURE(rc)
500 || (fFlags & RTPROC_FLAGS_SERVICE))
501 {
502 /*
503 * So if we want to start a process from a service (RTPROC_FLAGS_SERVICE),
504 * we have to do the following:
505 * - Check the credentials supplied and get the user SID.
506 * - If valid get the correct Explorer/VBoxTray instance corresponding to that
507 * user. This of course is only possible if that user is logged in (over
508 * physical console or terminal services).
509 * - If we found the user's Explorer/VBoxTray app, use and modify the token to
510 * use it in order to allow the newly started process acess the user's
511 * desktop. If there's no Explorer/VBoxTray app we cannot display the started
512 * process (but run it without UI).
513 *
514 * The following restrictions apply:
515 * - A process only can show its UI when the user the process should run
516 * under is logged in (has a desktop).
517 * - We do not want to display a process of user A run on the desktop
518 * of user B on multi session systems.
519 *
520 * The following rights are needed in order to use LogonUserW and
521 * CreateProcessAsUserW, so the local policy has to be modified to:
522 * - SE_TCB_NAME = Act as part of the operating system
523 * - SE_ASSIGNPRIMARYTOKEN_NAME = Create/replace a token object
524 * - SE_INCREASE_QUOTA_NAME
525 *
526 * We may fail here with ERROR_PRIVILEGE_NOT_HELD.
527 */
528 PHANDLE phToken = NULL;
529 HANDLE hTokenLogon = INVALID_HANDLE_VALUE;
530 fRc = LogonUserW(pwszUser,
531 /*
532 * Because we have to deal with http://support.microsoft.com/kb/245683
533 * for NULL domain names when running on NT4 here, pass an empty string if so.
534 * However, passing FQDNs should work!
535 */
536 ((DWORD)(LOBYTE(LOWORD(GetVersion()))) < 5) /* < Windows 2000. */
537 ? L"" /* NT4 and older. */
538 : NULL, /* Windows 2000 and up. */
539 pwszPassword,
540 LOGON32_LOGON_INTERACTIVE,
541 LOGON32_PROVIDER_DEFAULT,
542 &hTokenLogon);
543
544 BOOL fFound = FALSE;
545 HANDLE hTokenUserDesktop = INVALID_HANDLE_VALUE;
546 if (fRc)
547 {
548 if (fFlags & RTPROC_FLAGS_SERVICE)
549 {
550 DWORD cbName = 0; /* Must be zero to query size! */
551 DWORD cbDomain = 0;
552 SID_NAME_USE sidNameUse = SidTypeUser;
553 fRc = LookupAccountNameW(NULL,
554 pwszUser,
555 NULL,
556 &cbName,
557 NULL,
558 &cbDomain,
559 &sidNameUse);
560 if (!fRc)
561 dwErr = GetLastError();
562 if ( !fRc
563 && dwErr == ERROR_INSUFFICIENT_BUFFER
564 && cbName > 0)
565 {
566 dwErr = NO_ERROR;
567
568 PSID pSID = (PSID)RTMemAlloc(cbName * sizeof(wchar_t));
569 AssertPtrReturn(pSID, VERR_NO_MEMORY);
570
571 /** @todo No way to allocate a PRTUTF16 directly? */
572 PRTUTF16 pwszDomain = NULL;
573 if (cbDomain > 0)
574 {
575 pwszDomain = (PRTUTF16)RTMemAlloc(cbDomain * sizeof(RTUTF16));
576 AssertPtrReturn(pwszDomain, VERR_NO_MEMORY);
577 }
578
579 /* Note: Also supports FQDNs! */
580 if ( LookupAccountNameW(NULL, /* lpSystemName */
581 pwszUser,
582 pSID,
583 &cbName,
584 pwszDomain,
585 &cbDomain,
586 &sidNameUse)
587 && IsValidSid(pSID))
588 {
589 fFound = rtProcFindProcessByName(
590#ifdef VBOX
591 "VBoxTray.exe",
592#else
593 "explorer.exe"
594#endif
595 pSID, &hTokenUserDesktop);
596 }
597 else
598 dwErr = GetLastError(); /* LookupAccountNameW() failed. */
599 RTMemFree(pSID);
600 if (pwszDomain != NULL)
601 RTUtf16Free(pwszDomain);
602 }
603 }
604 else /* !RTPROC_FLAGS_SERVICE */
605 {
606 /* Nothing to do here right now. */
607 }
608
609 /*
610 * If we didn't find a matching VBoxTray, just use the token we got
611 * above from LogonUserW(). This enables us to at least run processes with
612 * desktop interaction without UI.
613 */
614 phToken = fFound ? &hTokenUserDesktop : &hTokenLogon;
615
616 /*
617 * Useful KB articles:
618 * http://support.microsoft.com/kb/165194/
619 * http://support.microsoft.com/kb/184802/
620 * http://support.microsoft.com/kb/327618/
621 */
622 fRc = CreateProcessAsUserW(*phToken,
623 pwszExec,
624 pwszCmdLine,
625 NULL, /* pProcessAttributes */
626 NULL, /* pThreadAttributes */
627 TRUE, /* fInheritHandles */
628 dwCreationFlags,
629 pwszzBlock,
630 NULL, /* pCurrentDirectory */
631 pStartupInfo,
632 pProcInfo);
633 if (fRc)
634 dwErr = NO_ERROR;
635 else
636 dwErr = GetLastError(); /* CreateProcessAsUserW() failed. */
637
638 if (hTokenUserDesktop != INVALID_HANDLE_VALUE)
639 CloseHandle(hTokenUserDesktop);
640 CloseHandle(hTokenLogon);
641 }
642 else
643 dwErr = GetLastError(); /* LogonUserW() failed. */
644 }
645
646 if (dwErr != NO_ERROR)
647 {
648 /*
649 * Map some important or much used Windows error codes
650 * to our error codes.
651 */
652 switch (dwErr)
653 {
654 case ERROR_NOACCESS:
655 case ERROR_PRIVILEGE_NOT_HELD:
656 rc = VERR_PERMISSION_DENIED;
657 break;
658
659 case ERROR_PASSWORD_EXPIRED:
660 case ERROR_ACCOUNT_RESTRICTION: /* See: http://support.microsoft.com/kb/303846/ */
661 rc = VERR_LOGON_FAILURE;
662 break;
663
664 default:
665 /* Could trigger a debug assertion! */
666 rc = RTErrConvertFromWin32(dwErr);
667 break;
668 }
669 }
670 else
671 rc = VINF_SUCCESS;
672 return rc;
673}
674
675RTR3DECL(int) RTProcCreateEx(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
676 PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
677 const char *pszPassword, PRTPROCESS phProcess)
678{
679 /*
680 * Input validation
681 */
682 AssertPtrReturn(pszExec, VERR_INVALID_POINTER);
683 AssertReturn(*pszExec, VERR_INVALID_PARAMETER);
684 AssertReturn(!(fFlags & ~(RTPROC_FLAGS_DAEMONIZE_DEPRECATED | RTPROC_FLAGS_DETACHED | RTPROC_FLAGS_SERVICE)), VERR_INVALID_PARAMETER);
685 AssertReturn(!(fFlags & RTPROC_FLAGS_DETACHED) || !phProcess, VERR_INVALID_PARAMETER);
686 AssertReturn(hEnv != NIL_RTENV, VERR_INVALID_PARAMETER);
687 AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
688 AssertPtrNullReturn(pszAsUser, VERR_INVALID_POINTER);
689 AssertReturn(!pszAsUser || *pszAsUser, VERR_INVALID_PARAMETER);
690 AssertReturn(!pszPassword || pszAsUser, VERR_INVALID_PARAMETER);
691 AssertPtrNullReturn(pszPassword, VERR_INVALID_POINTER);
692 /** @todo search the PATH (add flag for this). */
693
694 /*
695 * Initialize the globals.
696 */
697 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
698 AssertRCReturn(rc, rc);
699
700 /*
701 * Get the file descriptors for the handles we've been passed.
702 *
703 * It seems there is no point in trying to convince a child process's CRT
704 * that any of the standard file handles is non-TEXT. So, we don't...
705 */
706 STARTUPINFOW StartupInfo;
707 RT_ZERO(StartupInfo);
708 StartupInfo.cb = sizeof(StartupInfo);
709 StartupInfo.dwFlags = STARTF_USESTDHANDLES;
710#if 1 /* The CRT should keep the standard handles up to date. */
711 StartupInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
712 StartupInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
713 StartupInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
714#else
715 StartupInfo.hStdInput = _get_osfhandle(0);
716 StartupInfo.hStdOutput = _get_osfhandle(1);
717 StartupInfo.hStdError = _get_osfhandle(2);
718#endif
719 PCRTHANDLE paHandles[3] = { phStdIn, phStdOut, phStdErr };
720 HANDLE *aphStds[3] = { &StartupInfo.hStdInput, &StartupInfo.hStdOutput, &StartupInfo.hStdError };
721 DWORD afInhStds[3] = { 0xffffffff, 0xffffffff, 0xffffffff };
722 for (int i = 0; i < 3; i++)
723 {
724 if (paHandles[i])
725 {
726 AssertPtrReturn(paHandles[i], VERR_INVALID_POINTER);
727 switch (paHandles[i]->enmType)
728 {
729 case RTHANDLETYPE_FILE:
730 *aphStds[i] = paHandles[i]->u.hFile != NIL_RTFILE
731 ? (HANDLE)RTFileToNative(paHandles[i]->u.hFile)
732 : INVALID_HANDLE_VALUE;
733 break;
734
735 case RTHANDLETYPE_PIPE:
736 *aphStds[i] = paHandles[i]->u.hPipe != NIL_RTPIPE
737 ? (HANDLE)RTPipeToNative(paHandles[i]->u.hPipe)
738 : INVALID_HANDLE_VALUE;
739 break;
740
741 case RTHANDLETYPE_SOCKET:
742 *aphStds[i] = paHandles[i]->u.hSocket != NIL_RTSOCKET
743 ? (HANDLE)RTSocketToNative(paHandles[i]->u.hSocket)
744 : INVALID_HANDLE_VALUE;
745 break;
746
747 default:
748 AssertMsgFailedReturn(("%d: %d\n", i, paHandles[i]->enmType), VERR_INVALID_PARAMETER);
749 }
750
751 /* Get the inheritability of the handle. */
752 if (*aphStds[i] != INVALID_HANDLE_VALUE)
753 {
754 if (!GetHandleInformation(*aphStds[i], &afInhStds[i]))
755 {
756 rc = RTErrConvertFromWin32(GetLastError());
757 AssertMsgFailedReturn(("%Rrc %p\n", rc, *aphStds[i]), rc);
758 }
759 }
760 }
761 }
762
763 /*
764 * Set the inheritability any handles we're handing the child.
765 */
766 rc = VINF_SUCCESS;
767 for (int i = 0; i < 3; i++)
768 if ( (afInhStds[i] != 0xffffffff)
769 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
770 {
771 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
772 {
773 rc = RTErrConvertFromWin32(GetLastError());
774 AssertMsgFailedBreak(("%Rrc %p\n", rc, *aphStds[i]));
775 }
776 }
777
778 /*
779 * Create the environment block, command line and convert the executable
780 * name.
781 */
782 PRTUTF16 pwszzBlock;
783 if (RT_SUCCESS(rc))
784 rc = RTEnvQueryUtf16Block(hEnv, &pwszzBlock);
785 if (RT_SUCCESS(rc))
786 {
787 PRTUTF16 pwszCmdLine;
788 rc = RTGetOptArgvToUtf16String(&pwszCmdLine, papszArgs, RTGETOPTARGV_CNV_QUOTE_MS_CRT);
789 if (RT_SUCCESS(rc))
790 {
791 PRTUTF16 pwszExec;
792 rc = RTStrToUtf16(pszExec, &pwszExec);
793 if (RT_SUCCESS(rc))
794 {
795 /*
796 * Get going...
797 */
798 DWORD dwCreationFlags = CREATE_UNICODE_ENVIRONMENT;
799 if (fFlags & RTPROC_FLAGS_DETACHED)
800 dwCreationFlags |= DETACHED_PROCESS;
801
802 PROCESS_INFORMATION ProcInfo;
803 RT_ZERO(ProcInfo);
804
805 /*
806 * Only use the normal CreateProcess stuff if we have no user name
807 * and we are not running from a (Windows) service. Otherwise use
808 * the more advanced version in rtProcCreateAsUserHlp().
809 */
810 if ( pszAsUser == NULL
811 && !(fFlags & RTPROC_FLAGS_SERVICE))
812 {
813 if (CreateProcessW(pwszExec,
814 pwszCmdLine,
815 NULL, /* pProcessAttributes */
816 NULL, /* pThreadAttributes */
817 TRUE, /* fInheritHandles */
818 dwCreationFlags,
819 pwszzBlock,
820 NULL, /* pCurrentDirectory */
821 &StartupInfo,
822 &ProcInfo))
823 rc = VINF_SUCCESS;
824 else
825 rc = RTErrConvertFromWin32(GetLastError());
826 }
827 else
828 {
829 /*
830 * Convert the additional parameters and use a helper
831 * function to do the actual work.
832 */
833 PRTUTF16 pwszUser;
834 rc = RTStrToUtf16(pszAsUser, &pwszUser);
835 if (RT_SUCCESS(rc))
836 {
837 PRTUTF16 pwszPassword;
838 rc = RTStrToUtf16(pszPassword ? pszPassword : "", &pwszPassword);
839 if (RT_SUCCESS(rc))
840 {
841 rc = rtProcCreateAsUserHlp(pwszUser, pwszPassword,
842 pwszExec, pwszCmdLine, pwszzBlock, dwCreationFlags,
843 &StartupInfo, &ProcInfo, fFlags);
844
845 RTUtf16Free(pwszPassword);
846 }
847 RTUtf16Free(pwszUser);
848 }
849 }
850 if (RT_SUCCESS(rc))
851 {
852 CloseHandle(ProcInfo.hThread);
853 if (phProcess)
854 {
855 /*
856 * Add the process to the child process list so
857 * RTProcWait can reuse and close the process handle.
858 */
859 rtProcWinAddPid(ProcInfo.dwProcessId, ProcInfo.hProcess);
860 *phProcess = ProcInfo.dwProcessId;
861 }
862 else
863 CloseHandle(ProcInfo.hProcess);
864 rc = VINF_SUCCESS;
865 }
866 RTUtf16Free(pwszExec);
867 }
868 RTUtf16Free(pwszCmdLine);
869 }
870 RTEnvFreeUtf16Block(pwszzBlock);
871 }
872
873 /* Undo any handle inherit changes. */
874 for (int i = 0; i < 3; i++)
875 if ( (afInhStds[i] != 0xffffffff)
876 && !(afInhStds[i] & HANDLE_FLAG_INHERIT))
877 {
878 if (!SetHandleInformation(*aphStds[i], HANDLE_FLAG_INHERIT, 0))
879 AssertMsgFailed(("%Rrc %p\n", RTErrConvertFromWin32(GetLastError()), *aphStds[i]));
880 }
881
882 return rc;
883}
884
885
886
887RTR3DECL(int) RTProcWait(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
888{
889 AssertReturn(!(fFlags & ~(RTPROCWAIT_FLAGS_BLOCK | RTPROCWAIT_FLAGS_NOBLOCK)), VERR_INVALID_PARAMETER);
890 int rc = RTOnce(&g_rtProcWinInitOnce, rtProcWinInitOnce, NULL, NULL);
891 AssertRCReturn(rc, rc);
892
893 /*
894 * Try find the process among the ones we've spawned, otherwise, attempt
895 * opening the specified process.
896 */
897 HANDLE hProcess = rtProcWinFindPid(Process);
898 if (hProcess == NULL)
899 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | SYNCHRONIZE, FALSE, Process);
900 if (hProcess != NULL)
901 {
902 /*
903 * Wait for it to terminate.
904 */
905 DWORD Millies = fFlags == RTPROCWAIT_FLAGS_BLOCK ? INFINITE : 0;
906 DWORD WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
907 while (WaitRc == WAIT_IO_COMPLETION)
908 WaitRc = WaitForSingleObjectEx(hProcess, Millies, TRUE);
909 switch (WaitRc)
910 {
911 /*
912 * It has terminated.
913 */
914 case WAIT_OBJECT_0:
915 {
916 DWORD dwExitCode;
917 if (GetExitCodeProcess(hProcess, &dwExitCode))
918 {
919 /** @todo the exit code can be special statuses. */
920 if (pProcStatus)
921 {
922 pProcStatus->enmReason = RTPROCEXITREASON_NORMAL;
923 pProcStatus->iStatus = (int)dwExitCode;
924 }
925 rtProcWinRemovePid(Process);
926 return VINF_SUCCESS;
927 }
928 break;
929 }
930
931 /*
932 * It hasn't terminated just yet.
933 */
934 case WAIT_TIMEOUT:
935 return VERR_PROCESS_RUNNING;
936
937 /*
938 * Something went wrong...
939 */
940 case WAIT_FAILED:
941 break;
942 case WAIT_ABANDONED:
943 AssertFailed();
944 return VERR_GENERAL_FAILURE;
945 default:
946 AssertMsgFailed(("WaitRc=%RU32\n", WaitRc));
947 return VERR_GENERAL_FAILURE;
948 }
949 }
950 DWORD dwErr = GetLastError();
951 if (dwErr == ERROR_INVALID_PARAMETER)
952 return VERR_PROCESS_NOT_FOUND;
953 return RTErrConvertFromWin32(dwErr);
954}
955
956
957RTR3DECL(int) RTProcWaitNoResume(RTPROCESS Process, unsigned fFlags, PRTPROCSTATUS pProcStatus)
958{
959 /** @todo this isn't quite right. */
960 return RTProcWait(Process, fFlags, pProcStatus);
961}
962
963
964RTR3DECL(int) RTProcTerminate(RTPROCESS Process)
965{
966 int rc = VINF_SUCCESS;
967 HANDLE hProcess = rtProcWinFindPid(Process);
968 if (hProcess != NULL)
969 {
970 if (!TerminateProcess(hProcess, 127))
971 rc = RTErrConvertFromWin32(GetLastError());
972 }
973 else
974 {
975 hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, Process);
976 if (hProcess != NULL)
977 {
978 BOOL fRc = TerminateProcess(hProcess, 127);
979 DWORD dwErr = GetLastError();
980 CloseHandle(hProcess);
981 if (!fRc)
982 rc = RTErrConvertFromWin32(dwErr);
983 }
984 }
985 return rc;
986}
987
988
989RTR3DECL(uint64_t) RTProcGetAffinityMask(void)
990{
991 DWORD_PTR dwProcessAffinityMask = 0xffffffff;
992 DWORD_PTR dwSystemAffinityMask;
993
994 BOOL fRc = GetProcessAffinityMask(GetCurrentProcess(), &dwProcessAffinityMask, &dwSystemAffinityMask);
995 Assert(fRc);
996
997 return dwProcessAffinityMask;
998}
999
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette