diff options
Diffstat (limited to 'src/VBox/Additions/common')
131 files changed, 13297 insertions, 5235 deletions
diff --git a/src/VBox/Additions/common/Makefile.kmk b/src/VBox/Additions/common/Makefile.kmk index b5852b0d..a14519e4 100644 --- a/src/VBox/Additions/common/Makefile.kmk +++ b/src/VBox/Additions/common/Makefile.kmk @@ -22,12 +22,8 @@ include $(KBUILD_PATH)/subheader.kmk ifndef VBOX_ONLY_TESTSUITE include $(PATH_SUB_CURRENT)/VBoxGuestLib/Makefile.kmk include $(PATH_SUB_CURRENT)/VBoxControl/Makefile.kmk - if1of ($(KBUILD_TARGET), freebsd linux os2 solaris win) - include $(PATH_SUB_CURRENT)/VBoxGuest/Makefile.kmk - endif - if1of ($(KBUILD_TARGET), freebsd linux os2 solaris win) - include $(PATH_SUB_CURRENT)/VBoxService/Makefile.kmk - endif + include $(PATH_SUB_CURRENT)/VBoxGuest/Makefile.kmk + include $(PATH_SUB_CURRENT)/VBoxService/Makefile.kmk ifdef VBOX_WITH_CROGL include $(PATH_SUB_CURRENT)/crOpenGL/Makefile.kmk endif diff --git a/src/VBox/Additions/common/VBoxControl/Makefile.kmk b/src/VBox/Additions/common/VBoxControl/Makefile.kmk index b42f928e..e2de0e20 100644 --- a/src/VBox/Additions/common/VBoxControl/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxControl/Makefile.kmk @@ -42,6 +42,7 @@ VBoxControl_SOURCES = \ VBoxControl.cpp VBoxControl_SOURCES.win = \ VBoxControl.rc +VBoxControl_LDFLAGS.darwin = -framework IOKit include $(FILE_KBUILD_SUB_FOOTER) diff --git a/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp b/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp index afa7b69c..13242a09 100644 --- a/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp +++ b/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2010 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -36,6 +36,11 @@ #ifdef VBOX_WITH_GUEST_PROPS # include <VBox/HostServices/GuestPropertySvc.h> #endif +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER +# include <VBox/VBoxGuest.h> +# include "../VBoxGuestLib/VBGLR3Internal.h" /* HACK ALERT! Using vbglR3DoIOCtl directly!! */ +#endif + /******************************************************************************* * Global Variables * @@ -70,6 +75,7 @@ enum VBoxControlUsage #ifdef RT_OS_WINDOWS GET_VIDEO_ACCEL, SET_VIDEO_ACCEL, + VIDEO_FLAGS, LIST_CUST_MODES, ADD_CUST_MODE, REMOVE_CUST_MODE, @@ -100,27 +106,30 @@ static void usage(enum VBoxControlUsage eWhich = USAGE_ALL) doUsage("suppress the logo", g_pszProgName, "--nologo ..."); RTPrintf("\n"); -/* Exclude the Windows bits from the test version. Anyone who needs to test - * them can fix this. */ + /* Exclude the Windows bits from the test version. Anyone who needs to + test them can fix this. */ #if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST) - if (GET_VIDEO_ACCEL == eWhich || eWhich == USAGE_ALL) + if (eWhich == GET_VIDEO_ACCEL || eWhich == USAGE_ALL) doUsage("", g_pszProgName, "getvideoacceleration"); - if (SET_VIDEO_ACCEL == eWhich || eWhich == USAGE_ALL) + if (eWhich == SET_VIDEO_ACCEL || eWhich == USAGE_ALL) doUsage("<on|off>", g_pszProgName, "setvideoacceleration"); - if (LIST_CUST_MODES == eWhich || eWhich == USAGE_ALL) + if (eWhich == VIDEO_FLAGS || eWhich == USAGE_ALL) + doUsage("<get|set|clear|delete> [hex mask]", g_pszProgName, "videoflags"); + if (eWhich == LIST_CUST_MODES || eWhich == USAGE_ALL) doUsage("", g_pszProgName, "listcustommodes"); - if (ADD_CUST_MODE == eWhich || eWhich == USAGE_ALL) + if (eWhich == ADD_CUST_MODE || eWhich == USAGE_ALL) doUsage("<width> <height> <bpp>", g_pszProgName, "addcustommode"); - if (REMOVE_CUST_MODE == eWhich || eWhich == USAGE_ALL) + if (eWhich == REMOVE_CUST_MODE || eWhich == USAGE_ALL) doUsage("<width> <height> <bpp>", g_pszProgName, "removecustommode"); - if (SET_VIDEO_MODE == eWhich || eWhich == USAGE_ALL) + if (eWhich == SET_VIDEO_MODE || eWhich == USAGE_ALL) doUsage("<width> <height> <bpp> <screen>", g_pszProgName, "setvideomode"); #endif #ifdef VBOX_WITH_GUEST_PROPS - if (GUEST_PROP == eWhich || eWhich == USAGE_ALL) + if (eWhich == GUEST_PROP || eWhich == USAGE_ALL) { doUsage("get <property> [--verbose]", g_pszProgName, "guestproperty"); doUsage("set <property> [<value> [--flags <flags>]]", g_pszProgName, "guestproperty"); + doUsage("delete|unset <property>", g_pszProgName, "guestproperty"); doUsage("enumerate [--patterns <patterns>]", g_pszProgName, "guestproperty"); doUsage("wait <patterns>", g_pszProgName, "guestproperty"); doUsage("[--timestamp <last timestamp>]"); @@ -128,7 +137,7 @@ static void usage(enum VBoxControlUsage eWhich = USAGE_ALL) } #endif #ifdef VBOX_WITH_SHARED_FOLDERS - if (GUEST_SHAREDFOLDERS == eWhich || eWhich == USAGE_ALL) + if (eWhich == GUEST_SHAREDFOLDERS || eWhich == USAGE_ALL) { doUsage("list [-automount]", g_pszProgName, "sharedfolder"); } @@ -575,7 +584,7 @@ static RTEXITCODE handleSetVideoMode(int argc, char *argv[]) scr = atoi(argv[3]); } - HMODULE hUser = GetModuleHandle("USER32"); + HMODULE hUser = GetModuleHandle("user32.dll"); if (hUser) { @@ -603,38 +612,120 @@ static RTEXITCODE handleSetVideoMode(int argc, char *argv[]) return RTEXITCODE_SUCCESS; } -HKEY getVideoKey(bool writable) +static int checkVBoxVideoKey(HKEY hkeyVideo) { - HKEY hkeyDeviceMap = 0; - HKEY hkeyVideo = 0; - LONG status; + char szValue[128]; + DWORD len = sizeof(szValue); + DWORD dwKeyType; + LONG status = RegQueryValueExA(hkeyVideo, "Device Description", NULL, &dwKeyType, + (LPBYTE)szValue, &len); - status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap); - if ((status != ERROR_SUCCESS) || !hkeyDeviceMap) + if (status == ERROR_SUCCESS) + { + /* WDDM has additional chars after "Adapter" */ + static char sszDeviceDescription[] = "VirtualBox Graphics Adapter"; + if (_strnicmp(szValue, sszDeviceDescription, sizeof(sszDeviceDescription) - sizeof(char)) == 0) + { + return VINF_SUCCESS; + } + } + + return VERR_NOT_FOUND; +} + +static HKEY getVideoKey(bool writable) +{ + HKEY hkeyDeviceMap = 0; + LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap); + if (status != ERROR_SUCCESS || !hkeyDeviceMap) { VBoxControlError("Error opening video device map registry key!\n"); return 0; } - char szVideoLocation[256]; + + HKEY hkeyVideo = 0; + ULONG iDevice; DWORD dwKeyType; - szVideoLocation[0] = 0; - DWORD len = sizeof(szVideoLocation); - status = RegQueryValueExA(hkeyDeviceMap, "\\Device\\Video0", NULL, &dwKeyType, (LPBYTE)szVideoLocation, &len); + /* - * This value will start with a weird value: \REGISTRY\Machine - * Make sure this is true. + * Scan all '\Device\VideoX' REG_SZ keys to find VBox video driver entry. + * 'ObjectNumberList' REG_BINARY is an array of 32 bit device indexes (X). */ - if ( (status == ERROR_SUCCESS) - && (dwKeyType == REG_SZ) - && (_strnicmp(szVideoLocation, "\\REGISTRY\\Machine", 17) == 0)) + + /* Get the 'ObjectNumberList' */ + ULONG numDevices = 0; + DWORD adwObjectNumberList[256]; + DWORD len = sizeof(adwObjectNumberList); + status = RegQueryValueExA(hkeyDeviceMap, "ObjectNumberList", NULL, &dwKeyType, (LPBYTE)&adwObjectNumberList[0], &len); + + if ( status == ERROR_SUCCESS + && dwKeyType == REG_BINARY) { - /* open that branch */ - status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0, KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo); + numDevices = len / sizeof(DWORD); } else { - VBoxControlError("Error opening registry key '%s'\n", &szVideoLocation[18]); + /* The list might not exists. Use 'MaxObjectNumber' REG_DWORD and build a list. */ + DWORD dwMaxObjectNumber = 0; + len = sizeof(dwMaxObjectNumber); + status = RegQueryValueExA(hkeyDeviceMap, "MaxObjectNumber", NULL, &dwKeyType, (LPBYTE)&dwMaxObjectNumber, &len); + + if ( status == ERROR_SUCCESS + && dwKeyType == REG_DWORD) + { + /* 'MaxObjectNumber' is inclusive. */ + numDevices = RT_MIN(dwMaxObjectNumber + 1, RT_ELEMENTS(adwObjectNumberList)); + for (iDevice = 0; iDevice < numDevices; iDevice++) + { + adwObjectNumberList[iDevice] = iDevice; + } + } } + + if (numDevices == 0) + { + /* Always try '\Device\Video0' as the old code did. Enum can be used in this case in principle. */ + adwObjectNumberList[0] = 0; + numDevices = 1; + } + + /* Scan device entries */ + for (iDevice = 0; iDevice < numDevices; iDevice++) + { + char szValueName[64]; + RTStrPrintf(szValueName, sizeof(szValueName), "\\Device\\Video%u", adwObjectNumberList[iDevice]); + + char szVideoLocation[256]; + len = sizeof(szVideoLocation); + status = RegQueryValueExA(hkeyDeviceMap, szValueName, NULL, &dwKeyType, (LPBYTE)&szVideoLocation[0], &len); + + /* This value starts with '\REGISTRY\Machine' */ + if ( status == ERROR_SUCCESS + && dwKeyType == REG_SZ + && _strnicmp(szVideoLocation, "\\REGISTRY\\Machine", 17) == 0) + { + status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0, + KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo); + if (status == ERROR_SUCCESS) + { + int rc = checkVBoxVideoKey(hkeyVideo); + if (RT_SUCCESS(rc)) + { + /* Found, return hkeyVideo to the caller. */ + break; + } + + RegCloseKey(hkeyVideo); + hkeyVideo = 0; + } + } + } + + if (hkeyVideo == 0) + { + VBoxControlError("Error opening video registry key!\n"); + } + RegCloseKey(hkeyDeviceMap); return hkeyVideo; } @@ -692,6 +783,135 @@ static RTEXITCODE handleSetVideoAcceleration(int argc, char *argv[]) return RTEXITCODE_SUCCESS; } +static RTEXITCODE videoFlagsGet(void) +{ + HKEY hkeyVideo = getVideoKey(false); + + if (hkeyVideo) + { + DWORD dwFlags = 0; + DWORD len = sizeof(dwFlags); + DWORD dwKeyType; + ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &len); + if (status != ERROR_SUCCESS) + RTPrintf("Video flags: default\n"); + else + RTPrintf("Video flags: 0x%08X\n", dwFlags); + RegCloseKey(hkeyVideo); + return RTEXITCODE_SUCCESS; + } + + return RTEXITCODE_FAILURE; +} + +static RTEXITCODE videoFlagsDelete(void) +{ + HKEY hkeyVideo = getVideoKey(true); + + if (hkeyVideo) + { + ULONG status = RegDeleteValueA(hkeyVideo, "VBoxVideoFlags"); + if (status != ERROR_SUCCESS) + VBoxControlError("Error %d deleting video flags.\n", status); + RegCloseKey(hkeyVideo); + return RTEXITCODE_SUCCESS; + } + + return RTEXITCODE_FAILURE; +} + +static RTEXITCODE videoFlagsModify(bool fSet, int argc, char *argv[]) +{ + if (argc != 1) + { + VBoxControlError("Mask required.\n"); + return RTEXITCODE_FAILURE; + } + + uint32_t u32Mask = 0; + int rc = RTStrToUInt32Full(argv[0], 16, &u32Mask); + if (RT_FAILURE(rc)) + { + VBoxControlError("Invalid video flags mask.\n"); + return RTEXITCODE_FAILURE; + } + + RTEXITCODE exitCode = RTEXITCODE_SUCCESS; + + HKEY hkeyVideo = getVideoKey(true); + if (hkeyVideo) + { + DWORD dwFlags = 0; + DWORD len = sizeof(dwFlags); + DWORD dwKeyType; + ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &len); + if (status != ERROR_SUCCESS) + { + dwFlags = 0; + } + + dwFlags = fSet? (dwFlags | u32Mask): + (dwFlags & ~u32Mask); + + status = RegSetValueExA(hkeyVideo, "VBoxVideoFlags", 0, REG_DWORD, (LPBYTE)&dwFlags, sizeof(dwFlags)); + if (status != ERROR_SUCCESS) + { + VBoxControlError("Error %d writing video flags.\n", status); + exitCode = RTEXITCODE_FAILURE; + } + + RegCloseKey(hkeyVideo); + } + else + { + exitCode = RTEXITCODE_FAILURE; + } + + return exitCode; +} + +static RTEXITCODE handleVideoFlags(int argc, char *argv[]) +{ + /* Must have a keyword and optional value (32 bit hex string). */ + if (argc != 1 && argc != 2) + { + VBoxControlError("Invalid number of arguments.\n"); + usage(VIDEO_FLAGS); + return RTEXITCODE_FAILURE; + } + + RTEXITCODE exitCode = RTEXITCODE_SUCCESS; + + if (RTStrICmp(argv[0], "get") == 0) + { + exitCode = videoFlagsGet(); + } + else if (RTStrICmp(argv[0], "delete") == 0) + { + exitCode = videoFlagsDelete(); + } + else if (RTStrICmp(argv[0], "set") == 0) + { + exitCode = videoFlagsModify(true, argc - 1, &argv[1]); + } + else if (RTStrICmp(argv[0], "clear") == 0) + { + exitCode = videoFlagsModify(false, argc - 1, &argv[1]); + } + else + { + VBoxControlError("Invalid command.\n"); + exitCode = RTEXITCODE_FAILURE; + } + + if (exitCode != RTEXITCODE_SUCCESS) + { + usage(VIDEO_FLAGS); + } + + return exitCode; +} + #define MAX_CUSTOM_MODES 128 /* the table of custom modes */ @@ -1092,6 +1312,51 @@ static RTEXITCODE setGuestProperty(int argc, char *argv[]) /** + * Deletes a guest property from the guest property store. + * This is accessed through the "VBoxGuestPropSvc" HGCM service. + * + * @returns Command exit code. + * @note see the command line API description for parameters + */ +static RTEXITCODE deleteGuestProperty(int argc, char *argv[]) +{ + /* + * Check the syntax. We can deduce the correct syntax from the number of + * arguments. + */ + bool usageOK = true; + const char *pszName = NULL; + if (argc < 1) + usageOK = false; + if (!usageOK) + { + usage(GUEST_PROP); + return RTEXITCODE_FAILURE; + } + /* This is always needed. */ + pszName = argv[0]; + + /* + * Do the actual setting. + */ + uint32_t u32ClientId = 0; + int rc = VbglR3GuestPropConnect(&u32ClientId); + if (!RT_SUCCESS(rc)) + VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc); + if (RT_SUCCESS(rc)) + { + rc = VbglR3GuestPropDelete(u32ClientId, pszName); + if (!RT_SUCCESS(rc)) + VBoxControlError("Failed to delete the property value, error %Rrc\n", rc); + } + + if (u32ClientId != 0) + VbglR3GuestPropDisconnect(u32ClientId); + return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/** * Enumerates the properties in the guest property store. * This is accessed through the "VBoxGuestPropSvc" HGCM service. * @@ -1311,6 +1576,8 @@ static RTEXITCODE handleGuestProperty(int argc, char *argv[]) return getGuestProperty(argc - 1, argv + 1); else if (!strcmp(argv[0], "set")) return setGuestProperty(argc - 1, argv + 1); + else if (!strcmp(argv[0], "delete") || !strcmp(argv[0], "unset")) + return deleteGuestProperty(argc - 1, argv + 1); else if (!strcmp(argv[0], "enumerate")) return enumGuestProperty(argc - 1, argv + 1); else if (!strcmp(argv[0], "wait")) @@ -1429,6 +1696,32 @@ static RTEXITCODE handleWriteCoreDump(int argc, char *argv[]) } #endif +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER +/** + * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help} + */ +static RTEXITCODE handleDpc(int argc, char *argv[]) +{ +# ifndef VBOX_CONTROL_TEST + int rc; + for (int i = 0; i < 30; i++) + { + rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_DPC_LATENCY_CHECKER, NULL, 0); + if (RT_FAILURE(rc)) + break; + RTPrintf("%d\n", i); + } +# else + int rc = VERR_NOT_IMPLEMENTED; +# endif + if (RT_FAILURE(rc)) + return VBoxControlError("Error. rc=%Rrc\n", rc); + RTPrintf("Samples collection completed.\n"); + return RTEXITCODE_SUCCESS; +} +#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ + + /** * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: takesnapshot} */ @@ -1486,38 +1779,6 @@ static RTEXITCODE handleHelp(int argc, char *argv[]) return RTEXITCODE_SUCCESS; } -#ifdef VBOX_WITH_DPC_LATENCY_CHECKER -#include "..\VBoxGuestLib\VBGLR3Internal.h" - -static RTEXITCODE handleDpc(int argc, char *argv[]) -{ -#ifndef VBOX_CONTROL_TEST - int rc = VINF_SUCCESS; - int i; - for (i = 0; i < 30; i++) - { - rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_DPC, NULL, 0); - if (RT_FAILURE(rc)) - { - break; - } - RTPrintf("%d\n", i); - } -#else - int rc = VERR_NOT_IMPLEMENTED; -#endif - if (RT_SUCCESS(rc)) - { - RTPrintf("Samples collection completed.\n"); - return RTEXITCODE_SUCCESS; - } - else - { - VBoxControlError("Error. rc=%Rrc\n", rc); - return RTEXITCODE_FAILURE; - } -} -#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ /** command handler type */ typedef DECLCALLBACK(RTEXITCODE) FNVBOXCTRLCMDHANDLER(int argc, char *argv[]); @@ -1533,6 +1794,7 @@ struct COMMANDHANDLER #if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST) { "getvideoacceleration", handleGetVideoAcceleration }, { "setvideoacceleration", handleSetVideoAcceleration }, + { "videoflags", handleVideoFlags }, { "listcustommodes", handleListCustomModes }, { "addcustommode", handleAddCustomMode }, { "removecustommode", handleRemoveCustomMode }, @@ -1547,6 +1809,9 @@ struct COMMANDHANDLER #if !defined(VBOX_CONTROL_TEST) { "writecoredump", handleWriteCoreDump }, #endif +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER + { "dpc", handleDpc }, +#endif { "takesnapshot", handleTakeSnapshot }, { "savestate", handleSaveState }, { "suspend", handleSuspend }, @@ -1555,9 +1820,6 @@ struct COMMANDHANDLER { "powerdown", handlePowerOff }, { "getversion", handleVersion }, { "version", handleVersion }, -#ifdef VBOX_WITH_DPC_LATENCY_CHECKER - { "dpc", handleDpc }, -#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ { "help", handleHelp } }; diff --git a/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp b/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp index 7536162b..11185a74 100644 --- a/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp +++ b/src/VBox/Additions/common/VBoxControl/testcase/tstVBoxControl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -97,6 +97,14 @@ VBGLR3DECL(int) VbglR3GuestPropRead(uint32_t u32ClientId, return VINF_SUCCESS; } +VBGLR3DECL(int) VbglR3GuestPropDelete(uint32_t u32ClientId, + const char *pszName) +{ + RTPrintf("Called DEL_PROP, client %d, name %s...\n", + u32ClientId, pszName); + return VINF_SUCCESS; +} + struct VBGLR3GUESTPROPENUM { uint32_t u32; diff --git a/src/VBox/Additions/common/VBoxGuest/Makefile.kmk b/src/VBox/Additions/common/VBoxGuest/Makefile.kmk index 8ff81a91..ecaa22c4 100644 --- a/src/VBox/Additions/common/VBoxGuest/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxGuest/Makefile.kmk @@ -19,22 +19,26 @@ SUB_DEPTH = ../../../../.. include $(KBUILD_PATH)/subheader.kmk -if1of ($(KBUILD_TARGET), freebsd $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linux,) os2 solaris win) +if1of ($(KBUILD_TARGET), darwin freebsd haiku $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linux,) os2 solaris win) # # VBoxGuest - The Guest Additions Driver. # SYSMODS += VBoxGuest VBoxGuest_TEMPLATE = VBOXGUESTR0 VBoxGuest_NAME.freebsd = vboxguest + VBoxGuest_NAME.haiku = vboxguest VBoxGuest_NAME.linux = vboxguest VBoxGuest_NAME.solaris = vboxguest + VBoxGuest_INST.darwin = $(INST_ADDITIONS)VBoxGuest.kext/Contents/MacOS/ ifdef VBOX_SIGN_ADDITIONS # See Additions/WINNT/Makefile.kmk? VBoxGuest_INSTTYPE.win = none VBoxGuest_DEBUG_INSTTYPE.win = both endif + VBoxGuest_DEFS.haiku = VBOX_SVN_REV=$(VBOX_SVN_REV) _KERNEL_MODE=1 VBoxGuest_DEFS.linux = KBUILD_MODNAME=KBUILD_STR\(vboxguest\) KBUILD_BASENAME=KBUILD_STR\(vboxguest\) DEBUG_HASH=2 DEBUG_HASH2=3 EXPORT_SYMTAB VBoxGuest_DEFS.solaris = VBOX_SVN_REV=$(VBOX_SVN_REV) VBoxGuest_DEFS.win = # VBOX_WITH_VRDP_SESSION_HANDLING + VBoxGuest_DEFS.darwin = VBOX_GUESTDRV_WITH_RELEASE_LOGGER ifeq ($(KBUILD_TYPE),release) # Allow stopping/removing the driver without a reboot # in debug mode; this is very useful for testing the shutdown stuff! @@ -48,7 +52,9 @@ if1of ($(KBUILD_TARGET), freebsd $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linu $(if $(VBOX_WITH_DPC_LATENCY_CHECKER),VBOX_WITH_DPC_LATENCY_CHECKER,) VBoxGuest_DEPS.solaris += $(VBOX_SVN_REV_KMK) VBoxGuest_DEPS.linux += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEPS.haiku += $(VBOX_SVN_REV_HEADER) VBoxGuest_DEPS.freebsd += $(VBOX_SVN_REV_HEADER) + VBoxGuest_DEPS.darwin += $(VBOX_SVN_REV_HEADER) VBoxGuest_DEFS = VBGL_VBOXGUEST VBOX_WITH_HGCM VBoxGuest_INCS = . VBoxGuest_INCS.freebsd = $(VBoxGuest_0_OUTDIR) $(PATH_STAGE)/gen-sys-hdrs @@ -74,7 +80,7 @@ if1of ($(KBUILD_TARGET), freebsd $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linu VBoxGuest.cpp_SDKS = $(VBOX_WINDDK_GST_WLH) endif endif # win - ifn1of ($(KBUILD_TARGET), linux freebsd solaris) + ifn1of ($(KBUILD_TARGET), linux freebsd solaris haiku) VBoxGuest_SOURCES = VBoxGuest-$(KBUILD_TARGET).cpp else VBoxGuest_SOURCES = VBoxGuest-$(KBUILD_TARGET).c @@ -94,6 +100,16 @@ if1of ($(KBUILD_TARGET), freebsd $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linu $(PATH_STAGE)/gen-sys-hdrs/pci_if.h \ $(PATH_STAGE)/gen-sys-hdrs/bus_if.h \ $(PATH_STAGE)/gen-sys-hdrs/device_if.h + ifeq ($(KBUILD_TARGET),haiku) + # Haiku drivers cannot export symbols for other drivers, but modules can. + # Therefore vboxguest is a module containing the ring-0 guest lib, and vboxdev/vboxsf + # use this module to access the guest lib + SYSMODS += VBoxDev + VBoxDev_TEMPLATE = VBOXGUESTR0 + VBoxDev_NAME = vboxdev + VBoxDev_DEFS = VBOX_SVN_REV=$(VBOX_SVN_REV) _KERNEL_MODE=1 VBGL_VBOXGUEST VBOX_WITH_HGCM IN_RING0 + VBoxDev_SOURCES = VBoxDev-haiku.c VBoxGuest-haiku-stubs.c + endif else # OS/2: # The library order is crucial, so a bit of trickery is necessary. # A library is used to make sure that VBoxGuestA-os2.asm is first in the link. (temporary hack?) @@ -151,6 +167,32 @@ if1of ($(KBUILD_TARGET), freebsd $(if $(defined VBOX_WITH_ADDITION_DRIVERS),linu endif # win endif # enabled + +ifeq ($(KBUILD_TARGET), darwin) + # Files necessary to make a darwin kernel extension bundle. + INSTALLS += VBoxGuest.kext + VBoxGuest.kext_INST = $(INST_ADDITIONS)/VBoxGuest.kext/Contents/ + VBoxGuest.kext_SOURCES = $(VBoxGuest.kext_0_OUTDIR)/Info.plist + VBoxGuest.kext_CLEAN = $(VBoxGuest.kext_0_OUTDIR)/Info.plist + +$$(VBoxGuest.kext_0_OUTDIR)/Info.plist: \ + $(PATH_SUB_CURRENT)/darwin/Info.plist \ + $(VBOX_VERSION_MK) | $$(dir $$@) + $(call MSG_GENERATE,VBoxGuest,$@,$<) + $(QUIET)$(RM) -f $@ + $(QUIET)$(SED) \ + -e 's/@VBOX_VERSION_STRING@/$(VBOX_VERSION_STRING)/g' \ + -e 's/@VBOX_VERSION_MAJOR@/$(VBOX_VERSION_MAJOR)/g' \ + -e 's/@VBOX_VERSION_MINOR@/$(VBOX_VERSION_MINOR)/g' \ + -e 's/@VBOX_VERSION_BUILD@/$(VBOX_VERSION_BUILD)/g' \ + -e 's/@VBOX_VENDOR@/$(VBOX_VENDOR)/g' \ + -e 's/@VBOX_PRODUCT@/$(VBOX_PRODUCT)/g' \ + -e 's/@VBOX_C_YEAR@/$(VBOX_C_YEAR)/g' \ + --output $@ \ + $< +endif # darwin + + ifeq ($(KBUILD_TARGET),linux) # # Install the source files and script(s). diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c b/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c new file mode 100644 index 00000000..82163323 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxDev-haiku.c @@ -0,0 +1,446 @@ +/* $Id: VBoxDev-haiku.c $ */ +/** @file + * VBoxGuest kernel driver, Haiku Guest Additions, implementation. + */ + +/* + * Copyright (C) 2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <OS.h> +#include <Drivers.h> +#include <KernelExport.h> +#include <PCI.h> + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> + +#define DRIVER_NAME "vboxdev" +#define DEVICE_NAME "misc/vboxguest" +#define MODULE_NAME "generic/vboxguest" + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static status_t VBoxGuestHaikuOpen(const char *name, uint32 flags, void **cookie); +static status_t VBoxGuestHaikuClose(void *cookie); +static status_t VBoxGuestHaikuFree(void *cookie); +static status_t VBoxGuestHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len); +static status_t VBoxGuestHaikuSelect(void *cookie, uint8 event, uint32 ref, selectsync *sync); +static status_t VBoxGuestHaikuDeselect(void *cookie, uint8 event, selectsync *sync); +static status_t VBoxGuestHaikuWrite(void *cookie, off_t position, const void *data, size_t *numBytes); +static status_t VBoxGuestHaikuRead(void *cookie, off_t position, void *data, size_t *numBytes); + +static device_hooks g_VBoxGuestHaikuDeviceHooks = +{ + VBoxGuestHaikuOpen, + VBoxGuestHaikuClose, + VBoxGuestHaikuFree, + VBoxGuestHaikuIOCtl, + VBoxGuestHaikuRead, + VBoxGuestHaikuWrite, + VBoxGuestHaikuSelect, + VBoxGuestHaikuDeselect, +}; + + +/** + * Driver open hook. + * + * @param name The name of the device as returned by publish_devices. + * @param flags Open flags. + * @param cookie Where to store the session pointer. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuOpen(const char *name, uint32 flags, void **cookie) +{ + int rc; + PVBOXGUESTSESSION pSession; + + LogFlow((DRIVER_NAME ":VBoxGuestHaikuOpen\n")); + + /* + * Create a new session. + */ + rc = VBoxGuestCreateUserSession(&g_DevExt, &pSession); + if (RT_SUCCESS(rc)) + { + Log((DRIVER_NAME ":VBoxGuestHaikuOpen success: g_DevExt=%p pSession=%p rc=%d pid=%d\n",&g_DevExt, pSession, rc,(int)RTProcSelf())); + ASMAtomicIncU32(&cUsers); + *cookie = pSession; + return B_OK; + } + + LogRel((DRIVER_NAME ":VBoxGuestHaikuOpen: failed. rc=%d\n", rc)); + return RTErrConvertToErrno(rc); +} + + +/** + * Driver close hook. + * @param cookie The session. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuClose(void *cookie) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + Log(("VBoxGuestHaikuClose: pSession=%p\n", pSession)); + + /** @todo r=ramshankar: should we really be using the session spinlock here? */ + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + /* @todo we don't know if it belongs to this session!! */ + if (sState.selectSync) + { + //dprintf(DRIVER_NAME "close: unblocking select %p %x\n", sState.selectSync, sState.selectEvent); + notify_select_event(sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = (void *)NULL; + } + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + return B_OK; +} + + +/** + * Driver free hook. + * @param cookie The session. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuFree(void *cookie) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + Log(("VBoxGuestHaikuFree: pSession=%p\n", pSession)); + + /* + * Close the session if it's still hanging on to the device... + */ + if (VALID_PTR(pSession)) + { + VBoxGuestCloseSession(&g_DevExt, pSession); + ASMAtomicDecU32(&cUsers); + } + else + Log(("VBoxGuestHaikuFree: si_drv1=%p!\n", pSession)); + return B_OK; +} + + +/** + * Driver IOCtl entry. + * @param cookie The session. + * @param op The operation to perform. + * @param data The data associated with the operation. + * @param len Size of the data in bytes. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuIOCtl(void *cookie, uint32 op, void *data, size_t len) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl cookie=%p op=0x%08x data=%p len=%lu)\n", cookie, op, data, len)); + + int rc = B_OK; + + /* + * Validate the input. + */ + if (RT_UNLIKELY(!VALID_PTR(pSession))) + return EINVAL; + + /* + * Validate the request wrapper. + */ +#if 0 + if (IOCPARM_LEN(ulCmd) != sizeof(VBGLBIGREQ)) + { + Log((DRIVER_NAME ": VBoxGuestHaikuIOCtl: bad request %lu size=%lu expected=%d\n", ulCmd, IOCPARM_LEN(ulCmd), + sizeof(VBGLBIGREQ))); + return ENOTTY; + } +#endif + + if (RT_UNLIKELY(len > _1M * 16)) + { + dprintf(DRIVER_NAME ": VBoxGuestHaikuIOCtl: bad size %#x; pArg=%p Cmd=%lu.\n", (unsigned)len, data, op); + return EINVAL; + } + + /* + * Read the request. + */ + void *pvBuf = NULL; + if (RT_LIKELY(len > 0)) + { + pvBuf = RTMemTmpAlloc(len); + if (RT_UNLIKELY(!pvBuf)) + { + LogRel((DRIVER_NAME ":VBoxGuestHaikuIOCtl: RTMemTmpAlloc failed to alloc %d bytes.\n", len)); + return ENOMEM; + } + + /** @todo r=ramshankar: replace with RTR0MemUserCopyFrom() */ + rc = user_memcpy(pvBuf, data, len); + if (RT_UNLIKELY(rc < 0)) + { + RTMemTmpFree(pvBuf); + LogRel((DRIVER_NAME ":VBoxGuestHaikuIOCtl: user_memcpy failed; pvBuf=%p data=%p op=%d. rc=%d\n", pvBuf, data, op, rc)); + return EFAULT; + } + if (RT_UNLIKELY(!VALID_PTR(pvBuf))) + { + RTMemTmpFree(pvBuf); + LogRel((DRIVER_NAME ":VBoxGuestHaikuIOCtl: pvBuf invalid pointer %p\n", pvBuf)); + return EINVAL; + } + } + Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: pSession=%p pid=%d.\n", pSession,(int)RTProcSelf())); + + /* + * Process the IOCtl. + */ + size_t cbDataReturned; + rc = VBoxGuestCommonIOCtl(op, &g_DevExt, pSession, pvBuf, len, &cbDataReturned); + if (RT_SUCCESS(rc)) + { + rc = 0; + if (RT_UNLIKELY(cbDataReturned > len)) + { + Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: too much output data %d expected %d\n", cbDataReturned, len)); + cbDataReturned = len; + } + if (cbDataReturned > 0) + { + rc = user_memcpy(data, pvBuf, cbDataReturned); + if (RT_UNLIKELY(rc < 0)) + { + Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: user_memcpy failed; pvBuf=%p pArg=%p Cmd=%lu. rc=%d\n", pvBuf, data, op, rc)); + rc = EFAULT; + } + } + } + else + { + Log((DRIVER_NAME ":VBoxGuestHaikuIOCtl: VBoxGuestCommonIOCtl failed. rc=%d\n", rc)); + rc = EFAULT; + } + RTMemTmpFree(pvBuf); + return rc; +} + + +/** + * Driver select hook. + * + * @param cookie The session. + * @param event The event. + * @param ref ??? + * @param sync ??? + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuSelect(void *cookie, uint8 event, uint32 ref, selectsync *sync) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + status_t err = B_OK; + + switch (event) + { + case B_SELECT_READ: + break; + default: + return EINVAL; + } + + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + pSession->u32MousePosChangedSeq = u32CurSeq; + notify_select_event(sync, event); + } + else if (sState.selectSync == NULL) + { + sState.selectEvent = (uint8_t)event; + sState.selectRef = (uint32_t)ref; + sState.selectSync = (void *)sync; + } + else + err = B_WOULD_BLOCK; + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + + return err; +} + + +/** + * Driver deselect hook. + * @param cookie The session. + * @param event The event. + * @param sync ??? + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuDeselect(void *cookie, uint8 event, selectsync *sync) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + status_t err = B_OK; + //dprintf(DRIVER_NAME "deselect(,%d,%p)\n", event, sync); + + RTSpinlockAcquire(g_DevExt.SessionSpinlock); + + if (sState.selectSync == sync) + { + //dprintf(DRIVER_NAME "deselect: dropping: %p %x\n", sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = NULL; + } + else + err = B_OK; + + RTSpinlockRelease(g_DevExt.SessionSpinlock); + return err; +} + + +/** + * Driver write hook. + * @param cookie The session. + * @param position The offset. + * @param data Pointer to the data. + * @param numBytes Where to store the number of bytes written. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuWrite(void *cookie, off_t position, const void *data, size_t *numBytes) +{ + *numBytes = 0; + return B_OK; +} + + +/** + * Driver read hook. + * @param cookie The session. + * @param position The offset. + * @param data Pointer to the data. + * @param numBytes Where to store the number of bytes read. + * + * @return Haiku status code. + */ +static status_t VBoxGuestHaikuRead(void *cookie, off_t position, void *data, size_t *numBytes) +{ + PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)cookie; + + if (*numBytes == 0) + return B_OK; + + uint32_t u32CurSeq = ASMAtomicUoReadU32(&g_DevExt.u32MousePosChangedSeq); + if (pSession->u32MousePosChangedSeq != u32CurSeq) + { + pSession->u32MousePosChangedSeq = u32CurSeq; + *numBytes = 1; + return B_OK; + } + + *numBytes = 0; + return B_OK; +} + + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +status_t init_hardware() +{ + return get_module(MODULE_NAME, (module_info **)&g_VBoxGuest); +} + +status_t init_driver() +{ + return B_OK; +} + +device_hooks* find_device(const char *name) +{ + static device_hooks g_VBoxGuestHaikuDeviceHooks = + { + VBoxGuestHaikuOpen, + VBoxGuestHaikuClose, + VBoxGuestHaikuFree, + VBoxGuestHaikuIOCtl, + VBoxGuestHaikuRead, + VBoxGuestHaikuWrite, + VBoxGuestHaikuSelect, + VBoxGuestHaikuDeselect + }; + return &g_VBoxGuestHaikuDeviceHooks; +} + +const char** publish_devices() +{ + static const char *devices[] = { DEVICE_NAME, NULL }; + return devices; +} + +void uninit_driver() +{ + put_module(MODULE_NAME); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp new file mode 100644 index 00000000..3eb7dab4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-darwin.cpp @@ -0,0 +1,1085 @@ +/* $Id: VBoxGuest-darwin.cpp $ */ +/** @file + * VBoxGuest - Darwin Specifics. + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define LOG_GROUP LOG_GROUP_VBGD +/* + * Deal with conflicts first. + * PVM - BSD mess, that FreeBSD has correct a long time ago. + * iprt/types.h before sys/param.h - prevents UINT32_C and friends. + */ +#include <iprt/types.h> +#include <sys/param.h> +#undef PVM + +#include <IOKit/IOLib.h> /* Assert as function */ + +#include <VBox/version.h> +#include <iprt/asm.h> +#include <iprt/initterm.h> +#include <iprt/assert.h> +#include <iprt/spinlock.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> +#include <iprt/alloc.h> +#include <iprt/power.h> +#include <VBox/err.h> +#include <VBox/log.h> + +#include <mach/kmod.h> +#include <miscfs/devfs/devfs.h> +#include <sys/conf.h> +#include <sys/errno.h> +#include <sys/ioccom.h> +#include <sys/malloc.h> +#include <sys/proc.h> +#include <sys/kauth.h> +#include <IOKit/IOService.h> +#include <IOKit/IOUserClient.h> +#include <IOKit/pwr_mgt/RootDomain.h> +#include <IOKit/pci/IOPCIDevice.h> +#include <IOKit/IOBufferMemoryDescriptor.h> +#include <IOKit/IOFilterInterruptEventSource.h> +#include "VBoxGuestInternal.h" + + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ + +/** The system device node name. */ +#define DEVICE_NAME_SYS "vboxguest" +/** The user device node name. */ +#define DEVICE_NAME_USR "vboxguestu" + + + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +RT_C_DECLS_BEGIN +static kern_return_t VbgdDarwinStart(struct kmod_info *pKModInfo, void *pvData); +static kern_return_t VbgdDarwinStop(struct kmod_info *pKModInfo, void *pvData); +static int VbgdDarwinCharDevRemove(void); + +static int VbgdDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VbgdDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess); +static int VbgdDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess); +static int VbgdDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess); + +static int VbgdDarwinErr2DarwinErr(int rc); + +static IOReturn VbgdDarwinSleepHandler(void *pvTarget, void *pvRefCon, UInt32 uMessageType, IOService *pProvider, void *pvMessageArgument, vm_size_t argSize); +RT_C_DECLS_END + + +/******************************************************************************* +* Structures and Typedefs * +*******************************************************************************/ +/** + * The service class for handling the VMMDev PCI device. + * + * Instantiated when the module is loaded (and on PCI hotplugging?). + */ +class org_virtualbox_VBoxGuest : public IOService +{ + OSDeclareDefaultStructors(org_virtualbox_VBoxGuest); + +private: + IOPCIDevice *m_pIOPCIDevice; + IOMemoryMap *m_pMap; + IOFilterInterruptEventSource *m_pInterruptSrc; + + bool setupVmmDevInterrupts(IOService *pProvider); + bool disableVmmDevInterrupts(void); + bool isVmmDev(IOPCIDevice *pIOPCIDevice); + +public: + virtual bool start(IOService *pProvider); + virtual void stop(IOService *pProvider); + virtual bool terminate(IOOptionBits fOptions); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuest, IOService); + + +/** + * An attempt at getting that clientDied() notification. + * I don't think it'll work as I cannot figure out where/what creates the correct + * port right. + * + * Instantiated when userland does IOServiceOpen(). + */ +class org_virtualbox_VBoxGuestClient : public IOUserClient +{ + OSDeclareDefaultStructors(org_virtualbox_VBoxGuestClient); + +private: + PVBOXGUESTSESSION m_pSession; /**< The session. */ + task_t m_Task; /**< The client task. */ + org_virtualbox_VBoxGuest *m_pProvider; /**< The service provider. */ + +public: + virtual bool initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type); + virtual bool start(IOService *pProvider); + static void sessionClose(RTPROCESS Process); + virtual IOReturn clientClose(void); +}; + +OSDefineMetaClassAndStructors(org_virtualbox_VBoxGuestClient, IOUserClient); + + + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** + * Declare the module stuff. + */ +RT_C_DECLS_BEGIN +extern kern_return_t _start(struct kmod_info *pKModInfo, void *pvData); +extern kern_return_t _stop(struct kmod_info *pKModInfo, void *pvData); + +KMOD_EXPLICIT_DECL(VBoxGuest, VBOX_VERSION_STRING, _start, _stop) +DECLHIDDEN(kmod_start_func_t *) _realmain = VbgdDarwinStart; +DECLHIDDEN(kmod_stop_func_t *) _antimain = VbgdDarwinStop; +DECLHIDDEN(int) _kext_apple_cc = __APPLE_CC__; +RT_C_DECLS_END + + +/** + * Device extention & session data association structure. + */ +static VBOXGUESTDEVEXT g_DevExt; + +/** + * The character device switch table for the driver. + */ +static struct cdevsw g_DevCW = +{ + /*.d_open = */ VbgdDarwinOpen, + /*.d_close = */ VbgdDarwinClose, + /*.d_read = */ eno_rdwrt, + /*.d_write = */ eno_rdwrt, + /*.d_ioctl = */ VbgdDarwinIOCtl, + /*.d_stop = */ eno_stop, + /*.d_reset = */ eno_reset, + /*.d_ttys = */ NULL, + /*.d_select = */ eno_select, + /*.d_mmap = */ eno_mmap, + /*.d_strategy = */ eno_strat, + /*.d_getc = */ eno_getc, + /*.d_putc = */ eno_putc, + /*.d_type = */ 0 +}; + +/** Major device number. */ +static int g_iMajorDeviceNo = -1; +/** Registered devfs device handle. */ +static void *g_hDevFsDeviceSys = NULL; +/** Registered devfs device handle for the user device. */ +static void *g_hDevFsDeviceUsr = NULL; /**< @todo 4 later */ + +/** Spinlock protecting g_apSessionHashTab. */ +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; +/** Hash table */ +static PVBOXGUESTSESSION g_apSessionHashTab[19]; +/** Calculates the index into g_apSessionHashTab.*/ +#define SESSION_HASH(pid) ((pid) % RT_ELEMENTS(g_apSessionHashTab)) +/** The number of open sessions. */ +static int32_t volatile g_cSessions = 0; +/** The number of IOService class instances. */ +static bool volatile g_fInstantiated = 0; +/** The notifier handle for the sleep callback handler. */ +static IONotifier *g_pSleepNotifier = NULL; + + + +/** + * Start the kernel module. + */ +static kern_return_t VbgdDarwinStart(struct kmod_info *pKModInfo, void *pvData) +{ +#ifdef DEBUG + printf("VbgdDarwinStart\n"); +#endif + + /* + * Initialize IPRT. + */ + int rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + printf("VBoxGuest: RTR0Init failed with rc=%d\n", rc); + return KMOD_RETURN_FAILURE; + } + + return KMOD_RETURN_SUCCESS; +} + + +/* Register VBoxGuest char device */ +static int VbgdDarwinCharDevInit(void) +{ + int rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestDarwin"); + if (RT_FAILURE(rc)) + { + return KMOD_RETURN_FAILURE; + } + + /* + * Registering ourselves as a character device. + */ + g_iMajorDeviceNo = cdevsw_add(-1, &g_DevCW); + if (g_iMajorDeviceNo < 0) + { + VbgdDarwinCharDevRemove(); + return KMOD_RETURN_FAILURE; + } + + g_hDevFsDeviceSys = devfs_make_node(makedev(g_iMajorDeviceNo, 0), DEVFS_CHAR, + UID_ROOT, GID_WHEEL, 0666, DEVICE_NAME_SYS); + if (!g_hDevFsDeviceSys) + { + VbgdDarwinCharDevRemove(); + return KMOD_RETURN_FAILURE; + } + + /* Register a sleep/wakeup notification callback */ + g_pSleepNotifier = registerPrioritySleepWakeInterest(&VbgdDarwinSleepHandler, &g_DevExt, NULL); + if (g_pSleepNotifier == NULL) + { + VbgdDarwinCharDevRemove(); + return KMOD_RETURN_FAILURE; + } + + return KMOD_RETURN_SUCCESS; +} + + +/** + * Stop the kernel module. + */ +static kern_return_t VbgdDarwinStop(struct kmod_info *pKModInfo, void *pvData) +{ + RTR0TermForced(); +#ifdef DEBUG + printf("VbgdDarwinStop - done\n"); +#endif + return KMOD_RETURN_SUCCESS; +} + + +/* Unregister VBoxGuest char device */ +static int +VbgdDarwinCharDevRemove(void) +{ + int rc = KMOD_RETURN_SUCCESS; + + if (g_pSleepNotifier) + { + g_pSleepNotifier->remove(); + g_pSleepNotifier = NULL; + } + + if (g_hDevFsDeviceSys) + { + devfs_remove(g_hDevFsDeviceSys); + g_hDevFsDeviceSys = NULL; + } + + if (g_hDevFsDeviceUsr) + { + devfs_remove(g_hDevFsDeviceUsr); + g_hDevFsDeviceUsr = NULL; + } + + if (g_iMajorDeviceNo != -1) + { + int rc2 = cdevsw_remove(g_iMajorDeviceNo, &g_DevCW); + Assert(rc2 == g_iMajorDeviceNo); + g_iMajorDeviceNo = -1; + } + + if (g_Spinlock != NIL_RTSPINLOCK) + { + int rc2 = RTSpinlockDestroy(g_Spinlock); AssertRC(rc2); + g_Spinlock = NIL_RTSPINLOCK; + } + + return rc; +} + + +/** + * Device open. Called on open /dev/vboxguest and (later) /dev/vboxguestu. + * + * @param Dev The device number. + * @param fFlags ???. + * @param fDevType ???. + * @param pProcess The process issuing this request. + */ +static int VbgdDarwinOpen(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + /* + * Only two minor devices numbers are allowed. + */ + if (minor(Dev) != 0 && minor(Dev) != 1) + return EACCES; + + /* + * Find the session created by org_virtualbox_VBoxGuestClient, fail + * if no such session, and mark it as opened. We set the uid & gid + * here too, since that is more straight forward at this point. + */ + //const bool fUnrestricted = minor(Dev) == 0; + int rc = VINF_SUCCESS; + PVBOXGUESTSESSION pSession = NULL; + kauth_cred_t pCred = kauth_cred_proc_ref(pProcess); + if (pCred) + { + RTPROCESS Process = RTProcSelf(); + unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + + pSession = g_apSessionHashTab[iHash]; + while (pSession && pSession->Process != Process) + pSession = pSession->pNextHash; + if (pSession) + { + if (!pSession->fOpened) + { + pSession->fOpened = true; + /*pSession->fUnrestricted = fUnrestricted; - later */ + } + else + rc = VERR_ALREADY_LOADED; + } + else + rc = VERR_GENERAL_FAILURE; + + RTSpinlockReleaseNoInts(g_Spinlock); +#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1050 + kauth_cred_unref(&pCred); +#else /* 10.4 */ + /* The 10.4u SDK headers and 10.4.11 kernel source have inconsistent definitions + of kauth_cred_unref(), so use the other (now deprecated) API for releasing it. */ + kauth_cred_rele(pCred); +#endif /* 10.4 */ + } + else + rc = VERR_INVALID_PARAMETER; + + Log(("VbgdDarwinOpen: g_DevExt=%p pSession=%p rc=%d pid=%d\n", &g_DevExt, pSession, rc, proc_pid(pProcess))); + return VbgdDarwinErr2DarwinErr(rc); +} + + +/** + * Close device. + */ +static int VbgdDarwinClose(dev_t Dev, int fFlags, int fDevType, struct proc *pProcess) +{ + Log(("VbgdDarwinClose: pid=%d\n", (int)RTProcSelf())); + Assert(proc_pid(pProcess) == (int)RTProcSelf()); + + /* + * Hand the session closing to org_virtualbox_VBoxGuestClient. + */ + org_virtualbox_VBoxGuestClient::sessionClose(RTProcSelf()); + return 0; +} + + +/** + * Device I/O Control entry point. + * + * @returns Darwin for slow IOCtls and VBox status code for the fast ones. + * @param Dev The device number (major+minor). + * @param iCmd The IOCtl command. + * @param pData Pointer to the data (if any it's a VBOXGUESTIOCTLDATA (kernel copy)). + * @param fFlags Flag saying we're a character device (like we didn't know already). + * @param pProcess The process issuing this request. + */ +static int VbgdDarwinIOCtl(dev_t Dev, u_long iCmd, caddr_t pData, int fFlags, struct proc *pProcess) +{ + //const bool fUnrestricted = minor(Dev) == 0; + const RTPROCESS Process = proc_pid(pProcess); + const unsigned iHash = SESSION_HASH(Process); + PVBOXGUESTSESSION pSession; + + /* + * Find the session. + */ + RTSpinlockAcquire(g_Spinlock); + pSession = g_apSessionHashTab[iHash]; + while (pSession && pSession->Process != Process /*later: && pSession->fUnrestricted == fUnrestricted*/ && pSession->fOpened) + pSession = pSession->pNextHash; + RTSpinlockReleaseNoInts(g_Spinlock); + if (!pSession) + { + Log(("VBoxDrvDarwinIOCtl: WHAT?!? pSession == NULL! This must be a mistake... pid=%d iCmd=%#lx\n", + (int)Process, iCmd)); + return EINVAL; + } + + /* + * No high speed IOCtls here yet. + */ + + return VbgdDarwinIOCtlSlow(pSession, iCmd, pData, pProcess); +} + + +/** + * Worker for VbgdDarwinIOCtl that takes the slow IOCtl functions. + * + * @returns Darwin errno. + * + * @param pSession The session. + * @param iCmd The IOCtl command. + * @param pData Pointer to the kernel copy of the data buffer. + * @param pProcess The calling process. + */ +static int VbgdDarwinIOCtlSlow(PVBOXGUESTSESSION pSession, u_long iCmd, caddr_t pData, struct proc *pProcess) +{ + LogFlow(("VbgdDarwinIOCtlSlow: pSession=%p iCmd=%p pData=%p pProcess=%p\n", pSession, iCmd, pData, pProcess)); + + + /* + * Buffered or unbuffered? + */ + void *pvReqData; + user_addr_t pUser = 0; + void *pvPageBuf = NULL; + uint32_t cbReq = IOCPARM_LEN(iCmd); + if ((IOC_DIRMASK & iCmd) == IOC_INOUT) + { + /* + * Raw buffered request data, common code validates it. + */ + pvReqData = pData; + } + else if ((IOC_DIRMASK & iCmd) == IOC_VOID && !cbReq) + { + /* + * Get the header and figure out how much we're gonna have to read. + */ + VBGLBIGREQ Hdr; + pUser = (user_addr_t)*(void **)pData; + int rc = copyin(pUser, &Hdr, sizeof(Hdr)); + if (RT_UNLIKELY(rc)) + { + Log(("VbgdDarwinIOCtlSlow: copyin(%llx,Hdr,) -> %#x; iCmd=%#lx\n", (unsigned long long)pUser, rc, iCmd)); + return rc; + } + if (RT_UNLIKELY(Hdr.u32Magic != VBGLBIGREQ_MAGIC)) + { + Log(("VbgdDarwinIOCtlSlow: bad magic u32Magic=%#x; iCmd=%#lx\n", Hdr.u32Magic, iCmd)); + return EINVAL; + } + cbReq = Hdr.cbData; + if (RT_UNLIKELY(cbReq > _1M*16)) + { + Log(("VbgdDarwinIOCtlSlow: %#x; iCmd=%#lx\n", Hdr.cbData, iCmd)); + return EINVAL; + } + pUser = Hdr.pvDataR3; + + /* + * Allocate buffer and copy in the data. + */ + pvReqData = RTMemTmpAlloc(cbReq); + if (!pvReqData) + pvPageBuf = pvReqData = IOMallocAligned(RT_ALIGN_Z(cbReq, PAGE_SIZE), 8); + if (RT_UNLIKELY(!pvReqData)) + { + Log(("VbgdDarwinIOCtlSlow: failed to allocate buffer of %d bytes; iCmd=%#lx\n", cbReq, iCmd)); + return ENOMEM; + } + rc = copyin(pUser, pvReqData, Hdr.cbData); + if (RT_UNLIKELY(rc)) + { + Log(("VbgdDarwinIOCtlSlow: copyin(%llx,%p,%#x) -> %#x; iCmd=%#lx\n", + (unsigned long long)pUser, pvReqData, Hdr.cbData, rc, iCmd)); + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pvReqData); + return rc; + } + } + else + { + Log(("VbgdDarwinIOCtlSlow: huh? cbReq=%#x iCmd=%#lx\n", cbReq, iCmd)); + return EINVAL; + } + + /* + * Process the IOCtl. + */ + size_t cbReqRet = 0; + int rc = VBoxGuestCommonIOCtl(iCmd, &g_DevExt, pSession, pvReqData, cbReq, &cbReqRet); + if (RT_SUCCESS(rc)) + { + /* + * If not buffered, copy back the buffer before returning. + */ + if (pUser) + { + if (cbReqRet > cbReq) + { + Log(("VbgdDarwinIOCtlSlow: too much output! %#x > %#x; uCmd=%#lx!\n", cbReqRet, cbReq, iCmd)); + cbReqRet = cbReq; + } + rc = copyout(pvReqData, pUser, cbReqRet); + if (RT_UNLIKELY(rc)) + Log(("VbgdDarwinIOCtlSlow: copyout(%p,%llx,%#x) -> %d; uCmd=%#lx!\n", + pvReqData, (unsigned long long)pUser, cbReqRet, rc, iCmd)); + + /* cleanup */ + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pvReqData); + } + else + rc = 0; + } + else + { + /* + * The request failed, just clean up. + */ + if (pUser) + { + if (pvPageBuf) + IOFreeAligned(pvPageBuf, RT_ALIGN_Z(cbReq, PAGE_SIZE)); + else + RTMemTmpFree(pvReqData); + } + + Log(("VbgdDarwinIOCtlSlow: pid=%d iCmd=%lx pData=%p failed, rc=%d\n", proc_pid(pProcess), iCmd, (void *)pData, rc)); + rc = EINVAL; + } + + Log2(("VbgdDarwinIOCtlSlow: returns %d\n", rc)); + return rc; +} + + +/* + * The VBoxGuest IDC entry points. + * + * This code is shared with the other unixy OSes. + */ +#include "VBoxGuestIDC-unix.c.h" + + +void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + NOREF(pDevExt); +} + + +/** + * Callback for blah blah blah. + */ +IOReturn VbgdDarwinSleepHandler(void * /* pvTarget */, void *pvRefCon, UInt32 uMessageType, IOService * /* pProvider */, void * /* pvMessageArgument */, vm_size_t /* argSize */) +{ + LogFlow(("VBoxGuest: Got sleep/wake notice. Message type was %X\n", (uint)uMessageType)); + + if (uMessageType == kIOMessageSystemWillSleep) + RTPowerSignalEvent(RTPOWEREVENT_SUSPEND); + else if (uMessageType == kIOMessageSystemHasPoweredOn) + RTPowerSignalEvent(RTPOWEREVENT_RESUME); + + acknowledgeSleepWakeNotification(pvRefCon); + + return 0; +} + + +/** + * Converts an IPRT error code to a darwin error code. + * + * @returns corresponding darwin error code. + * @param rc IPRT status code. + */ +static int VbgdDarwinErr2DarwinErr(int rc) +{ + switch (rc) + { + case VINF_SUCCESS: return 0; + case VERR_GENERAL_FAILURE: return EACCES; + case VERR_INVALID_PARAMETER: return EINVAL; + case VERR_INVALID_MAGIC: return EILSEQ; + case VERR_INVALID_HANDLE: return ENXIO; + case VERR_INVALID_POINTER: return EFAULT; + case VERR_LOCK_FAILED: return ENOLCK; + case VERR_ALREADY_LOADED: return EEXIST; + case VERR_PERMISSION_DENIED: return EPERM; + case VERR_VERSION_MISMATCH: return ENOSYS; + } + + return EPERM; +} + + +/* + * + * org_virtualbox_VBoxGuest + * + */ + +/** + * Just a plug + */ +static void +interruptHandler(OSObject *pOwner, IOInterruptEventSource *pSrc, int cInts) +{ + NOREF(pOwner); + NOREF(pSrc); + NOREF(cInts); +} + +/** + * Callback triggered when interrupt occurs. + */ +static bool +checkForInterrupt(OSObject *pOwner, IOFilterInterruptEventSource *pSrc) +{ + if (!pSrc) + return false; + + bool fTaken = VBoxGuestCommonISR(&g_DevExt); + if (!fTaken) + printf("VBoxGuestCommonISR error\n"); + + return fTaken; +} + +bool +org_virtualbox_VBoxGuest::setupVmmDevInterrupts(IOService *pProvider) +{ + IOWorkLoop *pWorkLoop = (IOWorkLoop *)getWorkLoop(); + + if (!pWorkLoop) + return false; + + m_pInterruptSrc = IOFilterInterruptEventSource::filterInterruptEventSource(this, + &interruptHandler, + &checkForInterrupt, + pProvider); + + if (kIOReturnSuccess != pWorkLoop->addEventSource(m_pInterruptSrc)) + { + m_pInterruptSrc->disable(); + m_pInterruptSrc->release(); + m_pInterruptSrc = 0; + return false; + } + + m_pInterruptSrc->enable(); + + return true; +} + +bool +org_virtualbox_VBoxGuest::disableVmmDevInterrupts(void) +{ + IOWorkLoop *pWorkLoop = (IOWorkLoop *)getWorkLoop(); + + if (!pWorkLoop) + return false; + + if (!m_pInterruptSrc) + return false; + + m_pInterruptSrc->disable(); + pWorkLoop->removeEventSource(m_pInterruptSrc); + m_pInterruptSrc->release(); + m_pInterruptSrc = 0; + + return true; +} + +bool org_virtualbox_VBoxGuest::isVmmDev(IOPCIDevice *pIOPCIDevice) +{ + UInt16 uVendorId, uDeviceId; + + if (!pIOPCIDevice) + return false; + + uVendorId = m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID); + uDeviceId = m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID); + + if (uVendorId == VMMDEV_VENDORID && uDeviceId == VMMDEV_DEVICEID) + return true; + + return true; +} + + +/** + * Start this service. + */ +bool org_virtualbox_VBoxGuest::start(IOService *pProvider) +{ + if (!IOService::start(pProvider)) + return false; + + /* Low level initialization should be performed only once */ + if (!ASMAtomicCmpXchgBool(&g_fInstantiated, true, false)) + { + IOService::stop(pProvider); + return false; + } + + m_pIOPCIDevice = OSDynamicCast(IOPCIDevice, pProvider); + if (m_pIOPCIDevice) + { + if (isVmmDev(m_pIOPCIDevice)) + { + /* Enable memory response from VMM device */ + m_pIOPCIDevice->setMemoryEnable(true); + m_pIOPCIDevice->setIOEnable(true); + + IOMemoryDescriptor *pMem = m_pIOPCIDevice->getDeviceMemoryWithIndex(0); + if (pMem) + { + IOPhysicalAddress IOPortBasePhys = pMem->getPhysicalAddress(); + /* Check that returned value is from I/O port range (at least it is 16-bit lenght) */ + if((IOPortBasePhys >> 16) == 0) + { + + RTIOPORT IOPortBase = (RTIOPORT)IOPortBasePhys; + void *pvMMIOBase = NULL; + uint32_t cbMMIO = 0; + m_pMap = m_pIOPCIDevice->mapDeviceMemoryWithIndex(1); + if (m_pMap) + { + pvMMIOBase = (void *)m_pMap->getVirtualAddress(); + cbMMIO = m_pMap->getLength(); + } + + int rc = VBoxGuestInitDevExt(&g_DevExt, + IOPortBase, + pvMMIOBase, + cbMMIO, +#if ARCH_BITS == 64 + VBOXOSTYPE_MacOS_x64, +#else + VBOXOSTYPE_MacOS, +#endif + 0); + if (RT_SUCCESS(rc)) + { + rc = VbgdDarwinCharDevInit(); + if (rc == KMOD_RETURN_SUCCESS) + { + if (setupVmmDevInterrupts(pProvider)) + { + /* register the service. */ + registerService(); + LogRel(("VBoxGuest: Successfully started I/O kit class instance.\n")); + return true; + } + + LogRel(("VBoxGuest: Failed to set up interrupts\n")); + VbgdDarwinCharDevRemove(); + } + else + LogRel(("VBoxGuest: Failed to initialize character device (rc=%d).\n", rc)); + + VBoxGuestDeleteDevExt(&g_DevExt); + } + else + LogRel(("VBoxGuest: Failed to initialize common code (rc=%d).\n", rc)); + + if (m_pMap) + { + m_pMap->release(); + m_pMap = NULL; + } + } + } + else + LogRel(("VBoxGuest: The device missing is the I/O port range (#0).\n")); + } + else + LogRel(("VBoxGuest: Not the VMMDev (%#x:%#x).\n", + m_pIOPCIDevice->configRead16(kIOPCIConfigVendorID), m_pIOPCIDevice->configRead16(kIOPCIConfigDeviceID))); + } + else + LogRel(("VBoxGuest: Provider is not an instance of IOPCIDevice.\n")); + + ASMAtomicXchgBool(&g_fInstantiated, false); + + IOService::stop(pProvider); + + return false; +} + + +/** + * Stop this service. + */ +void org_virtualbox_VBoxGuest::stop(IOService *pProvider) +{ + LogFlow(("org_virtualbox_VBoxGuest::stop([%p], %p)\n", this, pProvider)); + + AssertReturnVoid(ASMAtomicReadBool(&g_fInstantiated)); + + /* Low level termination should be performed only once */ + if (!disableVmmDevInterrupts()) + LogRel(("vboxguest: unable to unregister interrupt handler\n")); + + VbgdDarwinCharDevRemove(); + VBoxGuestDeleteDevExt(&g_DevExt); + + if (m_pMap) + { + m_pMap->release(); + m_pMap = NULL; + } + + IOService::stop(pProvider); + + ASMAtomicWriteBool(&g_fInstantiated, false); + + LogRel(("vboxguest module unloaded\n")); +} + + +/** + * Termination request. + * + * @return true if we're ok with shutting down now, false if we're not. + * @param fOptions Flags. + */ +bool org_virtualbox_VBoxGuest::terminate(IOOptionBits fOptions) +{ + bool fRc; + LogFlow(("org_virtualbox_VBoxGuest::terminate: reference_count=%d g_cSessions=%d (fOptions=%#x)\n", + KMOD_INFO_NAME.reference_count, ASMAtomicUoReadS32(&g_cSessions), fOptions)); + if ( KMOD_INFO_NAME.reference_count != 0 + || ASMAtomicUoReadS32(&g_cSessions)) + fRc = false; + else + fRc = IOService::terminate(fOptions); + LogFlow(("org_virtualbox_SupDrv::terminate: returns %d\n", fRc)); + return fRc; +} + + +/* + * + * org_virtualbox_VBoxGuestClient + * + */ + + +/** + * Initializer called when the client opens the service. + */ +bool org_virtualbox_VBoxGuestClient::initWithTask(task_t OwningTask, void *pvSecurityId, UInt32 u32Type) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::initWithTask([%p], %#x, %p, %#x) (cur pid=%d proc=%p)\n", + this, OwningTask, pvSecurityId, u32Type, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)OwningTask == RTR0ProcHandleSelf(), ("%p %p\n", OwningTask, RTR0ProcHandleSelf())); + + if (!OwningTask) + return false; + if (IOUserClient::initWithTask(OwningTask, pvSecurityId , u32Type)) + { + m_Task = OwningTask; + m_pSession = NULL; + m_pProvider = NULL; + return true; + } + return false; +} + + +/** + * Start the client service. + */ +bool org_virtualbox_VBoxGuestClient::start(IOService *pProvider) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::start([%p], %p) (cur pid=%d proc=%p)\n", + this, pProvider, RTProcSelf(), RTR0ProcHandleSelf() )); + AssertMsgReturn((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), + ("%p %p\n", m_Task, RTR0ProcHandleSelf()), + false); + + if (IOUserClient::start(pProvider)) + { + m_pProvider = OSDynamicCast(org_virtualbox_VBoxGuest, pProvider); + if (m_pProvider) + { + Assert(!m_pSession); + + /* + * Create a new session. + */ + int rc = VBoxGuestCreateUserSession(&g_DevExt, &m_pSession); + if (RT_SUCCESS(rc)) + { + m_pSession->fOpened = false; + /* The fUnrestricted field is set on open. */ + + /* + * Insert it into the hash table, checking that there isn't + * already one for this process first. (One session per proc!) + */ + unsigned iHash = SESSION_HASH(m_pSession->Process); + RTSpinlockAcquire(g_Spinlock); + + PVBOXGUESTSESSION pCur = g_apSessionHashTab[iHash]; + if (pCur && pCur->Process != m_pSession->Process) + { + do pCur = pCur->pNextHash; + while (pCur && pCur->Process != m_pSession->Process); + } + if (!pCur) + { + m_pSession->pNextHash = g_apSessionHashTab[iHash]; + g_apSessionHashTab[iHash] = m_pSession; + m_pSession->pvVBoxGuestClient = this; + ASMAtomicIncS32(&g_cSessions); + rc = VINF_SUCCESS; + } + else + rc = VERR_ALREADY_LOADED; + + RTSpinlockRelease(g_Spinlock); + if (RT_SUCCESS(rc)) + { + Log(("org_virtualbox_VBoxGuestClient::start: created session %p for pid %d\n", m_pSession, (int)RTProcSelf())); + return true; + } + + LogFlow(("org_virtualbox_VBoxGuestClient::start: already got a session for this process (%p)\n", pCur)); + VBoxGuestCloseSession(&g_DevExt, m_pSession); + } + + m_pSession = NULL; + LogFlow(("org_virtualbox_VBoxGuestClient::start: rc=%Rrc from supdrvCreateSession\n", rc)); + } + else + LogFlow(("org_virtualbox_VBoxGuestClient::start: %p isn't org_virtualbox_VBoxGuest\n", pProvider)); + } + return false; +} + + +/** + * Common worker for clientClose and VBoxDrvDarwinClose. + */ +/* static */ void org_virtualbox_VBoxGuestClient::sessionClose(RTPROCESS Process) +{ + /* + * Find the session and remove it from the hash table. + * + * Note! Only one session per process. (Both start() and + * VbgdDarwinOpen makes sure this is so.) + */ + const unsigned iHash = SESSION_HASH(Process); + RTSpinlockAcquire(g_Spinlock); + PVBOXGUESTSESSION pSession = g_apSessionHashTab[iHash]; + if (pSession) + { + if (pSession->Process == Process) + { + g_apSessionHashTab[iHash] = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + } + else + { + PVBOXGUESTSESSION pPrev = pSession; + pSession = pSession->pNextHash; + while (pSession) + { + if (pSession->Process == Process) + { + pPrev->pNextHash = pSession->pNextHash; + pSession->pNextHash = NULL; + ASMAtomicDecS32(&g_cSessions); + break; + } + + /* next */ + pPrev = pSession; + pSession = pSession->pNextHash; + } + } + } + RTSpinlockRelease(g_Spinlock); + if (!pSession) + { + Log(("VBoxGuestClient::sessionClose: pSession == NULL, pid=%d; freed already?\n", (int)Process)); + return; + } + + /* + * Remove it from the client object. + */ + org_virtualbox_VBoxGuestClient *pThis = (org_virtualbox_VBoxGuestClient *)pSession->pvVBoxGuestClient; + pSession->pvVBoxGuestClient = NULL; + if (pThis) + { + Assert(pThis->m_pSession == pSession); + pThis->m_pSession = NULL; + } + + /* + * Close the session. + */ + VBoxGuestCloseSession(&g_DevExt, pSession); +} + + +/** + * Client exits normally. + */ +IOReturn org_virtualbox_VBoxGuestClient::clientClose(void) +{ + LogFlow(("org_virtualbox_VBoxGuestClient::clientClose([%p]) (cur pid=%d proc=%p)\n", this, RTProcSelf(), RTR0ProcHandleSelf())); + AssertMsg((RTR0PROCESS)m_Task == RTR0ProcHandleSelf(), ("%p %p\n", m_Task, RTR0ProcHandleSelf())); + + /* + * Clean up the session if it's still around. + * + * We cannot rely 100% on close, and in the case of a dead client + * we'll end up hanging inside vm_map_remove() if we postpone it. + */ + if (m_pSession) + { + sessionClose(RTProcSelf()); + Assert(!m_pSession); + } + + m_pProvider = NULL; + terminate(); + + return kIOReturnSuccess; +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c index 1720abb2..1552f715 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-freebsd.c @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c new file mode 100644 index 00000000..122df9e7 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku-stubs.c @@ -0,0 +1,448 @@ +/* $Id: VBoxGuest-haiku-stubs.c $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, stubs. + */ + +/* + * Copyright (C) 2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +/* + * This file provides stubs for calling VBox runtime functions through the vboxguest module. + * It should be linked into any driver or module that uses the VBox runtime, except vboxguest + * itself (which contains the actual library and therefore doesn't need stubs to call it). + */ + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/power.h> +#include <iprt/thread.h> + +// >>> file('/tmp/stubs.c', 'w').writelines([re.sub(r'^(?P<returntype>[^(]+) \(\*_(?P<functionname>[A-Za-z0-9_]+)\)\((?P<params>[^)]+)\);', lambda m: '%s %s(%s)\n{\n %sg_VBoxGuest->_%s(%s);\n}\n' % (m.group(1), m.group(2), m.group(3), ('return ' if m.group(1) != 'void' else ''), m.group(2), (', '.join(a.split(' ')[-1].replace('*', '') for a in m.group(3).split(',')) if m.group(3) != 'void' else '')), f) for f in functions]) + +struct vboxguest_module_info *g_VBoxGuest; + +size_t RTLogBackdoorPrintf(const char *pszFormat, ...) +{ + va_list args; + size_t cb; + + va_start(args, pszFormat); + cb = g_VBoxGuest->_RTLogBackdoorPrintf(pszFormat, args); + va_end(args); + + return cb; +} +size_t RTLogBackdoorPrintfV(const char *pszFormat, va_list args) +{ + return g_VBoxGuest->_RTLogBackdoorPrintfV(pszFormat, args); +} +int RTLogSetDefaultInstanceThread(PRTLOGGER pLogger, uintptr_t uKey) +{ + return g_VBoxGuest->_RTLogSetDefaultInstanceThread(pLogger, uKey); +} +int RTMemAllocExTag(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv) +{ + return g_VBoxGuest->_RTMemAllocExTag(cb, cbAlignment, fFlags, pszTag, ppv); +} +void* RTMemContAlloc(PRTCCPHYS pPhys, size_t cb) +{ + return g_VBoxGuest->_RTMemContAlloc(pPhys, cb); +} +void RTMemContFree(void *pv, size_t cb) +{ + g_VBoxGuest->_RTMemContFree(pv, cb); +} +void RTMemFreeEx(void *pv, size_t cb) +{ + g_VBoxGuest->_RTMemFreeEx(pv, cb); +} +bool RTMpIsCpuPossible(RTCPUID idCpu) +{ + return g_VBoxGuest->_RTMpIsCpuPossible(idCpu); +} +int RTMpNotificationDeregister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTMpNotificationDeregister(pfnCallback, pvUser); +} +int RTMpNotificationRegister(PFNRTMPNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTMpNotificationRegister(pfnCallback, pvUser); +} +int RTMpOnAll(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnAll(pfnWorker, pvUser1, pvUser2); +} +int RTMpOnOthers(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnOthers(pfnWorker, pvUser1, pvUser2); +} +int RTMpOnSpecific(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2) +{ + return g_VBoxGuest->_RTMpOnSpecific(idCpu, pfnWorker, pvUser1, pvUser2); +} +int RTPowerNotificationDeregister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTPowerNotificationDeregister(pfnCallback, pvUser); +} +int RTPowerNotificationRegister(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser) +{ + return g_VBoxGuest->_RTPowerNotificationRegister(pfnCallback, pvUser); +} +int RTPowerSignalEvent(RTPOWEREVENT enmEvent) +{ + return g_VBoxGuest->_RTPowerSignalEvent(enmEvent); +} +void RTR0AssertPanicSystem(void) +{ + g_VBoxGuest->_RTR0AssertPanicSystem(); +} +int RTR0Init(unsigned fReserved) +{ + return g_VBoxGuest->_RTR0Init(fReserved); +} +void* RTR0MemObjAddress(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjAddress(MemObj); +} +RTR3PTR RTR0MemObjAddressR3(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjAddressR3(MemObj); +} +int RTR0MemObjAllocContTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocContTag(pMemObj, cb, fExecutable, pszTag); +} +int RTR0MemObjAllocLowTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocLowTag(pMemObj, cb, fExecutable, pszTag); +} +int RTR0MemObjAllocPageTag(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPageTag(pMemObj, cb, fExecutable, pszTag); +} +int RTR0MemObjAllocPhysExTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysExTag(pMemObj, cb, PhysHighest, uAlignment, pszTag); +} +int RTR0MemObjAllocPhysNCTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysNCTag(pMemObj, cb, PhysHighest, pszTag); +} +int RTR0MemObjAllocPhysTag(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjAllocPhysTag(pMemObj, cb, PhysHighest, pszTag); +} +int RTR0MemObjEnterPhysTag(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjEnterPhysTag(pMemObj, Phys, cb, uCachePolicy, pszTag); +} +int RTR0MemObjFree(RTR0MEMOBJ MemObj, bool fFreeMappings) +{ + return g_VBoxGuest->_RTR0MemObjFree(MemObj, fFreeMappings); +} +RTHCPHYS RTR0MemObjGetPagePhysAddr(RTR0MEMOBJ MemObj, size_t iPage) +{ + return g_VBoxGuest->_RTR0MemObjGetPagePhysAddr(MemObj, iPage); +} +bool RTR0MemObjIsMapping(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjIsMapping(MemObj); +} +int RTR0MemObjLockKernelTag(PRTR0MEMOBJ pMemObj, void *pv, size_t cb, uint32_t fAccess, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjLockKernelTag(pMemObj, pv, cb, fAccess, pszTag); +} +int RTR0MemObjLockUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjLockUserTag(pMemObj, R3Ptr, cb, fAccess, R0Process, pszTag); +} +int RTR0MemObjMapKernelExTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapKernelExTag(pMemObj, MemObjToMap, pvFixed, uAlignment, fProt, offSub, cbSub, pszTag); +} +int RTR0MemObjMapKernelTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, unsigned fProt, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapKernelTag(pMemObj, MemObjToMap, pvFixed, uAlignment, fProt, pszTag); +} +int RTR0MemObjMapUserTag(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjMapUserTag(pMemObj, MemObjToMap, R3PtrFixed, uAlignment, fProt, R0Process, pszTag); +} +int RTR0MemObjProtect(RTR0MEMOBJ hMemObj, size_t offSub, size_t cbSub, uint32_t fProt) +{ + return g_VBoxGuest->_RTR0MemObjProtect(hMemObj, offSub, cbSub, fProt); +} +int RTR0MemObjReserveKernelTag(PRTR0MEMOBJ pMemObj, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjReserveKernelTag(pMemObj, pvFixed, cb, uAlignment, pszTag); +} +int RTR0MemObjReserveUserTag(PRTR0MEMOBJ pMemObj, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, RTR0PROCESS R0Process, const char *pszTag) +{ + return g_VBoxGuest->_RTR0MemObjReserveUserTag(pMemObj, R3PtrFixed, cb, uAlignment, R0Process, pszTag); +} +size_t RTR0MemObjSize(RTR0MEMOBJ MemObj) +{ + return g_VBoxGuest->_RTR0MemObjSize(MemObj); +} +RTR0PROCESS RTR0ProcHandleSelf(void) +{ + return g_VBoxGuest->_RTR0ProcHandleSelf(); +} +void RTR0Term(void) +{ + g_VBoxGuest->_RTR0Term(); +} +void RTR0TermForced(void) +{ + g_VBoxGuest->_RTR0TermForced(); +} +RTPROCESS RTProcSelf(void) +{ + return g_VBoxGuest->_RTProcSelf(); +} +uint32_t RTSemEventGetResolution(void) +{ + return g_VBoxGuest->_RTSemEventGetResolution(); +} +uint32_t RTSemEventMultiGetResolution(void) +{ + return g_VBoxGuest->_RTSemEventMultiGetResolution(); +} +int RTSemEventMultiWaitEx(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout) +{ + return g_VBoxGuest->_RTSemEventMultiWaitEx(hEventMultiSem, fFlags, uTimeout); +} +int RTSemEventMultiWaitExDebug(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return g_VBoxGuest->_RTSemEventMultiWaitExDebug(hEventMultiSem, fFlags, uTimeout, uId, pszFile, iLine, pszFunction); +} +int RTSemEventWaitEx(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout) +{ + return g_VBoxGuest->_RTSemEventWaitEx(hEventSem, fFlags, uTimeout); +} +int RTSemEventWaitExDebug(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, RTHCUINTPTR uId, RT_SRC_POS_DECL) +{ + return g_VBoxGuest->_RTSemEventWaitExDebug(hEventSem, fFlags, uTimeout, uId, pszFile, iLine, pszFunction); +} +bool RTThreadIsInInterrupt(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadIsInInterrupt(hThread); +} +void RTThreadPreemptDisable(PRTTHREADPREEMPTSTATE pState) +{ + g_VBoxGuest->_RTThreadPreemptDisable(pState); +} +bool RTThreadPreemptIsEnabled(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadPreemptIsEnabled(hThread); +} +bool RTThreadPreemptIsPending(RTTHREAD hThread) +{ + return g_VBoxGuest->_RTThreadPreemptIsPending(hThread); +} +bool RTThreadPreemptIsPendingTrusty(void) +{ + return g_VBoxGuest->_RTThreadPreemptIsPendingTrusty(); +} +bool RTThreadPreemptIsPossible(void) +{ + return g_VBoxGuest->_RTThreadPreemptIsPossible(); +} +void RTThreadPreemptRestore(PRTTHREADPREEMPTSTATE pState) +{ + g_VBoxGuest->_RTThreadPreemptRestore(pState); +} +uint32_t RTTimerGetSystemGranularity(void) +{ + return g_VBoxGuest->_RTTimerGetSystemGranularity(); +} +int RTTimerReleaseSystemGranularity(uint32_t u32Granted) +{ + return g_VBoxGuest->_RTTimerReleaseSystemGranularity(u32Granted); +} +int RTTimerRequestSystemGranularity(uint32_t u32Request, uint32_t *pu32Granted) +{ + return g_VBoxGuest->_RTTimerRequestSystemGranularity(u32Request, pu32Granted); +} +void RTSpinlockAcquire(RTSPINLOCK Spinlock) +{ + g_VBoxGuest->_RTSpinlockAcquire(Spinlock); +} +void RTSpinlockRelease(RTSPINLOCK Spinlock) +{ + g_VBoxGuest->_RTSpinlockRelease(Spinlock); +} +void RTSpinlockReleaseNoInts(RTSPINLOCK Spinlock) +{ + g_VBoxGuest->_RTSpinlockReleaseNoInts(Spinlock); +} +void* RTMemTmpAllocTag(size_t cb, const char *pszTag) +{ + return g_VBoxGuest->_RTMemTmpAllocTag(cb, pszTag); +} +void RTMemTmpFree(void *pv) +{ + g_VBoxGuest->_RTMemTmpFree(pv); +} +PRTLOGGER RTLogDefaultInstance(void) +{ + return g_VBoxGuest->_RTLogDefaultInstance(); +} +PRTLOGGER RTLogRelDefaultInstance(void) +{ + return g_VBoxGuest->_RTLogRelDefaultInstance(); +} +int RTErrConvertToErrno(int iErr) +{ + return g_VBoxGuest->_RTErrConvertToErrno(iErr); +} +int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, void *pvData, size_t cbData, size_t *pcbDataReturned) +{ + return g_VBoxGuest->_VBoxGuestCommonIOCtl(iFunction, pDevExt, pSession, pvData, cbData, pcbDataReturned); +} +int VBoxGuestCreateUserSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession) +{ + return g_VBoxGuest->_VBoxGuestCreateUserSession(pDevExt, ppSession); +} +void VBoxGuestCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + g_VBoxGuest->_VBoxGuestCloseSession(pDevExt, pSession); +} +void* VBoxGuestIDCOpen(uint32_t *pu32Version) +{ + return g_VBoxGuest->_VBoxGuestIDCOpen(pu32Version); +} +int VBoxGuestIDCClose(void *pvSession) +{ + return g_VBoxGuest->_VBoxGuestIDCClose(pvSession); +} +int VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned) +{ + return g_VBoxGuest->_VBoxGuestIDCCall(pvSession, iCmd, pvData, cbData, pcbDataReturned); +} +void RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction) +{ + g_VBoxGuest->_RTAssertMsg1Weak(pszExpr, uLine, pszFile, pszFunction); +} +void RTAssertMsg2Weak(const char *pszFormat, ...) +{ + va_list va; + va_start(va, pszFormat); + RTAssertMsg2WeakV(pszFormat, va); + va_end(va); +} +void RTAssertMsg2WeakV(const char *pszFormat, va_list va) +{ + g_VBoxGuest->_RTAssertMsg2WeakV(pszFormat, va); +} +bool RTAssertShouldPanic(void) +{ + return g_VBoxGuest->_RTAssertShouldPanic(); +} +int RTSemFastMutexCreate(PRTSEMFASTMUTEX phFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexCreate(phFastMtx); +} +int RTSemFastMutexDestroy(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexDestroy(hFastMtx); +} +int RTSemFastMutexRelease(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexRelease(hFastMtx); +} +int RTSemFastMutexRequest(RTSEMFASTMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemFastMutexRequest(hFastMtx); +} +int RTSemMutexCreate(PRTSEMMUTEX phFastMtx) +{ + return g_VBoxGuest->_RTSemMutexCreate(phFastMtx); +} +int RTSemMutexDestroy(RTSEMMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemMutexDestroy(hFastMtx); +} +int RTSemMutexRelease(RTSEMMUTEX hFastMtx) +{ + return g_VBoxGuest->_RTSemMutexRelease(hFastMtx); +} +int RTSemMutexRequest(RTSEMMUTEX hFastMtx, RTMSINTERVAL cMillies) +{ + return g_VBoxGuest->_RTSemMutexRequest(hFastMtx, cMillies); +} +int RTHeapSimpleRelocate(RTHEAPSIMPLE hHeap, uintptr_t offDelta) +{ + return g_VBoxGuest->_RTHeapSimpleRelocate(hHeap, offDelta); +} +int RTHeapOffsetInit(PRTHEAPOFFSET phHeap, void *pvMemory, size_t cbMemory) +{ + return g_VBoxGuest->_RTHeapOffsetInit(phHeap, pvMemory, cbMemory); +} +int RTHeapSimpleInit(PRTHEAPSIMPLE pHeap, void *pvMemory, size_t cbMemory) +{ + return g_VBoxGuest->_RTHeapSimpleInit(pHeap, pvMemory, cbMemory); +} +void* RTHeapOffsetAlloc(RTHEAPOFFSET hHeap, size_t cb, size_t cbAlignment) +{ + return g_VBoxGuest->_RTHeapOffsetAlloc(hHeap, cb, cbAlignment); +} +void* RTHeapSimpleAlloc(RTHEAPSIMPLE Heap, size_t cb, size_t cbAlignment) +{ + return g_VBoxGuest->_RTHeapSimpleAlloc(Heap, cb, cbAlignment); +} +void RTHeapOffsetFree(RTHEAPOFFSET hHeap, void *pv) +{ + g_VBoxGuest->_RTHeapOffsetFree(hHeap, pv); +} +void RTHeapSimpleFree(RTHEAPSIMPLE Heap, void *pv) +{ + g_VBoxGuest->_RTHeapSimpleFree(Heap, pv); +} + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c new file mode 100644 index 00000000..5aa31702 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.c @@ -0,0 +1,553 @@ +/* $Id: VBoxGuest-haiku.c $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, implementation. + */ + +/* + * Copyright (C) 2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#define IN_VBOXGUEST +#include <sys/param.h> +#include <sys/types.h> +#include <sys/uio.h> +#include <OS.h> +#include <Drivers.h> +#include <KernelExport.h> +#include <PCI.h> + +#include "VBoxGuest-haiku.h" +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/memobj.h> +#include <iprt/asm.h> +#include <iprt/timer.h> +#include <iprt/heap.h> + +#define MODULE_NAME VBOXGUEST_MODULE_NAME + +/* + * IRQ related functions. + */ +static void VBoxGuestHaikuRemoveIRQ(void *pvState); +static int VBoxGuestHaikuAddIRQ(void *pvState); +static int32 VBoxGuestHaikuISR(void *pvState); + +/* + * Available functions for kernel drivers. + */ +DECLVBGL(int) VBoxGuestHaikuServiceCall(void *pvSession, unsigned uCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); +DECLVBGL(void *) VBoxGuestHaikuServiceOpen(uint32_t *pu32Version); +DECLVBGL(int) VBoxGuestHaikuServiceClose(void *pvSession); +DECLVBGL(void *) VBoxGuestIDCOpen(uint32_t *pu32Version); +DECLVBGL(int) VBoxGuestIDCClose(void *pvSession); +DECLVBGL(int) VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); + +static status_t std_ops(int32 op, ...); + +static RTSPINLOCK g_Spinlock = NIL_RTSPINLOCK; + +int32 api_version = B_CUR_DRIVER_API_VERSION; + +/** List of cloned device. Managed by the kernel. */ +//static struct clonedevs *g_pVBoxGuestHaikuClones; +/** The dev_clone event handler tag. */ +//static eventhandler_tag g_VBoxGuestHaikuEHTag; +/** selinfo structure used for polling. */ +//static struct selinfo g_SelInfo; +/** PCI Bus Manager Module */ +static pci_module_info *gPCI; + +static struct vboxguest_module_info g_VBoxGuest = +{ + { + MODULE_NAME, + 0, + std_ops + }, + { 0 }, + { 0 }, + 0, + RTLogBackdoorPrintf, + RTLogBackdoorPrintfV, + RTLogSetDefaultInstanceThread, + RTMemAllocExTag, + RTMemContAlloc, + RTMemContFree, + RTMemFreeEx, + RTMpIsCpuPossible, + RTMpNotificationDeregister, + RTMpNotificationRegister, + RTMpOnAll, + RTMpOnOthers, + RTMpOnSpecific, + RTPowerNotificationDeregister, + RTPowerNotificationRegister, + RTPowerSignalEvent, + RTR0AssertPanicSystem, + RTR0Init, + RTR0MemObjAddress, + RTR0MemObjAddressR3, + RTR0MemObjAllocContTag, + RTR0MemObjAllocLowTag, + RTR0MemObjAllocPageTag, + RTR0MemObjAllocPhysExTag, + RTR0MemObjAllocPhysNCTag, + RTR0MemObjAllocPhysTag, + RTR0MemObjEnterPhysTag, + RTR0MemObjFree, + RTR0MemObjGetPagePhysAddr, + RTR0MemObjIsMapping, + RTR0MemObjLockKernelTag, + RTR0MemObjLockUserTag, + RTR0MemObjMapKernelExTag, + RTR0MemObjMapKernelTag, + RTR0MemObjMapUserTag, + RTR0MemObjProtect, + RTR0MemObjReserveKernelTag, + RTR0MemObjReserveUserTag, + RTR0MemObjSize, + RTR0ProcHandleSelf, + RTR0Term, + RTR0TermForced, + RTProcSelf, + RTSemEventGetResolution, + RTSemEventMultiGetResolution, + RTSemEventMultiWaitEx, + RTSemEventMultiWaitExDebug, + RTSemEventWaitEx, + RTSemEventWaitExDebug, + RTThreadIsInInterrupt, + RTThreadPreemptDisable, + RTThreadPreemptIsEnabled, + RTThreadPreemptIsPending, + RTThreadPreemptIsPendingTrusty, + RTThreadPreemptIsPossible, + RTThreadPreemptRestore, + RTTimerGetSystemGranularity, + RTTimerReleaseSystemGranularity, + RTTimerRequestSystemGranularity, + RTSpinlockAcquire, + RTSpinlockRelease, + RTSpinlockReleaseNoInts, + RTMemTmpAllocTag, + RTMemTmpFree, + RTLogDefaultInstance, + RTLogRelDefaultInstance, + RTErrConvertToErrno, + VBoxGuestCommonIOCtl, + VBoxGuestCreateUserSession, + VBoxGuestCloseSession, + VBoxGuestIDCOpen, + VBoxGuestIDCClose, + VBoxGuestIDCCall, + RTAssertMsg1Weak, + RTAssertMsg2Weak, + RTAssertMsg2WeakV, + RTAssertShouldPanic, + RTSemFastMutexCreate, + RTSemFastMutexDestroy, + RTSemFastMutexRelease, + RTSemFastMutexRequest, + RTSemMutexCreate, + RTSemMutexDestroy, + RTSemMutexRelease, + RTSemMutexRequest, + RTHeapSimpleRelocate, + RTHeapOffsetInit, + RTHeapSimpleInit, + RTHeapOffsetAlloc, + RTHeapSimpleAlloc, + RTHeapOffsetFree, + RTHeapSimpleFree +}; + +#if 0 +/** + * DEVFS event handler. + */ +static void VBoxGuestHaikuClone(void *pvArg, struct ucred *pCred, char *pszName, int cchName, struct cdev **ppDev) +{ + int iUnit; + int rc; + + Log(("VBoxGuestHaikuClone: pszName=%s ppDev=%p\n", pszName, ppDev)); + + /* + * One device node per user, si_drv1 points to the session. + * /dev/vboxguest<N> where N = {0...255}. + */ + if (!ppDev) + return; + if (strcmp(pszName, "vboxguest") == 0) + iUnit = -1; + else if (dev_stdclone(pszName, NULL, "vboxguest", &iUnit) != 1) + return; + if (iUnit >= 256) + { + Log(("VBoxGuestHaikuClone: iUnit=%d >= 256 - rejected\n", iUnit)); + return; + } + + Log(("VBoxGuestHaikuClone: pszName=%s iUnit=%d\n", pszName, iUnit)); + + rc = clone_create(&g_pVBoxGuestHaikuClones, &g_VBoxGuestHaikuDeviceHooks, &iUnit, ppDev, 0); + Log(("VBoxGuestHaikuClone: clone_create -> %d; iUnit=%d\n", rc, iUnit)); + if (rc) + { + *ppDev = make_dev(&g_VBoxGuestHaikuDeviceHooks, + iUnit, + UID_ROOT, + GID_WHEEL, + 0644, + "vboxguest%d", iUnit); + if (*ppDev) + { + dev_ref(*ppDev); + (*ppDev)->si_flags |= SI_CHEAPCLONE; + Log(("VBoxGuestHaikuClone: Created *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); + (*ppDev)->si_drv1 = (*ppDev)->si_drv2 = NULL; + } + else + Log(("VBoxGuestHaikuClone: make_dev iUnit=%d failed\n", iUnit)); + } + else + Log(("VBoxGuestHaikuClone: Existing *ppDev=%p iUnit=%d si_drv1=%p si_drv2=%p\n", + *ppDev, iUnit, (*ppDev)->si_drv1, (*ppDev)->si_drv2)); +} +#endif + + +static status_t VBoxGuestHaikuDetach(void) +{ + struct VBoxGuestDeviceState *pState = &sState; + + if (cUsers > 0) + return EBUSY; + + /* + * Reverse what we did in VBoxGuestHaikuAttach. + */ + VBoxGuestHaikuRemoveIRQ(pState); + + if (pState->iVMMDevMemAreaId) + delete_area(pState->iVMMDevMemAreaId); + + VBoxGuestDeleteDevExt(&g_DevExt); + +#ifdef DO_LOG + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogSetDefaultInstance(NULL); +// RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif + + RTSpinlockDestroy(g_Spinlock); + g_Spinlock = NIL_RTSPINLOCK; + + RTR0Term(); + return B_OK; +} + + +/** + * Interrupt service routine. + * + * @returns Whether the interrupt was from VMMDev. + * @param pvState Opaque pointer to the device state. + */ +static int32 VBoxGuestHaikuISR(void *pvState) +{ + LogFlow((MODULE_NAME ":VBoxGuestHaikuISR pvState=%p\n", pvState)); + + bool fOurIRQ = VBoxGuestCommonISR(&g_DevExt); + if (fOurIRQ) + return B_HANDLED_INTERRUPT; + return B_UNHANDLED_INTERRUPT; +} + + +void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) +{ + LogFlow((MODULE_NAME "::NativeISRMousePollEvent:\n")); + + status_t err = B_OK; + //dprintf(MODULE_NAME ": isr mouse\n"); + + /* + * Wake up poll waiters. + */ + //selwakeup(&g_SelInfo); + //XXX:notify_select_event(); + RTSpinlockAcquire(g_Spinlock); + + if (sState.selectSync) + { + //dprintf(MODULE_NAME ": isr mouse: notify\n"); + notify_select_event(sState.selectSync, sState.selectEvent); + sState.selectEvent = (uint8_t)0; + sState.selectRef = (uint32_t)0; + sState.selectSync = NULL; + } + else + err = B_ERROR; + + RTSpinlockRelease(g_Spinlock); +} + + +/** + * Sets IRQ for VMMDev. + * + * @returns Haiku error code. + * @param pvState Pointer to the state info structure. + */ +static int VBoxGuestHaikuAddIRQ(void *pvState) +{ + status_t err; + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + + AssertReturn(pState, VERR_INVALID_PARAMETER); + + err = install_io_interrupt_handler(pState->iIrqResId, VBoxGuestHaikuISR, pState, 0); + if (err == B_OK) + return VINF_SUCCESS; + return VERR_DEV_IO_ERROR; +} + + +/** + * Removes IRQ for VMMDev. + * + * @param pvState Opaque pointer to the state info structure. + */ +static void VBoxGuestHaikuRemoveIRQ(void *pvState) +{ + struct VBoxGuestDeviceState *pState = (struct VBoxGuestDeviceState *)pvState; + AssertPtr(pState); + + remove_io_interrupt_handler(pState->iIrqResId, VBoxGuestHaikuISR, pState); +} + + +static status_t VBoxGuestHaikuAttach(const pci_info *pDevice) +{ + status_t status; + int rc = VINF_SUCCESS; + int iResId = 0; + struct VBoxGuestDeviceState *pState = &sState; + static const char *const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + + AssertReturn(pDevice, B_BAD_VALUE); + + cUsers = 0; + + /* + * Initialize IPRT R0 driver, which internally calls OS-specific r0 init. + */ + rc = RTR0Init(0); + if (RT_FAILURE(rc)) + { + /** @todo r=ramshankar: use dprintf here. */ + LogFunc(("RTR0Init failed.\n")); + return ENXIO; + } + + rc = RTSpinlockCreate(&g_Spinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "VBoxGuestHaiku"); + if (RT_FAILURE(rc)) + { + LogRel(("VBoxGuestHaikuAttach: RTSpinlock create failed. rc=%Rrc\n", rc)); + return ENXIO; + } + +#ifdef DO_LOG + /* + * Create the release log. + * (We do that here instead of common code because we want to log + * early failures using the LogRel macro.) + */ + rc = RTLogCreate(&pRelLogger, 0 | RTLOGFLAGS_PREFIX_THREAD /* fFlags */, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, + RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER | RTLOGDEST_USER, NULL); + dprintf(MODULE_NAME ": RTLogCreate: %d\n", rc); + if (RT_SUCCESS(rc)) + { + //RTLogGroupSettings(pRelLogger, g_szLogGrp); + //RTLogFlags(pRelLogger, g_szLogFlags); + //RTLogDestinations(pRelLogger, "/var/log/vboxguest.log"); + RTLogRelSetDefaultInstance(pRelLogger); + RTLogSetDefaultInstance(pRelLogger); //XXX + } +#endif + + /* + * Allocate I/O port resource. + */ + pState->uIOPortBase = pDevice->u.h0.base_registers[0]; + /* @todo check flags for IO? */ + if (pState->uIOPortBase) + { + /* + * Map the MMIO region. + */ + uint32 phys = pDevice->u.h0.base_registers[1]; + /* @todo Check flags for mem? */ + pState->VMMDevMemSize = pDevice->u.h0.base_register_sizes[1]; + pState->iVMMDevMemAreaId = map_physical_memory("VirtualBox Guest MMIO", phys, pState->VMMDevMemSize, + B_ANY_KERNEL_BLOCK_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, + &pState->pMMIOBase); + if (pState->iVMMDevMemAreaId > 0 && pState->pMMIOBase) + { + /* + * Call the common device extension initializer. + */ + rc = VBoxGuestInitDevExt(&g_DevExt, pState->uIOPortBase, pState->pMMIOBase, pState->VMMDevMemSize, +#if ARCH_BITS == 64 + VBOXOSTYPE_Haiku_x64, +#else + VBOXOSTYPE_Haiku, +#endif + VMMDEV_EVENT_MOUSE_POSITION_CHANGED); + if (RT_SUCCESS(rc)) + { + /* + * Add IRQ of VMMDev. + */ + pState->iIrqResId = pDevice->u.h0.interrupt_line; + rc = VBoxGuestHaikuAddIRQ(pState); + if (RT_SUCCESS(rc)) + { + LogRel((MODULE_NAME ": loaded successfully\n")); + return B_OK; + } + + LogRel((MODULE_NAME ":VBoxGuestInitDevExt failed.\n")); + VBoxGuestDeleteDevExt(&g_DevExt); + } + else + LogRel((MODULE_NAME ":VBoxGuestHaikuAddIRQ failed.\n")); + } + else + LogRel((MODULE_NAME ":MMIO region setup failed.\n")); + } + else + LogRel((MODULE_NAME ":IOport setup failed.\n")); + + RTR0Term(); + return ENXIO; +} + + +static status_t VBoxGuestHaikuProbe(pci_info *pDevice) +{ + if ((pDevice->vendor_id == VMMDEV_VENDORID) && (pDevice->device_id == VMMDEV_DEVICEID)) + return B_OK; + + return ENXIO; +} + + +status_t init_module(void) +{ + status_t err = B_ENTRY_NOT_FOUND; + pci_info info; + int ix = 0; + + err = get_module(B_PCI_MODULE_NAME, (module_info **)&gPCI); + if (err != B_OK) + return err; + + while ((*gPCI->get_nth_pci_info)(ix++, &info) == B_OK) + { + if (VBoxGuestHaikuProbe(&info) == 0) + { + /* We found it */ + err = VBoxGuestHaikuAttach(&info); + return err; + } + } + + return B_ENTRY_NOT_FOUND; +} + + +void uninit_module(void) +{ + VBoxGuestHaikuDetach(); + put_module(B_PCI_MODULE_NAME); +} + + +static status_t std_ops(int32 op, ...) +{ + switch (op) + { + case B_MODULE_INIT: + return init_module(); + + case B_MODULE_UNINIT: + { + uninit_module(); + return B_OK; + } + + default: + return B_ERROR; + } +} + + +_EXPORT module_info *modules[] = +{ + (module_info *)&g_VBoxGuest, + NULL +}; + +/* Common code that depend on g_DevExt. */ +#include "VBoxGuestIDC-unix.c.h" + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h new file mode 100644 index 00000000..d577448c --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-haiku.h @@ -0,0 +1,226 @@ +/* $Id: VBoxGuest-haiku.h $ */ +/** @file + * VBoxGuest kernel module, Haiku Guest Additions, header. + */ + +/* + * Copyright (C) 2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +/* + * This code is based on: + * + * VirtualBox Guest Additions for Haiku. + * Copyright (c) 2011 Mike Smith <mike@scgtrp.net> + * François Revol <revol@free.fr> + * + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following + * conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES + * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR + * OTHER DEALINGS IN THE SOFTWARE. + */ + + +#ifndef ___VBoxGuest_haiku_h +#define ___VBoxGuest_haiku_h + +#include <OS.h> +#include <Drivers.h> +#include <drivers/module.h> + +#include "VBoxGuestInternal.h" +#include <VBox/log.h> +#include <iprt/assert.h> +#include <iprt/initterm.h> +#include <iprt/process.h> +#include <iprt/mem.h> +#include <iprt/asm.h> +#include <iprt/mp.h> +#include <iprt/power.h> +#include <iprt/thread.h> + +/** The module name. */ +#define VBOXGUEST_MODULE_NAME "generic/vboxguest" + +struct VBoxGuestDeviceState +{ + /** Resource ID of the I/O port */ + int iIOPortResId; + /** Pointer to the I/O port resource. */ +// struct resource *pIOPortRes; + /** Start address of the IO Port. */ + uint16_t uIOPortBase; + /** Resource ID of the MMIO area */ + area_id iVMMDevMemAreaId; + /** Pointer to the MMIO resource. */ +// struct resource *pVMMDevMemRes; + /** Handle of the MMIO resource. */ +// bus_space_handle_t VMMDevMemHandle; + /** Size of the memory area. */ + size_t VMMDevMemSize; + /** Mapping of the register space */ + void *pMMIOBase; + /** IRQ number */ + int iIrqResId; + /** IRQ resource handle. */ +// struct resource *pIrqRes; + /** Pointer to the IRQ handler. */ +// void *pfnIrqHandler; + /** VMMDev version */ + uint32_t u32Version; + + /** The (only) select data we wait on. */ + //XXX: should leave in pSession ? + uint8_t selectEvent; + uint32_t selectRef; + void *selectSync; +}; + +struct vboxguest_module_info +{ + module_info module; + + VBOXGUESTDEVEXT devExt; + struct VBoxGuestDeviceState _sState; + volatile uint32_t _cUsers; + + size_t(*_RTLogBackdoorPrintf)(const char *pszFormat, ...); + size_t(*_RTLogBackdoorPrintfV)(const char *pszFormat, va_list args); + int (*_RTLogSetDefaultInstanceThread)(PRTLOGGER pLogger, uintptr_t uKey); + int (*_RTMemAllocExTag)(size_t cb, size_t cbAlignment, uint32_t fFlags, const char *pszTag, void **ppv); + void* (*_RTMemContAlloc)(PRTCCPHYS pPhys, size_t cb); + void (*_RTMemContFree)(void *pv, size_t cb); + void (*_RTMemFreeEx)(void *pv, size_t cb); + bool (*_RTMpIsCpuPossible)(RTCPUID idCpu); + int (*_RTMpNotificationDeregister)(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + int (*_RTMpNotificationRegister)(PFNRTMPNOTIFICATION pfnCallback, void *pvUser); + int (*_RTMpOnAll)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTMpOnOthers)(PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTMpOnSpecific)(RTCPUID idCpu, PFNRTMPWORKER pfnWorker, void *pvUser1, void *pvUser2); + int (*_RTPowerNotificationDeregister)(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser); + int (*_RTPowerNotificationRegister)(PFNRTPOWERNOTIFICATION pfnCallback, void *pvUser); + int (*_RTPowerSignalEvent)(RTPOWEREVENT enmEvent); + void (*_RTR0AssertPanicSystem)(void); + int (*_RTR0Init)(unsigned fReserved); + void* (*_RTR0MemObjAddress)(RTR0MEMOBJ MemObj); + RTR3PTR(*_RTR0MemObjAddressR3)(RTR0MEMOBJ MemObj); + int (*_RTR0MemObjAllocContTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocLowTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocPageTag)(PRTR0MEMOBJ pMemObj, size_t cb, bool fExecutable, const char *pszTag); + int (*_RTR0MemObjAllocPhysExTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, size_t uAlignment, const char *pszTag); + int (*_RTR0MemObjAllocPhysNCTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag); + int (*_RTR0MemObjAllocPhysTag)(PRTR0MEMOBJ pMemObj, size_t cb, RTHCPHYS PhysHighest, const char *pszTag); + int (*_RTR0MemObjEnterPhysTag)(PRTR0MEMOBJ pMemObj, RTHCPHYS Phys, size_t cb, uint32_t uCachePolicy, const char *pszTag); + int (*_RTR0MemObjFree)(RTR0MEMOBJ MemObj, bool fFreeMappings); + RTHCPHYS(*_RTR0MemObjGetPagePhysAddr)(RTR0MEMOBJ MemObj, size_t iPage); + bool (*_RTR0MemObjIsMapping)(RTR0MEMOBJ MemObj); + int (*_RTR0MemObjLockKernelTag)(PRTR0MEMOBJ pMemObj, void *pv, size_t cb, uint32_t fAccess, const char *pszTag); + int (*_RTR0MemObjLockUserTag)(PRTR0MEMOBJ pMemObj, RTR3PTR R3Ptr, size_t cb, uint32_t fAccess, + RTR0PROCESS R0Process, const char *pszTag); + int (*_RTR0MemObjMapKernelExTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, size_t uAlignment, + unsigned fProt, size_t offSub, size_t cbSub, const char *pszTag); + int (*_RTR0MemObjMapKernelTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, void *pvFixed, + size_t uAlignment, unsigned fProt, const char *pszTag); + int (*_RTR0MemObjMapUserTag)(PRTR0MEMOBJ pMemObj, RTR0MEMOBJ MemObjToMap, RTR3PTR R3PtrFixed, + size_t uAlignment, unsigned fProt, RTR0PROCESS R0Process, const char *pszTag); + int (*_RTR0MemObjProtect)(RTR0MEMOBJ hMemObj, size_t offSub, size_t cbSub, uint32_t fProt); + int (*_RTR0MemObjReserveKernelTag)(PRTR0MEMOBJ pMemObj, void *pvFixed, size_t cb, size_t uAlignment, const char *pszTag); + int (*_RTR0MemObjReserveUserTag)(PRTR0MEMOBJ pMemObj, RTR3PTR R3PtrFixed, size_t cb, size_t uAlignment, + RTR0PROCESS R0Process, const char *pszTag); + size_t(*_RTR0MemObjSize)(RTR0MEMOBJ MemObj); + RTR0PROCESS(*_RTR0ProcHandleSelf)(void); + void (*_RTR0Term)(void); + void (*_RTR0TermForced)(void); + RTPROCESS(*_RTProcSelf)(void); + uint32_t(*_RTSemEventGetResolution)(void); + uint32_t(*_RTSemEventMultiGetResolution)(void); + int (*_RTSemEventMultiWaitEx)(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout); + int (*_RTSemEventMultiWaitExDebug)(RTSEMEVENTMULTI hEventMultiSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL); + int (*_RTSemEventWaitEx)(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout); + int (*_RTSemEventWaitExDebug)(RTSEMEVENT hEventSem, uint32_t fFlags, uint64_t uTimeout, + RTHCUINTPTR uId, RT_SRC_POS_DECL); + bool (*_RTThreadIsInInterrupt)(RTTHREAD hThread); + void (*_RTThreadPreemptDisable)(PRTTHREADPREEMPTSTATE pState); + bool (*_RTThreadPreemptIsEnabled)(RTTHREAD hThread); + bool (*_RTThreadPreemptIsPending)(RTTHREAD hThread); + bool (*_RTThreadPreemptIsPendingTrusty)(void); + bool (*_RTThreadPreemptIsPossible)(void); + void (*_RTThreadPreemptRestore)(PRTTHREADPREEMPTSTATE pState); + uint32_t(*_RTTimerGetSystemGranularity)(void); + int (*_RTTimerReleaseSystemGranularity)(uint32_t u32Granted); + int (*_RTTimerRequestSystemGranularity)(uint32_t u32Request, uint32_t *pu32Granted); + void (*_RTSpinlockAcquire)(RTSPINLOCK Spinlock); + void (*_RTSpinlockRelease)(RTSPINLOCK Spinlock); + void (*_RTSpinlockReleaseNoInts)(RTSPINLOCK Spinlock); + void* (*_RTMemTmpAllocTag)(size_t cb, const char *pszTag); + void (*_RTMemTmpFree)(void *pv); + PRTLOGGER(*_RTLogDefaultInstance)(void); + PRTLOGGER(*_RTLogRelDefaultInstance)(void); + int (*_RTErrConvertToErrno)(int iErr); + int (*_VBoxGuestCommonIOCtl)(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, + void *pvData, size_t cbData, size_t *pcbDataReturned); + int (*_VBoxGuestCreateUserSession)(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *ppSession); + void (*_VBoxGuestCloseSession)(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); + void* (*_VBoxGuestIDCOpen)(uint32_t *pu32Version); + int (*_VBoxGuestIDCClose)(void *pvSession); + int (*_VBoxGuestIDCCall)(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); + void (*_RTAssertMsg1Weak)(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction); + void (*_RTAssertMsg2Weak)(const char *pszFormat, ...); + void (*_RTAssertMsg2WeakV)(const char *pszFormat, va_list va); + bool (*_RTAssertShouldPanic)(void); + int (*_RTSemFastMutexCreate)(PRTSEMFASTMUTEX phFastMtx); + int (*_RTSemFastMutexDestroy)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemFastMutexRelease)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemFastMutexRequest)(RTSEMFASTMUTEX hFastMtx); + int (*_RTSemMutexCreate)(PRTSEMMUTEX phFastMtx); + int (*_RTSemMutexDestroy)(RTSEMMUTEX hFastMtx); + int (*_RTSemMutexRelease)(RTSEMMUTEX hFastMtx); + int (*_RTSemMutexRequest)(RTSEMMUTEX hFastMtx, RTMSINTERVAL cMillies); + int (*_RTHeapSimpleRelocate)(RTHEAPSIMPLE hHeap, uintptr_t offDelta); + int (*_RTHeapOffsetInit)(PRTHEAPOFFSET phHeap, void *pvMemory, size_t cbMemory); + int (*_RTHeapSimpleInit)(PRTHEAPSIMPLE pHeap, void *pvMemory, size_t cbMemory); + void* (*_RTHeapOffsetAlloc)(RTHEAPOFFSET hHeap, size_t cb, size_t cbAlignment); + void* (*_RTHeapSimpleAlloc)(RTHEAPSIMPLE Heap, size_t cb, size_t cbAlignment); + void (*_RTHeapOffsetFree)(RTHEAPOFFSET hHeap, void *pv); + void (*_RTHeapSimpleFree)(RTHEAPSIMPLE Heap, void *pv); +}; + + +#ifdef IN_VBOXGUEST +#define g_DevExt (g_VBoxGuest.devExt) +#define cUsers (g_VBoxGuest._cUsers) +#define sState (g_VBoxGuest._sState) +#else +#define g_DevExt (g_VBoxGuest->devExt) +#define cUsers (g_VBoxGuest->_cUsers) +#define sState (g_VBoxGuest->_sState) +extern struct vboxguest_module_info *g_VBoxGuest; +#endif + +#endif /* ___VBoxGuest_haiku_h */ + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c index b4270bae..a4824893 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-linux.c @@ -1,4 +1,4 @@ -/* $Rev: 80789 $ */ +/* $Rev: 88900 $ */ /** @file * VBoxGuest - Linux specifics. * @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -196,7 +196,11 @@ static struct miscdevice g_MiscDeviceUser = /** PCI hotplug structure. */ -static const struct pci_device_id __devinitdata g_VBoxGuestPciId[] = +static const struct pci_device_id +#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 8, 0) +__devinitdata +#endif +g_VBoxGuestPciId[] = { { vendor: VMMDEV_VENDORID, @@ -936,9 +940,9 @@ void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) * Wake up everyone that's in a poll() and post anyone that has * subscribed to async notifications. */ - Log(("VBoxGuestNativeISRMousePollEvent: wake_up_all\n")); + Log3(("VBoxGuestNativeISRMousePollEvent: wake_up_all\n")); wake_up_all(&g_PollEventQueue); - Log(("VBoxGuestNativeISRMousePollEvent: kill_fasync\n")); + Log3(("VBoxGuestNativeISRMousePollEvent: kill_fasync\n")); kill_fasync(&g_pFAsyncQueue, SIGIO, POLL_IN); #ifdef VBOXGUEST_WITH_INPUT_DRIVER /* Report events to the kernel input device */ @@ -957,7 +961,7 @@ void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) # endif } #endif - Log(("VBoxGuestNativeISRMousePollEvent: done\n")); + Log3(("VBoxGuestNativeISRMousePollEvent: done\n")); } diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp index a74f5595..c4ba2219 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -19,7 +19,7 @@ * * VBoxDrv - OS/2 specifics. * - * Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> + * Copyright (c) 2007-2012 knut st. osmundsen <bird-src-spam@anduin.net> * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def index 4728686e..15c04914 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-os2.def @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2007 Oracle Corporation +; Copyright (C) 2007-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c index a94e3848..7c7ff3fc 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.c @@ -696,6 +696,8 @@ static int VBoxGuestSolarisIOCtl(dev_t Dev, int Cmd, intptr_t pArg, int Mode, cr * which are not really failures that require logging. */ Log((DEVICE_NAME "::IOCtl: VBoxGuestCommonIOCtl failed. Cmd=%#x rc=%d\n", Cmd, rc)); + if (rc == VERR_PERMISSION_DENIED) /* RTErrConvertToErrno() below will ring-0 debug assert if we don't do this. */ + rc = VERR_ACCESS_DENIED; rc = RTErrConvertToErrno(rc); } *pVal = rc; diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf index 6c23af13..1e2c814f 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-solaris.conf @@ -1,7 +1,7 @@ # # OpenSolaris Guest Driver Configuration # -# Copyright (C) 2007-2010 Oracle Corporation +# Copyright (C) 2007-2012 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-legacy.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-legacy.cpp index 5f1ce5d7..3dd55441 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-legacy.cpp +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-legacy.cpp @@ -1,8 +1,10 @@ +/* $Id: VBoxGuest-win-legacy.cpp $ */ /** @file - * * VBoxGuest-win-legacy - Windows NT4 specifics. - * - * Copyright (C) 2010 Oracle Corporation + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -22,15 +24,12 @@ #include <VBox/log.h> #include <VBox/version.h> #include <VBox/VBoxGuestLib.h> +#include <iprt/string.h> /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ - -/* Reenable logging, this was #undef'ed on iprt/log.h for RING0. */ -#define LOG_ENABLED - #ifndef PCI_MAX_BUSES # define PCI_MAX_BUSES 256 #endif @@ -40,13 +39,12 @@ * Internal Functions * *******************************************************************************/ RT_C_DECLS_BEGIN -NTSTATUS vboxguestwinnt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath); -static NTSTATUS vboxguestwinnt4FindPCIDevice(PULONG pBusNumber, PPCI_SLOT_NUMBER pSlotNumber); +static NTSTATUS vbgdNt4FindPciDevice(PULONG pulBusNumber, PPCI_SLOT_NUMBER pSlotNumber); RT_C_DECLS_END #ifdef ALLOC_PRAGMA -#pragma alloc_text (INIT, vboxguestwinnt4CreateDevice) -#pragma alloc_text (INIT, vboxguestwinnt4FindPCIDevice) +# pragma alloc_text(INIT, vbgdNt4CreateDevice) +# pragma alloc_text(INIT, vbgdNt4FindPciDevice) #endif @@ -55,97 +53,86 @@ RT_C_DECLS_END * * @returns NT status code. * - * @param pDrvObj - * @param pDevObj - * @param pRegPath + * @param pDrvObj The driver object. + * @param pDevObj Unused. NULL. Dunno why it's here, makes no sense. + * @param pRegPath The driver registry path. */ -NTSTATUS vboxguestwinnt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath) +NTSTATUS vbgdNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath) { - int vrc = VINF_SUCCESS; - NTSTATUS rc = STATUS_SUCCESS; - - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: pDrvObj=%p, pDevObj=%p, pRegPath=%p\n", - pDrvObj, pDevObj, pRegPath)); + Log(("VBoxGuest::vbgdNt4CreateDevice: pDrvObj=%p, pDevObj=%p, pRegPath=%p\n", pDrvObj, pDevObj, pRegPath)); /* * Find our virtual PCI device */ - ULONG uBusNumber, uSlotNumber; - rc = vboxguestwinnt4FindPCIDevice(&uBusNumber, (PCI_SLOT_NUMBER*)&uSlotNumber); + ULONG uBusNumber; + PCI_SLOT_NUMBER SlotNumber; + NTSTATUS rc = vbgdNt4FindPciDevice(&uBusNumber, &SlotNumber); if (NT_ERROR(rc)) - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Device not found!\n")); - - bool fSymbolicLinkCreated = false; - UNICODE_STRING szDosName; - PDEVICE_OBJECT pDeviceObject = NULL; - if (NT_SUCCESS(rc)) { - /* - * Create device. - */ - UNICODE_STRING szDevName; - RtlInitUnicodeString(&szDevName, VBOXGUEST_DEVICE_NAME_NT); - rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &szDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); - if (NT_SUCCESS(rc)) - { - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Device created\n")); - - RtlInitUnicodeString(&szDosName, VBOXGUEST_DEVICE_NAME_DOS); - rc = IoCreateSymbolicLink(&szDosName, &szDevName); - if (NT_SUCCESS(rc)) - { - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Symlink created\n")); - fSymbolicLinkCreated = true; - } - else - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc)); - } - else - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc)); + Log(("VBoxGuest::vbgdNt4CreateDevice: Device not found!\n")); + return rc; } /* - * Setup the device extension. + * Create device. */ - PVBOXGUESTDEVEXT pDevExt = NULL; + UNICODE_STRING szDevName; + RtlInitUnicodeString(&szDevName, VBOXGUEST_DEVICE_NAME_NT); + PDEVICE_OBJECT pDeviceObject = NULL; + rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &szDevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (NT_SUCCESS(rc)) { - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Setting up device extension ...\n")); + Log(("VBoxGuest::vbgdNt4CreateDevice: Device created\n")); - pDevExt = (PVBOXGUESTDEVEXT)pDeviceObject->DeviceExtension; - RtlZeroMemory(pDevExt, sizeof(VBOXGUESTDEVEXT)); - } + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + rc = IoCreateSymbolicLink(&DosName, &szDevName); + if (NT_SUCCESS(rc)) + { + Log(("VBoxGuest::vbgdNt4CreateDevice: Symlink created\n")); - if (NT_SUCCESS(rc) && pDevExt) - { - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Device extension created\n")); + /* + * Setup the device extension. + */ + Log(("VBoxGuest::vbgdNt4CreateDevice: Setting up device extension ...\n")); - /* Store a reference to ourself. */ - pDevExt->win.s.pDeviceObject = pDeviceObject; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension; + RT_ZERO(*pDevExt); - /* Store bus and slot number we've queried before. */ - pDevExt->win.s.busNumber = uBusNumber; - pDevExt->win.s.slotNumber = uSlotNumber; + Log(("VBoxGuest::vbgdNt4CreateDevice: Device extension created\n")); - #ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION - rc = hlpRegisterBugCheckCallback(pDevExt); - #endif - } + /* Store a reference to ourself. */ + pDevExt->pDeviceObject = pDeviceObject; - /* Do the actual VBox init ... */ - if (NT_SUCCESS(rc)) - rc = vboxguestwinInit(pDrvObj, pDeviceObject, pRegPath); + /* Store bus and slot number we've queried before. */ + pDevExt->busNumber = uBusNumber; + pDevExt->slotNumber = SlotNumber.u.AsULONG; - /* Clean up in case of errors. */ - if (NT_ERROR(rc)) - { - if (fSymbolicLinkCreated && szDosName.Length > 0) - IoDeleteSymbolicLink(&szDosName); - if (pDeviceObject) - IoDeleteDevice(pDeviceObject); - } +#ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION + rc = hlpRegisterBugCheckCallback(pDevExt); +#endif - Log(("VBoxGuest::vboxguestwinnt4CreateDevice: Returning rc = 0x%x\n", rc)); + /* Do the actual VBox init ... */ + if (NT_SUCCESS(rc)) + { + rc = vbgdNtInit(pDrvObj, pDeviceObject, pRegPath); + if (NT_SUCCESS(rc)) + { + Log(("VBoxGuest::vbgdNt4CreateDevice: Returning rc = 0x%x (succcess)\n", rc)); + return rc; + } + + /* bail out */ + } + IoDeleteSymbolicLink(&DosName); + } + else + Log(("VBoxGuest::vbgdNt4CreateDevice: IoCreateSymbolicLink failed with rc = %#x\n", rc)); + IoDeleteDevice(pDeviceObject); + } + else + Log(("VBoxGuest::vbgdNt4CreateDevice: IoCreateDevice failed with rc = %#x\n", rc)); + Log(("VBoxGuest::vbgdNt4CreateDevice: Returning rc = 0x%x\n", rc)); return rc; } @@ -155,70 +142,57 @@ NTSTATUS vboxguestwinnt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDev * * @returns NT status code. * - * @param pBusNumber - * @param pSlotNumber - * + * @param pulBusNumber Where to return the bus number on success. + * @param pSlotNumber Where to return the slot number on success. */ -static NTSTATUS vboxguestwinnt4FindPCIDevice(PULONG pBusNumber, PPCI_SLOT_NUMBER pSlotNumber) +static NTSTATUS vbgdNt4FindPciDevice(PULONG pulBusNumber, PPCI_SLOT_NUMBER pSlotNumber) { - NTSTATUS rc; - - ULONG busNumber; - ULONG deviceNumber; - ULONG functionNumber; - PCI_SLOT_NUMBER slotNumber; - PCI_COMMON_CONFIG pciData; - - Log(("VBoxGuest::vboxguestwinnt4FindPCIDevice\n")); + Log(("VBoxGuest::vbgdNt4FindPciDevice\n")); - rc = STATUS_DEVICE_DOES_NOT_EXIST; - slotNumber.u.AsULONG = 0; + PCI_SLOT_NUMBER SlotNumber; + SlotNumber.u.AsULONG = 0; /* Scan each bus. */ - for (busNumber = 0; busNumber < PCI_MAX_BUSES; busNumber++) + for (ULONG ulBusNumber = 0; ulBusNumber < PCI_MAX_BUSES; ulBusNumber++) { /* Scan each device. */ - for (deviceNumber = 0; deviceNumber < PCI_MAX_DEVICES; deviceNumber++) + for (ULONG deviceNumber = 0; deviceNumber < PCI_MAX_DEVICES; deviceNumber++) { - slotNumber.u.bits.DeviceNumber = deviceNumber; + SlotNumber.u.bits.DeviceNumber = deviceNumber; /* Scan each function (not really required...). */ - for (functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++) + for (ULONG functionNumber = 0; functionNumber < PCI_MAX_FUNCTION; functionNumber++) { - slotNumber.u.bits.FunctionNumber = functionNumber; + SlotNumber.u.bits.FunctionNumber = functionNumber; /* Have a look at what's in this slot. */ - if (!HalGetBusData(PCIConfiguration, busNumber, slotNumber.u.AsULONG, - &pciData, sizeof(ULONG))) + PCI_COMMON_CONFIG PciData; + if (!HalGetBusData(PCIConfiguration, ulBusNumber, SlotNumber.u.AsULONG, &PciData, sizeof(ULONG))) { /* No such bus, we're done with it. */ deviceNumber = PCI_MAX_DEVICES; break; } - if (pciData.VendorID == PCI_INVALID_VENDORID) - { + if (PciData.VendorID == PCI_INVALID_VENDORID) /* We have to proceed to the next function. */ continue; - } /* Check if it's another device. */ - if ((pciData.VendorID != VMMDEV_VENDORID) || - (pciData.DeviceID != VMMDEV_DEVICEID)) - { + if ( PciData.VendorID != VMMDEV_VENDORID + || PciData.DeviceID != VMMDEV_DEVICEID) continue; - } /* Hooray, we've found it! */ - Log(("VBoxGuest::vboxguestwinnt4FindPCIDevice: Device found!\n")); + Log(("VBoxGuest::vbgdNt4FindPciDevice: Device found!\n")); - *pBusNumber = busNumber; - *pSlotNumber = slotNumber; - rc = STATUS_SUCCESS; + *pulBusNumber = ulBusNumber; + *pSlotNumber = SlotNumber; + return STATUS_SUCCESS; } } } - return rc; + return STATUS_DEVICE_DOES_NOT_EXIST; } diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-pnp.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-pnp.cpp index fb844b78..73ec6ff4 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-pnp.cpp +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win-pnp.cpp @@ -1,8 +1,10 @@ +/* $Id: VBoxGuest-win-pnp.cpp $ */ /** @file - * * VBoxGuest-win-pnp - Windows Plug'n'Play specifics. - * - * Copyright (C) 2010 Oracle Corporation + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -27,38 +29,29 @@ /******************************************************************************* * Defined Constants And Macros * *******************************************************************************/ -extern winVersion_t g_winVersion; - RT_C_DECLS_BEGIN -static NTSTATUS vboxguestwinSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict); -static NTSTATUS vboxguestwinPnPIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent); -static VOID vboxguestwinShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList); +static NTSTATUS vbgdNtSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict); +static NTSTATUS vbgdNtPnPIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent); +static VOID vbgdNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList); RT_C_DECLS_END #ifdef ALLOC_PRAGMA -#pragma alloc_text (PAGE, vboxguestwinPnP) -#pragma alloc_text (PAGE, vboxguestwinPower) -#pragma alloc_text (PAGE, vboxguestwinSendIrpSynchronously) -#pragma alloc_text (PAGE, vboxguestwinShowDeviceResources) +# pragma alloc_text(PAGE, vbgdNtPnP) +# pragma alloc_text(PAGE, vbgdNtPower) +# pragma alloc_text(PAGE, vbgdNtSendIrpSynchronously) +# pragma alloc_text(PAGE, vbgdNtShowDeviceResources) #endif -/* Reenable logging, this was #undef'ed on iprt/log.h for RING0. */ -#define LOG_ENABLED - - -/******************************************************************************* -* Internal Functions * -*******************************************************************************/ /** * Irp completion routine for PnP Irps we send. * * @param pDevObj Device object. * @param pIrp Request packet. - * @param event Semaphore. + * @param pEvent Semaphore. * @return NT status code */ -static NTSTATUS vboxguestwinPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent) +static NTSTATUS vbgdNtPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PKEVENT pEvent) { KeSetEvent(pEvent, 0, FALSE); return STATUS_MORE_PROCESSING_REQUIRED; @@ -73,21 +66,21 @@ static NTSTATUS vboxguestwinPnpIrpComplete(PDEVICE_OBJECT pDevObj, PIRP pIrp, PK * @param fStrict When set, returns an error if the IRP gives an error. * @return NT status code */ -static NTSTATUS vboxguestwinSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict) +static NTSTATUS vbgdNtSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pIrp, BOOLEAN fStrict) { - KEVENT event; + KEVENT Event; - KeInitializeEvent(&event, SynchronizationEvent, FALSE); + KeInitializeEvent(&Event, SynchronizationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(pIrp); - IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vboxguestwinPnpIrpComplete, - &event, TRUE, TRUE, TRUE); + IoSetCompletionRoutine(pIrp, (PIO_COMPLETION_ROUTINE)vbgdNtPnpIrpComplete, + &Event, TRUE, TRUE, TRUE); NTSTATUS rc = IoCallDriver(pDevObj, pIrp); if (rc == STATUS_PENDING) { - KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); + KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); rc = pIrp->IoStatus.Status; } @@ -97,7 +90,7 @@ static NTSTATUS vboxguestwinSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pI rc = STATUS_SUCCESS; } - Log(("VBoxGuest::vboxguestwinSendIrpSynchronously: Returning 0x%x\n", rc)); + Log(("VBoxGuest::vbgdNtSendIrpSynchronously: Returning 0x%x\n", rc)); return rc; } @@ -108,13 +101,13 @@ static NTSTATUS vboxguestwinSendIrpSynchronously(PDEVICE_OBJECT pDevObj, PIRP pI * @param pDevObj Device object. * @param pIrp Request packet. */ -NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; - PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); #ifdef LOG_ENABLED - static char* aszFnctName[] = + static char *s_apszFnctName[] = { "IRP_MN_START_DEVICE", "IRP_MN_QUERY_REMOVE_DEVICE", @@ -130,7 +123,7 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) "IRP_MN_QUERY_RESOURCE_REQUIREMENTS", "IRP_MN_QUERY_DEVICE_TEXT", "IRP_MN_FILTER_RESOURCE_REQUIREMENTS", - "", + "IRP_MN_0xE", "IRP_MN_READ_CONFIG", "IRP_MN_WRITE_CONFIG", "IRP_MN_EJECT", @@ -141,10 +134,8 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) "IRP_MN_DEVICE_USAGE_NOTIFICATION", "IRP_MN_SURPRISE_REMOVAL", }; - Log(("VBoxGuest::vboxguestwinGuestPnp: MinorFunction: %s\n", - pStack->MinorFunction < (sizeof(aszFnctName) / sizeof(aszFnctName[0])) - ? aszFnctName[pStack->MinorFunction] - : "Unknown")); + Log(("VBoxGuest::vbgdNtGuestPnp: MinorFunction: %s\n", + pStack->MinorFunction < RT_ELEMENTS(s_apszFnctName) ? s_apszFnctName[pStack->MinorFunction] : "Unknown")); #endif NTSTATUS rc = STATUS_SUCCESS; @@ -152,50 +143,50 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) { case IRP_MN_START_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: START_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: START_DEVICE\n")); /* This must be handled first by the lower driver. */ - rc = vboxguestwinSendIrpSynchronously(pDevExt->win.s.pNextLowerDriver, pIrp, TRUE); + rc = vbgdNtSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); if ( NT_SUCCESS(rc) && NT_SUCCESS(pIrp->IoStatus.Status)) { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n", + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: START_DEVICE: pStack->Parameters.StartDevice.AllocatedResources = %p\n", pStack->Parameters.StartDevice.AllocatedResources)); if (!pStack->Parameters.StartDevice.AllocatedResources) { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n", - pDevExt, pDevExt ? pDevExt->win.s.pNextLowerDriver : NULL)); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: START_DEVICE: No resources, pDevExt = %p, nextLowerDriver = %p!\n", + pDevExt, pDevExt ? pDevExt->pNextLowerDriver : NULL)); rc = STATUS_UNSUCCESSFUL; } else { - rc = vboxguestwinInit(pDevObj, pIrp); + rc = vbgdNtInit(pDevObj, pIrp); } } if (NT_ERROR(rc)) { - Log(("VBoxGuest::vboxguestwinGuestPnp: START_DEVICE: Error: rc = 0x%x\n", rc)); + Log(("VBoxGuest::vbgdNtGuestPnp: START_DEVICE: Error: rc = 0x%x\n", rc)); /* Need to unmap memory in case of errors ... */ - vboxguestwinUnmapVMMDevMemory(pDevExt); + vbgdNtUnmapVMMDevMemory(pDevExt); } break; } case IRP_MN_CANCEL_REMOVE_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: CANCEL_REMOVE_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: CANCEL_REMOVE_DEVICE\n")); /* This must be handled first by the lower driver. */ - rc = vboxguestwinSendIrpSynchronously(pDevExt->win.s.pNextLowerDriver, pIrp, TRUE); + rc = vbgdNtSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); - if (NT_SUCCESS(rc) && pDevExt->win.s.devState == PENDINGREMOVE) + if (NT_SUCCESS(rc) && pDevExt->devState == PENDINGREMOVE) { /* Return to the state prior to receiving the IRP_MN_QUERY_REMOVE_DEVICE request. */ - pDevExt->win.s.devState = pDevExt->win.s.prevDevState; + pDevExt->devState = pDevExt->prevDevState; } /* Complete the IRP. */ @@ -204,7 +195,7 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_SURPRISE_REMOVAL: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: IRP_MN_SURPRISE_REMOVAL\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: IRP_MN_SURPRISE_REMOVAL\n")); VBOXGUEST_UPDATE_DEVSTATE(pDevExt, SURPRISEREMOVED); @@ -218,7 +209,7 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); /* Do not complete the IRP. */ return rc; @@ -226,12 +217,12 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_QUERY_REMOVE_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: QUERY_REMOVE_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: QUERY_REMOVE_DEVICE\n")); #ifdef VBOX_REBOOT_ON_UNINSTALL - Log(("VBoxGuest::vboxguestwinGuestPnp: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n")); + Log(("VBoxGuest::vbgdNtGuestPnp: QUERY_REMOVE_DEVICE: Device cannot be removed without a reboot.\n")); rc = STATUS_UNSUCCESSFUL; -#endif /* VBOX_REBOOT_ON_UNINSTALL */ +#endif if (NT_SUCCESS(rc)) { @@ -242,8 +233,8 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); - Log(("VBoxGuest::vboxguestwinGuestPnp: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("VBoxGuest::vbgdNtGuestPnp: QUERY_REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); /* we must not do anything the IRP after doing IoSkip & CallDriver * since the driver below us will complete (or already have completed) the IRP. @@ -257,14 +248,15 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_REMOVE_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: REMOVE_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: REMOVE_DEVICE\n")); VBOXGUEST_UPDATE_DEVSTATE(pDevExt, REMOVED); /* Free hardware resources. */ - /* @todo this should actually free I/O ports, interrupts, etc. */ - rc = vboxguestwinCleanup(pDevObj); - Log(("VBoxGuest::vboxguestwinGuestPnp: REMOVE_DEVICE: vboxguestwinCleanup rc = 0x%08X\n", rc)); + /** @todo this should actually free I/O ports, interrupts, etc. + * Update/bird: vbgdNtCleanup actually does that... So, what's there to do? */ + rc = vbgdNtCleanup(pDevObj); + Log(("VBoxGuest::vbgdNtGuestPnp: REMOVE_DEVICE: vbgdNtCleanup rc = 0x%08X\n", rc)); /* * We need to send the remove down the stack before we detach, @@ -275,28 +267,28 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); - Log(("VBoxGuest::vboxguestwinGuestPnp: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("VBoxGuest::vbgdNtGuestPnp: REMOVE_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); - IoDetachDevice(pDevExt->win.s.pNextLowerDriver); + IoDetachDevice(pDevExt->pNextLowerDriver); - Log(("VBoxGuest::vboxguestwinGuestPnp: REMOVE_DEVICE: Removing device ...\n")); + Log(("VBoxGuest::vbgdNtGuestPnp: REMOVE_DEVICE: Removing device ...\n")); /* Destroy device extension and clean up everything else. */ - VBoxGuestDeleteDevExt(pDevExt); + VBoxGuestDeleteDevExt(&pDevExt->Core); /* Remove DOS device + symbolic link. */ UNICODE_STRING win32Name; RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS); IoDeleteSymbolicLink(&win32Name); - Log(("VBoxGuest::vboxguestwinGuestPnp: REMOVE_DEVICE: Deleting device ...\n")); + Log(("VBoxGuest::vbgdNtGuestPnp: REMOVE_DEVICE: Deleting device ...\n")); /* Last action: Delete our device! pDevObj is *not* failed * anymore after this call! */ IoDeleteDevice(pDevObj); - Log(("VBoxGuest::vboxguestwinGuestPnp: REMOVE_DEVICE: Device removed!\n")); + Log(("VBoxGuest::vbgdNtGuestPnp: REMOVE_DEVICE: Device removed!\n")); /* Propagating rc from IoCallDriver. */ return rc; /* Make sure that we don't do anything below here anymore! */ @@ -304,15 +296,15 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_CANCEL_STOP_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: CANCEL_STOP_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: CANCEL_STOP_DEVICE\n")); /* This must be handled first by the lower driver. */ - rc = vboxguestwinSendIrpSynchronously(pDevExt->win.s.pNextLowerDriver, pIrp, TRUE); + rc = vbgdNtSendIrpSynchronously(pDevExt->pNextLowerDriver, pIrp, TRUE); - if (NT_SUCCESS(rc) && pDevExt->win.s.devState == PENDINGSTOP) + if (NT_SUCCESS(rc) && pDevExt->devState == PENDINGSTOP) { /* Return to the state prior to receiving the IRP_MN_QUERY_STOP_DEVICE request. */ - pDevExt->win.s.devState = pDevExt->win.s.prevDevState; + pDevExt->devState = pDevExt->prevDevState; } /* Complete the IRP. */ @@ -321,12 +313,12 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_QUERY_STOP_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: QUERY_STOP_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: QUERY_STOP_DEVICE\n")); #ifdef VBOX_REBOOT_ON_UNINSTALL - Log(("VBoxGuest::vboxguestwinGuestPnp: QUERY_STOP_DEVICE: Device cannot be stopped without a reboot!\n")); + Log(("VBoxGuest::vbgdNtGuestPnp: QUERY_STOP_DEVICE: Device cannot be stopped without a reboot!\n")); pIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; -#endif /* VBOX_REBOOT_ON_UNINSTALL */ +#endif if (NT_SUCCESS(rc)) { @@ -337,8 +329,8 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); - Log(("VBoxGuest::vboxguestwinGuestPnp: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("VBoxGuest::vbgdNtGuestPnp: QUERY_STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); /* we must not do anything with the IRP after doing IoSkip & CallDriver * since the driver below us will complete (or already have completed) the IRP. @@ -352,22 +344,23 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) case IRP_MN_STOP_DEVICE: { - Log(("VBoxGuest::vboxguestwinVBoxGuestPnP: STOP_DEVICE\n")); + Log(("VBoxGuest::vbgdNtVBoxGuestPnP: STOP_DEVICE\n")); VBOXGUEST_UPDATE_DEVSTATE(pDevExt, STOPPED); /* Free hardware resources. */ - /* @todo this should actually free I/O ports, interrupts, etc. */ - rc = vboxguestwinCleanup(pDevObj); - Log(("VBoxGuest::vboxguestwinGuestPnp: STOP_DEVICE: cleaning up, rc = 0x%x\n", rc)); + /** @todo this should actually free I/O ports, interrupts, etc. + * Update/bird: vbgdNtCleanup actually does that... So, what's there to do? */ + rc = vbgdNtCleanup(pDevObj); + Log(("VBoxGuest::vbgdNtGuestPnp: STOP_DEVICE: cleaning up, rc = 0x%x\n", rc)); /* Pass to the lower driver. */ pIrp->IoStatus.Status = STATUS_SUCCESS; IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); - Log(("VBoxGuest::vboxguestwinGuestPnp: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); + Log(("VBoxGuest::vbgdNtGuestPnp: STOP_DEVICE: Next lower driver replied rc = 0x%x\n", rc)); return rc; } @@ -375,7 +368,7 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) default: { IoSkipCurrentIrpStackLocation(pIrp); - rc = IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); + rc = IoCallDriver(pDevExt->pNextLowerDriver, pIrp); return rc; } } @@ -383,7 +376,7 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) pIrp->IoStatus.Status = rc; IoCompleteRequest(pIrp, IO_NO_INCREMENT); - Log(("VBoxGuest::vboxguestwinGuestPnp: Returning with rc = 0x%x\n", rc)); + Log(("VBoxGuest::vbgdNtGuestPnp: Returning with rc = 0x%x\n", rc)); return rc; } @@ -396,19 +389,17 @@ NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pIrp IO request packet. * @param pContext Context value passed to IoSetCompletionRoutine in VBoxGuestPower. */ -NTSTATUS vboxguestwinPowerComplete(IN PDEVICE_OBJECT pDevObj, - IN PIRP pIrp, IN PVOID pContext) +static NTSTATUS vbgdNtPowerComplete(IN PDEVICE_OBJECT pDevObj, IN PIRP pIrp, IN PVOID pContext) { - PIO_STACK_LOCATION pIrpSp; - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pContext; +#ifdef VBOX_STRICT + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pContext; + PIO_STACK_LOCATION pIrpSp = IoGetCurrentIrpStackLocation(pIrp); - ASSERT(pDevExt); - ASSERT(pDevExt->signature == DEVICE_EXTENSION_SIGNATURE); + Assert(pDevExt); - pIrpSp = IoGetCurrentIrpStackLocation(pIrp); if (pIrpSp) { - ASSERT(pIrpSp->MajorFunction == IRP_MJ_POWER); + Assert(pIrpSp->MajorFunction == IRP_MJ_POWER); if (NT_SUCCESS(pIrp->IoStatus.Status)) { switch (pIrpSp->MinorFunction) @@ -429,6 +420,7 @@ NTSTATUS vboxguestwinPowerComplete(IN PDEVICE_OBJECT pDevObj, } } } +#endif return STATUS_SUCCESS; } @@ -441,56 +433,55 @@ NTSTATUS vboxguestwinPowerComplete(IN PDEVICE_OBJECT pDevObj, * @param pDevObj device object * @param pIrp IRP */ -NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; - POWER_STATE_TYPE powerType; - POWER_STATE powerState; - POWER_ACTION powerAction; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + POWER_STATE_TYPE enmPowerType = pStack->Parameters.Power.Type; + POWER_STATE PowerState = pStack->Parameters.Power.State; + POWER_ACTION enmPowerAction = pStack->Parameters.Power.ShutdownType; - Log(("VBoxGuest::vboxguestwinGuestPower\n")); - - powerType = pStack->Parameters.Power.Type; - powerAction = pStack->Parameters.Power.ShutdownType; - powerState = pStack->Parameters.Power.State; + Log(("VBoxGuest::vbgdNtGuestPower\n")); switch (pStack->MinorFunction) { case IRP_MN_SET_POWER: { - Log(("VBoxGuest::vboxguestwinGuestPower: IRP_MN_SET_POWER, type= %d\n", powerType)); - switch (powerType) + Log(("VBoxGuest::vbgdNtGuestPower: IRP_MN_SET_POWER, type= %d\n", enmPowerType)); + switch (enmPowerType) { case SystemPowerState: { - Log(("VBoxGuest::vboxguestwinGuestPower: SystemPowerState, action = %d, state = %d\n", powerAction, powerState)); + Log(("VBoxGuest::vbgdNtGuestPower: SystemPowerState, action = %d, state = %d/%d\n", + enmPowerAction, PowerState.SystemState, PowerState.DeviceState)); - switch (powerAction) + switch (enmPowerAction) { case PowerActionSleep: /* System now is in a working state. */ - if (powerState.SystemState == PowerSystemWorking) + if (PowerState.SystemState == PowerSystemWorking) { if ( pDevExt - && pDevExt->win.s.LastSystemPowerAction == PowerActionHibernate) + && pDevExt->LastSystemPowerAction == PowerActionHibernate) { - Log(("VBoxGuest::vboxguestwinGuestPower: Returning from hibernation!\n")); - int rc = VBoxGuestReinitDevExtAfterHibernation(pDevExt, vboxguestwinVersionToOSType(g_winVersion)); + Log(("VBoxGuest::vbgdNtGuestPower: Returning from hibernation!\n")); + int rc = VBoxGuestReinitDevExtAfterHibernation(&pDevExt->Core, + vbgdNtVersionToOSType(g_enmVbgdNtVer)); if (RT_FAILURE(rc)) - Log(("VBoxGuest::vboxguestwinGuestPower: Cannot re-init VMMDev chain, rc = %d!\n", rc)); + Log(("VBoxGuest::vbgdNtGuestPower: Cannot re-init VMMDev chain, rc = %d!\n", rc)); } } break; case PowerActionShutdownReset: { - Log(("VBoxGuest::vboxguestwinGuestPower: Power action reset!\n")); + Log(("VBoxGuest::vbgdNtGuestPower: Power action reset!\n")); /* Tell the VMM that we no longer support mouse pointer integration. */ VMMDevReqMouseStatus *pReq = NULL; - int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus), VMMDevReq_SetMouseStatus); + int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pReq, sizeof (VMMDevReqMouseStatus), + VMMDevReq_SetMouseStatus); if (RT_SUCCESS(vrc)) { pReq->mouseFeatures = 0; @@ -500,8 +491,7 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) vrc = VbglGRPerform(&pReq->header); if (RT_FAILURE(vrc)) { - Log(("VBoxGuest::PowerStateRequest: error communicating new power status to VMMDev. " - "vrc = %Rrc\n", vrc)); + Log(("VBoxGuest::PowerStateRequest: error communicating new power status to VMMDev. vrc = %Rrc\n", vrc)); } VbglGRFree(&pReq->header); @@ -515,12 +505,12 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) case PowerActionShutdown: case PowerActionShutdownOff: { - Log(("VBoxGuest::vboxguestwinGuestPower: Power action shutdown!\n")); - if (powerState.SystemState >= PowerSystemShutdown) + Log(("VBoxGuest::vbgdNtGuestPower: Power action shutdown!\n")); + if (PowerState.SystemState >= PowerSystemShutdown) { - Log(("VBoxGuest::vboxguestwinGuestPower: Telling the VMMDev to close the VM ...\n")); + Log(("VBoxGuest::vbgdNtGuestPower: Telling the VMMDev to close the VM ...\n")); - VMMDevPowerStateRequest *pReq = pDevExt->win.s.pPowerStateRequest; + VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest; int vrc = VERR_NOT_IMPLEMENTED; if (pReq) { @@ -530,10 +520,7 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) vrc = VbglGRPerform(&pReq->header); } if (RT_FAILURE(vrc)) - { - Log(("VBoxGuest::PowerStateRequest: Error communicating new power status to VMMDev. " - "vrc = %Rrc\n", vrc)); - } + Log(("VBoxGuest::PowerStateRequest: Error communicating new power status to VMMDev. vrc = %Rrc\n", vrc)); /* No need to do cleanup here; at this point we should've been * turned off by VMMDev already! */ @@ -543,7 +530,7 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) case PowerActionHibernate: - Log(("VBoxGuest::vboxguestwinGuestPower: Power action hibernate!\n")); + Log(("VBoxGuest::vbgdNtGuestPower: Power action hibernate!\n")); break; } @@ -552,7 +539,7 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) * This becomes handy when we return from hibernation for example. */ if (pDevExt) - pDevExt->win.s.LastSystemPowerAction = powerAction; + pDevExt->LastSystemPowerAction = enmPowerAction; break; } @@ -566,21 +553,22 @@ NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp) } /* - * Whether we are completing or relaying this power IRP, - * we must call PoStartNextPowerIrp. + * Whether we are completing or relaying this power IRP, + * we must call PoStartNextPowerIrp. */ PoStartNextPowerIrp(pIrp); /* - * Send the IRP down the driver stack, - * using PoCallDriver (not IoCallDriver, as for non-power irps). + * Send the IRP down the driver stack, using PoCallDriver + * (not IoCallDriver, as for non-power irps). */ IoCopyCurrentIrpStackLocationToNext(pIrp); IoSetCompletionRoutine(pIrp, - vboxguestwinPowerComplete, + vbgdNtPowerComplete, (PVOID)pDevExt, TRUE, TRUE, TRUE); - return PoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); + return PoCallDriver(pDevExt->pNextLowerDriver, pIrp); } + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp index 9299ae5b..d4ba77b2 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.cpp @@ -1,8 +1,10 @@ +/* $Id: VBoxGuest-win.cpp $ */ /** @file - * * VBoxGuest - Windows specifics. - * - * Copyright (C) 2010 Oracle Corporation + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -25,6 +27,7 @@ #include <VBox/log.h> #include <VBox/VBoxGuestLib.h> +#include <iprt/string.h> /* * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist @@ -35,22 +38,23 @@ # undef ExFreePool #endif + /******************************************************************************* * Internal Functions * *******************************************************************************/ RT_C_DECLS_BEGIN -static NTSTATUS vboxguestwinAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj); -static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj); -static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinClose(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue); -static NTSTATUS vboxguestwinSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp); -static NTSTATUS vboxguestwinNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj); +static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj); +static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue); +static NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp); +static NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp); #ifdef DEBUG -static void vboxguestwinDoTests(void); +static void vbgdNtDoTests(void); #endif RT_C_DECLS_END @@ -63,19 +67,24 @@ ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath); RT_C_DECLS_END #ifdef ALLOC_PRAGMA -#pragma alloc_text (INIT, DriverEntry) -#pragma alloc_text (PAGE, vboxguestwinAddDevice) -#pragma alloc_text (PAGE, vboxguestwinUnload) -#pragma alloc_text (PAGE, vboxguestwinCreate) -#pragma alloc_text (PAGE, vboxguestwinClose) -#pragma alloc_text (PAGE, vboxguestwinIOCtl) -#pragma alloc_text (PAGE, vboxguestwinShutdown) -#pragma alloc_text (PAGE, vboxguestwinNotSupportedStub) -#pragma alloc_text (PAGE, vboxguestwinScanPCIResourceList) +# pragma alloc_text(INIT, DriverEntry) +# pragma alloc_text(PAGE, vbgdNtAddDevice) +# pragma alloc_text(PAGE, vbgdNtUnload) +# pragma alloc_text(PAGE, vbgdNtCreate) +# pragma alloc_text(PAGE, vbgdNtClose) +# pragma alloc_text(PAGE, vbgdNtShutdown) +# pragma alloc_text(PAGE, vbgdNtNotSupportedStub) +# pragma alloc_text(PAGE, vbgdNtScanPCIResourceList) #endif -/** The detected Windows version. */ -winVersion_t g_winVersion; + +/******************************************************************************* +* Global Variables * +*******************************************************************************/ +/** The detected NT (windows) version. */ +VBGDNTVER g_enmVbgdNtVer = VBGDNTVER_INVALID; + + /** * Driver entry point. @@ -90,61 +99,70 @@ ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) Log(("VBoxGuest::DriverEntry. Driver built: %s %s\n", __DATE__, __TIME__)); - ULONG majorVersion; - ULONG minorVersion; - ULONG buildNumber; - BOOLEAN bCheckedBuild = PsGetVersion(&majorVersion, &minorVersion, &buildNumber, NULL); - Log(("VBoxGuest::DriverEntry: Running on Windows NT version %d.%d, build %d\n", majorVersion, minorVersion, buildNumber)); - if (bCheckedBuild) + /* + * Check if the the NT version is supported and initializing + * g_enmVbgdNtVer in the process. + */ + ULONG ulMajorVer; + ULONG ulMinorVer; + ULONG ulBuildNo; + BOOLEAN fCheckedBuild = PsGetVersion(&ulMajorVer, &ulMinorVer, &ulBuildNo, NULL); + Log(("VBoxGuest::DriverEntry: Running on Windows NT version %u.%u, build %u\n", ulMajorVer, ulMinorVer, ulBuildNo)); + if (fCheckedBuild) Log(("VBoxGuest::DriverEntry: Running on a Windows checked build (debug)!\n")); #ifdef DEBUG - vboxguestwinDoTests(); + vbgdNtDoTests(); #endif - switch (majorVersion) + switch (ulMajorVer) { case 6: /* Windows Vista or Windows 7 (based on minor ver) */ - switch (minorVersion) + switch (ulMinorVer) { case 0: /* Note: Also could be Windows 2008 Server! */ - g_winVersion = WINVISTA; + g_enmVbgdNtVer = VBGDNTVER_WINVISTA; break; case 1: /* Note: Also could be Windows 2008 Server R2! */ - g_winVersion = WIN7; + g_enmVbgdNtVer = VBGDNTVER_WIN7; break; case 2: - g_winVersion = WIN8; + g_enmVbgdNtVer = VBGDNTVER_WIN8; + break; + case 3: + g_enmVbgdNtVer = VBGDNTVER_WIN81; break; default: - Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", - majorVersion, minorVersion)); + Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer)); rc = STATUS_DRIVER_UNABLE_TO_LOAD; break; } break; case 5: - switch (minorVersion) + switch (ulMinorVer) { case 2: - g_winVersion = WIN2K3; + g_enmVbgdNtVer = VBGDNTVER_WIN2K3; break; case 1: - g_winVersion = WINXP; + g_enmVbgdNtVer = VBGDNTVER_WINXP; break; case 0: - g_winVersion = WIN2K; + g_enmVbgdNtVer = VBGDNTVER_WIN2K; break; default: - Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", - majorVersion, minorVersion)); + Log(("VBoxGuest::DriverEntry: Unknown version of Windows (%u.%u), refusing!\n", ulMajorVer, ulMinorVer)); rc = STATUS_DRIVER_UNABLE_TO_LOAD; } break; case 4: - g_winVersion = WINNT4; + g_enmVbgdNtVer = VBGDNTVER_WINNT4; break; default: - Log(("VBoxGuest::DriverEntry: At least Windows NT4 required!\n")); + if (ulMajorVer < 4) + Log(("VBoxGuest::DriverEntry: At least Windows NT4 required! (%u.%u)\n", ulMajorVer, ulMinorVer)); + else + Log(("VBoxGuest::DriverEntry: Too new version %u.%u!\n", ulMajorVer, ulMinorVer)); rc = STATUS_DRIVER_UNABLE_TO_LOAD; + break; } if (NT_SUCCESS(rc)) @@ -152,21 +170,21 @@ ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) /* * Setup the driver entry points in pDrvObj. */ - pDrvObj->DriverUnload = vboxguestwinUnload; - pDrvObj->MajorFunction[IRP_MJ_CREATE] = vboxguestwinCreate; - pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vboxguestwinClose; - pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vboxguestwinIOCtl; - pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vboxguestwinInternalIOCtl; - pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vboxguestwinShutdown; - pDrvObj->MajorFunction[IRP_MJ_READ] = vboxguestwinNotSupportedStub; - pDrvObj->MajorFunction[IRP_MJ_WRITE] = vboxguestwinNotSupportedStub; + pDrvObj->DriverUnload = vbgdNtUnload; + pDrvObj->MajorFunction[IRP_MJ_CREATE] = vbgdNtCreate; + pDrvObj->MajorFunction[IRP_MJ_CLOSE] = vbgdNtClose; + pDrvObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] = vbgdNtIOCtl; + pDrvObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = vbgdNtInternalIOCtl; + pDrvObj->MajorFunction[IRP_MJ_SHUTDOWN] = vbgdNtShutdown; + pDrvObj->MajorFunction[IRP_MJ_READ] = vbgdNtNotSupportedStub; + pDrvObj->MajorFunction[IRP_MJ_WRITE] = vbgdNtNotSupportedStub; #ifdef TARGET_NT4 - rc = vboxguestwinnt4CreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath); + rc = vbgdNt4CreateDevice(pDrvObj, NULL /* pDevObj */, pRegPath); #else - pDrvObj->MajorFunction[IRP_MJ_PNP] = vboxguestwinPnP; - pDrvObj->MajorFunction[IRP_MJ_POWER] = vboxguestwinPower; - pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vboxguestwinSystemControl; - pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vboxguestwinAddDevice; + pDrvObj->MajorFunction[IRP_MJ_PNP] = vbgdNtPnP; + pDrvObj->MajorFunction[IRP_MJ_POWER] = vbgdNtPower; + pDrvObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = vbgdNtSystemControl; + pDrvObj->DriverExtension->AddDevice = (PDRIVER_ADD_DEVICE)vbgdNtAddDevice; #endif } @@ -183,84 +201,79 @@ ULONG DriverEntry(PDRIVER_OBJECT pDrvObj, PUNICODE_STRING pRegPath) * @param pDrvObj Driver object * @param pDevObj Device object */ -static NTSTATUS vboxguestwinAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj) +static NTSTATUS vbgdNtAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj) { NTSTATUS rc; - Log(("VBoxGuest::vboxguestwinGuestAddDevice\n")); + Log(("VBoxGuest::vbgdNtGuestAddDevice\n")); /* * Create device. */ + UNICODE_STRING DevName; + RtlInitUnicodeString(&DevName, VBOXGUEST_DEVICE_NAME_NT); PDEVICE_OBJECT pDeviceObject = NULL; - PVBOXGUESTDEVEXT pDevExt = NULL; - UNICODE_STRING devName; - UNICODE_STRING win32Name; - RtlInitUnicodeString(&devName, VBOXGUEST_DEVICE_NAME_NT); - rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXT), &devName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); + rc = IoCreateDevice(pDrvObj, sizeof(VBOXGUESTDEVEXTWIN), &DevName, FILE_DEVICE_UNKNOWN, 0, FALSE, &pDeviceObject); if (NT_SUCCESS(rc)) { /* * Create symbolic link (DOS devices). */ - RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS); - rc = IoCreateSymbolicLink(&win32Name, &devName); + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + rc = IoCreateSymbolicLink(&DosName, &DevName); if (NT_SUCCESS(rc)) { /* * Setup the device extension. */ - pDevExt = (PVBOXGUESTDEVEXT)pDeviceObject->DeviceExtension; - RtlZeroMemory(pDevExt, sizeof(VBOXGUESTDEVEXT)); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDeviceObject->DeviceExtension; + RT_ZERO(*pDevExt); - KeInitializeSpinLock(&pDevExt->win.s.MouseEventAccessLock); + KeInitializeSpinLock(&pDevExt->MouseEventAccessLock); - pDevExt->win.s.pDeviceObject = pDeviceObject; - pDevExt->win.s.prevDevState = STOPPED; - pDevExt->win.s.devState = STOPPED; + pDevExt->pDeviceObject = pDeviceObject; + pDevExt->prevDevState = STOPPED; + pDevExt->devState = STOPPED; - pDevExt->win.s.pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj); - if (pDevExt->win.s.pNextLowerDriver == NULL) + pDevExt->pNextLowerDriver = IoAttachDeviceToDeviceStack(pDeviceObject, pDevObj); + if (pDevExt->pNextLowerDriver != NULL) { - Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n")); - rc = STATUS_DEVICE_NOT_CONNECTED; - } - } - else - Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc)); - } - else - Log(("VBoxGuest::vboxguestwinGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc)); - - if (NT_SUCCESS(rc)) - { - /* - * If we reached this point we're fine with the basic driver setup, - * so continue to init our own things. - */ + /* + * If we reached this point we're fine with the basic driver setup, + * so continue to init our own things. + */ #ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION - vboxguestwinBugCheckCallback(pDevExt); /* Ignore failure! */ + vbgdNtBugCheckCallback(pDevExt); /* Ignore failure! */ #endif - /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */ - pDeviceObject->Flags |= DO_POWER_PAGABLE; + if (NT_SUCCESS(rc)) + { + /* VBoxGuestPower is pageable; ensure we are not called at elevated IRQL */ + pDeviceObject->Flags |= DO_POWER_PAGABLE; - /* Driver is ready now. */ - pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; - } + /* Driver is ready now. */ + pDeviceObject->Flags &= ~DO_DEVICE_INITIALIZING; + Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x (success)\n", rc)); + return rc; + } - /* Cleanup on error. */ - if (NT_ERROR(rc)) - { - if (pDevExt) - { - if (pDevExt->win.s.pNextLowerDriver) - IoDetachDevice(pDevExt->win.s.pNextLowerDriver); + IoDetachDevice(pDevExt->pNextLowerDriver); + } + else + { + Log(("VBoxGuest::vbgdNtGuestAddDevice: IoAttachDeviceToDeviceStack did not give a nextLowerDriver!\n")); + rc = STATUS_DEVICE_NOT_CONNECTED; + } + + /* bail out */ + IoDeleteSymbolicLink(&DosName); } - IoDeleteSymbolicLink(&win32Name); - if (pDeviceObject) - IoDeleteDevice(pDeviceObject); + else + Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateSymbolicLink failed with rc=%#x!\n", rc)); + IoDeleteDevice(pDeviceObject); } - - Log(("VBoxGuest::vboxguestwinGuestAddDevice: returning with rc = 0x%x\n", rc)); + else + Log(("VBoxGuest::vbgdNtGuestAddDevice: IoCreateDevice failed with rc=%#x!\n", rc)); + Log(("VBoxGuest::vbgdNtGuestAddDevice: returning with rc = 0x%x\n", rc)); return rc; } #endif @@ -271,17 +284,16 @@ static NTSTATUS vboxguestwinAddDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDe * * @param pResourceList list of device resources. */ -static void vboxguestwinShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList) +static void vbgdNtShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceList) { #ifdef LOG_ENABLED - PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = pResourceList->PartialDescriptors; - ULONG nres = pResourceList->Count; - ULONG i; + PCM_PARTIAL_RESOURCE_DESCRIPTOR pResource = pResourceList->PartialDescriptors; + ULONG cResources = pResourceList->Count; - for (i = 0; i < nres; ++i, ++resource) + for (ULONG i = 0; i < cResources; ++i, ++pResource) { - ULONG uType = resource->Type; - static char* aszName[] = + ULONG uType = pResource->Type; + static char const * const s_apszName[] = { "CmResourceTypeNull", "CmResourceTypePort", @@ -295,28 +307,27 @@ static void vboxguestwinShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceL "CmResourceTypeSubAllocateFrom", }; - Log(("VBoxGuest::vboxguestwinShowDeviceResources: Type %s", - uType < (sizeof(aszName) / sizeof(aszName[0])) - ? aszName[uType] : "Unknown")); + Log(("VBoxGuest::vbgdNtShowDeviceResources: Type %s", + uType < RT_ELEMENTS(s_apszName) ? s_apszName[uType] : "Unknown")); switch (uType) { case CmResourceTypePort: case CmResourceTypeMemory: - Log(("VBoxGuest::vboxguestwinShowDeviceResources: Start %8X%8.8lX length %X\n", - resource->u.Port.Start.HighPart, resource->u.Port.Start.LowPart, - resource->u.Port.Length)); + Log(("VBoxGuest::vbgdNtShowDeviceResources: Start %8X%8.8lX length %X\n", + pResource->u.Port.Start.HighPart, pResource->u.Port.Start.LowPart, + pResource->u.Port.Length)); break; case CmResourceTypeInterrupt: - Log(("VBoxGuest::vboxguestwinShowDeviceResources: Level %X, Vector %X, Affinity %X\n", - resource->u.Interrupt.Level, resource->u.Interrupt.Vector, - resource->u.Interrupt.Affinity)); + Log(("VBoxGuest::vbgdNtShowDeviceResources: Level %X, Vector %X, Affinity %X\n", + pResource->u.Interrupt.Level, pResource->u.Interrupt.Vector, + pResource->u.Interrupt.Affinity)); break; case CmResourceTypeDma: - Log(("VBoxGuest::vboxguestwinShowDeviceResources: Channel %d, Port %X\n", - resource->u.Dma.Channel, resource->u.Dma.Port)); + Log(("VBoxGuest::vbgdNtShowDeviceResources: Channel %d, Port %X\n", + pResource->u.Dma.Channel, pResource->u.Dma.Port)); break; default: @@ -335,24 +346,24 @@ static void vboxguestwinShowDeviceResources(PCM_PARTIAL_RESOURCE_LIST pResourceL * @param pIrp Request packet. */ #ifndef TARGET_NT4 -NTSTATUS vboxguestwinInit(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp) #else -NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath) +NTSTATUS vbgdNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath) #endif { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; #ifndef TARGET_NT4 - PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); #endif - Log(("VBoxGuest::vboxguestwinInit\n")); + Log(("VBoxGuest::vbgdNtInit\n")); int rc = STATUS_SUCCESS; #ifdef TARGET_NT4 /* * Let's have a look at what our PCI adapter offers. */ - Log(("VBoxGuest::vboxguestwinInit: Starting to scan PCI resources of VBoxGuest ...\n")); + Log(("VBoxGuest::vbgdNtInit: Starting to scan PCI resources of VBoxGuest ...\n")); /* Assign the PCI resources. */ PCM_RESOURCE_LIST pResourceList = NULL; @@ -360,18 +371,17 @@ NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICO RtlInitUnicodeString(&classNameString, L"VBoxGuestAdapter"); rc = HalAssignSlotResources(pRegPath, &classNameString, pDrvObj, pDevObj, - PCIBus, pDevExt->win.s.busNumber, pDevExt->win.s.slotNumber, + PCIBus, pDevExt->busNumber, pDevExt->slotNumber, &pResourceList); if (pResourceList && pResourceList->Count > 0) - vboxguestwinShowDeviceResources(&pResourceList->List[0].PartialResourceList); + vbgdNtShowDeviceResources(&pResourceList->List[0].PartialResourceList); if (NT_SUCCESS(rc)) - rc = vboxguestwinScanPCIResourceList(pResourceList, pDevExt); + rc = vbgdNtScanPCIResourceList(pResourceList, pDevExt); #else if (pStack->Parameters.StartDevice.AllocatedResources->Count > 0) - vboxguestwinShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList); + vbgdNtShowDeviceResources(&pStack->Parameters.StartDevice.AllocatedResources->List[0].PartialResourceList); if (NT_SUCCESS(rc)) - rc = vboxguestwinScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, - pDevExt); + rc = vbgdNtScanPCIResourceList(pStack->Parameters.StartDevice.AllocatedResourcesTranslated, pDevExt); #endif if (NT_SUCCESS(rc)) { @@ -381,40 +391,40 @@ NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICO */ void *pvMMIOBase = NULL; uint32_t cbMMIO = 0; - rc = vboxguestwinMapVMMDevMemory(pDevExt, - pDevExt->win.s.vmmDevPhysMemoryAddress, - pDevExt->win.s.vmmDevPhysMemoryLength, - &pvMMIOBase, - &cbMMIO); + rc = vbgdNtMapVMMDevMemory(pDevExt, + pDevExt->vmmDevPhysMemoryAddress, + pDevExt->vmmDevPhysMemoryLength, + &pvMMIOBase, + &cbMMIO); if (NT_SUCCESS(rc)) { - pDevExt->pVMMDevMemory = (VMMDevMemory *)pvMMIOBase; + pDevExt->Core.pVMMDevMemory = (VMMDevMemory *)pvMMIOBase; - Log(("VBoxGuest::vboxguestwinInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->pVMMDevMemory = 0x%p\n", - pvMMIOBase, pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL)); + Log(("VBoxGuest::vbgdNtInit: pvMMIOBase = 0x%p, pDevExt = 0x%p, pDevExt->Core.pVMMDevMemory = 0x%p\n", + pvMMIOBase, pDevExt, pDevExt ? pDevExt->Core.pVMMDevMemory : NULL)); - int vrc = VBoxGuestInitDevExt(pDevExt, - pDevExt->IOPortBase, + int vrc = VBoxGuestInitDevExt(&pDevExt->Core, + pDevExt->Core.IOPortBase, pvMMIOBase, cbMMIO, - vboxguestwinVersionToOSType(g_winVersion), + vbgdNtVersionToOSType(g_enmVbgdNtVer), VMMDEV_EVENT_MOUSE_POSITION_CHANGED); if (RT_FAILURE(vrc)) { - Log(("VBoxGuest::vboxguestwinInit: Could not init device extension, rc = %Rrc!\n", vrc)); + Log(("VBoxGuest::vbgdNtInit: Could not init device extension, rc = %Rrc!\n", vrc)); rc = STATUS_DEVICE_CONFIGURATION_ERROR; } } else - Log(("VBoxGuest::vboxguestwinInit: Could not map physical address of VMMDev, rc = 0x%x!\n", rc)); + Log(("VBoxGuest::vbgdNtInit: Could not map physical address of VMMDev, rc = 0x%x!\n", rc)); } if (NT_SUCCESS(rc)) { - int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->win.s.pPowerStateRequest, - sizeof (VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus); + int vrc = VbglGRAlloc((VMMDevRequestHeader **)&pDevExt->pPowerStateRequest, + sizeof(VMMDevPowerStateRequest), VMMDevReq_SetPowerStatus); if (RT_FAILURE(vrc)) { - Log(("VBoxGuest::vboxguestwinInit: Alloc for pPowerStateRequest failed, rc = %Rrc\n", vrc)); + Log(("VBoxGuest::vbgdNtInit: Alloc for pPowerStateRequest failed, rc = %Rrc\n", vrc)); rc = STATUS_UNSUCCESSFUL; } } @@ -424,68 +434,68 @@ NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICO /* * Register DPC and ISR. */ - Log(("VBoxGuest::vboxguestwinInit: Initializing DPC/ISR ...\n")); + Log(("VBoxGuest::vbgdNtInit: Initializing DPC/ISR ...\n")); - IoInitializeDpcRequest(pDevExt->win.s.pDeviceObject, vboxguestwinDpcHandler); + IoInitializeDpcRequest(pDevExt->pDeviceObject, vbgdNtDpcHandler); #ifdef TARGET_NT4 ULONG uInterruptVector; KIRQL irqLevel; /* Get an interrupt vector. */ /* Only proceed if the device provides an interrupt. */ - if ( pDevExt->win.s.interruptLevel - || pDevExt->win.s.interruptVector) + if ( pDevExt->interruptLevel + || pDevExt->interruptVector) { - Log(("VBoxGuest::vboxguestwinInit: Getting interrupt vector (HAL): Bus: %u, IRQL: %u, Vector: %u\n", - pDevExt->win.s.busNumber, pDevExt->win.s.interruptLevel, pDevExt->win.s.interruptVector)); + Log(("VBoxGuest::vbgdNtInit: Getting interrupt vector (HAL): Bus: %u, IRQL: %u, Vector: %u\n", + pDevExt->busNumber, pDevExt->interruptLevel, pDevExt->interruptVector)); uInterruptVector = HalGetInterruptVector(PCIBus, - pDevExt->win.s.busNumber, - pDevExt->win.s.interruptLevel, - pDevExt->win.s.interruptVector, + pDevExt->busNumber, + pDevExt->interruptLevel, + pDevExt->interruptVector, &irqLevel, - &pDevExt->win.s.interruptAffinity); - Log(("VBoxGuest::vboxguestwinInit: HalGetInterruptVector returns vector %u\n", uInterruptVector)); + &pDevExt->interruptAffinity); + Log(("VBoxGuest::vbgdNtInit: HalGetInterruptVector returns vector %u\n", uInterruptVector)); if (uInterruptVector == 0) - Log(("VBoxGuest::vboxguestwinInit: No interrupt vector found!\n")); + Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n")); } else - Log(("VBoxGuest::vboxguestwinInit: Device does not provide an interrupt!\n")); + Log(("VBoxGuest::vbgdNtInit: Device does not provide an interrupt!\n")); #endif - if (pDevExt->win.s.interruptVector) + if (pDevExt->interruptVector) { - Log(("VBoxGuest::vboxguestwinInit: Connecting interrupt ...\n")); + Log(("VBoxGuest::vbgdNtInit: Connecting interrupt ...\n")); - rc = IoConnectInterrupt(&pDevExt->win.s.pInterruptObject, /* Out: interrupt object. */ - (PKSERVICE_ROUTINE)vboxguestwinIsrHandler, /* Our ISR handler. */ - pDevExt, /* Device context. */ - NULL, /* Optional spinlock. */ + rc = IoConnectInterrupt(&pDevExt->pInterruptObject, /* Out: interrupt object. */ + (PKSERVICE_ROUTINE)vbgdNtIsrHandler, /* Our ISR handler. */ + pDevExt, /* Device context. */ + NULL, /* Optional spinlock. */ #ifdef TARGET_NT4 - uInterruptVector, /* Interrupt vector. */ - irqLevel, /* Interrupt level. */ - irqLevel, /* Interrupt level. */ + uInterruptVector, /* Interrupt vector. */ + irqLevel, /* Interrupt level. */ + irqLevel, /* Interrupt level. */ #else - pDevExt->win.s.interruptVector, /* Interrupt vector. */ - (KIRQL)pDevExt->win.s.interruptLevel, /* Interrupt level. */ - (KIRQL)pDevExt->win.s.interruptLevel, /* Interrupt level. */ + pDevExt->interruptVector, /* Interrupt vector. */ + (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */ + (KIRQL)pDevExt->interruptLevel, /* Interrupt level. */ #endif - pDevExt->win.s.interruptMode, /* LevelSensitive or Latched. */ - TRUE, /* Shareable interrupt. */ - pDevExt->win.s.interruptAffinity, /* CPU affinity. */ - FALSE); /* Don't save FPU stack. */ + pDevExt->interruptMode, /* LevelSensitive or Latched. */ + TRUE, /* Shareable interrupt. */ + pDevExt->interruptAffinity, /* CPU affinity. */ + FALSE); /* Don't save FPU stack. */ if (NT_ERROR(rc)) - Log(("VBoxGuest::vboxguestwinInit: Could not connect interrupt, rc = 0x%x\n", rc)); + Log(("VBoxGuest::vbgdNtInit: Could not connect interrupt, rc = 0x%x\n", rc)); } else - Log(("VBoxGuest::vboxguestwinInit: No interrupt vector found!\n")); + Log(("VBoxGuest::vbgdNtInit: No interrupt vector found!\n")); } #ifdef VBOX_WITH_HGCM - Log(("VBoxGuest::vboxguestwinInit: Allocating kernel session data ...\n")); - int vrc = VBoxGuestCreateKernelSession(pDevExt, &pDevExt->win.s.pKernelSession); + Log(("VBoxGuest::vbgdNtInit: Allocating kernel session data ...\n")); + int vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pDevExt->pKernelSession); if (RT_FAILURE(vrc)) { - Log(("VBoxGuest::vboxguestwinInit: Failed to allocated kernel session data! rc = %Rrc\n", rc)); + Log(("VBoxGuest::vbgdNtInit: Failed to allocated kernel session data! rc = %Rrc\n", rc)); rc = STATUS_UNSUCCESSFUL; } #endif @@ -493,25 +503,25 @@ NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICO if (RT_SUCCESS(rc)) { ULONG ulValue = 0; - NTSTATUS s = vboxguestwinRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled", - &ulValue); - if (NT_SUCCESS(s)) + NTSTATUS rcNt = vbgdNtRegistryReadDWORD(RTL_REGISTRY_SERVICES, L"VBoxGuest", L"LoggingEnabled", &ulValue); + if (NT_SUCCESS(rcNt)) { - pDevExt->fLoggingEnabled = ulValue >= 0xFF; - if (pDevExt->fLoggingEnabled) + pDevExt->Core.fLoggingEnabled = ulValue >= 0xFF; + if (pDevExt->Core.fLoggingEnabled) Log(("Logging to release log enabled (0x%x)", ulValue)); } /* Ready to rumble! */ - Log(("VBoxGuest::vboxguestwinInit: Device is ready!\n")); + Log(("VBoxGuest::vbgdNtInit: Device is ready!\n")); VBOXGUEST_UPDATE_DEVSTATE(pDevExt, WORKING); } else - { - pDevExt->win.s.pInterruptObject = NULL; - } + pDevExt->pInterruptObject = NULL; - Log(("VBoxGuest::vboxguestwinInit: Returned with rc = 0x%x\n", rc)); + /** @todo r=bird: The error cleanup here is completely missing. We'll leak a + * whole bunch of things... */ + + Log(("VBoxGuest::vbgdNtInit: Returned with rc = 0x%x\n", rc)); return rc; } @@ -522,38 +532,38 @@ NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICO * * @param pDrvObj Driver object. */ -NTSTATUS vboxguestwinCleanup(PDEVICE_OBJECT pDevObj) +NTSTATUS vbgdNtCleanup(PDEVICE_OBJECT pDevObj) { - Log(("VBoxGuest::vboxguestwinCleanup\n")); + Log(("VBoxGuest::vbgdNtCleanup\n")); - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; if (pDevExt) { #if 0 /* @todo: test & enable cleaning global session data */ #ifdef VBOX_WITH_HGCM - if (pDevExt->win.s.pKernelSession) + if (pDevExt->pKernelSession) { - VBoxGuestCloseSession(pDevExt, pDevExt->win.s.pKernelSession); - pDevExt->win.s.pKernelSession = NULL; + VBoxGuestCloseSession(pDevExt, pDevExt->pKernelSession); + pDevExt->pKernelSession = NULL; } #endif #endif - if (pDevExt->win.s.pInterruptObject) + if (pDevExt->pInterruptObject) { - IoDisconnectInterrupt(pDevExt->win.s.pInterruptObject); - pDevExt->win.s.pInterruptObject = NULL; + IoDisconnectInterrupt(pDevExt->pInterruptObject); + pDevExt->pInterruptObject = NULL; } - /* @todo: cleanup the rest stuff */ + /** @todo: cleanup the rest stuff */ #ifdef VBOX_WITH_GUEST_BUGCHECK_DETECTION hlpDeregisterBugCheckCallback(pDevExt); /* ignore failure! */ #endif /* According to MSDN we have to unmap previously mapped memory. */ - vboxguestwinUnmapVMMDevMemory(pDevExt); + vbgdNtUnmapVMMDevMemory(pDevExt); } return STATUS_SUCCESS; } @@ -564,11 +574,11 @@ NTSTATUS vboxguestwinCleanup(PDEVICE_OBJECT pDevObj) * * @param pDrvObj Driver object. */ -static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj) +static void vbgdNtUnload(PDRIVER_OBJECT pDrvObj) { - Log(("VBoxGuest::vboxguestwinGuestUnload\n")); + Log(("VBoxGuest::vbgdNtGuestUnload\n")); #ifdef TARGET_NT4 - vboxguestwinCleanup(pDrvObj->DeviceObject); + vbgdNtCleanup(pDrvObj->DeviceObject); /* Destroy device extension and clean up everything else. */ if (pDrvObj->DeviceObject && pDrvObj->DeviceObject->DeviceExtension) @@ -578,18 +588,18 @@ static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj) * I don't think it's possible to unload a driver which processes have * opened, at least we'll blindly assume that here. */ - UNICODE_STRING win32Name; - RtlInitUnicodeString(&win32Name, VBOXGUEST_DEVICE_NAME_DOS); - NTSTATUS rc = IoDeleteSymbolicLink(&win32Name); + UNICODE_STRING DosName; + RtlInitUnicodeString(&DosName, VBOXGUEST_DEVICE_NAME_DOS); + NTSTATUS rc = IoDeleteSymbolicLink(&DosName); IoDeleteDevice(pDrvObj->DeviceObject); -#else /* TARGET_NT4 */ +#else /* !TARGET_NT4 */ /* On a PnP driver this routine will be called after * IRP_MN_REMOVE_DEVICE (where we already did the cleanup), * so don't do anything here (yet). */ -#endif +#endif /* !TARGET_NT4 */ - Log(("VBoxGuest::vboxguestwinGuestUnload: returning\n")); + Log(("VBoxGuest::vbgdNtGuestUnload: returning\n")); } @@ -599,19 +609,18 @@ static void vboxguestwinUnload(PDRIVER_OBJECT pDrvObj) * @param pDevObj Device object. * @param pIrp Request packet. */ -static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) +static NTSTATUS vbgdNtCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) { /** @todo AssertPtrReturn(pIrp); */ - PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); /** @todo AssertPtrReturn(pStack); */ - PFILE_OBJECT pFileObj = pStack->FileObject; - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; - NTSTATUS rc = STATUS_SUCCESS; + PFILE_OBJECT pFileObj = pStack->FileObject; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + NTSTATUS rc = STATUS_SUCCESS; - if (pDevExt->win.s.devState != WORKING) + if (pDevExt->devState != WORKING) { - Log(("VBoxGuest::vboxguestwinGuestCreate: device is not working currently: %d!\n", - pDevExt->win.s.devState)); + Log(("VBoxGuest::vbgdNtGuestCreate: device is not working currently: %d!\n", pDevExt->devState)); rc = STATUS_UNSUCCESSFUL; } else if (pStack->Parameters.Create.Options & FILE_DIRECTORY_FILE) @@ -620,7 +629,7 @@ static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) * We are not remotely similar to a directory... * (But this is possible.) */ - Log(("VBoxGuest::vboxguestwinGuestCreate: Uhm, we're not a directory!\n")); + Log(("VBoxGuest::vbgdNtGuestCreate: Uhm, we're not a directory!\n")); rc = STATUS_NOT_A_DIRECTORY; } else @@ -628,7 +637,7 @@ static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) #ifdef VBOX_WITH_HGCM if (pFileObj) { - Log(("VBoxGuest::vboxguestwinGuestCreate: File object type = %d\n", + Log(("VBoxGuest::vbgdNtGuestCreate: File object type = %d\n", pFileObj->Type)); int vrc; @@ -639,12 +648,12 @@ static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) * Create a session object if we have a valid file object. This session object * exists for every R3 process. */ - vrc = VBoxGuestCreateUserSession(pDevExt, &pSession); + vrc = VBoxGuestCreateUserSession(&pDevExt->Core, &pSession); } else { /* ... otherwise we've been called from R0! */ - vrc = VBoxGuestCreateKernelSession(pDevExt, &pSession); + vrc = VBoxGuestCreateKernelSession(&pDevExt->Core, &pSession); } if (RT_SUCCESS(vrc)) pFileObj->FsContext = pSession; @@ -657,7 +666,7 @@ static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) pIrp->IoStatus.Status = rc; IoCompleteRequest(pIrp, IO_NO_INCREMENT); - Log(("VBoxGuest::vboxguestwinGuestCreate: Returning 0x%x\n", rc)); + Log(("VBoxGuest::vbgdNtGuestCreate: Returning 0x%x\n", rc)); return rc; } @@ -668,20 +677,20 @@ static NTSTATUS vboxguestwinCreate(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pDevObj Device object. * @param pIrp Request packet. */ -static NTSTATUS vboxguestwinClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) +static NTSTATUS vbgdNtClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; - PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); - PFILE_OBJECT pFileObj = pStack->FileObject; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); + PFILE_OBJECT pFileObj = pStack->FileObject; - Log(("VBoxGuest::vboxguestwinGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n", + Log(("VBoxGuest::vbgdNtGuestClose: pDevExt=0x%p pFileObj=0x%p FsContext=0x%p\n", pDevExt, pFileObj, pFileObj->FsContext)); #ifdef VBOX_WITH_HGCM /* Close both, R0 and R3 sessions. */ PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pFileObj->FsContext; if (pSession) - VBoxGuestCloseSession(pDevExt, pSession); + VBoxGuestCloseSession(&pDevExt->Core, pSession); #endif pFileObj->FsContext = NULL; @@ -699,16 +708,16 @@ static NTSTATUS vboxguestwinClose(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pDevObj Device object. * @param pIrp Request packet. */ -static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +static NTSTATUS vbgdNtIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS Status = STATUS_SUCCESS; - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode; char *pBuf = (char *)pIrp->AssociatedIrp.SystemBuffer; /* All requests are buffered. */ size_t cbData = pStack->Parameters.DeviceIoControl.InputBufferLength; - unsigned cbOut = 0; + size_t cbOut = 0; /* Do we have a file object associated?*/ PFILE_OBJECT pFileObj = pStack->FileObject; @@ -716,7 +725,7 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) if (pFileObj) /* ... then we might have a session object as well! */ pSession = (PVBOXGUESTSESSION)pFileObj->FsContext; - Log(("VBoxGuest::vboxguestwinIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n", + Log(("VBoxGuest::vbgdNtIOCtl: uCmd=%u, pDevExt=0x%p, pSession=0x%p\n", uCmd, pDevExt, pSession)); /* We don't have a session associated with the file object? So this seems @@ -725,8 +734,8 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) * shall have its own context of course, no hacks, pleeease. */ if (pSession == NULL) { - Log(("VBoxGuest::vboxguestwinIOCtl: Using kernel session data ...\n")); - pSession = pDevExt->win.s.pKernelSession; + Log(("VBoxGuest::vbgdNtIOCtl: Using kernel session data ...\n")); + pSession = pDevExt->pKernelSession; } /* @@ -739,14 +748,14 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) #ifdef VBOX_WITH_VRDP_SESSION_HANDLING case VBOXGUEST_IOCTL_ENABLE_VRDP_SESSION: { - LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n", + LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis")); if (!pDevExt->fVRDPEnabled) { KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA; pDevExt->fVRDPEnabled = true; - LogRel(("VBoxGuest::vboxguestwinIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", + LogRel(("VBoxGuest::vbgdNtIOCtl: ENABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", pSharedUserData->ActiveConsoleId)); pDevExt->ulOldActiveConsoleId = pSharedUserData->ActiveConsoleId; pSharedUserData->ActiveConsoleId = 2; @@ -756,14 +765,14 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) case VBOXGUEST_IOCTL_DISABLE_VRDP_SESSION: { - LogRel(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n", + LogRel(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Currently: %sabled\n", pDevExt->fVRDPEnabled? "en": "dis")); if (pDevExt->fVRDPEnabled) { KUSER_SHARED_DATA *pSharedUserData = (KUSER_SHARED_DATA *)KI_USER_SHARED_DATA; pDevExt->fVRDPEnabled = false; - Log(("VBoxGuest::vboxguestwinIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", + Log(("VBoxGuest::vbgdNtIOCtl: DISABLE_VRDP_SESSION: Current active console ID: 0x%08X\n", pSharedUserData->ActiveConsoleId)); pSharedUserData->ActiveConsoleId = pDevExt->ulOldActiveConsoleId; pDevExt->ulOldActiveConsoleId = 0; @@ -780,16 +789,16 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) * Process the common IOCtls. */ size_t cbDataReturned; - int vrc = VBoxGuestCommonIOCtl(uCmd, pDevExt, pSession, pBuf, cbData, &cbDataReturned); + int vrc = VBoxGuestCommonIOCtl(uCmd, &pDevExt->Core, pSession, pBuf, cbData, &cbDataReturned); - Log(("VBoxGuest::vboxguestwinGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n", + Log(("VBoxGuest::vbgdNtGuestDeviceControl: rc=%Rrc, pBuf=0x%p, cbData=%u, cbDataReturned=%u\n", vrc, pBuf, cbData, cbDataReturned)); if (RT_SUCCESS(vrc)) { if (RT_UNLIKELY(cbDataReturned > cbData)) { - Log(("VBoxGuest::vboxguestwinGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData)); + Log(("VBoxGuest::vbgdNtGuestDeviceControl: Too much output data %u - expected %u!\n", cbDataReturned, cbData)); cbDataReturned = cbData; Status = STATUS_BUFFER_TOO_SMALL; } @@ -800,9 +809,9 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { if ( vrc == VERR_NOT_SUPPORTED || vrc == VERR_INVALID_PARAMETER) - { Status = STATUS_INVALID_PARAMETER; - } + else if (vrc == VERR_OUT_OF_RANGE) + Status = STATUS_INVALID_BUFFER_SIZE; else Status = STATUS_UNSUCCESSFUL; } @@ -815,32 +824,29 @@ static NTSTATUS vboxguestwinIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) IoCompleteRequest(pIrp, IO_NO_INCREMENT); - //Log(("VBoxGuest::vboxguestwinGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status)); + //Log(("VBoxGuest::vbgdNtGuestDeviceControl: returned cbOut=%d rc=%#x\n", cbOut, Status)); return Status; } /** * Internal Device I/O Control entry point. * - * We do not want to allow some IOCTLs to be originated from user mode, this is - * why we have a different entry point for internal IOCTLs. - * * @param pDevObj Device object. * @param pIrp Request packet. - * - * @todo r=bird: This is no need for this extra function for the purpose of - * securing an IOCTL from user space access. VBoxGuestCommonIOCtl - * has a way to do this already, see VBOXGUEST_IOCTL_GETVMMDEVPORT. */ -static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +static NTSTATUS vbgdNtInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { NTSTATUS Status = STATUS_SUCCESS; - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; PIO_STACK_LOCATION pStack = IoGetCurrentIrpStackLocation(pIrp); unsigned int uCmd = (unsigned int)pStack->Parameters.DeviceIoControl.IoControlCode; bool fProcessed = false; unsigned Info = 0; + /* + * Override common behavior of some operations. + */ + /** @todo r=bird: Better to add dedicated worker functions for this! */ switch (uCmd) { case VBOXGUEST_IOCTL_SET_MOUSE_NOTIFY_CALLBACK: @@ -859,9 +865,9 @@ static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) /* we need a lock here to avoid concurrency with the set event functionality */ KIRQL OldIrql; - KeAcquireSpinLock(&pDevExt->win.s.MouseEventAccessLock, &OldIrql); - pDevExt->MouseNotifyCallback = *pInfo; - KeReleaseSpinLock(&pDevExt->win.s.MouseEventAccessLock, OldIrql); + KeAcquireSpinLock(&pDevExt->MouseEventAccessLock, &OldIrql); + pDevExt->Core.MouseNotifyCallback = *pInfo; + KeReleaseSpinLock(&pDevExt->MouseEventAccessLock, OldIrql); Status = STATUS_SUCCESS; break; @@ -870,8 +876,6 @@ static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) default: break; } - - if (fProcessed) { pIrp->IoStatus.Status = Status; @@ -881,7 +885,10 @@ static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) return Status; } - return vboxguestwinIOCtl(pDevObj, pIrp); + /* + * No override, go to common code. + */ + return vbgdNtIOCtl(pDevObj, pIrp); } @@ -892,16 +899,16 @@ static NTSTATUS vboxguestwinInternalIOCtl(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pDevObj Device object. * @param pIrp IRP. */ -NTSTATUS vboxguestwinSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; - Log(("VBoxGuest::vboxguestwinGuestSystemControl\n")); + Log(("VBoxGuest::vbgdNtGuestSystemControl\n")); /* Always pass it on to the next driver. */ IoSkipCurrentIrpStackLocation(pIrp); - return IoCallDriver(pDevExt->win.s.pNextLowerDriver, pIrp); + return IoCallDriver(pDevExt->pNextLowerDriver, pIrp); } @@ -912,13 +919,13 @@ NTSTATUS vboxguestwinSystemControl(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pDevObj Device object. * @param pIrp IRP. */ -NTSTATUS vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; - Log(("VBoxGuest::vboxguestwinGuestShutdown\n")); + Log(("VBoxGuest::vbgdNtGuestShutdown\n")); - VMMDevPowerStateRequest *pReq = pDevExt->win.s.pPowerStateRequest; + VMMDevPowerStateRequest *pReq = pDevExt->pPowerStateRequest; if (pReq) { pReq->header.requestType = VMMDevReq_SetPowerStatus; @@ -927,7 +934,7 @@ NTSTATUS vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp) int rc = VbglGRPerform(&pReq->header); if (RT_FAILURE(rc)) { - Log(("VBoxGuest::vboxguestwinGuestShutdown: Error performing request to VMMDev! " + Log(("VBoxGuest::vbgdNtGuestShutdown: Error performing request to VMMDev! " "rc = %Rrc\n", rc)); } } @@ -942,9 +949,9 @@ NTSTATUS vboxguestwinShutdown(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pDevObj Device object. * @param pIrp IRP. */ -NTSTATUS vboxguestwinNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp) +NTSTATUS vbgdNtNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp) { - Log(("VBoxGuest::vboxguestwinGuestNotSupportedStub\n")); + Log(("VBoxGuest::vbgdNtGuestNotSupportedStub\n")); pIrp->IoStatus.Information = 0; pIrp->IoStatus.Status = STATUS_NOT_SUPPORTED; @@ -962,28 +969,28 @@ NTSTATUS vboxguestwinNotSupportedStub(PDEVICE_OBJECT pDevObj, PIRP pIrp) * @param pIrp Interrupt request packet. * @param pContext Context specific pointer. */ -void vboxguestwinDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext) +void vbgdNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pDevObj->DeviceExtension; - Log(("VBoxGuest::vboxguestwinGuestDpcHandler: pDevExt=0x%p\n", pDevExt)); + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pDevObj->DeviceExtension; + Log(("VBoxGuest::vbgdNtGuestDpcHandler: pDevExt=0x%p\n", pDevExt)); /* test & reset the counter */ - if (ASMAtomicXchgU32(&pDevExt->u32MousePosChangedSeq, 0)) + if (ASMAtomicXchgU32(&pDevExt->Core.u32MousePosChangedSeq, 0)) { /* we need a lock here to avoid concurrency with the set event ioctl handler thread, * i.e. to prevent the event from destroyed while we're using it */ Assert(KeGetCurrentIrql() == DISPATCH_LEVEL); - KeAcquireSpinLockAtDpcLevel(&pDevExt->win.s.MouseEventAccessLock); + KeAcquireSpinLockAtDpcLevel(&pDevExt->MouseEventAccessLock); - if (pDevExt->MouseNotifyCallback.pfnNotify) - pDevExt->MouseNotifyCallback.pfnNotify(pDevExt->MouseNotifyCallback.pvUser); + if (pDevExt->Core.MouseNotifyCallback.pfnNotify) + pDevExt->Core.MouseNotifyCallback.pfnNotify(pDevExt->Core.MouseNotifyCallback.pvUser); - KeReleaseSpinLockFromDpcLevel(&pDevExt->win.s.MouseEventAccessLock); + KeReleaseSpinLockFromDpcLevel(&pDevExt->MouseEventAccessLock); } /* Process the wake-up list we were asked by the scheduling a DPC - * in vboxguestwinIsrHandler(). */ - VBoxGuestWaitDoWakeUps(pDevExt); + * in vbgdNtIsrHandler(). */ + VBoxGuestWaitDoWakeUps(&pDevExt->Core); } @@ -994,35 +1001,35 @@ void vboxguestwinDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID * @param pInterrupt Interrupt that was triggered. * @param pServiceContext Context specific pointer. */ -BOOLEAN vboxguestwinIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext) +BOOLEAN vbgdNtIsrHandler(PKINTERRUPT pInterrupt, PVOID pServiceContext) { - PVBOXGUESTDEVEXT pDevExt = (PVBOXGUESTDEVEXT)pServiceContext; + PVBOXGUESTDEVEXTWIN pDevExt = (PVBOXGUESTDEVEXTWIN)pServiceContext; if (pDevExt == NULL) return FALSE; - /*Log(("VBoxGuest::vboxguestwinGuestIsrHandler: pDevExt = 0x%p, pVMMDevMemory = 0x%p\n", + /*Log(("VBoxGuest::vbgdNtGuestIsrHandler: pDevExt = 0x%p, pVMMDevMemory = 0x%p\n", pDevExt, pDevExt ? pDevExt->pVMMDevMemory : NULL));*/ /* Enter the common ISR routine and do the actual work. */ - BOOLEAN fIRQTaken = VBoxGuestCommonISR(pDevExt); + BOOLEAN fIRQTaken = VBoxGuestCommonISR(&pDevExt->Core); /* If we need to wake up some events we do that in a DPC to make * sure we're called at the right IRQL. */ if (fIRQTaken) { - Log(("VBoxGuest::vboxguestwinGuestIsrHandler: IRQ was taken! pInterrupt = 0x%p, pDevExt = 0x%p\n", + Log(("VBoxGuest::vbgdNtGuestIsrHandler: IRQ was taken! pInterrupt = 0x%p, pDevExt = 0x%p\n", pInterrupt, pDevExt)); - if (ASMAtomicUoReadU32(&pDevExt->u32MousePosChangedSeq) || !RTListIsEmpty(&pDevExt->WakeUpList)) + if (ASMAtomicUoReadU32(&pDevExt->Core.u32MousePosChangedSeq) || !RTListIsEmpty(&pDevExt->Core.WakeUpList)) { - Log(("VBoxGuest::vboxguestwinGuestIsrHandler: Requesting DPC ...\n")); - IoRequestDpc(pDevExt->win.s.pDeviceObject, pDevExt->win.s.pCurrentIrp, NULL); + Log(("VBoxGuest::vbgdNtGuestIsrHandler: Requesting DPC ...\n")); + IoRequestDpc(pDevExt->pDeviceObject, pDevExt->pCurrentIrp, NULL); } } return fIRQTaken; } -/* +/** * Overridden routine for mouse polling events. * * @param pDevExt Device extension structure. @@ -1047,8 +1054,7 @@ void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt) * not specified in ulRoot), on output this will retrieve the looked up * registry value if found. */ -NTSTATUS vboxguestwinRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, - PULONG puValue) +NTSTATUS vbgdNtRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwszName, PULONG puValue) { if (!pwszPath || !pwszName || !puValue) return STATUS_INVALID_PARAMETER; @@ -1079,17 +1085,17 @@ NTSTATUS vboxguestwinRegistryReadDWORD(ULONG ulRoot, PCWSTR pwszPath, PWSTR pwsz * @param pResList Resource list * @param pDevExt Device extension */ -NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXT pDevExt) +NTSTATUS vbgdNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt) { /* Enumerate the resource list. */ - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Found %d resources\n", + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Found %d resources\n", pResList->List->PartialResourceList.Count)); NTSTATUS rc = STATUS_SUCCESS; PCM_PARTIAL_RESOURCE_DESCRIPTOR pPartialData = NULL; ULONG rangeCount = 0; ULONG cMMIORange = 0; - PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->win.s.pciBaseAddress; + PVBOXGUESTWINBASEADDRESS pBaseAddress = pDevExt->pciBaseAddress; for (ULONG i = 0; i < pResList->List->PartialResourceList.Count; i++) { pPartialData = &pResList->List->PartialResourceList.PartialDescriptors[i]; @@ -1100,14 +1106,15 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD /* Overflow protection. */ if (rangeCount < PCI_TYPE0_ADDRESSES) { - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n", - pPartialData->u.Port.Start.HighPart, - pPartialData->u.Port.Start.LowPart, - pPartialData->u.Port.Length)); + Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range: Base = %08x:%08x, Length = %08x\n", + pPartialData->u.Port.Start.HighPart, + pPartialData->u.Port.Start.LowPart, + pPartialData->u.Port.Length)); /* Save the IO port base. */ - /** @todo Not so good. */ - pDevExt->IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart; + /** @todo Not so good. + * Update/bird: What is not so good? That we just consider the last range? */ + pDevExt->Core.IOPortBase = (RTIOPORT)pPartialData->u.Port.Start.LowPart; /* Save resource information. */ pBaseAddress->RangeStart = pPartialData->u.Port.Start; @@ -1115,10 +1122,10 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD pBaseAddress->RangeInMemory = FALSE; pBaseAddress->ResourceMapped = FALSE; - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n", - pPartialData->u.Port.Start.HighPart, - pPartialData->u.Port.Start.LowPart, - pPartialData->u.Port.Length)); + Log(("VBoxGuest::vbgdNtScanPCIResourceList: I/O range for VMMDev found! Base = %08x:%08x, Length = %08x\n", + pPartialData->u.Port.Start.HighPart, + pPartialData->u.Port.Start.LowPart, + pPartialData->u.Port.Length)); /* Next item ... */ rangeCount++; pBaseAddress++; @@ -1128,25 +1135,21 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD case CmResourceTypeInterrupt: { - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n", + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Interrupt: Level = %x, Vector = %x, Mode = %x\n", pPartialData->u.Interrupt.Level, pPartialData->u.Interrupt.Vector, pPartialData->Flags)); /* Save information. */ - pDevExt->win.s.interruptLevel = pPartialData->u.Interrupt.Level; - pDevExt->win.s.interruptVector = pPartialData->u.Interrupt.Vector; - pDevExt->win.s.interruptAffinity = pPartialData->u.Interrupt.Affinity; + pDevExt->interruptLevel = pPartialData->u.Interrupt.Level; + pDevExt->interruptVector = pPartialData->u.Interrupt.Vector; + pDevExt->interruptAffinity = pPartialData->u.Interrupt.Affinity; /* Check interrupt mode. */ if (pPartialData->Flags & CM_RESOURCE_INTERRUPT_LATCHED) - { - pDevExt->win.s.interruptMode = Latched; - } + pDevExt->interruptMode = Latched; else - { - pDevExt->win.s.interruptMode = LevelSensitive; - } + pDevExt->interruptMode = LevelSensitive; break; } @@ -1155,7 +1158,7 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD /* Overflow protection. */ if (rangeCount < PCI_TYPE0_ADDRESSES) { - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n", + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range: Base = %08x:%08x, Length = %08x\n", pPartialData->u.Memory.Start.HighPart, pPartialData->u.Memory.Start.LowPart, pPartialData->u.Memory.Length)); @@ -1166,8 +1169,8 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD && (pPartialData->Flags & VBOX_CM_PRE_VISTA_MASK) == CM_RESOURCE_MEMORY_READ_WRITE) { /* Save physical MMIO base + length for VMMDev. */ - pDevExt->win.s.vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start; - pDevExt->win.s.vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length; + pDevExt->vmmDevPhysMemoryAddress = pPartialData->u.Memory.Start; + pDevExt->vmmDevPhysMemoryLength = (ULONG)pPartialData->u.Memory.Length; /* Save resource information. */ pBaseAddress->RangeStart = pPartialData->u.Memory.Start; @@ -1175,7 +1178,7 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD pBaseAddress->RangeInMemory = TRUE; pBaseAddress->ResourceMapped = FALSE; - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n", + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Memory range for VMMDev found! Base = %08x:%08x, Length = %08x\n", pPartialData->u.Memory.Start.HighPart, pPartialData->u.Memory.Start.LowPart, pPartialData->u.Memory.Length)); @@ -1185,7 +1188,7 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD } else { - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Ignoring memory: Flags = %08x\n", + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Ignoring memory: Flags = %08x\n", pPartialData->Flags)); } } @@ -1194,14 +1197,14 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD default: { - Log(("VBoxGuest::vboxguestwinScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type)); + Log(("VBoxGuest::vbgdNtScanPCIResourceList: Unhandled resource found, type = %d\n", pPartialData->Type)); break; } } } /* Memorize the number of resources found. */ - pDevExt->win.s.pciAddressCount = rangeCount; + pDevExt->pciAddressCount = rangeCount; return rc; } @@ -1212,48 +1215,46 @@ NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTD * @return NTSTATUS * * @param pDevExt The device extension. - * @param physicalAdr Physical address to map. - * @param ulLength Length (in bytes) to map. + * @param PhysAddr Physical address to map. + * @param cbToMap Number of bytes to map. * @param ppvMMIOBase Pointer of mapped I/O base. * @param pcbMMIO Length of mapped I/O base. */ -NTSTATUS vboxguestwinMapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt, PHYSICAL_ADDRESS physicalAdr, ULONG ulLength, - void **ppvMMIOBase, uint32_t *pcbMMIO) +NTSTATUS vbgdNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap, + void **ppvMMIOBase, uint32_t *pcbMMIO) { AssertPtrReturn(pDevExt, VERR_INVALID_POINTER); AssertPtrReturn(ppvMMIOBase, VERR_INVALID_POINTER); /* pcbMMIO is optional. */ NTSTATUS rc = STATUS_SUCCESS; - if (physicalAdr.LowPart > 0) /* We're mapping below 4GB. */ + if (PhysAddr.LowPart > 0) /* We're mapping below 4GB. */ { - VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(physicalAdr, ulLength, MmNonCached); - Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: pVMMDevMemory = 0x%x\n", pVMMDevMemory)); + VMMDevMemory *pVMMDevMemory = (VMMDevMemory *)MmMapIoSpace(PhysAddr, cbToMap, MmNonCached); + Log(("VBoxGuest::vbgdNtMapVMMDevMemory: pVMMDevMemory = 0x%x\n", pVMMDevMemory)); if (pVMMDevMemory) { - Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n", + Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory: Version = 0x%x, Size = %d\n", pVMMDevMemory->u32Version, pVMMDevMemory->u32Size)); /* Check version of the structure; do we have the right memory version? */ - if (pVMMDevMemory->u32Version != VMMDEV_MEMORY_VERSION) - { - Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: Wrong version (%u), refusing operation!\n", - pVMMDevMemory->u32Version)); - - /* Not our version, refuse operation and unmap the memory. */ - vboxguestwinUnmapVMMDevMemory(pDevExt); - rc = STATUS_UNSUCCESSFUL; - } - else + if (pVMMDevMemory->u32Version == VMMDEV_MEMORY_VERSION) { /* Save results. */ *ppvMMIOBase = pVMMDevMemory; if (pcbMMIO) /* Optional. */ *pcbMMIO = pVMMDevMemory->u32Size; - Log(("VBoxGuest::vboxguestwinMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", + Log(("VBoxGuest::vbgdNtMapVMMDevMemory: VMMDevMemory found and mapped! pvMMIOBase = 0x%p\n", *ppvMMIOBase)); } + else + { + /* Not our version, refuse operation and unmap the memory. */ + Log(("VBoxGuest::vbgdNtMapVMMDevMemory: Wrong version (%u), refusing operation!\n", pVMMDevMemory->u32Version)); + vbgdNtUnmapVMMDevMemory(pDevExt); + rc = STATUS_UNSUCCESSFUL; + } } else rc = STATUS_UNSUCCESSFUL; @@ -1267,34 +1268,34 @@ NTSTATUS vboxguestwinMapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt, PHYSICAL_ADDRESS * * @param pDevExt The device extension. */ -void vboxguestwinUnmapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt) +void vbgdNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt) { - Log(("VBoxGuest::vboxguestwinUnmapVMMDevMemory: pVMMDevMemory = 0x%x\n", pDevExt->pVMMDevMemory)); - if (pDevExt->pVMMDevMemory) + Log(("VBoxGuest::vbgdNtUnmapVMMDevMemory: pVMMDevMemory = 0x%x\n", pDevExt->Core.pVMMDevMemory)); + if (pDevExt->Core.pVMMDevMemory) { - MmUnmapIoSpace((void*)pDevExt->pVMMDevMemory, pDevExt->win.s.vmmDevPhysMemoryLength); - pDevExt->pVMMDevMemory = NULL; + MmUnmapIoSpace((void*)pDevExt->Core.pVMMDevMemory, pDevExt->vmmDevPhysMemoryLength); + pDevExt->Core.pVMMDevMemory = NULL; } - pDevExt->win.s.vmmDevPhysMemoryAddress.QuadPart = 0; - pDevExt->win.s.vmmDevPhysMemoryLength = 0; + pDevExt->vmmDevPhysMemoryAddress.QuadPart = 0; + pDevExt->vmmDevPhysMemoryLength = 0; } -VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) +VBOXOSTYPE vbgdNtVersionToOSType(VBGDNTVER enmNtVer) { VBOXOSTYPE enmOsType; - switch (winVer) + switch (enmNtVer) { - case WINNT4: + case VBGDNTVER_WINNT4: enmOsType = VBOXOSTYPE_WinNT4; break; - case WIN2K: + case VBGDNTVER_WIN2K: enmOsType = VBOXOSTYPE_Win2k; break; - case WINXP: + case VBGDNTVER_WINXP: #if ARCH_BITS == 64 enmOsType = VBOXOSTYPE_WinXP_x64; #else @@ -1302,7 +1303,7 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) #endif break; - case WIN2K3: + case VBGDNTVER_WIN2K3: #if ARCH_BITS == 64 enmOsType = VBOXOSTYPE_Win2k3_x64; #else @@ -1310,7 +1311,7 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) #endif break; - case WINVISTA: + case VBGDNTVER_WINVISTA: #if ARCH_BITS == 64 enmOsType = VBOXOSTYPE_WinVista_x64; #else @@ -1318,7 +1319,7 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) #endif break; - case WIN7: + case VBGDNTVER_WIN7: #if ARCH_BITS == 64 enmOsType = VBOXOSTYPE_Win7_x64; #else @@ -1326,7 +1327,7 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) #endif break; - case WIN8: + case VBGDNTVER_WIN8: #if ARCH_BITS == 64 enmOsType = VBOXOSTYPE_Win8_x64; #else @@ -1334,6 +1335,14 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) #endif break; + case VBGDNTVER_WIN81: +#if ARCH_BITS == 64 + enmOsType = VBOXOSTYPE_Win81_x64; +#else + enmOsType = VBOXOSTYPE_Win81; +#endif + break; + default: /* We don't know, therefore NT family. */ enmOsType = VBOXOSTYPE_WinNT; @@ -1350,8 +1359,7 @@ VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer) static uint32_t vboxugestwinAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32Mask) { AssertPtrReturn(pu32Bits, 0); - LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(long *)pu32Bits, - u32Mask)); + LogFlowFunc(("*pu32Bits=0x%x, u32Mask=0x%x\n", *(uint32_t *)pu32Bits, u32Mask)); uint32_t u32Result = 0; uint32_t u32WorkingMask = u32Mask; int iBitOffset = ASMBitFirstSetU32 (u32WorkingMask); @@ -1369,8 +1377,7 @@ static uint32_t vboxugestwinAtomicBitsTestAndClear(void *pu32Bits, uint32_t u32M } -static void vboxguestwinTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, - uint32_t u32Exp) +static void vbgdNtTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t u32Bits, uint32_t u32Exp) { ULONG u32Bits2 = u32Bits; uint32_t u32Result = vboxugestwinAtomicBitsTestAndClear(&u32Bits2, u32Mask); @@ -1385,104 +1392,134 @@ static void vboxguestwinTestAtomicTestAndClearBitsU32(uint32_t u32Mask, uint32_t } -static void vboxguestwinDoTests() +static void vbgdNtDoTests(void) { - vboxguestwinTestAtomicTestAndClearBitsU32(0x00, 0x23, 0); - vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0, 0); - vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x22, 0); - vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1); - vboxguestwinTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10); - vboxguestwinTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22); + vbgdNtTestAtomicTestAndClearBitsU32(0x00, 0x23, 0); + vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0, 0); + vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x22, 0); + vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x23, 0x1); + vbgdNtTestAtomicTestAndClearBitsU32(0x11, 0x32, 0x10); + vbgdNtTestAtomicTestAndClearBitsU32(0x22, 0x23, 0x22); } #endif /* DEBUG */ #ifdef VBOX_WITH_DPC_LATENCY_CHECKER -#pragma pack(1) + +/* + * DPC latency checker. + */ + +/** + * One DPC latency sample. + */ typedef struct DPCSAMPLE { - LARGE_INTEGER PerfDelta; - LARGE_INTEGER PerfCounter; - LARGE_INTEGER PerfFrequency; - uint64_t u64TSC; + LARGE_INTEGER PerfDelta; + LARGE_INTEGER PerfCounter; + LARGE_INTEGER PerfFrequency; + uint64_t u64TSC; } DPCSAMPLE; +AssertCompileSize(DPCSAMPLE, 4*8); +/** + * The DPC latency measurement workset. + */ typedef struct DPCDATA { - KDPC Dpc; - KTIMER Timer; - KSPIN_LOCK SpinLock; + KDPC Dpc; + KTIMER Timer; + KSPIN_LOCK SpinLock; + + ULONG ulTimerRes; - ULONG ulTimerRes; + bool volatile fFinished; - LARGE_INTEGER DueTime; + /** The timer interval (relative). */ + LARGE_INTEGER DueTime; - BOOLEAN fFinished; + LARGE_INTEGER PerfCounterPrev; - LARGE_INTEGER PerfCounterPrev; + /** Align the sample array on a 64 byte boundrary just for the off chance + * that we'll get cache line aligned memory backing this structure. */ + uint32_t auPadding[ARCH_BITS == 32 ? 5 : 7]; - int iSampleCount; - DPCSAMPLE aSamples[8192]; + int cSamples; + DPCSAMPLE aSamples[8192]; } DPCDATA; -#pragma pack(1) -#define VBOXGUEST_DPC_TAG 'DPCS' +AssertCompileMemberAlignment(DPCDATA, aSamples, 64); + +# define VBOXGUEST_DPC_TAG 'DPCS' + -static VOID DPCDeferredRoutine(struct _KDPC *Dpc, - PVOID DeferredContext, - PVOID SystemArgument1, - PVOID SystemArgument2) +/** + * DPC callback routine for the DPC latency measurement code. + * + * @param pDpc The DPC, not used. + * @param pvDeferredContext Pointer to the DPCDATA. + * @param SystemArgument1 System use, ignored. + * @param SystemArgument2 System use, ignored. + */ +static VOID vbgdNtDpcLatencyCallback(PKDPC pDpc, PVOID pvDeferredContext, PVOID SystemArgument1, PVOID SystemArgument2) { - DPCDATA *pData = (DPCDATA *)DeferredContext; + DPCDATA *pData = (DPCDATA *)pvDeferredContext; KeAcquireSpinLockAtDpcLevel(&pData->SpinLock); - if (pData->iSampleCount >= RT_ELEMENTS(pData->aSamples)) + if (pData->cSamples >= RT_ELEMENTS(pData->aSamples)) + pData->fFinished = true; + else { - pData->fFinished = 1; - KeReleaseSpinLockFromDpcLevel(&pData->SpinLock); - return; - } - - DPCSAMPLE *pSample = &pData->aSamples[pData->iSampleCount++]; + DPCSAMPLE *pSample = &pData->aSamples[pData->cSamples++]; - pSample->u64TSC = ASMReadTSC(); - pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency); - pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart; + pSample->u64TSC = ASMReadTSC(); + pSample->PerfCounter = KeQueryPerformanceCounter(&pSample->PerfFrequency); + pSample->PerfDelta.QuadPart = pSample->PerfCounter.QuadPart - pData->PerfCounterPrev.QuadPart; - pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart; + pData->PerfCounterPrev.QuadPart = pSample->PerfCounter.QuadPart; - KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc); + KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc); + } KeReleaseSpinLockFromDpcLevel(&pData->SpinLock); } -int VBoxGuestCommonIOCtl_DPC(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, - void *pvData, size_t cbData, size_t *pcbDataReturned) -{ - int rc = VINF_SUCCESS; - /* Allocate a non paged memory for samples and related data. */ +/** + * Handles the DPC latency checker request. + * + * @returns VBox status code. + */ +int VbgdNtIOCtl_DpcLatencyChecker(void) +{ + /* + * Allocate a block of non paged memory for samples and related data. + */ DPCDATA *pData = (DPCDATA *)ExAllocatePoolWithTag(NonPagedPool, sizeof(DPCDATA), VBOXGUEST_DPC_TAG); - if (!pData) { RTLogBackdoorPrintf("VBoxGuest: DPC: DPCDATA allocation failed.\n"); return VERR_NO_MEMORY; } - KeInitializeDpc(&pData->Dpc, DPCDeferredRoutine, pData); + /* + * Initialize the data. + */ + KeInitializeDpc(&pData->Dpc, vbgdNtDpcLatencyCallback, pData); KeInitializeTimer(&pData->Timer); KeInitializeSpinLock(&pData->SpinLock); - pData->fFinished = 0; - pData->iSampleCount = 0; + pData->fFinished = false; + pData->cSamples = 0; pData->PerfCounterPrev.QuadPart = 0; pData->ulTimerRes = ExSetTimerResolution(1000 * 10, 1); pData->DueTime.QuadPart = -(int64_t)pData->ulTimerRes / 10; - /* Start the DPC measurements. */ + /* + * Start the DPC measurements and wait for a full set. + */ KeSetTimer(&pData->Timer, pData->DueTime, &pData->Dpc); while (!pData->fFinished) @@ -1494,22 +1531,25 @@ int VBoxGuestCommonIOCtl_DPC(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSessio ExSetTimerResolution(0, 0); - /* Log everything to the host. */ + /* + * Log everything to the host. + */ RTLogBackdoorPrintf("DPC: ulTimerRes = %d\n", pData->ulTimerRes); - int i; - for (i = 0; i < pData->iSampleCount; i++) + for (int i = 0; i < pData->cSamples; i++) { DPCSAMPLE *pSample = &pData->aSamples[i]; RTLogBackdoorPrintf("[%d] pd %lld pc %lld pf %lld t %lld\n", - i, - pSample->PerfDelta.QuadPart, - pSample->PerfCounter.QuadPart, - pSample->PerfFrequency.QuadPart, - pSample->u64TSC); + i, + pSample->PerfDelta.QuadPart, + pSample->PerfCounter.QuadPart, + pSample->PerfFrequency.QuadPart, + pSample->u64TSC); } ExFreePoolWithTag(pData, VBOXGUEST_DPC_TAG); - return rc; + return VINF_SUCCESS; } + #endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ + diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.h index a3b02659..aee63677 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.h +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest-win.h @@ -1,8 +1,10 @@ +/* $Id: VBoxGuest-win.h $ */ /** @file - * * VBoxGuest - Windows specifics. - * - * Copyright (C) 2010 Oracle Corporation + */ + +/* + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -16,9 +18,6 @@ #ifndef ___VBoxGuest_win_h #define ___VBoxGuest_win_h -/******************************************************************************* -* Header Files * -*******************************************************************************/ #include <iprt/cdefs.h> @@ -37,14 +36,9 @@ RT_C_DECLS_END #include <VBox/VMMDev.h> #include <VBox/VBoxGuest.h> +#include "VBoxGuestInternal.h" -/******************************************************************************* -* Structures and Typedefs * -*******************************************************************************/ - -/** Pointer to the VBoxGuest per session data. */ -typedef struct VBOXGUESTSESSION *PVBOXGUESTSESSION; /** Possible device states for our state machine. */ enum DEVSTATE @@ -77,6 +71,8 @@ typedef struct VBOXGUESTWINBASEADDRESS /** Windows-specific device extension bits. */ typedef struct VBOXGUESTDEVEXTWIN { + VBOXGUESTDEVEXT Core; + /** Our functional driver object. */ PDEVICE_OBJECT pDeviceObject; /** Top of the stack. */ @@ -119,64 +115,69 @@ typedef struct VBOXGUESTDEVEXTWIN VMMDevEvents *pIrqAckEvents; /** Pre-allocated kernel session data. This is needed - * for handling kernel IOCtls. */ - PVBOXGUESTSESSION pKernelSession; + * for handling kernel IOCtls. */ + struct VBOXGUESTSESSION *pKernelSession; /** Spinlock protecting MouseNotifyCallback. Required since the consumer is * in a DPC callback and not the ISR. */ KSPIN_LOCK MouseEventAccessLock; } VBOXGUESTDEVEXTWIN, *PVBOXGUESTDEVEXTWIN; -#define VBOXGUEST_UPDATE_DEVSTATE(_pDevExt, _newDevState) do { \ - (_pDevExt)->win.s.prevDevState = (_pDevExt)->win.s.devState; \ - (_pDevExt)->win.s.devState = (_newDevState); \ -} while (0) - -/******************************************************************************* -* Defined Constants And Macros * -*******************************************************************************/ +/** NT (windows) version identifier. */ +typedef enum VBGDNTVER +{ + VBGDNTVER_INVALID = 0, + VBGDNTVER_WINNT4, + VBGDNTVER_WIN2K, + VBGDNTVER_WINXP, + VBGDNTVER_WIN2K3, + VBGDNTVER_WINVISTA, + VBGDNTVER_WIN7, + VBGDNTVER_WIN8, + VBGDNTVER_WIN81, +} VBGDNTVER; +extern VBGDNTVER g_enmVbgdNtVer; + + +#define VBOXGUEST_UPDATE_DEVSTATE(a_pDevExt, a_newDevState) \ + do { \ + (a_pDevExt)->prevDevState = (a_pDevExt)->devState; \ + (a_pDevExt)->devState = (a_newDevState); \ + } while (0) /** CM_RESOURCE_MEMORY_* flags which were used on XP or earlier. */ #define VBOX_CM_PRE_VISTA_MASK (0x3f) -/** Windows version identifier. */ -typedef enum -{ - WINNT4 = 1, - WIN2K = 2, - WINXP = 3, - WIN2K3 = 4, - WINVISTA = 5, - WIN7 = 6, - WIN8 = 7 -} winVersion_t; -extern winVersion_t winVersion; - - -/******************************************************************************* -* Declared prototypes for helper routines used in both (PnP and legacy) * -* driver versions. * -*******************************************************************************/ -#include "VBoxGuestInternal.h" RT_C_DECLS_BEGIN + #ifdef TARGET_NT4 -NTSTATUS vboxguestwinnt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath); -NTSTATUS vboxguestwinInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath); +NTSTATUS vbgdNt4CreateDevice(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath); #else -NTSTATUS vboxguestwinInit(PDEVICE_OBJECT pDevObj, PIRP pIrp); +NTSTATUS vbgdNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp); +NTSTATUS vbgdNtPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp); +NTSTATUS vbgdNtPower(PDEVICE_OBJECT pDevObj, PIRP pIrp); #endif -NTSTATUS vboxguestwinCleanup(PDEVICE_OBJECT pDevObj); -NTSTATUS vboxguestwinPnP(PDEVICE_OBJECT pDevObj, PIRP pIrp); -VOID vboxguestwinDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext); -BOOLEAN vboxguestwinIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext); -NTSTATUS vboxguestwinScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXT pDevExt); -NTSTATUS vboxguestwinMapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt, PHYSICAL_ADDRESS physicalAdr, ULONG ulLength, - void **ppvMMIOBase, uint32_t *pcbMMIO); -void vboxguestwinUnmapVMMDevMemory(PVBOXGUESTDEVEXT pDevExt); -VBOXOSTYPE vboxguestwinVersionToOSType(winVersion_t winVer); -NTSTATUS vboxguestwinPower(PDEVICE_OBJECT pDevObj, PIRP pIrp); + +/** @name Common routines used in both (PnP and legacy) driver versions. + * @{ + */ +#ifdef TARGET_NT4 +NTSTATUS vbgdNtInit(PDRIVER_OBJECT pDrvObj, PDEVICE_OBJECT pDevObj, PUNICODE_STRING pRegPath); +#else +NTSTATUS vbgdNtInit(PDEVICE_OBJECT pDevObj, PIRP pIrp); +#endif +NTSTATUS vbgdNtCleanup(PDEVICE_OBJECT pDevObj); +VOID vbgdNtDpcHandler(PKDPC pDPC, PDEVICE_OBJECT pDevObj, PIRP pIrp, PVOID pContext); +BOOLEAN vbgdNtIsrHandler(PKINTERRUPT interrupt, PVOID serviceContext); +NTSTATUS vbgdNtScanPCIResourceList(PCM_RESOURCE_LIST pResList, PVBOXGUESTDEVEXTWIN pDevExt); +NTSTATUS vbgdNtMapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt, PHYSICAL_ADDRESS PhysAddr, ULONG cbToMap, + void **ppvMMIOBase, uint32_t *pcbMMIO); +void vbgdNtUnmapVMMDevMemory(PVBOXGUESTDEVEXTWIN pDevExt); +VBOXOSTYPE vbgdNtVersionToOSType(VBGDNTVER enmNtVer); +/** @} */ + RT_C_DECLS_END #ifdef TARGET_NT4 @@ -184,9 +185,9 @@ RT_C_DECLS_END * XP DDK #defines ExFreePool to ExFreePoolWithTag. The latter does not exist * on NT4, so... The same for ExAllocatePool. */ -#undef ExAllocatePool -#undef ExFreePool +# undef ExAllocatePool +# undef ExFreePool #endif -#endif /* ___VBoxGuest_win_h */ +#endif /* !___VBoxGuest_win_h */ diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp b/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp index 594e0921..b1ffd3ba 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuest.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2012 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -54,7 +54,7 @@ # include <Windows.h> # endif #endif -#if defined(RT_OS_SOLARIS) +#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN) # include <iprt/rand.h> #endif @@ -70,17 +70,61 @@ static void testSetMouseStatus(void); #endif static int VBoxGuestCommonIOCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fFeatures); -#ifdef VBOX_WITH_DPC_LATENCY_CHECKER -int VBoxGuestCommonIOCtl_DPC(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, - void *pvData, size_t cbData, size_t *pcbDataReturned); -#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ +static int VBoxGuestCommonGuestCapsAcquire(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fOrMask, uint32_t fNotMask, VBOXGUESTCAPSACQUIRE_FLAGS enmFlags); + +#define VBOXGUEST_ACQUIRE_STYLE_EVENTS (VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST | VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST) + +DECLINLINE(uint32_t) VBoxGuestCommonGetHandledEventsLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) +{ + if (!pDevExt->u32AcquireModeGuestCaps) + return VMMDEV_EVENT_VALID_EVENT_MASK; + + uint32_t u32AllowedGuestCaps = pSession->u32AquiredGuestCaps | (VMMDEV_EVENT_VALID_EVENT_MASK & ~pDevExt->u32AcquireModeGuestCaps); + uint32_t u32CleanupEvents = VBOXGUEST_ACQUIRE_STYLE_EVENTS; + if (u32AllowedGuestCaps & VMMDEV_GUEST_SUPPORTS_GRAPHICS) + u32CleanupEvents &= ~VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + if (u32AllowedGuestCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) + u32CleanupEvents &= ~VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + + return VMMDEV_EVENT_VALID_EVENT_MASK & ~u32CleanupEvents; +} + +DECLINLINE(uint32_t) VBoxGuestCommonGetAndCleanPendingEventsLocked(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fReqEvents) +{ + uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents & VBoxGuestCommonGetHandledEventsLocked(pDevExt, pSession); + if (fMatches) + ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches); + return fMatches; +} + +DECLINLINE(bool) VBoxGuestCommonGuestCapsModeSet(PVBOXGUESTDEVEXT pDevExt, uint32_t fCaps, bool fAcquire, uint32_t *pu32OtherVal) +{ + uint32_t *pVal = fAcquire ? &pDevExt->u32AcquireModeGuestCaps : &pDevExt->u32SetModeGuestCaps; + const uint32_t fNotVal = !fAcquire ? pDevExt->u32AcquireModeGuestCaps : pDevExt->u32SetModeGuestCaps; + bool fResult = true; + RTSpinlockAcquire(pDevExt->EventSpinlock); + + if (!(fNotVal & fCaps)) + *pVal |= fCaps; + else + { + AssertMsgFailed(("trying to change caps mode\n")); + fResult = false; + } + + RTSpinlockReleaseNoInts(pDevExt->EventSpinlock); + + if (pu32OtherVal) + *pu32OtherVal = fNotVal; + return fResult; +} /******************************************************************************* * Global Variables * *******************************************************************************/ static const size_t cbChangeMemBalloonReq = RT_OFFSETOF(VMMDevChangeMemBalloon, aPhysPage[VMMDEV_MEMORY_BALLOON_CHUNK_PAGES]); -#if defined(RT_OS_SOLARIS) +#if defined(RT_OS_DARWIN) || defined(RT_OS_SOLARIS) /** * Drag in the rest of IRPT since we share it with the * rest of the kernel modules on Solaris. @@ -100,7 +144,7 @@ PFNRT g_apfnVBoxGuestIPRTDeps[] = (PFNRT)RTSemMutexIsOwned, NULL }; -#endif /* RT_OS_SOLARIS */ +#endif /* RT_OS_DARWIN || RT_OS_SOLARIS */ /** @@ -418,7 +462,7 @@ static int vboxGuestSetBalloonSizeKernel(PVBOXGUESTDEVEXT pDevExt, uint32_t cBal pDevExt->MemBalloon.paMemObj = (PRTR0MEMOBJ)RTMemAllocZ(sizeof(RTR0MEMOBJ) * pDevExt->MemBalloon.cMaxChunks); if (!pDevExt->MemBalloon.paMemObj) { - LogRel(("VBoxGuestSetBalloonSizeKernel: no memory for paMemObj!\n")); + LogRel(("vboxGuestSetBalloonSizeKernel: no memory for paMemObj!\n")); return VERR_NO_MEMORY; } } @@ -706,6 +750,20 @@ int VBoxGuestInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, int rc, rc2; unsigned i; +#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER + /* + * Create the release log. + */ + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + PRTLOGGER pRelLogger; + rc = RTLogCreate(&pRelLogger, 0 /* fFlags */, "all", + "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_STDOUT | RTLOGDEST_DEBUGGER, NULL); + if (RT_SUCCESS(rc)) + RTLogRelSetDefaultInstance(pRelLogger); + /** @todo Add native hook for getting logger config parameters and setting + * them. On linux we should use the module parameter stuff... */ +#endif + /* * Adjust fFixedEvents. */ @@ -772,6 +830,10 @@ int VBoxGuestInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, pVMMDev->u32Version, VMMDEV_MEMORY_VERSION, pVMMDev->u32Size, cbMMIO)); } + pDevExt->u32AcquireModeGuestCaps = 0; + pDevExt->u32SetModeGuestCaps = 0; + pDevExt->u32GuestCaps = 0; + /* * Create the wait and session spinlocks as well as the ballooning mutex. */ @@ -856,6 +918,11 @@ int VBoxGuestInitDevExt(PVBOXGUESTDEVEXT pDevExt, uint16_t IOPortBase, rc2 = RTSemFastMutexDestroy(pDevExt->MemBalloon.hMtx); AssertRC(rc2); rc2 = RTSpinlockDestroy(pDevExt->EventSpinlock); AssertRC(rc2); rc2 = RTSpinlockDestroy(pDevExt->SessionSpinlock); AssertRC(rc2); + +#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif return rc; /* (failed) */ } @@ -925,6 +992,12 @@ void VBoxGuestDeleteDevExt(PVBOXGUESTDEVEXT pDevExt) pDevExt->IOPortBase = 0; pDevExt->pIrqAckEvents = NULL; + +#ifdef VBOX_GUESTDRV_WITH_RELEASE_LOGGER + RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); + RTLogDestroy(RTLogSetDefaultInstance(NULL)); +#endif + } @@ -987,7 +1060,7 @@ int VBoxGuestCreateKernelSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION *pp return VINF_SUCCESS; } - +static int VBoxGuestCommonIOCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession); /** * Closes a VBoxGuest session. @@ -1001,6 +1074,10 @@ void VBoxGuestCloseSession(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession) Log(("VBoxGuestCloseSession: pSession=%p proc=%RTproc (%d) r0proc=%p\n", pSession, pSession->Process, (int)pSession->Process, (uintptr_t)pSession->R0Process)); /** @todo %RTr0proc */ + VBoxGuestCommonGuestCapsAcquire(pDevExt, pSession, 0, UINT32_MAX, VBOXGUESTCAPSACQUIRE_FLAGS_NONE); + + VBoxGuestCommonIOCtl_CancelAllWaitEvents(pDevExt, pSession); + #ifdef VBOX_WITH_HGCM for (i = 0; i < RT_ELEMENTS(pSession->aHGCMClientIds); i++) if (pSession->aHGCMClientIds[i]) @@ -1291,13 +1368,12 @@ int VBoxGuestCommonIOCtl_SetMouseNotifyCallback(PVBOXGUESTDEVEXT pDevExt, VBoxGu * * @returns VINF_SUCCESS if we've left the spinlock and can return immediately. */ -DECLINLINE(int) WaitEventCheckCondition(PVBOXGUESTDEVEXT pDevExt, VBoxGuestWaitEventInfo *pInfo, +DECLINLINE(int) WaitEventCheckCondition(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VBoxGuestWaitEventInfo *pInfo, int iEvent, const uint32_t fReqEvents) { - uint32_t fMatches = pDevExt->f32PendingEvents & fReqEvents; + uint32_t fMatches = VBoxGuestCommonGetAndCleanPendingEventsLocked(pDevExt, pSession, fReqEvents); if (fMatches) { - ASMAtomicAndU32(&pDevExt->f32PendingEvents, ~fMatches); RTSpinlockReleaseNoInts(pDevExt->EventSpinlock); pInfo->u32EventFlagsOut = fMatches; @@ -1333,7 +1409,7 @@ static int VBoxGuestCommonIOCtl_WaitEvent(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSE iEvent = ASMBitFirstSetU32(fReqEvents) - 1; if (RT_UNLIKELY(iEvent < 0)) { - Log(("VBoxGuestCommonIOCtl: WAITEVENT: Invalid input mask %#x!!\n", fReqEvents)); + LogRel(("VBoxGuestCommonIOCtl: WAITEVENT: Invalid input mask %#x!!\n", fReqEvents)); return VERR_INVALID_PARAMETER; } @@ -1341,7 +1417,7 @@ static int VBoxGuestCommonIOCtl_WaitEvent(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSE * Check the condition up front, before doing the wait-for-event allocations. */ RTSpinlockAcquire(pDevExt->EventSpinlock); - rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents); + rc = WaitEventCheckCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents); if (rc == VINF_SUCCESS) return rc; @@ -1364,7 +1440,7 @@ static int VBoxGuestCommonIOCtl_WaitEvent(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSE */ RTSpinlockAcquire(pDevExt->EventSpinlock); RTListAppend(&pDevExt->WaitList, &pWait->ListNode); - rc = WaitEventCheckCondition(pDevExt, pInfo, iEvent, fReqEvents); + rc = WaitEventCheckCondition(pDevExt, pSession, pInfo, iEvent, fReqEvents); if (rc == VINF_SUCCESS) { VBoxGuestWaitFreeUnlocked(pDevExt, pWait); @@ -1465,6 +1541,7 @@ static int VBoxGuestCommonIOCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PV } RTSpinlockReleaseNoInts(pDevExt->EventSpinlock); Assert(rc == 0); + NOREF(rc); #ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP VBoxGuestWaitDoWakeUps(pDevExt); @@ -1481,7 +1558,7 @@ static int VBoxGuestCommonIOCtl_CancelAllWaitEvents(PVBOXGUESTDEVEXT pDevExt, PV * @param enmType The request type. * @param pReqHdr The request. */ -static int VBoxGuestCheckIfVMMReqAllowed(PVBOXGUESTSESSION pSession, VMMDevRequestType enmType, +static int VBoxGuestCheckIfVMMReqAllowed(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VMMDevRequestType enmType, VMMDevRequestHeader const *pReqHdr) { /* @@ -1541,12 +1618,34 @@ static int VBoxGuestCheckIfVMMReqAllowed(PVBOXGUESTSESSION pSession, VMMDevReque case VMMDevReq_GetPageSharingStatus: case VMMDevReq_DebugIsPageShared: case VMMDevReq_ReportGuestStats: + case VMMDevReq_ReportGuestUserState: case VMMDevReq_GetStatisticsChangeRequest: case VMMDevReq_ChangeMemBalloon: enmRequired = kLevel_TrustedUsers; break; /* + * Anyone. But not for CapsAcquire mode + */ + case VMMDevReq_SetGuestCapabilities: + { + VMMDevReqGuestCapabilities2 *pCaps = (VMMDevReqGuestCapabilities2*)pReqHdr; + uint32_t fAcquireCaps = 0; + if (!VBoxGuestCommonGuestCapsModeSet(pDevExt, pCaps->u32OrMask, false, &fAcquireCaps)) + { + AssertFailed(); + LogRel(("calling caps set for acquired caps %d\n", pCaps->u32OrMask)); + enmRequired = kLevel_NoOne; + break; + } + /* hack to adjust the notcaps. + * @todo: move to a better place + * user-mode apps are allowed to pass any mask to the notmask, + * the driver cleans up them accordingly */ + pCaps->u32NotMask &= ~fAcquireCaps; + /* do not break, make it fall through to the below enmRequired setting */ + } + /* * Anyone. */ case VMMDevReq_GetMouseStatus: @@ -1563,11 +1662,11 @@ static int VBoxGuestCheckIfVMMReqAllowed(PVBOXGUESTSESSION pSession, VMMDevReque case VMMDevReq_VideoModeSupported: case VMMDevReq_GetHeightReduction: case VMMDevReq_GetDisplayChangeRequest2: - case VMMDevReq_SetGuestCapabilities: case VMMDevReq_VideoModeSupported2: case VMMDevReq_VideoAccelEnable: case VMMDevReq_VideoAccelFlush: case VMMDevReq_VideoSetVisibleRegion: + case VMMDevReq_GetDisplayChangeRequestEx: case VMMDevReq_GetSeamlessChangeRequest: case VMMDevReq_GetVRDPChangeRequest: case VMMDevReq_LogString: @@ -1641,13 +1740,13 @@ static int VBoxGuestCommonIOCtl_VMMRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTS if (cbReq < cbMinSize) { - Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n", + LogRel(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid hdr size %#x, expected >= %#x; type=%#x!!\n", cbReq, cbMinSize, enmType)); return VERR_INVALID_PARAMETER; } if (cbReq > cbData) { - Log(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n", + LogRel(("VBoxGuestCommonIOCtl: VMMREQUEST: invalid size %#x, expected >= %#x (hdr); type=%#x!!\n", cbData, cbReq, enmType)); return VERR_INVALID_PARAMETER; } @@ -1659,7 +1758,7 @@ static int VBoxGuestCommonIOCtl_VMMRequest(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTS return rc; } - rc = VBoxGuestCheckIfVMMReqAllowed(pSession, enmType, pReqHdr); + rc = VBoxGuestCheckIfVMMReqAllowed(pDevExt, pSession, enmType, pReqHdr); if (RT_FAILURE(rc)) { Log(("VBoxGuestCommonIOCtl: VMMREQUEST: Operation not allowed! type=%#x rc=%Rrc\n", enmType, rc)); @@ -1974,7 +2073,7 @@ static int VBoxGuestCommonIOCtl_HGCMCall(PVBOXGUESTDEVEXT pDevExt, if (cbData < cbActual) { LogRel(("VBoxGuestCommonIOCtl: HGCM_CALL: cbData=%#zx (%zu) required size is %#zx (%zu)\n", - cbData, cbActual)); + cbData, cbData, cbActual, cbActual)); return VERR_INVALID_PARAMETER; } @@ -2283,6 +2382,9 @@ static int VBoxGuestCommonIOCtl_SetMouseStatus(PVBOXGUESTDEVEXT pDevExt, PVBOXGU RTSpinlockAcquire(pDevExt->SessionSpinlock); + /* For all the bits which the guest is allowed to set, check whether the + * requested value is different to the current one and adjust the global + * usage counter and if appropriate the global state if so. */ for (i = 0; i < sizeof(fFeatures) * 8; i++) { if (RT_BIT_32(i) & VMMDEV_MOUSE_GUEST_MASK) @@ -2417,6 +2519,170 @@ static int VBoxGuestCommonIOCtl_Log(PVBOXGUESTDEVEXT pDevExt, const char *pch, s return VINF_SUCCESS; } +static bool VBoxGuestCommonGuestCapsValidateValues(uint32_t fCaps) +{ + if (fCaps & (~(VMMDEV_GUEST_SUPPORTS_SEAMLESS | VMMDEV_GUEST_SUPPORTS_GUEST_HOST_WINDOW_MAPPING | VMMDEV_GUEST_SUPPORTS_GRAPHICS))) + return false; + + return true; +} + +static void VBoxGuestCommonCheckEvents(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fGenFakeEvents) +{ + RTSpinlockAcquire(pDevExt->EventSpinlock); + uint32_t fEvents = fGenFakeEvents | pDevExt->f32PendingEvents; + PVBOXGUESTWAIT pWait; + PVBOXGUESTWAIT pSafe; + + RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode) + { + uint32_t fHandledEvents = VBoxGuestCommonGetHandledEventsLocked(pDevExt, pWait->pSession); + if ( (pWait->fReqEvents & fEvents & fHandledEvents) + && !pWait->fResEvents) + { + pWait->fResEvents = pWait->fReqEvents & fEvents & fHandledEvents; + Assert(!(fGenFakeEvents & pWait->fResEvents) || pSession == pWait->pSession); + fEvents &= ~pWait->fResEvents; + RTListNodeRemove(&pWait->ListNode); +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + RTListAppend(&pDevExt->WakeUpList, &pWait->ListNode); +#else + RTListAppend(&pDevExt->WokenUpList, &pWait->ListNode); + int rc = RTSemEventMultiSignal(pWait->Event); + AssertRC(rc); +#endif + if (!fEvents) + break; + } + } + ASMAtomicWriteU32(&pDevExt->f32PendingEvents, fEvents); + + RTSpinlockReleaseNoInts(pDevExt->EventSpinlock); + +#ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP + VBoxGuestWaitDoWakeUps(pDevExt); +#endif +} + +static int VBoxGuestCommonGuestCapsAcquire(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, uint32_t fOrMask, uint32_t fNotMask, VBOXGUESTCAPSACQUIRE_FLAGS enmFlags) +{ + uint32_t fSetCaps = 0; + + if (!VBoxGuestCommonGuestCapsValidateValues(fOrMask)) + { + LogRel(("VBoxGuestCommonGuestCapsAcquire: pSession(0x%p), OR(0x%x), NOT(0x%x), flags(0x%x) -- invalid fOrMask\n", + pSession, fOrMask, fNotMask, enmFlags)); + return VERR_INVALID_PARAMETER; + } + + if ( enmFlags != VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE + && enmFlags != VBOXGUESTCAPSACQUIRE_FLAGS_NONE) + { + LogRel(("VBoxGuestCommonGuestCapsAcquire: pSession(0x%p), OR(0x%x), NOT(0x%x), flags(0x%x) -- invalid enmFlags %d\n", + pSession, fOrMask, fNotMask, enmFlags)); + return VERR_INVALID_PARAMETER; + } + + if (!VBoxGuestCommonGuestCapsModeSet(pDevExt, fOrMask, true, &fSetCaps)) + { + LogRel(("VBoxGuestCommonGuestCapsAcquire: pSession(0x%p), OR(0x%x), NOT(0x%x), flags(0x%x) -- calling caps acquire for set caps\n", + pSession, fOrMask, fNotMask, enmFlags)); + return VERR_INVALID_STATE; + } + + if (enmFlags & VBOXGUESTCAPSACQUIRE_FLAGS_CONFIG_ACQUIRE_MODE) + { + Log(("VBoxGuestCommonGuestCapsAcquire: pSession(0x%p), OR(0x%x), NOT(0x%x), flags(0x%x) -- configured acquire caps: 0x%x\n", + pSession, fOrMask, fNotMask, enmFlags)); + return VINF_SUCCESS; + } + + /* the fNotMask no need to have all values valid, + * invalid ones will simply be ignored */ + uint32_t fCurrentOwnedCaps; + uint32_t fSessionNotCaps; + uint32_t fSessionOrCaps; + uint32_t fOtherConflictingCaps; + + fNotMask &= ~fOrMask; + + RTSpinlockAcquire(pDevExt->EventSpinlock); + + fCurrentOwnedCaps = pSession->u32AquiredGuestCaps; + fSessionNotCaps = fCurrentOwnedCaps & fNotMask; + fSessionOrCaps = fOrMask & ~fCurrentOwnedCaps; + fOtherConflictingCaps = pDevExt->u32GuestCaps & ~fCurrentOwnedCaps; + fOtherConflictingCaps &= fSessionOrCaps; + + if (!fOtherConflictingCaps) + { + if (fSessionOrCaps) + { + pSession->u32AquiredGuestCaps |= fSessionOrCaps; + pDevExt->u32GuestCaps |= fSessionOrCaps; + } + + if (fSessionNotCaps) + { + pSession->u32AquiredGuestCaps &= ~fSessionNotCaps; + pDevExt->u32GuestCaps &= ~fSessionNotCaps; + } + } + + RTSpinlockReleaseNoInts(pDevExt->EventSpinlock); + + if (fOtherConflictingCaps) + { + Log(("VBoxGuest: Caps 0x%x were busy\n", fOtherConflictingCaps)); + return VERR_RESOURCE_BUSY; + } + + /* now do host notification outside the lock */ + if (!fSessionOrCaps && !fSessionNotCaps) + { + /* no changes, return */ + return VINF_SUCCESS; + } + + int rc = VBoxGuestSetGuestCapabilities(fSessionOrCaps, fSessionNotCaps); + if (RT_FAILURE(rc)) + { + LogRel(("VBoxGuestCommonGuestCapsAcquire: VBoxGuestSetGuestCapabilities failed, rc=%Rrc\n", rc)); + + /* Failure branch + * this is generally bad since e.g. failure to release the caps may result in other sessions not being able to use it + * so we are not trying to restore the caps back to their values before the VBoxGuestCommonGuestCapsAcquire call, + * but just pretend everithing is OK. + * @todo: better failure handling mechanism? */ + } + + /* success! */ + uint32_t fGenFakeEvents = 0; + + if (fSessionOrCaps & VMMDEV_GUEST_SUPPORTS_SEAMLESS) + { + /* generate the seamless change event so that the r3 app could synch with the seamless state + * although this introduces a false alarming of r3 client, it still solve the problem of + * client state inconsistency in multiuser environment */ + fGenFakeEvents |= VMMDEV_EVENT_SEAMLESS_MODE_CHANGE_REQUEST; + } + + /* since the acquire filter mask has changed, we need to process events in any way to ensure they go from pending events field + * to the proper (un-filtered) entries */ + VBoxGuestCommonCheckEvents(pDevExt, pSession, fGenFakeEvents); + + return VINF_SUCCESS; +} + +static int VBoxGuestCommonIOCTL_GuestCapsAcquire(PVBOXGUESTDEVEXT pDevExt, PVBOXGUESTSESSION pSession, VBoxGuestCapsAquire *pAcquire) +{ + int rc = VBoxGuestCommonGuestCapsAcquire(pDevExt, pSession, pAcquire->u32OrMask, pAcquire->u32NotMask, pAcquire->enmFlags); + if (RT_FAILURE(rc)) + LogRel(("VBoxGuestCommonGuestCapsAcquire: failed rc=%Rrc\n", rc)); + pAcquire->rc = rc; + return VINF_SUCCESS; +} + /** * Common IOCtl for user to kernel and kernel to kernel communication. @@ -2477,7 +2743,7 @@ int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUES if (cbData != (cb)) \ { \ LogFunc((mnemonic ": cbData=%#zx (%zu) expected is %#zx (%zu)\n", \ - cbData, cbData, (size_t)(cb), (size_t)(cb))); \ + cbData, cbData, (size_t)(cb), (size_t)(cb))); \ return VERR_BUFFER_OVERFLOW; \ } \ if ((cb) != 0 && !VALID_PTR(pvData)) \ @@ -2497,12 +2763,6 @@ int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUES CHECKRET_MIN_SIZE("VMMREQUEST", sizeof(VMMDevRequestHeader)); rc = VBoxGuestCommonIOCtl_VMMRequest(pDevExt, pSession, (VMMDevRequestHeader *)pvData, cbData, pcbDataReturned); } -#ifdef VBOX_WITH_DPC_LATENCY_CHECKER - else if (VBOXGUEST_IOCTL_STRIP_SIZE(iFunction) == VBOXGUEST_IOCTL_STRIP_SIZE(VBOXGUEST_IOCTL_DPC)) - { - rc = VBoxGuestCommonIOCtl_DPC(pDevExt, pSession, pvData, cbData, pcbDataReturned); - } -#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */ #ifdef VBOX_WITH_HGCM /* * These ones are a bit tricky. @@ -2640,10 +2900,23 @@ int VBoxGuestCommonIOCtl(unsigned iFunction, PVBOXGUESTDEVEXT pDevExt, PVBOXGUES *(uint32_t *)pvData); break; +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER + case VBOXGUEST_IOCTL_DPC_LATENCY_CHECKER: + CHECKRET_SIZE("DPC_LATENCY_CHECKER", 0); + rc = VbgdNtIOCtl_DpcLatencyChecker(); + break; +#endif + + case VBOXGUEST_IOCTL_GUEST_CAPS_ACQUIRE: + CHECKRET_SIZE("GUEST_CAPS_ACQUIRE", sizeof(VBoxGuestCapsAquire)); + rc = VBoxGuestCommonIOCTL_GuestCapsAcquire(pDevExt, pSession, (VBoxGuestCapsAquire*)pvData); + *pcbDataReturned = sizeof(VBoxGuestCapsAquire); + break; + default: { - LogRel(("VBoxGuestCommonIOCtl: Unknown request iFunction=%#x Stripped size=%#x\n", iFunction, - VBOXGUEST_IOCTL_STRIP_SIZE(iFunction))); + LogRel(("VBoxGuestCommonIOCtl: Unknown request iFunction=%#x stripped size=%#x\n", + iFunction, VBOXGUEST_IOCTL_STRIP_SIZE(iFunction))); rc = VERR_NOT_SUPPORTED; break; } @@ -2704,7 +2977,7 @@ bool VBoxGuestCommonISR(PVBOXGUESTDEVEXT pDevExt) PVBOXGUESTWAIT pWait; PVBOXGUESTWAIT pSafe; - Log(("VBoxGuestCommonISR: acknowledge events succeeded %#RX32\n", fEvents)); + Log3(("VBoxGuestCommonISR: acknowledge events succeeded %#RX32\n", fEvents)); /* * VMMDEV_EVENT_MOUSE_POSITION_CHANGED can only be polled for. @@ -2748,10 +3021,11 @@ bool VBoxGuestCommonISR(PVBOXGUESTDEVEXT pDevExt) fEvents |= pDevExt->f32PendingEvents; RTListForEachSafe(&pDevExt->WaitList, pWait, pSafe, VBOXGUESTWAIT, ListNode) { - if ( (pWait->fReqEvents & fEvents) + uint32_t fHandledEvents = VBoxGuestCommonGetHandledEventsLocked(pDevExt, pWait->pSession); + if ( (pWait->fReqEvents & fEvents & fHandledEvents) && !pWait->fResEvents) { - pWait->fResEvents = pWait->fReqEvents & fEvents; + pWait->fResEvents = pWait->fReqEvents & fEvents & fHandledEvents; fEvents &= ~pWait->fResEvents; RTListNodeRemove(&pWait->ListNode); #ifdef VBOXGUEST_USE_DEFERRED_WAKE_UP @@ -2800,6 +3074,7 @@ bool VBoxGuestCommonISR(PVBOXGUESTDEVEXT pDevExt) ASMAtomicDecU32(&pDevExt->cISR); Assert(rc == 0); + NOREF(rc); return fOurIrq; } diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm b/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm index bb8757f1..008dd625 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestA-os2.asm @@ -4,7 +4,7 @@ ; ; -; Copyright (C) 2007 knut st. osmundsen <bird-src-spam@anduin.net> +; Copyright (C) 2007-2013 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; @@ -20,7 +20,7 @@ ; ; VBoxDrv - OS/2 assembly file, the first file in the link. ; -; Copyright (c) 2007 knut st. osmundsen <bird-src-spam@anduin.net> +; Copyright (c) 2007-2010 knut st. osmundsen <bird-src-spam@anduin.net> ; ; Permission is hereby granted, free of charge, to any person ; obtaining a copy of this software and associated documentation diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuestIDC-unix.c.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuestIDC-unix.c.h index 87ad299b..de0a5125 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuestIDC-unix.c.h +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestIDC-unix.c.h @@ -1,4 +1,4 @@ -/* $Rev: 78530 $ */ +/* $Rev: 84795 $ */ /** @file * VBoxGuest - Inter Driver Communication, unix implementation. * @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,9 +28,12 @@ /** @todo Use some header that we have in common with VBoxGuestLib.h... */ -DECLVBGL(void *) VBoxGuestIDCOpen(uint32_t *pu32Version); -DECLVBGL(int) VBoxGuestIDCClose(void *pvSession); -DECLVBGL(int) VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); +/** @todo fix DECLVBGL usage. */ +RT_C_DECLS_BEGIN +DECLEXPORT(void *) VBOXCALL VBoxGuestIDCOpen(uint32_t *pu32Version); +DECLEXPORT(int) VBOXCALL VBoxGuestIDCClose(void *pvSession); +DECLEXPORT(int) VBOXCALL VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned); +RT_C_DECLS_END /** @@ -39,7 +42,7 @@ DECLVBGL(int) VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, siz * @returns Opaque pointer to session object. * @param pu32Version Where to store VMMDev version. */ -DECLVBGL(void *) VBoxGuestIDCOpen(uint32_t *pu32Version) +DECLEXPORT(void *) VBOXCALL VBoxGuestIDCOpen(uint32_t *pu32Version) { PVBOXGUESTSESSION pSession; int rc; @@ -96,7 +99,7 @@ DECLVBGL(void *) VBoxGuestIDCOpen(uint32_t *pu32Version) * @returns VBox error code. * @param pvState Opaque pointer to the session object. */ -DECLVBGL(int) VBoxGuestIDCClose(void *pvSession) +DECLEXPORT(int) VBOXCALL VBoxGuestIDCClose(void *pvSession) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; LogFlow(("VBoxGuestIDCClose:\n")); @@ -131,7 +134,7 @@ DECLVBGL(int) VBoxGuestIDCClose(void *pvSession) * @param cbData Size of the data buffer. * @param pcbDataReturned Where to store the amount of returned data. */ -DECLVBGL(int) VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned) +DECLEXPORT(int) VBOXCALL VBoxGuestIDCCall(void *pvSession, unsigned iCmd, void *pvData, size_t cbData, size_t *pcbDataReturned) { PVBOXGUESTSESSION pSession = (PVBOXGUESTSESSION)pvSession; LogFlow(("VBoxGuestIDCCall: %pvSession=%p Cmd=%u pvData=%p cbData=%d\n", pvSession, iCmd, pvData, cbData)); diff --git a/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h b/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h index 5a15aeee..e91b7678 100644 --- a/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h +++ b/src/VBox/Additions/common/VBoxGuest/VBoxGuestInternal.h @@ -107,7 +107,6 @@ typedef struct VBOXGUESTMEMBALLOON /** Pointer to a memory balloon. */ typedef VBOXGUESTMEMBALLOON *PVBOXGUESTMEMBALLOON; - /** * VBox guest device (data) extension. */ @@ -180,17 +179,12 @@ typedef struct VBOXGUESTDEVEXT uint32_t volatile cISR; /** Callback and user data for a kernel mouse handler. */ VBoxGuestMouseSetNotifyCallback MouseNotifyCallback; - - /** Windows part. */ - union - { -#ifdef ___VBoxGuest_win_h - VBOXGUESTDEVEXTWIN s; -#else - uint32_t dummy; -#endif - } win; - + /* list of caps used in acquire mode */ + uint32_t u32AcquireModeGuestCaps; + /* list of caps used in set mode */ + uint32_t u32SetModeGuestCaps; + /* currently acquired (and reported) guest caps */ + uint32_t u32GuestCaps; } VBOXGUESTDEVEXT; /** Pointer to the VBoxGuest driver data. */ typedef VBOXGUESTDEVEXT *PVBOXGUESTDEVEXT; @@ -205,7 +199,7 @@ typedef VBOXGUESTDEVEXT *PVBOXGUESTDEVEXT; */ typedef struct VBOXGUESTSESSION { -#if defined(RT_OS_OS2) || defined(RT_OS_FREEBSD) || defined(RT_OS_SOLARIS) +#if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_OS2) || defined(RT_OS_SOLARIS) /** Pointer to the next session with the same hash. */ PVBOXGUESTSESSION pNextHash; #endif @@ -234,7 +228,14 @@ typedef struct VBOXGUESTSESSION /** Mouse features supported. A feature enabled in any guest session will * be enabled for the host. */ uint32_t volatile fMouseStatus; - +#ifdef RT_OS_DARWIN + /** Pointer to the associated org_virtualbox_VBoxGuestClient object. */ + void *pvVBoxGuestClient; + /** Whether this session has been opened or not. */ + bool fOpened; +#endif + /* Guest Caps Acquired & Reported by this session */ + uint32_t u32AquiredGuestCaps; } VBOXGUESTSESSION; RT_C_DECLS_BEGIN @@ -274,6 +275,11 @@ DECLVBGL(int) VBoxGuestNativeServiceCall(void *pvOpaque, unsigned int iCmd, v */ void VBoxGuestNativeISRMousePollEvent(PVBOXGUESTDEVEXT pDevExt); + +#ifdef VBOX_WITH_DPC_LATENCY_CHECKER +int VbgdNtIOCtl_DpcLatencyChecker(void); +#endif + RT_C_DECLS_END #endif diff --git a/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist b/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist new file mode 100644 index 00000000..30cd22b9 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuest/darwin/Info.plist @@ -0,0 +1,50 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>CFBundleDevelopmentRegion</key> <string>English</string> + <key>CFBundleExecutable</key> <string>VBoxGuest</string> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxGuest</string> + <key>CFBundleInfoDictionaryVersion</key> <string>6.0</string> + <key>CFBundleName</key> <string>VBoxGuest</string> + <key>CFBundlePackageType</key> <string>KEXT</string> + <key>CFBundleSignature</key> <string>????</string> + <key>NSHumanReadableCopyright</key> <string>Copyright © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string> + <key>CFBundleGetInfoString</key> <string>@VBOX_PRODUCT@ @VBOX_VERSION_STRING@, © 2007-@VBOX_C_YEAR@ @VBOX_VENDOR@</string> + <key>CFBundleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>CFBundleShortVersionString</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>OSBundleCompatibleVersion</key> <string>@VBOX_VERSION_MAJOR@.@VBOX_VERSION_MINOR@.@VBOX_VERSION_BUILD@</string> + <key>IOKitPersonalities</key> + <dict> + <key>VBoxGuest</key> + <dict> + <key>CFBundleIdentifier</key> <string>org.virtualbox.kext.VBoxGuest</string> + <key>IOClass</key> <string>org_virtualbox_VBoxGuest</string> + <key>IOMatchCategory</key> <string>org_virtualbox_VBoxGuest</string> + <key>IOUserClientClass</key> <string>org_virtualbox_VBoxGuestClient</string> + <key>IOKitDebug</key> <integer>65535</integer> + <key>IOProviderClass</key> <string>IOPCIDevice</string> + <key>IONameMatch</key> <string>pci80ee,cafe</string> + </dict> + </dict> + <key>OSBundleLibraries</key> + <dict> + <key>com.apple.iokit.IOPCIFamily</key> <string>2.5</string> <!-- TODO: Figure the version in mac os x 10.4. --> + <key>com.apple.kpi.bsd</key> <string>8.0.0</string> + <key>com.apple.kpi.mach</key> <string>8.0.0</string> + <key>com.apple.kpi.libkern</key> <string>8.0.0</string> + <key>com.apple.kpi.unsupported</key> <string>8.0.0</string> + <key>com.apple.kpi.iokit</key> <string>8.0.0</string> + </dict> + <key>OSBundleLibraries_x86_64</key> + <dict> + <key>com.apple.iokit.IOPCIFamily</key> <string>2.6</string> + <key>com.apple.kpi.bsd</key> <string>10.0.0d4</string> + <key>com.apple.kpi.mach</key> <string>10.0.0d3</string> + <key>com.apple.kpi.libkern</key> <string>10.0.0d3</string> + <key>com.apple.kpi.iokit</key> <string>10.0.0d3</string> + <key>com.apple.kpi.unsupported</key> <string>10.0.0d3</string> + </dict> +</dict> +</plist> + diff --git a/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile index c0326547..a25cc9f2 100644 --- a/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile +++ b/src/VBox/Additions/common/VBoxGuest/freebsd/Makefile @@ -4,7 +4,7 @@ # # -# Copyright (C) 2006-2009 Oracle Corporation +# Copyright (C) 2006-2012 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/linux/Makefile b/src/VBox/Additions/common/VBoxGuest/linux/Makefile index 5b03a9a1..09f8bbc1 100644 --- a/src/VBox/Additions/common/VBoxGuest/linux/Makefile +++ b/src/VBox/Additions/common/VBoxGuest/linux/Makefile @@ -1,10 +1,10 @@ -# $Revision: 79403 $ +# $Revision: 83575 $ ## @file # VirtualBox Guest Additions Module Makefile. # # -# Copyright (C) 2006-2011 Oracle Corporation +# Copyright (C) 2006-2012 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest index f733baa0..f9f8a9d7 100755 --- a/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest +++ b/src/VBox/Additions/common/VBoxGuest/linux/files_vboxguest @@ -5,7 +5,7 @@ # # -# Copyright (C) 2007-2010 Oracle Corporation +# Copyright (C) 2007-2012 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf index 09b56d39..04932fca 100644 --- a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf +++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuest.inf @@ -1,7 +1,10 @@ +; $Id: VBoxGuest.inf $ +;; @file +; INF file for installing the VirtualBox Windows guest driver. ; -; INF file for installing the VirtualBox Windows guest driver + ; -; Copyright (C) 2006-2011 Oracle Corporation +; Copyright (C) 2006-2013 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp index eb9a7b69..3f76ef9d 100644 --- a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp +++ b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestInst.cpp @@ -1,8 +1,10 @@ +/* $Id: VBoxGuestInst.cpp $ */ /** @file - * - * Small tool to (un)install the VBoxGuest device driver - * - * Copyright (C) 2006-2007 Oracle Corporation + * Small tool to (un)install the VBoxGuest device driver. + */ + +/* + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -30,7 +32,7 @@ -int installDriver(void) +static int installDriver(void) { /* * Assume it didn't exist, so we'll create the service. @@ -57,17 +59,14 @@ int installDriver(void) "System", NULL, NULL, NULL, NULL); if (!hService) - { printf("CreateService failed! lasterr=%d\n", GetLastError()); - } else - { + else CloseServiceHandle(hService); - } CloseServiceHandle(hSMgrCreate); return hService ? 0 : -1; } -int uninstallDriver(void) +static int uninstallDriver(void) { int rc = -1; SC_HANDLE hSMgr = OpenSCManager(NULL, NULL, SERVICE_CHANGE_CONFIG); @@ -96,8 +95,9 @@ int uninstallDriver(void) return rc; } +#ifdef TESTMODE -HANDLE openDriver(void) +static HANDLE openDriver(void) { HANDLE hDevice; @@ -115,43 +115,32 @@ HANDLE openDriver(void) return hDevice; } -int closeDriver(HANDLE hDevice) +static int closeDriver(HANDLE hDevice) { CloseHandle(hDevice); return 0; } -#ifdef TESTMODE -typedef struct TESTFOO -{ - int values[16]; -} TESTFOO, *PTESTFOO; - -int performTest(void) +static int performTest(void) { int rc = 0; HANDLE hDevice = openDriver(); if (hDevice != INVALID_HANDLE_VALUE) - { - DWORD cbReturned; - closeDriver(hDevice); - } else - { + else printf("openDriver failed!\n"); - } - return rc; } -#endif -void displayHelpAndExit(char *programName) +#endif /* TESTMODE */ + +static int usage(char *programName) { printf("error, syntax: %s [install|uninstall]\n", programName); - exit(1); + return 1; } int main(int argc, char **argv) @@ -162,50 +151,36 @@ int main(int argc, char **argv) #endif if (argc != 2) - { - displayHelpAndExit(argv[0]); - } + return usage(argv[0]); + if (strcmp(argv[1], "install") == 0) - { installMode = true; - } else - if (strcmp(argv[1], "uninstall") == 0) - { + else if (strcmp(argv[1], "uninstall") == 0) installMode = false; - } else #ifdef TESTMODE - if (strcmp(argv[1], "test") == 0) - { + else if (strcmp(argv[1], "test") == 0) testMode = true; - } else #endif - { - displayHelpAndExit(argv[0]); - } + else + return usage(argv[0]); - int rc; + int rc; #ifdef TESTMODE if (testMode) - { rc = performTest(); - } else + else #endif if (installMode) - { rc = installDriver(); - } else - { + else rc = uninstallDriver(); - } if (rc == 0) - { printf("operation completed successfully!\n"); - } else - { + else printf("error: operation failed with status code %d\n", rc); - } return rc; } + diff --git a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestMsg.mc b/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestMsg.mc deleted file mode 100644 index 65a14b5f..00000000 --- a/src/VBox/Additions/common/VBoxGuest/win/VBoxGuestMsg.mc +++ /dev/null @@ -1,60 +0,0 @@ -; -; VBoxGuest Device Driver Messages -; -; Copyright (C) 2006-2007 Oracle Corporation -; -; This file is part of VirtualBox Open Source Edition (OSE), as -; available from http://www.virtualbox.org. This file is free software; -; you can redistribute it and/or modify it under the terms of the GNU -; General Public License (GPL) as published by the Free Software -; Foundation, in version 2 as it comes in the "COPYING" file of the -; VirtualBox OSE distribution. VirtualBox OSE is distributed in the -; hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. -; - - -;// -;// Status values are 32 bit values arranged as follows: -;// -;// 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1 -;// 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 -;// +---+-+-------------------------+-------------------------------+ -;// |Sev|C| Facility | Code | -;// +---+-+-------------------------+-------------------------------+ -;// -;// where -;// -;// Sev - is the severity code -;// -;// 00 - Success -;// 01 - Informational -;// 10 - Warning -;// 11 - Error -;// -;// C - is the Customer code flag -;// -;// Facility - is the facility code -;// -;// Code - is the facility's status code -;// -; -MessageIdTypedef=NTSTATUS - -SeverityNames=(Success=0x0:STATUS_SEVERITY_SUCCESS - Informational=0x1:STATUS_SEVERITY_INFORMATIONAL - Warning=0x2:STATUS_SEVERITY_WARNING - Error=0x3:STATUS_SEVERITY_ERROR - ) - -FacilityNames=(System=0x0 - RpcRuntime=0x2:FACILITY_RPC_RUNTIME - RpcStubs=0x3:FACILITY_RPC_STUBS - Io=0x4:FACILITY_IO_ERROR_CODE - VBoxGuestClass=0xee:FACILITY_VBOXGUESTCLASS_ERROR_CODE - ) - - -MessageId=0x0001 Facility=VBoxGuestClass Severity=Informational SymbolicName=VBOXGUESTCLASS_FOO -Language=English -VBoxGuest has something to say %1. -. diff --git a/src/VBox/Additions/common/VBoxGuestLib/GenericRequest.cpp b/src/VBox/Additions/common/VBoxGuestLib/GenericRequest.cpp index ea1fe187..cae7a8f3 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/GenericRequest.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/GenericRequest.cpp @@ -1,10 +1,10 @@ -/* $Revision: 71271 $ */ +/* $Revision: 87431 $ */ /** @file * VBoxGuestLibR0 - Generic VMMDev request management. */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -69,20 +69,24 @@ DECLVBGL(int) VbglGRVerify (const VMMDevRequestHeader *pReq, size_t cbReq) return VINF_SUCCESS; } - /* This can be a variable size request. Check the request type and limit the size + /* + * This can be a variable size request. Check the request type and limit the size * to VMMDEV_MAX_VMMDEVREQ_SIZE, which is max size supported by the host. + * + * Note: Keep this list sorted for easier human lookup! */ - if ( pReq->requestType == VMMDevReq_LogString - || pReq->requestType == VMMDevReq_VideoSetVisibleRegion - || pReq->requestType == VMMDevReq_SetPointerShape + if ( pReq->requestType == VMMDevReq_ChangeMemBalloon #ifdef VBOX_WITH_64_BITS_GUESTS || pReq->requestType == VMMDevReq_HGCMCall32 || pReq->requestType == VMMDevReq_HGCMCall64 #else || pReq->requestType == VMMDevReq_HGCMCall #endif /* VBOX_WITH_64_BITS_GUESTS */ - || pReq->requestType == VMMDevReq_ChangeMemBalloon - || pReq->requestType == VMMDevReq_RegisterSharedModule) + || pReq->requestType == VMMDevReq_RegisterSharedModule + || pReq->requestType == VMMDevReq_ReportGuestUserState + || pReq->requestType == VMMDevReq_LogString + || pReq->requestType == VMMDevReq_SetPointerShape + || pReq->requestType == VMMDevReq_VideoSetVisibleRegion) { if (cbReq > VMMDEV_MAX_VMMDEVREQ_SIZE) { diff --git a/src/VBox/Additions/common/VBoxGuestLib/HGCM.cpp b/src/VBox/Additions/common/VBoxGuestLib/HGCM.cpp index f6aa77b4..5521c142 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/HGCM.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/HGCM.cpp @@ -1,4 +1,4 @@ -/* $Revision: 76853 $ */ +/* $Revision: 83575 $ */ /** @file * VBoxGuestLib - Host-Guest Communication Manager. * @@ -7,7 +7,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/HGCMInternal.cpp b/src/VBox/Additions/common/VBoxGuestLib/HGCMInternal.cpp index fa2703aa..efd8d76b 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/HGCMInternal.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/HGCMInternal.cpp @@ -1,10 +1,10 @@ -/* $Revision: 77449 $ */ +/* $Revision: 88366 $ */ /** @file * VBoxGuestLib - Host-Guest Communication Manager internal functions, implemented by VBoxGuest */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -49,9 +49,10 @@ #define VBGLR0_MAX_HGCM_USER_PARM (24*_1M) /** The max parameter buffer size for a kernel request. */ #define VBGLR0_MAX_HGCM_KERNEL_PARM (16*_1M) -#ifdef RT_OS_LINUX +#if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) /** Linux needs to use bounce buffers since RTR0MemObjLockUser has unwanted - * side effects. */ + * side effects. + * Darwin 32bit & 64bit also needs this because of 4GB/4GB user/kernel space. */ # define USE_BOUNCE_BUFFERS #endif diff --git a/src/VBox/Additions/common/VBoxGuestLib/Init.cpp b/src/VBox/Additions/common/VBoxGuestLib/Init.cpp index c21ce6cd..5298ffe7 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/Init.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/Init.cpp @@ -1,10 +1,10 @@ -/* $Revision: 79609 $ */ +/* $Revision: 89635 $ */ /** @file * VBoxGuestLibR0 - Library initialization. */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -83,7 +83,7 @@ static void vbglQueryDriverInfo (void) int rc = VINF_SUCCESS; rc = RTSemMutexRequest(g_vbgldata.mutexDriverInit, RT_INDEFINITE_WAIT); - + if (RT_FAILURE(rc)) return; @@ -266,7 +266,7 @@ DECLVBGL(int) VbglInit (void) { vbglTerminateCommon (); } - + } return rc; diff --git a/src/VBox/Additions/common/VBoxGuestLib/Makefile.kmk b/src/VBox/Additions/common/VBoxGuestLib/Makefile.kmk index ed8c689e..244fc2b4 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxGuestLib/Makefile.kmk @@ -4,7 +4,7 @@ # # -# Copyright (C) 2006-2012 Oracle Corporation +# Copyright (C) 2006-2013 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; @@ -105,6 +105,7 @@ VBoxGuestR3Lib_SOURCES = \ VBoxGuestR3LibCpuHotPlug.cpp \ VBoxGuestR3LibCredentials.cpp \ VBoxGuestR3LibEvent.cpp \ + VBoxGuestR3LibGuestUser.cpp \ VBoxGuestR3LibGR.cpp \ VBoxGuestR3LibHostChannel.cpp \ VBoxGuestR3LibLog.cpp \ diff --git a/src/VBox/Additions/common/VBoxGuestLib/PhysHeap.cpp b/src/VBox/Additions/common/VBoxGuestLib/PhysHeap.cpp index 51f1281d..1f1d24f5 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/PhysHeap.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/PhysHeap.cpp @@ -1,10 +1,10 @@ -/* $Revision: 67140 $ */ +/* $Revision: 85891 $ */ /** @file * VBoxGuestLibR0 - Physical memory heap. */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -100,7 +100,7 @@ struct _VBGLPHYSHEAPCHUNK uint32_t cbSize; /* Physical address of the chunk */ - RTCCPHYS physAddr; + uint32_t physAddr; /* Number of allocated blocks in the chunk */ int32_t cAllocatedBlocks; @@ -318,9 +318,11 @@ static VBGLPHYSHEAPBLOCK *vbglPhysHeapChunkAlloc (uint32_t cbSize) return NULL; } + AssertRelease(physAddr < _4G && physAddr + cbSize <= _4G); + pChunk->u32Signature = VBGL_PH_CHUNKSIGNATURE; pChunk->cbSize = cbSize; - pChunk->physAddr = physAddr; + pChunk->physAddr = (uint32_t)physAddr; pChunk->cAllocatedBlocks = 0; pChunk->pNext = g_vbgldata.pChunkHead; pChunk->pPrev = NULL; @@ -493,9 +495,9 @@ DECLVBGL(void *) VbglPhysHeapAlloc (uint32_t cbSize) return vbglPhysHeapBlock2Data (pBlock); } -DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p) +DECLVBGL(uint32_t) VbglPhysHeapGetPhysAddr (void *p) { - RTCCPHYS physAddr = 0; + uint32_t physAddr = 0; VBGLPHYSHEAPBLOCK *pBlock = vbglPhysHeapData2Block (p); if (pBlock) @@ -504,7 +506,7 @@ DECLVBGL(RTCCPHYS) VbglPhysHeapGetPhysAddr (void *p) ("pBlock = %p, pBlock->fu32Flags = %08X\n", pBlock, pBlock->fu32Flags)); if (pBlock->fu32Flags & VBGL_PH_BF_ALLOCATED) - physAddr = pBlock->pChunk->physAddr + ((char *)p - (char *)pBlock->pChunk); + physAddr = pBlock->pChunk->physAddr + (uint32_t)((uintptr_t)p - (uintptr_t)pBlock->pChunk); } return physAddr; diff --git a/src/VBox/Additions/common/VBoxGuestLib/SysHlp.cpp b/src/VBox/Additions/common/VBoxGuestLib/SysHlp.cpp index c50f287c..008c5e14 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/SysHlp.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/SysHlp.cpp @@ -1,10 +1,10 @@ -/* $Revision: 78886 $ */ +/* $Revision: 83575 $ */ /** @file * VBoxGuestLibR0 - IDC with VBoxGuest and HGCM helpers. */ /* - * Copyright (C) 2006-2009 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -309,7 +309,13 @@ int vbglDriverIOCtl (VBGLDRIVER *pDriver, uint32_t u32Function, void *pvData, ui if (rc != STATUS_SUCCESS) Log(("vbglDriverIOCtl: ntstatus=%x\n", rc)); - return NT_SUCCESS(rc)? VINF_SUCCESS: VERR_VBGL_IOCTL_FAILED; + if (NT_SUCCESS(rc)) + return VINF_SUCCESS; + if (rc == STATUS_INVALID_PARAMETER) + return VERR_INVALID_PARAMETER; + if (rc == STATUS_INVALID_BUFFER_SIZE) + return VERR_OUT_OF_RANGE; + return VERR_VBGL_IOCTL_FAILED; # elif defined (RT_OS_OS2) if ( pDriver->u32Session diff --git a/src/VBox/Additions/common/VBoxGuestLib/SysHlp.h b/src/VBox/Additions/common/VBoxGuestLib/SysHlp.h index 9e78e404..68603647 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/SysHlp.h +++ b/src/VBox/Additions/common/VBoxGuestLib/SysHlp.h @@ -1,10 +1,10 @@ -/* $Revision: 78884 $ */ +/* $Revision: 83575 $ */ /** @file * VBoxGuestLibR0 - System dependent helpers internal header. */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBGLInternal.h b/src/VBox/Additions/common/VBoxGuestLib/VBGLInternal.h index 98cf339c..6fe015da 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBGLInternal.h +++ b/src/VBox/Additions/common/VBoxGuestLib/VBGLInternal.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -138,20 +138,13 @@ extern VBGLDATA g_vbgldata; * * @param a_fLocked For the windows shared folders workarounds. * - * @remarks Disable the PageList feature for 64-bit Windows, because shared - * folders do not work, if this is enabled. This should be reenabled - * again when the problem is fixed. - * @remarks Disabled the PageList feature for 32-bit Windows, see xTracker - * ticket 6096 and public ticket 10290. Hopefully this is the same - * issue as on Windows/AMD64. + * @remarks Disabled the PageList feature for locked memory on Windows, + * because a new MDL is created by VBGL to get the page addresses + * and the pages from the MDL are marked as dirty when they should not. */ #if defined(RT_OS_WINDOWS) -# ifdef RT_ARCH_AMD64 -# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) ( 0 ) -# else -# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ - ( !(a_fLocked) && (g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) -# endif +# define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ + ( !(a_fLocked) && (g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) #else # define VBGLR0_CAN_USE_PHYS_PAGE_LIST(a_fLocked) \ ( !!(g_vbgldata.hostVersion.features & VMMDEV_HVF_HGCM_PHYS_PAGE_LIST) ) diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBGLR3Internal.h b/src/VBox/Additions/common/VBoxGuestLib/VBGLR3Internal.h index a36e8129..463c13dc 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBGLR3Internal.h +++ b/src/VBox/Additions/common/VBoxGuestLib/VBGLR3Internal.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestLog.h b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestLog.h index 0046f8ea..fccd343e 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestLog.h +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestLog.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibCrOgl.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibCrOgl.cpp index 3fa03d20..893b4718 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibCrOgl.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibCrOgl.cpp @@ -91,9 +91,18 @@ DECLVBGL(int) vboxCrCtlConConnect(HVBOXCRCTL hCtl, uint32_t *pu32ClientID) RTStrCopy(info.Loc.u.host.achName, sizeof (info.Loc.u.host.achName), "VBoxSharedCrOpenGL"); rc = vbglDriverIOCtl (&hCtl->driver, VBOXGUEST_IOCTL_HGCM_CONNECT, &info, sizeof (info)); if (RT_SUCCESS(rc)) - *pu32ClientID = info.u32ClientID; - else - *pu32ClientID = 0; + { + rc = info.result; + if (RT_SUCCESS(rc)) + { + Assert(info.u32ClientID); + *pu32ClientID = info.u32ClientID; + return rc; + } + } + + Assert(RT_FAILURE(rc)); + *pu32ClientID = 0; return rc; } diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.c b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.c index a792acaf..3ecb4388 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.c +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.c @@ -1,10 +1,10 @@ -/* $Revision: 76380 $ */ +/* $Revision: 84251 $ */ /** @file * VBoxGuestR0LibSharedFolders - Ring 0 Shared Folders calls. */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -27,11 +27,12 @@ /* Entire file is ifdef'ed with !VBGL_VBOXGUEST */ #ifndef VBGL_VBOXGUEST +/******************************************************************************* +* Header Files * +*******************************************************************************/ #define LOG_GROUP LOG_GROUP_SHARED_FOLDERS - #ifdef RT_OS_LINUX # include "VBoxGuestR0LibSharedFolders.h" -# define DbgPrint RTAssertMsg2Weak #else # include "VBoxGuestR0LibSharedFolders.h" #endif @@ -41,6 +42,10 @@ #include <iprt/path.h> #include <iprt/string.h> + +/******************************************************************************* +* Defined Constants And Macros * +*******************************************************************************/ #define SHFL_CPARMS_SET_UTF8 0 #define SHFL_CPARMS_SET_SYMLINKS 0 @@ -52,9 +57,6 @@ (a)->u32Function = SHFL_FN_##b; \ (a)->cParms = SHFL_CPARMS_##b -#ifndef RT_OS_WINDOWS -# define RtlZeroMemory(a, b) memset (a, 0, b) -#endif DECLVBGL(int) vboxInit (void) @@ -72,14 +74,12 @@ DECLVBGL(void) vboxUninit (void) DECLVBGL(int) vboxConnect (PVBSFCLIENT pClient) { - int rc = VINF_SUCCESS; - + int rc; VBoxGuestHGCMConnectInfo data; - RtlZeroMemory (&data, sizeof (VBoxGuestHGCMConnectInfo)); - pClient->handle = NULL; + RT_ZERO(data); data.result = VINF_SUCCESS; data.Loc.type = VMMDevHGCMLoc_LocalHost_Existing; strcpy (data.Loc.u.host.achName, "VBoxSharedFolders"); @@ -111,8 +111,7 @@ DECLVBGL(void) vboxDisconnect (PVBSFCLIENT pClient) if (pClient->handle == NULL) return; /* not connected */ - RtlZeroMemory (&data, sizeof (VBoxGuestHGCMDisconnectInfo)); - + RT_ZERO(data); data.result = VINF_SUCCESS; data.u32ClientID = pClient->ulClientID; @@ -125,7 +124,7 @@ DECLVBGL(void) vboxDisconnect (PVBSFCLIENT pClient) } DECLVBGL(int) vboxCallQueryMappings (PVBSFCLIENT pClient, SHFLMAPPING paMappings[], - uint32_t *pcMappings) + uint32_t *pcMappings) { int rc = VINF_SUCCESS; @@ -442,6 +441,52 @@ DECLVBGL(int) vboxCallRead(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, return rc; } +DECLVBGL(int) VbglR0SharedFolderReadPageList(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToRead = *pcbBuffer; + uint32_t cbData = sizeof(VBoxSFRead) + RT_UOFFSETOF(HGCMPageListInfo, aPages[cPages]); + VBoxSFRead *pData = (VBoxSFRead *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL(&pData->callInfo, READ, pClient); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToRead; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToRead; + pData->buffer.u.PageList.offset = sizeof(VBoxSFRead); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_FROM_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglHGCMCall(pClient->handle, &pData->callInfo, cbData); + if (RT_SUCCESS (rc)) + { + rc = pData->callInfo.result; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + DECLVBGL(int) vboxCallWrite(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked) { @@ -524,6 +569,52 @@ DECLVBGL(int) VbglR0SfWritePhysCont(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHAND } +DECLVBGL(int) VbglR0SharedFolderWritePageList(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages) +{ + uint32_t cbToWrite = *pcbBuffer; + uint32_t cbData = sizeof(VBoxSFWrite) + RT_UOFFSETOF(HGCMPageListInfo, aPages[cPages]); + VBoxSFWrite *pData = (VBoxSFWrite *)RTMemTmpAlloc(cbData); + HGCMPageListInfo *pPgLst = (HGCMPageListInfo *)(pData + 1); + uint16_t iPage; + int rc; + + if (RT_UNLIKELY(!pData)) + return VERR_NO_TMP_MEMORY; + + VBOX_INIT_CALL(&pData->callInfo, WRITE, pClient); + + pData->root.type = VMMDevHGCMParmType_32bit; + pData->root.u.value32 = pMap->root; + + pData->handle.type = VMMDevHGCMParmType_64bit; + pData->handle.u.value64 = hFile; + pData->offset.type = VMMDevHGCMParmType_64bit; + pData->offset.u.value64 = offset; + pData->cb.type = VMMDevHGCMParmType_32bit; + pData->cb.u.value32 = cbToWrite; + pData->buffer.type = VMMDevHGCMParmType_PageList; + pData->buffer.u.PageList.size = cbToWrite; + pData->buffer.u.PageList.offset = sizeof(VBoxSFWrite); + + pPgLst->flags = VBOX_HGCM_F_PARM_DIRECTION_TO_HOST; + pPgLst->offFirstPage = offFirstPage; + pPgLst->cPages = cPages; + for (iPage = 0; iPage < cPages; iPage++) + pPgLst->aPages[iPage] = paPages[iPage]; + + rc = VbglHGCMCall (pClient->handle, &pData->callInfo, cbData); + if (RT_SUCCESS (rc)) + { + rc = pData->callInfo.result; + *pcbBuffer = pData->cb.u.value32; + } + + RTMemTmpFree(pData); + return rc; +} + DECLVBGL(int) vboxCallFlush(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile) { int rc = VINF_SUCCESS; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.h b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.h index 3947a13d..5e5c9576 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.h +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR0LibSharedFolders.h @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -172,8 +172,14 @@ DECLVBGL(int) vboxCallRename (PVBSFCLIENT pClient, PVBSFMAP pMap, PSHFLSTRING pS DECLVBGL(int) vboxCallFlush (PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile); DECLVBGL(int) vboxCallRead (PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked); +DECLVBGL(int) VbglR0SharedFolderReadPageList(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages); DECLVBGL(int) vboxCallWrite (PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, uint8_t *pBuffer, bool fLocked); DECLVBGL(int) VbglR0SfWritePhysCont(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint32_t *pcbBuffer, RTCCPHYS PhysBuffer); +DECLVBGL(int) VbglR0SharedFolderWritePageList(PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, + uint64_t offset, uint32_t *pcbBuffer, + uint16_t offFirstPage, uint16_t cPages, RTGCPHYS64 *paPages); DECLVBGL(int) vboxCallLock (PVBSFCLIENT pClient, PVBSFMAP pMap, SHFLHANDLE hFile, uint64_t offset, uint64_t cbSize, uint32_t fLock); diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3Lib.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3Lib.cpp index 32ccf4c4..80e5b877 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3Lib.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3Lib.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -36,14 +36,20 @@ # define INCL_ERRORS # include <os2.h> -#elif defined(RT_OS_FREEBSD) \ +#elif defined(RT_OS_DARWIN) \ + || defined(RT_OS_FREEBSD) \ + || defined(RT_OS_HAIKU) \ || defined(RT_OS_LINUX) \ || defined(RT_OS_SOLARIS) # include <sys/types.h> # include <sys/stat.h> -# if defined(RT_OS_LINUX) /** @todo check this on solaris+freebsd as well. */ +# if defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) /** @todo check this on solaris+freebsd as well. */ # include <sys/ioctl.h> # endif +# if defined(RT_OS_DARWIN) +# include <mach/mach_port.h> +# include <IOKit/IOKitLib.h> +# endif # include <errno.h> # include <unistd.h> #endif @@ -85,6 +91,10 @@ static RTFILE g_File = NIL_RTFILE; * inside a single process space. */ static uint32_t volatile g_cInits = 0; +#ifdef RT_OS_DARWIN +/** I/O Kit connection handle. */ +static io_connect_t g_uConnection = 0; +#endif @@ -189,6 +199,40 @@ static int vbglR3Init(const char *pszDeviceName) } g_File = (RTFILE)hf; +#elif defined(RT_OS_DARWIN) + /* + * Darwin is kind of special we need to engage the device via I/O first + * before we open it via the BSD device node. + */ + mach_port_t MasterPort; + kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &MasterPort); + if (kr != kIOReturnSuccess) + return VERR_GENERAL_FAILURE; + + CFDictionaryRef ClassToMatch = IOServiceMatching("org_virtualbox_VBoxGuest"); + if (!ClassToMatch) + return VERR_GENERAL_FAILURE; + + io_service_t ServiceObject = IOServiceGetMatchingService(kIOMasterPortDefault, ClassToMatch); + if (!ServiceObject) + return VERR_NOT_FOUND; + + io_connect_t uConnection; + kr = IOServiceOpen(ServiceObject, mach_task_self(), 0, &uConnection); + IOObjectRelease(ServiceObject); + if (kr != kIOReturnSuccess) + return VERR_OPEN_FAILED; + + RTFILE hFile; + int rc = RTFileOpen(&hFile, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); + if (RT_FAILURE(rc)) + { + IOServiceClose(uConnection); + return rc; + } + g_File = hFile; + g_uConnection = uConnection; + #elif defined(VBOX_VBGLR3_XFREE86) int File = xf86open(pszDeviceName, XF86_O_RDWR); if (File == -1) @@ -197,7 +241,7 @@ static int vbglR3Init(const char *pszDeviceName) #else - /* The default implementation. (linux, solaris, freebsd) */ + /* The default implementation. (linux, solaris, freebsd, haiku) */ RTFILE File; int rc = RTFileOpen(&File, pszDeviceName, RTFILE_O_READWRITE | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); if (RT_FAILURE(rc)) @@ -262,13 +306,22 @@ VBGLR3DECL(void) VbglR3Term(void) Assert(fRc); NOREF(fRc); # elif defined(RT_OS_OS2) - RTFILE File = g_File; g_File = NIL_RTFILE; AssertReturnVoid(File != NIL_RTFILE); APIRET rc = DosClose((uintptr_t)File); AssertMsg(!rc, ("%ld\n", rc)); +#elif defined(RT_OS_DARWIN) + io_connect_t uConnection = g_uConnection; + RTFILE hFile = g_File; + g_uConnection = 0; + g_File = NIL_RTFILE; + kern_return_t kr = IOServiceClose(uConnection); + AssertMsg(kr == kIOReturnSuccess, ("%#x (%d)\n", kr, kr)); + int rc = RTFileClose(hFile); + AssertRC(rc); + # else /* The IPRT case. */ RTFILE File = g_File; g_File = NIL_RTFILE; @@ -357,7 +410,7 @@ int vbglR3DoIOCtl(unsigned iFunction, void *pvData, size_t cbData) } return VINF_SUCCESS; -#elif defined(RT_OS_LINUX) +#elif defined(RT_OS_DARWIN) || defined(RT_OS_LINUX) # ifdef VBOX_VBGLR3_XFREE86 int rc = xf86ioctl((int)g_File, iFunction, pvData); # else @@ -380,6 +433,20 @@ int vbglR3DoIOCtl(unsigned iFunction, void *pvData, size_t cbData) NOREF(cbData); return rc; +#elif defined(RT_OS_HAIKU) + /* The ioctl hook in Haiku does take the len parameter when specified, + * so just use it. */ + int rc = ioctl((int)g_File, iFunction, pvData, cbData); + if (RT_LIKELY(rc == 0)) + return VINF_SUCCESS; + + /* Positive values are negated VBox error status codes. */ + if (rc > 0) + rc = -rc; + else + rc = RTErrConvertFromErrno(errno); + return rc; + #elif defined(VBOX_VBGLR3_XFREE86) /* PORTME - This is preferred over the RTFileIOCtl variant below, just be careful with the (int). */ /** @todo test status code passing! */ diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibClipboard.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibClipboard.cpp index 2c13b0aa..045d4670 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibClipboard.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibClipboard.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibCredentials.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibCredentials.cpp index a25515e1..7809a420 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibCredentials.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibCredentials.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDaemonize.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDaemonize.cpp index a314e522..c6ab6545 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDaemonize.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibDaemonize.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2009 Oracle Corporation + * Copyright (C) 2007-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -28,10 +28,7 @@ /******************************************************************************* * Header Files * *******************************************************************************/ -#if defined(RT_OS_DARWIN) -# error "PORTME" - -#elif defined(RT_OS_OS2) +#if defined(RT_OS_OS2) # define INCL_BASE # define INCL_ERRORS # include <os2.h> @@ -73,10 +70,7 @@ */ VBGLR3DECL(int) VbglR3Daemonize(bool fNoChDir, bool fNoClose) { -#if defined(RT_OS_DARWIN) -# error "PORTME" - -#elif defined(RT_OS_OS2) +#if defined(RT_OS_OS2) PPIB pPib; PTIB pTib; DosGetInfoBlocks(&pTib, &pPib); diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGR.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGR.cpp index b8a3fa7f..a5e16501 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGR.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGR.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp index 514d60c9..2bd554c4 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestCtrl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010-2011 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -49,10 +49,10 @@ using namespace guestControl; * Connects to the guest control service. * * @returns VBox status code - * @param pu32ClientId Where to put the client id on success. The client id - * must be passed to all the other calls to the service. + * @param puClientId Where to put The client ID on success. The client ID + * must be passed to all the other calls to the service. */ -VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pu32ClientId) +VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *puClientId) { VBoxGuestHGCMConnectInfo Info; Info.result = VERR_WRONG_ORDER; @@ -66,7 +66,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pu32ClientId) { rc = Info.result; if (RT_SUCCESS(rc)) - *pu32ClientId = Info.u32ClientID; + *puClientId = Info.u32ClientID; } return rc; } @@ -76,13 +76,13 @@ VBGLR3DECL(int) VbglR3GuestCtrlConnect(uint32_t *pu32ClientId) * Disconnect from the guest control service. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). */ -VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t u32ClientId) +VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t uClientId) { VBoxGuestHGCMDisconnectInfo Info; Info.result = VERR_WRONG_ORDER; - Info.u32ClientID = u32ClientId; + Info.u32ClientID = uClientId; int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_DISCONNECT, &Info, sizeof(Info)); if (RT_SUCCESS(rc)) @@ -96,22 +96,22 @@ VBGLR3DECL(int) VbglR3GuestCtrlDisconnect(uint32_t u32ClientId) * This will block until a message becomes available. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). * @param puMsg Where to store the message id. * @param puNumParms Where to store the number of parameters which will be received * in a second call to the host. */ -VBGLR3DECL(int) VbglR3GuestCtrlWaitForHostMsg(uint32_t u32ClientId, uint32_t *puMsg, uint32_t *puNumParms) +VBGLR3DECL(int) VbglR3GuestCtrlMsgWaitFor(uint32_t uClientId, uint32_t *puMsg, uint32_t *puNumParms) { AssertPtrReturn(puMsg, VERR_INVALID_POINTER); AssertPtrReturn(puNumParms, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgType Msg; + HGCMMsgCmdWaitFor Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; /* Tell the host we want our next command. */ - Msg.hdr.cParms = 2; /* Just peek for the next message! */ + Msg.hdr.u32ClientID = uClientId; + Msg.hdr.u32Function = GUEST_MSG_WAIT; /* Tell the host we want our next command. */ + Msg.hdr.cParms = 2; /* Just peek for the next message! */ VbglHGCMParmUInt32Set(&Msg.msg, 0); VbglHGCMParmUInt32Set(&Msg.num_parms, 0); @@ -131,17 +131,139 @@ VBGLR3DECL(int) VbglR3GuestCtrlWaitForHostMsg(uint32_t u32ClientId, uint32_t *pu /** + * Asks the host guest control service to set a command filter to this + * client so that it only will receive certain commands in the future. + * The filter(s) are a bitmask for the context IDs, served from the host. + * + * @return IPRT status code. + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). + * @param uValue The value to filter messages for. + * @param uMaskAdd Filter mask to add. + * @param uMaskRemove Filter mask to remove. + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgFilterSet(uint32_t uClientId, uint32_t uValue, + uint32_t uMaskAdd, uint32_t uMaskRemove) +{ + HGCMMsgCmdFilterSet Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = uClientId; + Msg.hdr.u32Function = GUEST_MSG_FILTER_SET; /* Tell the host we want to set a filter. */ + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.value, uValue); + VbglHGCMParmUInt32Set(&Msg.mask_add, uMaskAdd); + VbglHGCMParmUInt32Set(&Msg.mask_remove, uMaskRemove); + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + rc = Msg.hdr.result; + return rc; +} + + +/** + * Disables a previously set message filter. + * + * @return IPRT status code. + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgFilterUnset(uint32_t uClientId) +{ + HGCMMsgCmdFilterUnset Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = uClientId; + Msg.hdr.u32Function = GUEST_MSG_FILTER_UNSET; /* Tell the host we want to unset the filter. */ + Msg.hdr.cParms = 1; + + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + rc = Msg.hdr.result; + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlMsgReply(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc) +{ + return VbglR3GuestCtrlMsgReplyEx(pCtx, rc, 0 /* uType */, + NULL /* pvPayload */, 0 /* cbPayload */); +} + + +VBGLR3DECL(int) VbglR3GuestCtrlMsgReplyEx(PVBGLR3GUESTCTRLCMDCTX pCtx, + int rc, uint32_t uType, + void *pvPayload, uint32_t cbPayload) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + /* Everything else is optional. */ + + HGCMMsgCmdReply Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_REPLY; + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.rc, (uint32_t)rc); /* int vs. uint32_t */ + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmPtrSet(&Msg.payload, pvPayload, cbPayload); + + int rc2 = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc3 = Msg.hdr.result; + if (RT_FAILURE(rc3)) + rc2 = rc3; + } + return rc2; +} + + +/** + * Tells the host service to skip the current message returned by + * VbglR3GuestCtrlMsgWaitFor(). + * + * @return IPRT status code. + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). + */ +VBGLR3DECL(int) VbglR3GuestCtrlMsgSkip(uint32_t uClientId) +{ + HGCMMsgCmdSkip Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = uClientId; + Msg.hdr.u32Function = GUEST_MSG_SKIP; /* Tell the host we want to skip + the current assigned command. */ + Msg.hdr.cParms = 1; + + VbglHGCMParmUInt32Set(&Msg.flags, 0 /* Flags, unused */); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + rc = Msg.hdr.result; + + return rc; +} + + +/** * Asks the host to cancel (release) all pending waits which were deferred. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). + * @param uClientId The client ID returned by VbglR3GuestCtrlConnect(). */ -VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t u32ClientId) +VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t uClientId) { - VBoxGuestCtrlHGCMMsgCancelPendingWaits Msg; + HGCMMsgCancelPendingWaits Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; + Msg.hdr.u32ClientID = uClientId; Msg.hdr.u32Function = GUEST_CANCEL_PENDING_WAITS; Msg.hdr.cParms = 0; @@ -157,28 +279,238 @@ VBGLR3DECL(int) VbglR3GuestCtrlCancelPendingWaits(uint32_t u32ClientId) /** + * Asks a specific guest session to close. + * + * @return IPRT status code. + * @param pCtx Host context. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + HGCMMsgSessionClose Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_SESSION_CLOSE; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.flags, uFlags); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlSessionNotify(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uType, uint32_t uResult) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgSessionNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_SESSION_NOTIFY; + Msg.hdr.cParms = 3; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, uType); + VbglHGCMParmUInt32Set(&Msg.result, uResult); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Retrieves the request to create a new guest session. + * + * @return IPRT status code. + * @param pCtx Host context. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puProtocol, + char *pszUser, uint32_t cbUser, + char *pszPassword, uint32_t cbPassword, + char *pszDomain, uint32_t cbDomain, + uint32_t *puFlags, uint32_t *puSessionID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 6, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puProtocol, VERR_INVALID_POINTER); + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + AssertPtrReturn(pszPassword, VERR_INVALID_POINTER); + AssertPtrReturn(pszDomain, VERR_INVALID_POINTER); + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); + + HGCMMsgSessionOpen Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmUInt32Set(&Msg.protocol, 0); + VbglHGCMParmPtrSet(&Msg.username, pszUser, cbUser); + VbglHGCMParmPtrSet(&Msg.password, pszPassword, cbPassword); + VbglHGCMParmPtrSet(&Msg.domain, pszDomain, cbDomain); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + { + rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.protocol.GetUInt32(puProtocol); + Msg.flags.GetUInt32(puFlags); + + if (puSessionID) + *puSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + } + + return rc; +} + + +/** + * Retrieves the request to terminate an existing guest session. + * + * @return IPRT status code. + * @param pCtx Host context. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlSessionGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puFlags, uint32_t *puSessionID) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); + + HGCMMsgSessionClose Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + { + rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(puFlags); + + if (puSessionID) + *puSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pCtx->uContextID); + } + } + + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlPathGetRename(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszSource, uint32_t cbSource, + char *pszDest, uint32_t cbDest, + uint32_t *puFlags) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); + + AssertPtrReturn(pszSource, VERR_INVALID_POINTER); + AssertReturn(cbSource, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDest, VERR_INVALID_POINTER); + AssertReturn(cbDest, VERR_INVALID_PARAMETER); + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); + + HGCMMsgPathRename Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmPtrSet(&Msg.source, pszSource, cbSource); + VbglHGCMParmPtrSet(&Msg.dest, pszDest, cbDest); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + { + rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(puFlags); + } + } + return rc; +} + + +/** * Allocates and gets host data, based on the message id. * * This will block until data becomes available. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). - * @param uNumParms ** @todo Docs! + ** @todo Move the parameters in an own struct! */ -VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdExec(uint32_t u32ClientId, uint32_t cParms, - uint32_t *puContext, - char *pszCmd, uint32_t cbCmd, - uint32_t *puFlags, - char *pszArgs, uint32_t cbArgs, uint32_t *pcArgs, - char *pszEnv, uint32_t *pcbEnv, uint32_t *pcEnvVars, - char *pszUser, uint32_t cbUser, - char *pszPassword, uint32_t cbPassword, - uint32_t *pcMsTimeLimit) -{ - AssertReturn(cParms == 11, VERR_INVALID_PARAMETER); - - AssertPtrReturn(puContext, VERR_INVALID_POINTER); +VBGLR3DECL(int) VbglR3GuestCtrlProcGetStart(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszCmd, uint32_t cbCmd, + uint32_t *puFlags, + char *pszArgs, uint32_t cbArgs, uint32_t *pcArgs, + char *pszEnv, uint32_t *pcbEnv, uint32_t *pcEnvVars, + char *pszUser, uint32_t cbUser, + char *pszPassword, uint32_t cbPassword, + uint32_t *puTimeoutMS, + uint32_t *puPriority, + uint64_t *puAffinity, uint32_t cbAffinity, uint32_t *pcAffinity) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pszCmd, VERR_INVALID_POINTER); AssertPtrReturn(puFlags, VERR_INVALID_POINTER); AssertPtrReturn(pszArgs, VERR_INVALID_POINTER); @@ -186,16 +518,14 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdExec(uint32_t u32ClientId, uint AssertPtrReturn(pszEnv, VERR_INVALID_POINTER); AssertPtrReturn(pcbEnv, VERR_INVALID_POINTER); AssertPtrReturn(pcEnvVars, VERR_INVALID_POINTER); - AssertPtrReturn(pszUser, VERR_INVALID_POINTER); - AssertPtrReturn(pszPassword, VERR_INVALID_POINTER); - AssertPtrReturn(pcMsTimeLimit, VERR_INVALID_POINTER); + AssertPtrReturn(puTimeoutMS, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgExecCmd Msg; + HGCMMsgProcExec Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 11; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmPtrSet(&Msg.cmd, pszCmd, cbCmd); @@ -205,9 +535,27 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdExec(uint32_t u32ClientId, uint VbglHGCMParmUInt32Set(&Msg.num_env, 0); VbglHGCMParmUInt32Set(&Msg.cb_env, 0); VbglHGCMParmPtrSet(&Msg.env, pszEnv, *pcbEnv); - VbglHGCMParmPtrSet(&Msg.username, pszUser, cbUser); - VbglHGCMParmPtrSet(&Msg.password, pszPassword, cbPassword); - VbglHGCMParmUInt32Set(&Msg.timeout, 0); + if (pCtx->uProtocol < 2) + { + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + AssertReturn(cbUser, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszPassword, VERR_INVALID_POINTER); + AssertReturn(pszPassword, VERR_INVALID_PARAMETER); + + VbglHGCMParmPtrSet(&Msg.u.v1.username, pszUser, cbUser); + VbglHGCMParmPtrSet(&Msg.u.v1.password, pszPassword, cbPassword); + VbglHGCMParmUInt32Set(&Msg.u.v1.timeout, 0); + } + else + { + AssertPtrReturn(puAffinity, VERR_INVALID_POINTER); + AssertReturn(cbAffinity, VERR_INVALID_PARAMETER); + + VbglHGCMParmUInt32Set(&Msg.u.v2.timeout, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.priority, 0); + VbglHGCMParmUInt32Set(&Msg.u.v2.num_affinity, 0); + VbglHGCMParmPtrSet(&Msg.u.v2.affinity, puAffinity, cbAffinity); + } int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) @@ -219,12 +567,21 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdExec(uint32_t u32ClientId, uint } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.flags.GetUInt32(puFlags); Msg.num_args.GetUInt32(pcArgs); Msg.num_env.GetUInt32(pcEnvVars); Msg.cb_env.GetUInt32(pcbEnv); - Msg.timeout.GetUInt32(pcMsTimeLimit); + if (pCtx->uProtocol < 2) + { + Msg.u.v1.timeout.GetUInt32(puTimeoutMS); + } + else + { + Msg.u.v2.timeout.GetUInt32(puTimeoutMS); + Msg.u.v2.priority.GetUInt32(puPriority); + Msg.u.v2.num_affinity.GetUInt32(pcAffinity); + } } } return rc; @@ -237,27 +594,24 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdExec(uint32_t u32ClientId, uint * This will block until data becomes available. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). - * @param cParms ** @todo Docs! */ -VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdOutput(uint32_t u32ClientId, uint32_t cParms, - uint32_t *puContext, uint32_t *puPID, - uint32_t *puHandle, uint32_t *puFlags) +VBGLR3DECL(int) VbglR3GuestCtrlProcGetOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puHandle, uint32_t *puFlags) { - AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); AssertPtrReturn(puPID, VERR_INVALID_POINTER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); AssertPtrReturn(puFlags, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgExecOut Msg; + HGCMMsgProcOutput Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 4; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.pid, 0); @@ -274,7 +628,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdOutput(uint32_t u32ClientId, ui } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.pid.GetUInt32(puPID); Msg.handle.GetUInt32(puHandle); Msg.flags.GetUInt32(puFlags); @@ -291,30 +645,27 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdOutput(uint32_t u32ClientId, ui * This will block until data becomes available. * * @returns VBox status code. - * @param u32ClientId The client id returned by VbglR3GuestCtrlConnect(). - * @param cParms ** @todo Docs! */ -VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdInput(uint32_t u32ClientId, uint32_t cParms, - uint32_t *puContext, uint32_t *puPID, - uint32_t *puFlags, - void *pvData, uint32_t cbData, - uint32_t *pcbSize) +VBGLR3DECL(int) VbglR3GuestCtrlProcGetInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puFlags, + void *pvData, uint32_t cbData, + uint32_t *pcbSize) { - AssertReturn(cParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); AssertPtrReturn(puPID, VERR_INVALID_POINTER); AssertPtrReturn(puFlags, VERR_INVALID_POINTER); AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgExecIn Msg; + HGCMMsgProcInput Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 5; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.pid, 0); @@ -332,7 +683,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdInput(uint32_t u32ClientId, uin } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.pid.GetUInt32(puPID); Msg.flags.GetUInt32(puFlags); Msg.size.GetUInt32(pcbSize); @@ -342,151 +693,156 @@ VBGLR3DECL(int) VbglR3GuestCtrlExecGetHostCmdInput(uint32_t u32ClientId, uin } -/** - * Reports the process status (along with some other stuff) to the host. - * - * @returns VBox status code. - ** @todo Docs! - */ -VBGLR3DECL(int) VbglR3GuestCtrlExecReportStatus(uint32_t u32ClientId, - uint32_t u32Context, - uint32_t u32PID, - uint32_t u32Status, - uint32_t u32Flags, - void *pvData, - uint32_t cbData) +VBGLR3DECL(int) VbglR3GuestCtrlDirGetRemove(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszPath, uint32_t cbPath, + uint32_t *puFlags) { - VBoxGuestCtrlHGCMMsgExecStatus Msg; + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); - Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_EXEC_SEND_STATUS; - Msg.hdr.cParms = 5; + AssertPtrReturn(pszPath, VERR_INVALID_POINTER); + AssertReturn(cbPath, VERR_INVALID_PARAMETER); + AssertPtrReturn(puFlags, VERR_INVALID_POINTER); - VbglHGCMParmUInt32Set(&Msg.context, u32Context); - VbglHGCMParmUInt32Set(&Msg.pid, u32PID); - VbglHGCMParmUInt32Set(&Msg.status, u32Status); - VbglHGCMParmUInt32Set(&Msg.flags, u32Flags); - VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + HGCMMsgDirRemove Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmPtrSet(&Msg.path, pszPath, cbPath); + VbglHGCMParmUInt32Set(&Msg.flags, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { int rc2 = Msg.hdr.result; if (RT_FAILURE(rc2)) + { rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.flags.GetUInt32(puFlags); + } } return rc; } -/** - * Sends output (from stdout/stderr) from a running process. - * - * @returns VBox status code. - ** @todo Docs! - */ -VBGLR3DECL(int) VbglR3GuestCtrlExecSendOut(uint32_t u32ClientId, - uint32_t u32Context, - uint32_t u32PID, - uint32_t u32Handle, - uint32_t u32Flags, - void *pvData, - uint32_t cbData) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + char *pszFileName, uint32_t cbFileName, + char *pszAccess, uint32_t cbAccess, + char *pszDisposition, uint32_t cbDisposition, + char *pszSharing, uint32_t cbSharing, + uint32_t *puCreationMode, + uint64_t *puOffset) { - VBoxGuestCtrlHGCMMsgExecOut Msg; + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + AssertReturn(pCtx->uNumParms == 7, VERR_INVALID_PARAMETER); - Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_EXEC_SEND_OUTPUT; - Msg.hdr.cParms = 5; + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertReturn(cbFileName, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszAccess, VERR_INVALID_POINTER); + AssertReturn(cbAccess, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszDisposition, VERR_INVALID_POINTER); + AssertReturn(cbDisposition, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszSharing, VERR_INVALID_POINTER); + AssertReturn(cbSharing, VERR_INVALID_PARAMETER); + AssertPtrReturn(puCreationMode, VERR_INVALID_POINTER); + AssertPtrReturn(puOffset, VERR_INVALID_POINTER); - VbglHGCMParmUInt32Set(&Msg.context, u32Context); - VbglHGCMParmUInt32Set(&Msg.pid, u32PID); - VbglHGCMParmUInt32Set(&Msg.handle, u32Handle); - VbglHGCMParmUInt32Set(&Msg.flags, u32Flags); - VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + HGCMMsgFileOpen Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName); + VbglHGCMParmPtrSet(&Msg.openmode, pszAccess, cbAccess); + VbglHGCMParmPtrSet(&Msg.disposition, pszDisposition, cbDisposition); + VbglHGCMParmPtrSet(&Msg.sharing, pszSharing, cbSharing); + VbglHGCMParmUInt32Set(&Msg.creationmode, 0); + VbglHGCMParmUInt64Set(&Msg.offset, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { int rc2 = Msg.hdr.result; if (RT_FAILURE(rc2)) + { rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.creationmode.GetUInt32(puCreationMode); + Msg.offset.GetUInt64(puOffset); + } } return rc; } -/** - * Reports back the input status to the host. - * - * @returns VBox status code. - ** @todo Docs! - */ -VBGLR3DECL(int) VbglR3GuestCtrlExecReportStatusIn(uint32_t u32ClientId, - uint32_t u32Context, - uint32_t u32PID, - uint32_t u32Status, - uint32_t u32Flags, - uint32_t cbWritten) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetClose(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) { - VBoxGuestCtrlHGCMMsgExecStatusIn Msg; + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); - Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = u32ClientId; - Msg.hdr.u32Function = GUEST_EXEC_SEND_INPUT_STATUS; - Msg.hdr.cParms = 5; + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); - VbglHGCMParmUInt32Set(&Msg.context, u32Context); - VbglHGCMParmUInt32Set(&Msg.pid, u32PID); - VbglHGCMParmUInt32Set(&Msg.status, u32Status); - VbglHGCMParmUInt32Set(&Msg.flags, u32Flags); - VbglHGCMParmUInt32Set(&Msg.written, cbWritten); + HGCMMsgFileClose Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmUInt32Set(&Msg.handle, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) { int rc2 = Msg.hdr.result; if (RT_FAILURE(rc2)) + { rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + } } return rc; } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdOpen(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - char *pszFileName, uint32_t cbFileName, - char *pszOpenMode, uint32_t cbOpenMode, - char *pszDisposition, uint32_t cbDisposition, - uint32_t *puCreationMode, - uint64_t *puOffset) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetRead(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puToRead) { - AssertReturn(cParms == 6, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); - AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); - AssertReturn(cbFileName, VERR_INVALID_PARAMETER); - AssertPtrReturn(pszOpenMode, VERR_INVALID_POINTER); - AssertReturn(cbOpenMode, VERR_INVALID_PARAMETER); - AssertPtrReturn(pszDisposition, VERR_INVALID_POINTER); - AssertReturn(cbDisposition, VERR_INVALID_PARAMETER); - AssertPtrReturn(puCreationMode, VERR_INVALID_POINTER); - AssertPtrReturn(puOffset, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 3, VERR_INVALID_PARAMETER); + AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileOpen Msg; + HGCMMsgFileRead Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 6; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); - VbglHGCMParmPtrSet(&Msg.filename, pszFileName, cbFileName); - VbglHGCMParmPtrSet(&Msg.openmode, pszOpenMode, cbOpenMode); - VbglHGCMParmPtrSet(&Msg.disposition, pszDisposition, cbDisposition); - VbglHGCMParmUInt32Set(&Msg.creationmode, 0); - VbglHGCMParmUInt64Set(&Msg.offset, 0); + VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) @@ -498,32 +854,35 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdOpen(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); - Msg.creationmode.GetUInt32(puCreationMode); - Msg.offset.GetUInt64(puOffset); + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.handle.GetUInt32(puHandle); + Msg.size.GetUInt32(puToRead); } } return rc; } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdClose(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - uint32_t *puHandle) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetReadAt(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puToRead, uint64_t *puOffset) { - AssertReturn(cParms == 2, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); + AssertPtrReturn(puToRead, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileClose Msg; + HGCMMsgFileReadAt Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 2; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmUInt32Set(&Msg.offset, 0); + VbglHGCMParmUInt32Set(&Msg.size, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) @@ -535,32 +894,37 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdClose(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.handle.GetUInt32(puHandle); + Msg.offset.GetUInt64(puOffset); + Msg.size.GetUInt32(puToRead); } } return rc; } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdRead(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - uint32_t *puHandle, uint32_t *puToRead) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize) { - AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); - AssertPtrReturn(puToRead, VERR_INVALID_POINTER); + AssertPtrReturn(pvData, VERR_INVALID_POINTER); + AssertReturn(cbData, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileRead Msg; + HGCMMsgFileWrite Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 4; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.handle, 0); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); VbglHGCMParmUInt32Set(&Msg.size, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); @@ -573,38 +937,38 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdRead(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.handle.GetUInt32(puHandle); - Msg.size.GetUInt32(puToRead); + Msg.size.GetUInt32(pcbSize); } } return rc; } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdWrite(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - uint32_t *puHandle, - void *pvData, uint32_t cbData, - uint32_t *pcbSize) + +VBGLR3DECL(int) VbglR3GuestCtrlFileGetWriteAt(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle, + void *pvData, uint32_t cbData, uint32_t *pcbSize, uint64_t *puOffset) { - AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); AssertPtrReturn(pvData, VERR_INVALID_POINTER); AssertReturn(cbData, VERR_INVALID_PARAMETER); AssertPtrReturn(pcbSize, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileWrite Msg; + HGCMMsgFileWriteAt Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 4; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.handle, 0); VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); VbglHGCMParmUInt32Set(&Msg.size, 0); + VbglHGCMParmUInt32Set(&Msg.offset, 0); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) @@ -616,7 +980,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdWrite(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.handle.GetUInt32(puHandle); Msg.size.GetUInt32(pcbSize); } @@ -625,23 +989,22 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdWrite(uint32_t uClientId, } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdSeek(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - uint32_t *puHandle, - uint32_t *puSeekMethod, uint64_t *puOffset) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puHandle, uint32_t *puSeekMethod, uint64_t *puOffset) { - AssertReturn(cParms == 4, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 4, VERR_INVALID_PARAMETER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); AssertPtrReturn(puSeekMethod, VERR_INVALID_POINTER); AssertPtrReturn(puOffset, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileSeek Msg; + HGCMMsgFileSeek Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 4; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.handle, 0); @@ -658,7 +1021,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdSeek(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.handle.GetUInt32(puHandle); Msg.method.GetUInt32(puSeekMethod); Msg.offset.GetUInt64(puOffset); @@ -668,20 +1031,19 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdSeek(uint32_t uClientId, } -VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdTell(uint32_t uClientId, uint32_t cParms, - uint32_t *puContext, - uint32_t *puHandle) +VBGLR3DECL(int) VbglR3GuestCtrlFileGetTell(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puHandle) { - AssertReturn(cParms == 2, VERR_INVALID_PARAMETER); - AssertPtrReturn(puContext, VERR_INVALID_POINTER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); AssertPtrReturn(puHandle, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileTell Msg; + HGCMMsgFileTell Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; - Msg.hdr.u32Function = GUEST_GET_HOST_MSG; - Msg.hdr.cParms = 2; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; VbglHGCMParmUInt32Set(&Msg.context, 0); VbglHGCMParmUInt32Set(&Msg.handle, 0); @@ -696,7 +1058,7 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdTell(uint32_t uClientId, } else { - Msg.context.GetUInt32(puContext); + Msg.context.GetUInt32(&pCtx->uContextID); Msg.handle.GetUInt32(puHandle); } } @@ -704,27 +1066,376 @@ VBGLR3DECL(int) VbglR3GuestCtrlFileGetHostCmdTell(uint32_t uClientId, } -VBGLR3DECL(int) VbglR3GuestCtrlFileNotify(uint32_t uClientId, - uint32_t uContext, uint32_t uHandle, - uint32_t uType, - void *pvPayload, uint32_t cbPayload) +VBGLR3DECL(int) VbglR3GuestCtrlProcGetTerminate(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t *puPID) { - AssertPtrReturn(uContext, VERR_INVALID_POINTER); - AssertPtrReturn(uHandle, VERR_INVALID_POINTER); - AssertPtrReturn(pvPayload, VERR_INVALID_POINTER); - AssertReturn(cbPayload, VERR_INVALID_PARAMETER); + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); - VBoxGuestCtrlHGCMMsgFileNotify Msg; + AssertReturn(pCtx->uNumParms == 2, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + HGCMMsgProcTerminate Msg; Msg.hdr.result = VERR_WRONG_ORDER; - Msg.hdr.u32ClientID = uClientId; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + { + rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + } + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlProcGetWaitFor(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t *puPID, uint32_t *puWaitFlags, uint32_t *puTimeoutMS) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + AssertReturn(pCtx->uNumParms == 5, VERR_INVALID_PARAMETER); + AssertPtrReturn(puPID, VERR_INVALID_POINTER); + + HGCMMsgProcWaitFor Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_MSG_WAIT; + Msg.hdr.cParms = pCtx->uNumParms; + + VbglHGCMParmUInt32Set(&Msg.context, 0); + VbglHGCMParmUInt32Set(&Msg.pid, 0); + VbglHGCMParmUInt32Set(&Msg.flags, 0); + VbglHGCMParmUInt32Set(&Msg.timeout, 0); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + { + rc = rc2; + } + else + { + Msg.context.GetUInt32(&pCtx->uContextID); + Msg.pid.GetUInt32(puPID); + Msg.flags.GetUInt32(puWaitFlags); + Msg.timeout.GetUInt32(puTimeoutMS); + } + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbOpen(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint32_t uFileHandle) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_OPEN); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + VbglHGCMParmUInt32Set(&Msg.u.open.handle, uFileHandle); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbClose(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 3; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_CLOSE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbError(PVBGLR3GUESTCTRLCMDCTX pCtx, uint32_t uRc) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 3; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_ERROR); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbRead(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; Msg.hdr.u32Function = GUEST_FILE_NOTIFY; Msg.hdr.cParms = 4; - VbglHGCMParmUInt32Set(&Msg.context, uContext); + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_READ); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + VbglHGCMParmPtrSet(&Msg.u.read.data, pvData, cbData); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbWrite(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint32_t uWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_WRITE); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + VbglHGCMParmUInt32Set(&Msg.u.write.written, uWritten); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbSeek(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint64_t uOffActual) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_SEEK); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + VbglHGCMParmUInt64Set(&Msg.u.seek.offset, uOffActual); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +VBGLR3DECL(int) VbglR3GuestCtrlFileCbTell(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uRc, uint64_t uOffActual) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMReplyFileNotify Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_FILE_NOTIFY; + Msg.hdr.cParms = 4; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.type, GUEST_FILE_NOTIFYTYPE_TELL); + VbglHGCMParmUInt32Set(&Msg.rc, uRc); + + VbglHGCMParmUInt64Set(&Msg.u.tell.offset, uOffActual); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Callback for reporting a guest process status (along with some other stuff) to the host. + * + * @returns VBox status code. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatus(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, uint32_t uFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatus Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_EXEC_STATUS; + Msg.hdr.cParms = 5; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, uFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Sends output (from stdout/stderr) from a running process. + * + * @returns VBox status code. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbOutput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID,uint32_t uHandle, uint32_t uFlags, + void *pvData, uint32_t cbData) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcOutput Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_EXEC_OUTPUT; + Msg.hdr.cParms = 5; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); VbglHGCMParmUInt32Set(&Msg.handle, uHandle); - VbglHGCMParmUInt32Set(&Msg.type, uType); - VbglHGCMParmPtrSet(&Msg.payload, pvPayload, cbPayload); + VbglHGCMParmUInt32Set(&Msg.flags, uFlags); + VbglHGCMParmPtrSet(&Msg.data, pvData, cbData); + + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + { + int rc2 = Msg.hdr.result; + if (RT_FAILURE(rc2)) + rc = rc2; + } + return rc; +} + + +/** + * Callback for reporting back the input status of a guest process to the host. + * + * @returns VBox status code. + ** @todo Docs! + */ +VBGLR3DECL(int) VbglR3GuestCtrlProcCbStatusInput(PVBGLR3GUESTCTRLCMDCTX pCtx, + uint32_t uPID, uint32_t uStatus, + uint32_t uFlags, uint32_t cbWritten) +{ + AssertPtrReturn(pCtx, VERR_INVALID_POINTER); + + HGCMMsgProcStatusInput Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = pCtx->uClientID; + Msg.hdr.u32Function = GUEST_EXEC_INPUT_STATUS; + Msg.hdr.cParms = 5; + + VbglHGCMParmUInt32Set(&Msg.context, pCtx->uContextID); + VbglHGCMParmUInt32Set(&Msg.pid, uPID); + VbglHGCMParmUInt32Set(&Msg.status, uStatus); + VbglHGCMParmUInt32Set(&Msg.flags, uFlags); + VbglHGCMParmUInt32Set(&Msg.written, cbWritten); int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); if (RT_SUCCESS(rc)) diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestProp.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestProp.cpp index 32c7d796..48539199 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestProp.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestProp.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -783,6 +783,32 @@ VBGLR3DECL(void) VbglR3GuestPropEnumFree(PVBGLR3GUESTPROPENUM pHandle) /** + * Deletes a guest property. + * + * @returns VBox status code. + * @param u32ClientId The client id returned by VbglR3InvsSvcConnect(). + * @param pszName The property to delete. Utf8 + */ +VBGLR3DECL(int) VbglR3GuestPropDelete(uint32_t u32ClientId, const char *pszName) +{ + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + + DelProperty Msg; + + Msg.hdr.result = VERR_WRONG_ORDER; + Msg.hdr.u32ClientID = u32ClientId; + Msg.hdr.u32Function = DEL_PROP; + Msg.hdr.cParms = 1; + VbglHGCMParmPtrSetString(&Msg.name, pszName); + int rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_HGCM_CALL(sizeof(Msg)), &Msg, sizeof(Msg)); + if (RT_SUCCESS(rc)) + rc = Msg.hdr.result; + + return rc; +} + + +/** * Deletes a set of keys. * * The set is specified in the same way as for VbglR3GuestPropEnum. diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestUser.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestUser.cpp new file mode 100644 index 00000000..56b95785 --- /dev/null +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibGuestUser.cpp @@ -0,0 +1,114 @@ +/* $Id: VBoxGuestR3LibGuestUser.cpp $ */ +/** @file + * VBoxGuestR3Lib - Ring-3 Support Library for VirtualBox guest additions, + * guest user reporting / utility functions. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL) only, as it comes in the "COPYING.CDDL" file of the + * VirtualBox OSE distribution, in which case the provisions of the + * CDDL are applicable instead of those of the GPL. + * + * You may elect to license modified versions of this file under the + * terms and conditions of either the GPL or the CDDL or both. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <iprt/assert.h> +#include <VBox/log.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#include "VBGLR3Internal.h" + + +/** + * Reports a state change of a specific guest user. + * + * @returns IPRT status value + * @param pszUser Guest user name to report state for. + * @param pszDomain Domain the guest user's account is bound to. + * @param enmState Guest user state to report. + * @param puDetails Pointer to state details. Optional. + * @param cbDetails Size (in bytes) of state details. Pass 0 + * if puDetails is NULL. + */ +VBGLR3DECL(int) VbglR3GuestUserReportState(const char *pszUser, + const char *pszDomain, + VBoxGuestUserState enmState, + uint8_t *puDetails, + uint32_t cbDetails) +{ + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + /* pszDomain is optional. */ + /* puDetails is optional. */ + + uint32_t cbBase = sizeof(VMMDevReportGuestUserState); + uint32_t cbUser = strlen(pszUser) + 1; /* Include terminating zero */ + uint32_t cbDomain = pszDomain ? strlen(pszDomain) + 1 /* Dito */ : 0; + + /* Allocate enough space for all fields. */ + unsigned long cbSize = + cbBase + + cbUser + + cbDomain + + cbDetails; + + AssertReturn(cbSize, VERR_BUFFER_UNDERFLOW); + VMMDevReportGuestUserState *pReport = + (VMMDevReportGuestUserState *)RTMemAllocZ(cbSize); + if (!pReport) + return VERR_NO_MEMORY; + + int rc = vmmdevInitRequest(&pReport->header, VMMDevReq_ReportGuestUserState); + if (RT_SUCCESS(rc)) + { + pReport->header.size = cbSize; + + pReport->status.state = enmState; + pReport->status.cbUser = cbUser; + pReport->status.cbDomain = cbDomain; + pReport->status.cbDetails = cbDetails; + + /* + * Note: cbOffDynamic contains the first dynamic array entry within + * VBoxGuestUserStatus. + * Therefore it's vital to *not* change the order of the struct members + * without altering this code. Don't try this at home. + */ + uint32_t cbOffDynamic = + RT_OFFSETOF(VBoxGuestUserStatus, szUser); + + /* pDynamic marks the beginning for the dynamically allocated areas. */ + uint8_t *pDynamic = (uint8_t *)&pReport->status; + pDynamic += cbOffDynamic; + AssertPtr(pDynamic); + + memcpy(pDynamic, pszUser, cbUser); + if (cbDomain) + memcpy(pDynamic + cbUser, pszDomain, cbDomain); + if (cbDetails) + memcpy(pDynamic + cbUser + cbDomain, puDetails, cbDetails); + + rc = vbglR3GRPerform(&pReport->header); + } + + RTMemFree(pReport); + return rc; +} + diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostChannel.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostChannel.cpp index 7051ae62..35ee6b5c 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostChannel.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostChannel.cpp @@ -24,7 +24,7 @@ * terms and conditions of either the GPL or the CDDL or both. */ - + #include <iprt/mem.h> #include <VBox/HostServices/VBoxHostChannel.h> diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostVersion.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostVersion.cpp index 03cfce8f..e253838b 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostVersion.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibHostVersion.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibMouse.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibMouse.cpp index 25fc44ee..52e39ab2 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibMouse.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibMouse.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSeamless.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSeamless.cpp index aa146281..60b47a08 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSeamless.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSeamless.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSharedFolders.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSharedFolders.cpp index 0d6be8ed..1ebec9cd 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSharedFolders.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibSharedFolders.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -199,6 +199,11 @@ VBGLR3DECL(int) VbglR3SharedFolderGetMappings(uint32_t u32ClientId, bool fAutoMo ppaMappingsTemp = NULL; } + /* In this case, just return success with 0 mappings */ + if ( rc == VERR_INVALID_PARAMETER + && fAutoMountOnly) + rc = VINF_SUCCESS; + *ppaMappings = ppaMappingsTemp; return rc; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibTime.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibTime.cpp index c03b2c22..13830afe 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibTime.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibTime.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibVideo.cpp b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibVideo.cpp index 3298d85d..fa972a3e 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibVideo.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VBoxGuestR3LibVideo.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2009 Oracle Corporation + * Copyright (C) 2007-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -139,6 +139,69 @@ VBGLR3DECL(int) VbglR3SetPointerShapeReq(VMMDevReqMousePointer *pReq) rc = pReq->header.rc; return rc; } +/** + * Query the last display change request sent from the host to the guest. + * + * @returns iprt status value + * @param pcx Where to store the horizontal pixel resolution + * requested (a value of zero means do not change). + * @param pcy Where to store the vertical pixel resolution + * requested (a value of zero means do not change). + * @param pcBits Where to store the bits per pixel requested (a value + * of zero means do not change). + * @param iDisplay Where to store the display number the request was for + * - 0 for the primary display, 1 for the first + * secondary display, etc. + * @param fAck whether or not to acknowledge the newest request sent by + * the host. If this is set, the function will return the + * most recent host request, otherwise it will return the + * last request to be acknowledged. + * + * @param pcOriginX New horizontal position of the secondary monitor. + * @param pcOriginY New vertical position of the secondary monitor. + * param pfEnabled Secondary monitor is enabled or not. + * + */ +VBGLR3DECL(int) VbglR3GetDisplayChangeRequestEx(uint32_t *pcx, uint32_t *pcy, uint32_t *pcBits, + uint32_t *piDisplay, uint32_t *pcOriginX, uint32_t *pcOriginY, + bool *pfEnabled, bool fAck) +{ + VMMDevDisplayChangeRequestEx Req; + int rc = VINF_SUCCESS; + AssertPtrReturn(pcx, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcy, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcBits, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcOriginX, VERR_INVALID_PARAMETER); + AssertPtrReturn(pcOriginY, VERR_INVALID_PARAMETER); + AssertPtrReturn(piDisplay, VERR_INVALID_PARAMETER); + AssertPtrReturn(pfEnabled, VERR_INVALID_PARAMETER); + RT_ZERO(Req); + rc = vmmdevInitRequest(&Req.header, VMMDevReq_GetDisplayChangeRequestEx); + if (RT_FAILURE(rc)) + { + LogRelFlowFunc(("DisplayChangeRequest Extended not supported. Can't Init the Req.\n")); + return rc; + } + + if (fAck) + Req.eventAck = VMMDEV_EVENT_DISPLAY_CHANGE_REQUEST; + rc = vbglR3GRPerform(&Req.header); + if (RT_SUCCESS(rc)) + rc = Req.header.rc; + if (RT_SUCCESS(rc)) + { + *pcx = Req.xres; + *pcy = Req.yres; + *pcBits = Req.bpp; + *piDisplay = Req.display; + *pcOriginX = Req.cxOrigin; + *pcOriginY = Req.cyOrigin; + *pfEnabled = Req.fEnabled; + LogRel(("VbglR3GetDisplayChangeRequestEx: pcx=%d pcy=%d display=%d orgX=%d orgY=%d and Enabled=%d\n", + *pcx, *pcy, *piDisplay, *pcOriginX, *pcOriginY, *pfEnabled)); + } + return rc; +} /** @@ -146,7 +209,6 @@ VBGLR3DECL(int) VbglR3SetPointerShapeReq(VMMDevReqMousePointer *pReq) * * @returns iprt status value * @param pcx Where to store the horizontal pixel resolution - * requested (a value of zero means do not change). * @param pcy Where to store the vertical pixel resolution * requested (a value of zero means do not change). * @param pcBits Where to store the bits per pixel requested (a value diff --git a/src/VBox/Additions/common/VBoxGuestLib/VMMDev.cpp b/src/VBox/Additions/common/VBoxGuestLib/VMMDev.cpp index 00779237..beda4bb2 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VMMDev.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VMMDev.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxGuestLib/VbglR0CanUsePhysPageList.cpp b/src/VBox/Additions/common/VBoxGuestLib/VbglR0CanUsePhysPageList.cpp index a8764556..e8ecf07f 100644 --- a/src/VBox/Additions/common/VBoxGuestLib/VbglR0CanUsePhysPageList.cpp +++ b/src/VBox/Additions/common/VBoxGuestLib/VbglR0CanUsePhysPageList.cpp @@ -1,10 +1,10 @@ -/* $Revision: 77449 $ */ +/* $Revision: 83618 $ */ /** @file * VBoxGuestLibR0 - Physical memory heap. */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -34,6 +34,9 @@ */ DECLR0VBGL(bool) VbglR0CanUsePhysPageList(void) { + /* a_fLocked is false, because the actual capability of the host is requested. + * See VBGLR0_CAN_USE_PHYS_PAGE_LIST definition. + */ int rc = vbglR0Enter(); return RT_SUCCESS(rc) && VBGLR0_CAN_USE_PHYS_PAGE_LIST(/*a_fLocked =*/ false); diff --git a/src/VBox/Additions/common/VBoxService/Makefile.kmk b/src/VBox/Additions/common/VBoxService/Makefile.kmk index e00f66c6..8a42bab1 100644 --- a/src/VBox/Additions/common/VBoxService/Makefile.kmk +++ b/src/VBox/Additions/common/VBoxService/Makefile.kmk @@ -4,7 +4,7 @@ # # -# Copyright (C) 2007-2012 Oracle Corporation +# Copyright (C) 2007-2013 Oracle Corporation # # This file is part of VirtualBox Open Source Edition (OSE), as # available from http://www.virtualbox.org. This file is free software; @@ -38,6 +38,9 @@ VBoxService_DEFS += \ VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\" VBoxService_DEFS.win += _WIN32_WINNT=0x0501 VBoxService_DEFS.os2 = VBOX_WITH_HGCM VBOXSERVICE_CLIPBOARD +ifdef VBOX_WITH_DBUS + VBoxService_DEFS += VBOX_WITH_DBUS +endif ifdef VBOX_WITH_GUEST_PROPS VBoxService_DEFS += VBOX_WITH_GUEST_PROPS VBOXSERVICE_VMINFO endif @@ -48,7 +51,10 @@ ifdef VBOX_WITH_MEMBALLOON VBoxService_DEFS += VBOX_WITH_MEMBALLOON endif if1of ($(KBUILD_TARGET), win) - VBoxService_DEFS += VBOX_WITH_PAGE_SHARING + VBoxService_DEFS += VBOXSERVICE_PAGE_SHARING + ifdef VBOX_WITH_MMR + VBoxService_DEFS += VBOX_WITH_MMR + endif endif if1of ($(KBUILD_TARGET), linux) VBoxService_DEFS += VBOXSERVICE_CPUHOTPLUG @@ -70,7 +76,8 @@ VBoxService_SOURCES = \ ifdef VBOX_WITH_GUEST_CONTROL VBoxService_SOURCES += \ VBoxServiceControl.cpp \ - VBoxServiceControlThread.cpp + VBoxServiceControlProcess.cpp \ + VBoxServiceControlSession.cpp endif ifdef VBOX_WITH_MEMBALLOON @@ -112,6 +119,8 @@ VBoxService_SOURCES.os2 = \ VBoxService-os2.def \ VBoxServiceClipboard-os2.cpp +VBoxService_LDFLAGS.darwin = -framework IOKit + VBoxService_LIBS += \ $(VBOX_LIB_IPRT_GUEST_R3) \ $(VBOX_LIB_VBGL_R3) \ @@ -120,6 +129,12 @@ if1of ($(KBUILD_TARGET), linux) VBoxService_LIBS += \ crypt endif +ifdef VBOX_WITH_DBUS + if1of ($(KBUILD_TARGET), linux solaris) # FreeBSD? + VBoxService_LIBS += \ + dl + endif +endif ifdef VBOX_WITH_GUEST_PROPS VBoxService_LIBS.win += \ Secur32.lib \ diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-os2.def b/src/VBox/Additions/common/VBoxService/VBoxService-os2.def index 2c14ceed..aed84c68 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxService-os2.def +++ b/src/VBox/Additions/common/VBoxService/VBoxService-os2.def @@ -3,7 +3,7 @@ ; ; -; Copyright (C) 2007 Oracle Corporation +; Copyright (C) 2007-2010 Oracle Corporation ; ; This file is part of VirtualBox Open Source Edition (OSE), as ; available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp index e09173c1..eb970fa8 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxService-win.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,6 +21,7 @@ *******************************************************************************/ #include <iprt/assert.h> #include <iprt/err.h> +#include <iprt/system.h> /* For querying OS version. */ #include <VBox/VBoxGuestLib.h> #include "VBoxServiceInternal.h" @@ -132,18 +133,40 @@ static BOOL vboxServiceWinSetStatus(DWORD dwStatus, DWORD dwCheckPoint) g_dwWinServiceLastStatus = dwStatus; SERVICE_STATUS ss; + RT_ZERO(ss); + ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; ss.dwCurrentState = dwStatus; - ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; + /* Don't accept controls when in start pending state. */ + if (ss.dwCurrentState != SERVICE_START_PENDING) + { + ss.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN; #ifndef TARGET_NT4 - ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE; + /* Don't use SERVICE_ACCEPT_SESSIONCHANGE on Windows 2000. + * This makes SCM angry. */ + char szOSVersion[32]; + int rc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, + szOSVersion, sizeof(szOSVersion)); + if (RT_SUCCESS(rc)) + { + if (RTStrVersionCompare(szOSVersion, "5.1") >= 0) + ss.dwControlsAccepted |= SERVICE_ACCEPT_SESSIONCHANGE; + } + else + VBoxServiceError("Error determining OS version, rc=%Rrc\n", rc); #endif + } + ss.dwWin32ExitCode = NO_ERROR; ss.dwServiceSpecificExitCode = 0; /* Not used */ ss.dwCheckPoint = dwCheckPoint; ss.dwWaitHint = 3000; - return SetServiceStatus(g_hWinServiceStatus, &ss); + BOOL fStatusSet = SetServiceStatus(g_hWinServiceStatus, &ss); + if (!fStatusSet) + VBoxServiceError("Error reporting service status=%ld (controls=%x, checkpoint=%ld) to SCM: %ld\n", + dwStatus, ss.dwControlsAccepted, dwCheckPoint, GetLastError()); + return fStatusSet; } @@ -339,6 +362,8 @@ static int vboxServiceWinStart(void) if (RT_SUCCESS(rc)) { + vboxServiceWinSetStatus(SERVICE_START_PENDING, 0); + rc = VBoxServiceStartServices(); if (RT_SUCCESS(rc)) { diff --git a/src/VBox/Additions/common/VBoxService/VBoxService.cpp b/src/VBox/Additions/common/VBoxService/VBoxService.cpp index 639b97f7..8ce44dae 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxService.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxService.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2011 Oracle Corporation + * Copyright (C) 2007-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -56,6 +56,9 @@ #include <VBox/log.h> #include "VBoxServiceInternal.h" +#ifdef VBOX_WITH_GUEST_CONTROL +# include "VBoxServiceControl.h" +#endif /******************************************************************************* @@ -65,6 +68,7 @@ char *g_pszProgName = (char *)""; /** The current verbosity level. */ int g_cVerbosity = 0; +char g_szLogFile[RTPATH_MAX + 128] = ""; /** Logging parameters. */ /** @todo Make this configurable later. */ static PRTLOGGER g_pLoggerRelease = NULL; @@ -126,7 +130,7 @@ static struct # endif { &g_VMStatistics, NIL_RTTHREAD, false, false, false, false, true }, #endif -#if defined(VBOX_WITH_PAGE_SHARING) && defined(RT_OS_WINDOWS) +#if defined(VBOXSERVICE_PAGE_SHARING) { &g_PageSharing, NIL_RTTHREAD, false, false, false, false, true }, #endif #ifdef VBOX_WITH_SHARED_FOLDERS @@ -211,11 +215,12 @@ static void VBoxServiceLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmP /** * Creates the default release logger outputting to the specified file. + * Pass NULL for disabled logging. * * @return IPRT status code. * @param pszLogFile Filename for log output. Optional. */ -static int VBoxServiceLogCreate(const char *pszLogFile) +int VBoxServiceLogCreate(const char *pszLogFile) { /* Create release logger (stdout + file). */ static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; @@ -241,7 +246,8 @@ static int VBoxServiceLogCreate(const char *pszLogFile) return rc; } -static void VBoxServiceLogDestroy(void) + +void VBoxServiceLogDestroy(void) { RTLogDestroy(RTLogRelSetDefaultInstance(NULL)); } @@ -633,6 +639,8 @@ int VBoxServiceStopServices(void) g_aServices[j].pDesc->pfnStop(); } + VBoxServiceVerbose(3, "All stop functions for services called\n"); + /* * Wait for all the service threads to complete. */ @@ -745,6 +753,7 @@ void VBoxServiceMainWait(void) int main(int argc, char **argv) { RTEXITCODE rcExit; + bool fUserSession = false; /* * Init globals and such. @@ -769,14 +778,32 @@ int main(int argc, char **argv) return rcExit; #endif +#ifdef VBOX_WITH_GUEST_CONTROL + /* + * Check if we're the specially spawned VBoxService.exe process that + * handles a guest control session. + */ + if ( argc >= 2 + && !RTStrICmp(argv[1], "guestsession")) + fUserSession = true; +#endif + /* * Connect to the kernel part before daemonizing so we can fail and * complain if there is some kind of problem. We need to initialize the * guest lib *before* we do the pre-init just in case one of services needs * do to some initial stuff with it. */ - VBoxServiceVerbose(2, "Calling VbgR3Init()\n"); - rc = VbglR3Init(); + if (fUserSession) + { + VBoxServiceVerbose(2, "Calling VbgR3InitUser()\n"); + rc = VbglR3InitUser(); + } + else + { + VBoxServiceVerbose(2, "Calling VbgR3Init()\n"); + rc = VbglR3Init(); + } if (RT_FAILURE(rc)) { if (rc == VERR_ACCESS_DENIED) @@ -790,12 +817,19 @@ int main(int argc, char **argv) * Check if we're the specially spawned VBoxService.exe process that * handles page fusion. This saves an extra executable. */ - if ( argc == 2 - && !strcmp(argv[1], "--pagefusionfork")) + if ( argc == 2 + && !RTStrICmp(argv[1], "pagefusion")) return VBoxServicePageSharingInitFork(); #endif - char szLogFile[RTPATH_MAX + 128] = ""; +#ifdef VBOX_WITH_GUEST_CONTROL + /* + * Check if we're the specially spawned VBoxService.exe process that + * handles a guest control session. + */ + if (fUserSession) + return VBoxServiceControlSessionForkInit(argc, argv); +#endif /* * Parse the arguments. @@ -845,17 +879,17 @@ int main(int argc, char **argv) { bool fFound = false; - if (cch > sizeof("enable-") && !memcmp(psz, "enable-", sizeof("enable-") - 1)) + if (cch > sizeof("enable-") && !memcmp(psz, RT_STR_TUPLE("enable-"))) for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++) if ((fFound = !RTStrICmp(psz + sizeof("enable-") - 1, g_aServices[j].pDesc->pszName))) g_aServices[j].fEnabled = true; - if (cch > sizeof("disable-") && !memcmp(psz, "disable-", sizeof("disable-") - 1)) + if (cch > sizeof("disable-") && !memcmp(psz, RT_STR_TUPLE("disable-"))) for (unsigned j = 0; !fFound && j < RT_ELEMENTS(g_aServices); j++) if ((fFound = !RTStrICmp(psz + sizeof("disable-") - 1, g_aServices[j].pDesc->pszName))) g_aServices[j].fEnabled = false; - if (cch > sizeof("only-") && !memcmp(psz, "only-", sizeof("only-") - 1)) + if (cch > sizeof("only-") && !memcmp(psz, RT_STR_TUPLE("only-"))) for (unsigned j = 0; j < RT_ELEMENTS(g_aServices); j++) { g_aServices[j].fEnabled = !RTStrICmp(psz + sizeof("only-") - 1, g_aServices[j].pDesc->pszName); @@ -925,7 +959,7 @@ int main(int argc, char **argv) case 'l': { rc = VBoxServiceArgString(argc, argv, psz + 1, &i, - szLogFile, sizeof(szLogFile)); + g_szLogFile, sizeof(g_szLogFile)); if (rc) return rc; psz = NULL; @@ -960,10 +994,10 @@ int main(int argc, char **argv) if (vboxServiceCountEnabledServices() == 0) return RTMsgErrorExit(RTEXITCODE_SYNTAX, "At least one service must be enabled\n"); - rc = VBoxServiceLogCreate(strlen(szLogFile) ? szLogFile : NULL); + rc = VBoxServiceLogCreate(strlen(g_szLogFile) ? g_szLogFile : NULL); if (RT_FAILURE(rc)) return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)", - strlen(szLogFile) ? szLogFile : "<None>", rc); + strlen(g_szLogFile) ? g_szLogFile : "<None>", rc); /* Call pre-init if we didn't do it already. */ rcExit = vboxServiceLazyPreInit(); diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp index 06d172e4..190418b4 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceAutoMount.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010-2011 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -243,22 +243,32 @@ static int VBoxServiceAutoMountSharedFolder(const char *pszShareName, const char int rc = VINF_SUCCESS; char szAlreadyMountedTo[RTPATH_MAX]; - /* If a Shared Folder already is mounted but not to our desired mount point, - * do an unmount first! */ - if ( VBoxServiceAutoMountShareIsMounted(pszShareName, szAlreadyMountedTo, sizeof(szAlreadyMountedTo)) - && RTStrICmp(pszMountPoint, szAlreadyMountedTo)) + bool fSkip = false; + + /* Already mounted? */ + if (VBoxServiceAutoMountShareIsMounted(pszShareName, szAlreadyMountedTo, sizeof(szAlreadyMountedTo))) { - VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder \"%s\" already mounted to \"%s\", unmounting ...\n", - pszShareName, szAlreadyMountedTo); - rc = VBoxServiceAutoMountUnmount(szAlreadyMountedTo); - if (RT_FAILURE(rc)) - VBoxServiceError("VBoxServiceAutoMountWorker: Failed to unmount \"%s\", %s (%d)!\n", - szAlreadyMountedTo, strerror(errno), errno); + fSkip = true; + /* Do if it not mounted to our desired mount point */ + if (RTStrICmp(pszMountPoint, szAlreadyMountedTo)) + { + VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder \"%s\" already mounted to \"%s\", unmounting ...\n", + pszShareName, szAlreadyMountedTo); + rc = VBoxServiceAutoMountUnmount(szAlreadyMountedTo); + if (RT_FAILURE(rc)) + VBoxServiceError("VBoxServiceAutoMountWorker: Failed to unmount \"%s\", %s (%d)!\n", + szAlreadyMountedTo, strerror(errno), errno); + else + fSkip = false; + } + if (fSkip) + VBoxServiceVerbose(3, "VBoxServiceAutoMountWorker: Shared folder \"%s\" already mounted to \"%s\", skipping\n", + pszShareName, szAlreadyMountedTo); } - if (RT_SUCCESS(rc)) + if (!fSkip && RT_SUCCESS(rc)) rc = VBoxServiceAutoMountPrepareMountPoint(pszMountPoint, pszShareName, pOpts); - if (RT_SUCCESS(rc)) + if (!fSkip && RT_SUCCESS(rc)) { #ifdef RT_OS_SOLARIS char achOptBuf[MAX_MNTOPT_STR] = { '\0', }; diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp index ecfd743c..3d5d0a86 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceBalloon.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp index 60b96671..3f88642a 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceClipboard-os2.cpp @@ -957,8 +957,8 @@ static DECLCALLBACK(int) VBoxServiceClipboardOS2Worker(bool volatile *pfShutdown while (WinGetMsg(g_habWorker, &qmsg, NULLHANDLE, NULLHANDLE, 0)) { if (qmsg.msg != WM_TIMER) - VBoxServiceVerbose(6, "WinGetMsg -> hwnd=%p msg=%#x mp1=%p mp2=%p time=%#x ptl=%d,%d rsrv=%#x\n", - qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2, qmsg.time, qmsg.ptl.x, qmsg.ptl.y, qmsg.reserved); + VBoxServiceVerbose(6, "WinGetMsg -> hwnd=%p msg=%#x mp1=%p mp2=%p time=%#x ptl=%d,%d rsrv=%#x\n", + qmsg.hwnd, qmsg.msg, qmsg.mp1, qmsg.mp2, qmsg.time, qmsg.ptl.x, qmsg.ptl.y, qmsg.reserved); WinDispatchMsg(g_habWorker, &qmsg); } VBoxServiceVerbose(2, "clipboard: Exited PM message loop. *pfShutdown=%RTbool\n", *pfShutdown); diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp index 07778cd7..cd6d5ee6 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2012 Oracle Corporation + * Copyright (C) 2012-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,15 +21,18 @@ *******************************************************************************/ #include <iprt/asm.h> #include <iprt/assert.h> +#include <iprt/env.h> #include <iprt/file.h> #include <iprt/getopt.h> #include <iprt/mem.h> #include <iprt/path.h> +#include <iprt/process.h> #include <iprt/semaphore.h> #include <iprt/thread.h> #include <VBox/VBoxGuestLib.h> #include <VBox/HostServices/GuestControlSvc.h> #include "VBoxServiceInternal.h" +#include "VBoxServiceControl.h" #include "VBoxServiceUtils.h" using namespace guestControl; @@ -41,89 +44,45 @@ using namespace guestControl; static uint32_t g_uControlIntervalMS = 0; /** The semaphore we're blocking our main control thread on. */ static RTSEMEVENTMULTI g_hControlEvent = NIL_RTSEMEVENTMULTI; +/** The VM session ID. Changes whenever the VM is restored or reset. */ +static uint64_t g_idControlSession; /** The guest control service client ID. */ static uint32_t g_uControlSvcClientID = 0; /** How many started guest processes are kept into memory for supplying * information to the host. Default is 256 processes. If 0 is specified, * the maximum number of processes is unlimited. */ static uint32_t g_uControlProcsMaxKept = 256; -#ifdef DEBUG - static bool g_fControlDumpStdErr = false; - static bool g_fControlDumpStdOut = false; -#endif -/** List of active guest control threads (VBOXSERVICECTRLTHREAD). */ -static RTLISTANCHOR g_lstControlThreadsActive; -/** List of inactive guest control threads (VBOXSERVICECTRLTHREAD). */ -static RTLISTANCHOR g_lstControlThreadsInactive; -/** Critical section protecting g_GuestControlExecThreads. */ -static RTCRITSECT g_csControlThreads; -/** List of guest control files (VBOXSERVICECTRLFILE). - **@todo Use a map (later). */ -static RTLISTANCHOR g_lstControlFiles; -/** The internal file count for building our internal file handles. - * Should be enough for now. */ -static uint32_t g_uControlFileCount = 0; - +/** List of guest control session threads (VBOXSERVICECTRLSESSIONTHREAD). + * A guest session thread represents a forked guest session process + * of VBoxService. */ +RTLISTANCHOR g_lstControlSessionThreads; +/** The local session object used for handling all session-related stuff. + * When using the legacy guest control protocol (< 2), this session runs + * under behalf of the VBoxService main process. On newer protocol versions + * each session is a forked version of VBoxService using the appropriate + * user credentials for opening a guest session. These forked sessions then + * are kept in VBOXSERVICECTRLSESSIONTHREAD structures. */ +VBOXSERVICECTRLSESSION g_Session; /******************************************************************************* * Internal Functions * *******************************************************************************/ -/** @todo Shorten "VBoxServiceControl" to "gstsvcCntl". */ -static int VBoxServiceControlReapThreads(void); -static int VBoxServiceControlStartAllowed(bool *pbAllowed); -static int VBoxServiceControlHandleCmdStartProc(uint32_t u32ClientId, uint32_t uNumParms); -static int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, void *pvScratchBuf, size_t cbScratchBuf); -static int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms); -static int VBoxServiceControlHandleFileOpen(uint32_t idClient, uint32_t cParms); -static int VBoxServiceControlHandleFileClose(uint32_t idClient, uint32_t cParms); -static int VBoxServiceControlHandleFileRead(uint32_t idClient, uint32_t cParms); -static int VBoxServiceControlHandleFileWrite(uint32_t idClient, uint32_t cParms, void *pvScratchBuf, size_t cbScratchBuf); -static int VBoxServiceControlHandleFileSeek(uint32_t idClient, uint32_t cParms); -static int VBoxServiceControlHandleFileTell(uint32_t idClient, uint32_t cParms); - -#ifdef DEBUG -static int vboxServiceControlDump(const char *pszFileName, void *pvBuf, size_t cbBuf) -{ - AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); - AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); - - if (!cbBuf) - return VINF_SUCCESS; - - char szFile[RTPATH_MAX]; - - int rc = RTPathTemp(szFile, sizeof(szFile)); - if (RT_SUCCESS(rc)) - rc = RTPathAppend(szFile, sizeof(szFile), pszFileName); - - if (RT_SUCCESS(rc)) - { - VBoxServiceVerbose(4, "Dumping %ld bytes to \"%s\"\n", cbBuf, szFile); - - RTFILE fh; - rc = RTFileOpen(&fh, szFile, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE); - if (RT_SUCCESS(rc)) - { - rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */); - RTFileClose(fh); - } - } - - return rc; -} -#endif +static int gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static void VBoxServiceControlShutdown(void); /** @copydoc VBOXSERVICE::pfnPreInit */ static DECLCALLBACK(int) VBoxServiceControlPreInit(void) { + int rc; #ifdef VBOX_WITH_GUEST_PROPS /* * Read the service options from the VM's guest properties. * Note that these options can be overridden by the command line options later. */ uint32_t uGuestPropSvcClientID; - int rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID); + rc = VbglR3GuestPropConnect(&uGuestPropSvcClientID); if (RT_FAILURE(rc)) { if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */ @@ -132,23 +91,27 @@ static DECLCALLBACK(int) VBoxServiceControlPreInit(void) rc = VINF_SUCCESS; } else - VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc); + VBoxServiceError("Failed to connect to the guest property service, rc=%Rrc\n", rc); } else { - rc = VBoxServiceReadPropUInt32(uGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--control-procs-max-kept", - &g_uControlProcsMaxKept, 0, UINT32_MAX - 1); - VbglR3GuestPropDisconnect(uGuestPropSvcClientID); } if (rc == VERR_NOT_FOUND) /* If a value is not found, don't be sad! */ rc = VINF_SUCCESS; - return rc; #else /* Nothing to do here yet. */ - return VINF_SUCCESS; + rc = VINF_SUCCESS; #endif + + if (RT_SUCCESS(rc)) + { + /* Init session object. */ + rc = GstCntlSessionInit(&g_Session, 0 /* Flags */); + } + + return rc; } @@ -161,18 +124,15 @@ static DECLCALLBACK(int) VBoxServiceControlOption(const char **ppszShort, int ar else if (!strcmp(argv[*pi], "--control-interval")) rc = VBoxServiceArgUInt32(argc, argv, "", pi, &g_uControlIntervalMS, 1, UINT32_MAX - 1); - else if (!strcmp(argv[*pi], "--control-procs-max-kept")) - rc = VBoxServiceArgUInt32(argc, argv, "", pi, - &g_uControlProcsMaxKept, 0, UINT32_MAX - 1); #ifdef DEBUG - else if (!strcmp(argv[*pi], "--control-dump-stderr")) + else if (!strcmp(argv[*pi], "--control-dump-stdout")) { - g_fControlDumpStdErr = true; + g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT; rc = 0; /* Flag this command as parsed. */ } - else if (!strcmp(argv[*pi], "--control-dump-stdout")) + else if (!strcmp(argv[*pi], "--control-dump-stderr")) { - g_fControlDumpStdOut = true; + g_Session.uFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR; rc = 0; /* Flag this command as parsed. */ } #endif @@ -193,19 +153,18 @@ static DECLCALLBACK(int) VBoxServiceControlInit(void) int rc = RTSemEventMultiCreate(&g_hControlEvent); AssertRCReturn(rc, rc); - rc = VbglR3GuestCtrlConnect(&g_uControlSvcClientID); + VbglR3GetSessionId(&g_idControlSession); + /* The status code is ignored as this information is not available with VBox < 3.2.10. */ + + if (RT_SUCCESS(rc)) + rc = VbglR3GuestCtrlConnect(&g_uControlSvcClientID); if (RT_SUCCESS(rc)) { - VBoxServiceVerbose(3, "Service client ID: %#x\n", g_uControlSvcClientID); - - /* Init lists. */ - RTListInit(&g_lstControlThreadsActive); - RTListInit(&g_lstControlThreadsInactive); - RTListInit(&g_lstControlFiles); + VBoxServiceVerbose(3, "Guest control service client ID=%RU32\n", + g_uControlSvcClientID); - /* Init critical section for protecting the thread lists. */ - rc = RTCritSectInit(&g_csControlThreads); - AssertRC(rc); + /* Init session thread list. */ + RTListInit(&g_lstControlSessionThreads); } else { @@ -244,75 +203,91 @@ DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown) uint8_t *pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf); AssertPtrReturn(pvScratchBuf, VERR_NO_MEMORY); - /* - * Execution loop. - * - * @todo - */ + VBGLR3GUESTCTRLCMDCTX ctxHost = { g_uControlSvcClientID }; + /* Set default protocol version to 1. */ + ctxHost.uProtocol = 1; + for (;;) { VBoxServiceVerbose(3, "Waiting for host msg ...\n"); uint32_t uMsg = 0; uint32_t cParms = 0; - rc = VbglR3GuestCtrlWaitForHostMsg(g_uControlSvcClientID, &uMsg, &cParms); + rc = VbglR3GuestCtrlMsgWaitFor(g_uControlSvcClientID, &uMsg, &cParms); if (rc == VERR_TOO_MUCH_DATA) { +#ifdef DEBUG VBoxServiceVerbose(4, "Message requires %ld parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms); +#endif rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */ } else if (RT_FAILURE(rc)) VBoxServiceVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */ if (RT_SUCCESS(rc)) { - VBoxServiceVerbose(3, "Msg=%u (%u parms) retrieved\n", uMsg, cParms); - switch (uMsg) - { - case HOST_CANCEL_PENDING_WAITS: - VBoxServiceVerbose(3, "Host asked us to quit ...\n"); - break; - - case HOST_EXEC_CMD: - rc = VBoxServiceControlHandleCmdStartProc(g_uControlSvcClientID, cParms); - break; - - case HOST_EXEC_SET_INPUT: - rc = VBoxServiceControlHandleCmdSetInput(g_uControlSvcClientID, cParms, - pvScratchBuf, cbScratchBuf); - break; - - case HOST_EXEC_GET_OUTPUT: - rc = VBoxServiceControlHandleCmdGetOutput(g_uControlSvcClientID, cParms); - break; + VBoxServiceVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms); - case HOST_FILE_OPEN: - rc = VBoxServiceControlHandleFileOpen(g_uControlSvcClientID, cParms); - break; + /* Set number of parameters for current host context. */ + ctxHost.uNumParms = cParms; - case HOST_FILE_CLOSE: - rc = VBoxServiceControlHandleFileClose(g_uControlSvcClientID, cParms); - break; + /* Check for VM session change. */ + uint64_t idNewSession = g_idControlSession; + int rc2 = VbglR3GetSessionId(&idNewSession); + if ( RT_SUCCESS(rc2) + && (idNewSession != g_idControlSession)) + { + VBoxServiceVerbose(1, "The VM session ID changed\n"); + g_idControlSession = idNewSession; - case HOST_FILE_READ: - rc = VBoxServiceControlHandleFileRead(g_uControlSvcClientID, cParms); - break; + /* Close all opened guest sessions -- all context IDs, sessions etc. + * are now invalid. */ + rc2 = GstCntlSessionClose(&g_Session); + AssertRC(rc2); + } - case HOST_FILE_WRITE: - rc = VBoxServiceControlHandleFileWrite(g_uControlSvcClientID, cParms, - pvScratchBuf, cbScratchBuf); + switch (uMsg) + { + case HOST_CANCEL_PENDING_WAITS: + VBoxServiceVerbose(1, "We were asked to quit ...\n"); break; - case HOST_FILE_SEEK: - rc = VBoxServiceControlHandleFileSeek(g_uControlSvcClientID, cParms); + case HOST_SESSION_CREATE: + rc = gstcntlHandleSessionOpen(&ctxHost); break; - case HOST_FILE_TELL: - rc = VBoxServiceControlHandleFileTell(g_uControlSvcClientID, cParms); + case HOST_SESSION_CLOSE: + rc = gstcntlHandleSessionClose(&ctxHost); break; default: - VBoxServiceVerbose(3, "Unsupported message from host! Msg=%u\n", uMsg); - /* Don't terminate here; just wait for the next message. */ + { + /* + * Protocol v1 did not have support for (dedicated) + * guest sessions, so all actions need to be performed + * under behalf of VBoxService's main executable. + * + * The global session object then acts as a host for all + * started guest processes which bring all their + * credentials with them with the actual guest process + * execution call. + */ + if (ctxHost.uProtocol == 1) + { + rc = GstCntlSessionHandler(&g_Session, uMsg, &ctxHost, + pvScratchBuf, cbScratchBuf, pfShutdown); + } + else + { + /* + * ... on newer protocols handling all other commands is + * up to the guest session fork of VBoxService, so just + * skip all not wanted messages here. + */ + rc = VbglR3GuestCtrlMsgSkip(g_uControlSvcClientID); + VBoxServiceVerbose(3, "Skipping uMsg=%RU32, cParms=%RU32, rc=%Rrc\n", + uMsg, cParms, rc); + } break; + } } } @@ -320,7 +295,6 @@ DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown) if ( *pfShutdown || (RT_SUCCESS(rc) && uMsg == HOST_CANCEL_PENDING_WAITS)) { - rc = VINF_SUCCESS; break; } @@ -328,630 +302,96 @@ DECLCALLBACK(int) VBoxServiceControlWorker(bool volatile *pfShutdown) RTThreadYield(); } + VBoxServiceVerbose(0, "Guest control service stopped\n"); + /* Delete scratch buffer. */ if (pvScratchBuf) RTMemFree(pvScratchBuf); + VBoxServiceVerbose(0, "Guest control worker returned with rc=%Rrc\n", rc); return rc; } -/** - * Handles starting processes on the guest. - * - * @returns IPRT status code. - * @param uClientID The HGCM client session ID. - * @param cParms The number of parameters the host is offering. - */ -static int VBoxServiceControlHandleCmdStartProc(uint32_t uClientID, uint32_t cParms) -{ - uint32_t uContextID = 0; - - int rc; - bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */ - if (cParms == 11) - { - VBOXSERVICECTRLPROCESS proc; - RT_ZERO(proc); - - /* Initialize maximum environment block size -- needed as input - * parameter to retrieve the stuff from the host. On output this then - * will contain the actual block size. */ - proc.cbEnv = sizeof(proc.szEnv); - - rc = VbglR3GuestCtrlExecGetHostCmdExec(uClientID, - cParms, - &uContextID, - /* Command */ - proc.szCmd, sizeof(proc.szCmd), - /* Flags */ - &proc.uFlags, - /* Arguments */ - proc.szArgs, sizeof(proc.szArgs), &proc.uNumArgs, - /* Environment */ - proc.szEnv, &proc.cbEnv, &proc.uNumEnvVars, - /* Credentials */ - proc.szUser, sizeof(proc.szUser), - proc.szPassword, sizeof(proc.szPassword), - /* Timelimit */ - &proc.uTimeLimitMS); - if (RT_SUCCESS(rc)) - { - VBoxServiceVerbose(3, "Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, szUser=%s, szPassword=%s, uTimeout=%u\n", - proc.szCmd, proc.uFlags, - proc.uNumArgs ? proc.szArgs : "<None>", - proc.uNumEnvVars ? proc.szEnv : "<None>", - proc.szUser, -#ifdef DEBUG - proc.szPassword, -#else - "XXX", /* Never show passwords in release mode. */ -#endif - proc.uTimeLimitMS); - - rc = VBoxServiceControlReapThreads(); - if (RT_FAILURE(rc)) - VBoxServiceError("Reaping stopped processes failed with rc=%Rrc\n", rc); - /* Keep going. */ - - rc = VBoxServiceControlStartAllowed(&fStartAllowed); - if (RT_SUCCESS(rc)) - { - if (fStartAllowed) - { - rc = VBoxServiceControlThreadStart(uContextID, &proc); - } - else - rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */ - } - } - } - else - rc = VERR_INVALID_PARAMETER; /* Incorrect number of parameters. */ - - /* In case of an error we need to notify the host to not wait forever for our response. */ - if (RT_FAILURE(rc)) - { - VBoxServiceError("Starting process failed with rc=%Rrc\n", rc); - - /* - * Note: The context ID can be 0 because we mabye weren't able to fetch the command - * from the host. The host in case has to deal with that! - */ - int rc2 = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID /* Might be 0 */, 0 /* PID, invalid */, - PROC_STS_ERROR, rc, - NULL /* pvData */, 0 /* cbData */); - if (RT_FAILURE(rc2)) - { - VBoxServiceError("Error sending start process status to host, rc=%Rrc\n", rc2); - if (RT_SUCCESS(rc)) - rc = rc2; - } - } - - return rc; -} - - -/** - * Gets output from stdout/stderr of a specified guest process. - * - * @return IPRT status code. - * @param uPID PID of process to retrieve the output from. - * @param uHandleId Stream ID (stdout = 0, stderr = 2) to get the output from. - * @param uTimeout Timeout (in ms) to wait for output becoming available. - * @param pvBuf Pointer to a pre-allocated buffer to store the output. - * @param cbBuf Size (in bytes) of the pre-allocated buffer. - * @param pcbRead Pointer to number of bytes read. Optional. - */ -int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uCID, - uint32_t uHandleId, uint32_t uTimeout, - void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead) +static int gstcntlHandleSessionOpen(PVBGLR3GUESTCTRLCMDCTX pHostCtx) { - AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); - AssertReturn(cbBuf, VERR_INVALID_PARAMETER); - /* pcbRead is optional. */ - - int rc = VINF_SUCCESS; - VBOXSERVICECTRLREQUESTTYPE reqType; - switch (uHandleId) - { - case OUTPUT_HANDLE_ID_STDERR: - reqType = VBOXSERVICECTRLREQUEST_STDERR_READ; - break; - - case OUTPUT_HANDLE_ID_STDOUT: - case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED: - reqType = VBOXSERVICECTRLREQUEST_STDOUT_READ; - break; - - default: - rc = VERR_INVALID_PARAMETER; - break; - } - - PVBOXSERVICECTRLREQUEST pRequest; - if (RT_SUCCESS(rc)) - { - rc = VBoxServiceControlThreadRequestAllocEx(&pRequest, reqType, - pvBuf, cbBuf, uCID); - if (RT_SUCCESS(rc)) - rc = VBoxServiceControlThreadPerform(uPID, pRequest); - - if (RT_SUCCESS(rc)) - { - if (pcbRead) - *pcbRead = pRequest->cbData; - } - - VBoxServiceControlThreadRequestFree(pRequest); - } - - return rc; -} - - -/** - * Sets the specified guest thread to a certain list. - * - * @return IPRT status code. - * @param enmList List to move thread to. - * @param pThread Thread to set inactive. - */ -int VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLISTTYPE enmList, - PVBOXSERVICECTRLTHREAD pThread) -{ - AssertReturn(enmList > VBOXSERVICECTRLTHREADLIST_UNKNOWN, VERR_INVALID_PARAMETER); - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - - int rc = RTCritSectEnter(&g_csControlThreads); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + VBOXSERVICECTRLSESSIONSTARTUPINFO ssInfo = { 0 }; + int rc = VbglR3GuestCtrlSessionGetOpen(pHostCtx, + &ssInfo.uProtocol, + ssInfo.szUser, sizeof(ssInfo.szUser), + ssInfo.szPassword, sizeof(ssInfo.szPassword), + ssInfo.szDomain, sizeof(ssInfo.szDomain), + &ssInfo.uFlags, &ssInfo.uSessionID); if (RT_SUCCESS(rc)) { - VBoxServiceVerbose(3, "Setting thread (PID %u) to list %d\n", - pThread->uPID, enmList); - - PRTLISTANCHOR pAnchor = NULL; - switch (enmList) - { - case VBOXSERVICECTRLTHREADLIST_STOPPED: - pAnchor = &g_lstControlThreadsInactive; - break; - - case VBOXSERVICECTRLTHREADLIST_RUNNING: - pAnchor = &g_lstControlThreadsActive; - break; - - default: - AssertMsgFailed(("Unknown list type: %u", enmList)); - break; - } - - if (!pAnchor) - rc = VERR_INVALID_PARAMETER; - - if (RT_SUCCESS(rc)) - { - if (pThread->pAnchor != NULL) - { - /* If thread was assigned to a list before, - * remove the thread from the old list first. */ - /* rc = */ RTListNodeRemove(&pThread->Node); - } + /* The session open call has the protocol version the host + * wants to use. So update the current protocol version with the one the + * host wants to use in subsequent calls. */ + pHostCtx->uProtocol = ssInfo.uProtocol; + VBoxServiceVerbose(3, "Client ID=%RU32 now is using protocol %RU32\n", + pHostCtx->uClientID, pHostCtx->uProtocol); - /* Add thread to desired list. */ - /* rc = */ RTListAppend(pAnchor, &pThread->Node); - pThread->pAnchor = pAnchor; - } - - int rc2 = RTCritSectLeave(&g_csControlThreads); - if (RT_SUCCESS(rc)) - rc = rc2; + rc = GstCntlSessionThreadCreate(&g_lstControlSessionThreads, + &ssInfo, NULL /* ppSessionThread */); } - return VINF_SUCCESS; -} - - -/** - * Injects input to a specified running process. - * - * @return IPRT status code. - * @param uPID PID of process to set the input for. - * @param fPendingClose Flag indicating whether this is the last input block sent to the process. - * @param pvBuf Pointer to a buffer containing the actual input data. - * @param cbBuf Size (in bytes) of the input buffer data. - * @param pcbWritten Pointer to number of bytes written to the process. Optional. - */ -int VBoxServiceControlSetInput(uint32_t uPID, uint32_t uCID, - bool fPendingClose, - void *pvBuf, uint32_t cbBuf, - uint32_t *pcbWritten) -{ - /* pvBuf is optional. */ - /* cbBuf is optional. */ - /* pcbWritten is optional. */ - - PVBOXSERVICECTRLREQUEST pRequest; - int rc = VBoxServiceControlThreadRequestAllocEx(&pRequest, - fPendingClose - ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF - : VBOXSERVICECTRLREQUEST_STDIN_WRITE, - pvBuf, cbBuf, uCID); - if (RT_SUCCESS(rc)) - { - rc = VBoxServiceControlThreadPerform(uPID, pRequest); - if (RT_SUCCESS(rc)) - { - if (pcbWritten) - *pcbWritten = pRequest->cbData; - } - - VBoxServiceControlThreadRequestFree(pRequest); - } - - return rc; -} - - -/** - * Handles input for a started process by copying the received data into its - * stdin pipe. - * - * @returns IPRT status code. - * @param idClient The HGCM client session ID. - * @param cParms The number of parameters the host is - * offering. - * @param pvScratchBuf The scratch buffer. - * @param cbScratchBuf The scratch buffer size for retrieving the input data. - */ -static int VBoxServiceControlHandleCmdSetInput(uint32_t idClient, uint32_t cParms, - void *pvScratchBuf, size_t cbScratchBuf) -{ - AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER); - AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); - - uint32_t uContextID; - uint32_t uPID; - uint32_t uFlags; - uint32_t cbSize; - - uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */ - uint32_t cbWritten = 0; /* Number of bytes written to the guest. */ - - /* - * Ask the host for the input data. - */ - int rc = VbglR3GuestCtrlExecGetHostCmdInput(idClient, cParms, - &uContextID, &uPID, &uFlags, - pvScratchBuf, cbScratchBuf, &cbSize); if (RT_FAILURE(rc)) { - VBoxServiceError("[PID %u]: Failed to retrieve exec input command! Error: %Rrc\n", - uPID, rc); - } - else if (cbSize > cbScratchBuf) - { - VBoxServiceError("[PID %u]: Too much input received! cbSize=%u, cbScratchBuf=%u\n", - uPID, cbSize, cbScratchBuf); - rc = VERR_INVALID_PARAMETER; - } - else - { - /* - * Is this the last input block we need to deliver? Then let the pipe know ... - */ - bool fPendingClose = false; - if (uFlags & INPUT_FLAG_EOF) - { - fPendingClose = true; - VBoxServiceVerbose(4, "[PID %u]: Got last input block of size %u ...\n", - uPID, cbSize); - } - - rc = VBoxServiceControlSetInput(uPID, uContextID, fPendingClose, pvScratchBuf, - cbSize, &cbWritten); - VBoxServiceVerbose(4, "[PID %u]: Written input, CID=%u, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n", - uPID, uContextID, rc, uFlags, fPendingClose, cbSize, cbWritten); - if (RT_SUCCESS(rc)) - { - uStatus = INPUT_STS_WRITTEN; - uFlags = 0; /* No flags at the moment. */ - } - else - { - if (rc == VERR_BAD_PIPE) - uStatus = INPUT_STS_TERMINATED; - else if (rc == VERR_BUFFER_OVERFLOW) - uStatus = INPUT_STS_OVERFLOW; - } - } - - /* - * If there was an error and we did not set the host status - * yet, then do it now. - */ - if ( RT_FAILURE(rc) - && uStatus == INPUT_STS_UNDEFINED) - { - uStatus = INPUT_STS_ERROR; - uFlags = rc; + /* Report back on failure. On success this will be done + * by the forked session thread. */ + int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx, + GUEST_SESSION_NOTIFYTYPE_ERROR, rc /* uint32_t vs. int */); + if (RT_FAILURE(rc2)) + VBoxServiceError("Reporting session error status on open failed with rc=%Rrc\n", rc2); } - Assert(uStatus > INPUT_STS_UNDEFINED); - - VBoxServiceVerbose(3, "[PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n", - uPID, uContextID, uStatus, uFlags, cbWritten); - /* Note: Since the context ID is unique the request *has* to be completed here, - * regardless whether we got data or not! Otherwise the progress object - * on the host never will get completed! */ - rc = VbglR3GuestCtrlExecReportStatusIn(idClient, uContextID, uPID, - uStatus, uFlags, (uint32_t)cbWritten); - - if (RT_FAILURE(rc)) - VBoxServiceError("[PID %u]: Failed to report input status! Error: %Rrc\n", - uPID, rc); + VBoxServiceVerbose(3, "Opening a new guest session returned rc=%Rrc\n", rc); return rc; } -static PVBOXSERVICECTRLFILE VBoxControlGetFile(uint32_t uHandle) +static int gstcntlHandleSessionClose(PVBGLR3GUESTCTRLCMDCTX pHostCtx) { - PVBOXSERVICECTRLFILE pFileCur = NULL; - /** @todo Use a map later! */ - RTListForEach(&g_lstControlFiles, pFileCur, VBOXSERVICECTRLFILE, Node) - { - if (pFileCur->uHandle == uHandle) - return pFileCur; - } - - return NULL; -} + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); - -static int VBoxServiceControlHandleFileOpen(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - - char szFile[RTPATH_MAX]; - char szOpenMode[64]; - char szDisposition[64]; - uint32_t uCreationMode; - uint64_t uOffset; - - int rc = VbglR3GuestCtrlFileGetHostCmdOpen(idClient, cParms, &uContextID, - /* File to open. */ - szFile, sizeof(szFile), - /* Open mode. */ - szOpenMode, sizeof(szOpenMode), - /* Disposition. */ - szDisposition, sizeof(szDisposition), - /* Creation mode. */ - &uCreationMode, - /* Offset. */ - &uOffset); + uint32_t uSessionID, uFlags; + int rc = VbglR3GuestCtrlSessionGetClose(pHostCtx, &uFlags, &uSessionID); if (RT_SUCCESS(rc)) { - PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAlloc(sizeof(VBOXSERVICECTRLFILE)); - if (!pFile) - return VERR_NO_MEMORY; - - if (!RTStrPrintf(pFile->szName, sizeof(pFile->szName), "%s", szFile)) - rc = VERR_BUFFER_UNDERFLOW; + rc = VERR_NOT_FOUND; - if (RT_SUCCESS(rc)) + PVBOXSERVICECTRLSESSIONTHREAD pThread; + RTListForEach(&g_lstControlSessionThreads, pThread, VBOXSERVICECTRLSESSIONTHREAD, Node) { - uint64_t fFlags = RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE; /** @todo Modes! */ - rc = RTFileOpen(&pFile->hFile, pFile->szName, fFlags); - if ( RT_SUCCESS(rc) - && uOffset) + if (pThread->StartupInfo.uSessionID == uSessionID) { - /* Seeking is optional. */ - int rc2 = RTFileSeek(pFile->hFile, (int64_t)uOffset, RTFILE_SEEK_BEGIN, NULL /* Current offset */); - if (RT_FAILURE(rc2)) - VBoxServiceVerbose(3, "[File %s]: Seeking to offset %RU64 failed; rc=%Rrc\n", - pFile->szName, uOffset, rc); + rc = GstCntlSessionThreadDestroy(pThread, uFlags); + break; } - else - VBoxServiceVerbose(3, "[File %s]: Opening failed; rc=%Rrc\n", - pFile->szName, rc); - } - - uint32_t uHandle = 0; - if (RT_SUCCESS(rc)) - { - VBoxServiceVerbose(3, "[File %s]: Opened.\n", pFile->szName); - - uHandle = g_uControlFileCount++; - pFile->uHandle = uHandle; - /* rc = */ RTListAppend(&g_lstControlFiles, &pFile->Node); } - +#if 0 if (RT_FAILURE(rc)) - RTMemFree(pFile); - - /* Report back in any case. */ - int rc2 = VbglR3GuestCtrlFileNotify(idClient, uContextID, uHandle, - GUESTFILENOTIFYTYPE_OPEN, &rc, sizeof(rc)); - if (RT_FAILURE(rc2)) - VBoxServiceError("[File %s]: Failed to report open status, rc=%Rrc\n", - szFile, rc2); - if (RT_SUCCESS(rc)) - rc = rc2; - } - return rc; -} - - -static int VBoxServiceControlHandleFileClose(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - uint32_t uHandle; - - int rc = VbglR3GuestCtrlFileGetHostCmdClose(idClient, cParms, &uContextID, - /* File handle to close. */ - &uHandle); - if (RT_SUCCESS(rc)) - { - PVBOXSERVICECTRLFILE pFile = VBoxControlGetFile(uHandle); - if (pFile) - { - rc = RTFileClose(pFile->hFile); - } - else - rc = VERR_NOT_FOUND; - - /* Report back in any case. */ - int rc2 = VbglR3GuestCtrlFileNotify(idClient, uContextID, uHandle, - GUESTFILENOTIFYTYPE_CLOSE, &rc, sizeof(rc)); - if (RT_FAILURE(rc2)) - VBoxServiceError("Failed to report close status, rc=%Rrc\n", rc2); - if (RT_SUCCESS(rc)) - rc = rc2; - } - return rc; -} - - -static int VBoxServiceControlHandleFileRead(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - uint32_t uHandle; - uint32_t cbToRead; - - int rc = VbglR3GuestCtrlFileGetHostCmdRead(idClient, cParms, &uContextID, - &uHandle, &cbToRead); - if (RT_SUCCESS(rc)) - { - - } - return rc; -} - - -static int VBoxServiceControlHandleFileWrite(uint32_t idClient, uint32_t cParms, - void *pvScratchBuf, size_t cbScratchBuf) -{ - AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER); - AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); - - uint32_t uContextID; - uint32_t uHandle; - uint32_t cbToWrite; - - int rc = VbglR3GuestCtrlFileGetHostCmdWrite(idClient, cParms, &uContextID, - &uHandle, pvScratchBuf, cbScratchBuf, - &cbToWrite); - if (RT_SUCCESS(rc)) - { - - } - return rc; -} - - -static int VBoxServiceControlHandleFileSeek(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - uint32_t uHandle; - uint32_t uSeekMethod; - uint64_t uOffset; /* Will be converted to int64_t. */ - - int rc = VbglR3GuestCtrlFileGetHostCmdSeek(idClient, cParms, &uContextID, - &uHandle, &uSeekMethod, &uOffset); - if (RT_SUCCESS(rc)) - { - - } - return rc; -} - - -static int VBoxServiceControlHandleFileTell(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - uint32_t uHandle; - - int rc = VbglR3GuestCtrlFileGetHostCmdTell(idClient, cParms, &uContextID, - &uHandle); - if (RT_SUCCESS(rc)) - { - - } - return rc; -} - - -/** - * Handles the guest control output command. - * - * @return IPRT status code. - * @param idClient The HGCM client session ID. - * @param cParms The number of parameters the host is offering. - */ -static int VBoxServiceControlHandleCmdGetOutput(uint32_t idClient, uint32_t cParms) -{ - uint32_t uContextID; - uint32_t uPID; - uint32_t uHandleID; - uint32_t uFlags; - - int rc = VbglR3GuestCtrlExecGetHostCmdOutput(idClient, cParms, - &uContextID, &uPID, &uHandleID, &uFlags); - if (RT_SUCCESS(rc)) - { - uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K); - if (pBuf) { - uint32_t cbRead = 0; - rc = VBoxServiceControlExecGetOutput(uPID, uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */, - pBuf, _64K /* cbSize */, &cbRead); - VBoxServiceVerbose(3, "[PID %u]: Got output, rc=%Rrc, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n", - uPID, rc, uContextID, cbRead, uHandleID, uFlags); - -#ifdef DEBUG - if ( g_fControlDumpStdErr - && uHandleID == OUTPUT_HANDLE_ID_STDERR) + /* Report back on failure. On success this will be done + * by the forked session thread. */ + int rc2 = VbglR3GuestCtrlSessionNotify(pHostCtx, + GUEST_SESSION_NOTIFYTYPE_ERROR, rc); + if (RT_FAILURE(rc2)) { - char szPID[RTPATH_MAX]; - if (!RTStrPrintf(szPID, sizeof(szPID), "VBoxService_PID%u_StdOut.txt", uPID)) - rc = VERR_BUFFER_UNDERFLOW; + VBoxServiceError("Reporting session error status on close failed with rc=%Rrc\n", rc2); if (RT_SUCCESS(rc)) - rc = vboxServiceControlDump(szPID, pBuf, cbRead); + rc = rc2; } - else if ( g_fControlDumpStdOut - && ( uHandleID == OUTPUT_HANDLE_ID_STDOUT - || uHandleID == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED)) - { - char szPID[RTPATH_MAX]; - if (!RTStrPrintf(szPID, sizeof(szPID), "VBoxService_PID%u_StdOut.txt", uPID)) - rc = VERR_BUFFER_UNDERFLOW; - if (RT_SUCCESS(rc)) - rc = vboxServiceControlDump(szPID, pBuf, cbRead); - AssertRC(rc); - } -#endif - /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary - * data which the host needs to work with -- so just pass through all data unfiltered! */ - - /* Note: Since the context ID is unique the request *has* to be completed here, - * regardless whether we got data or not! Otherwise the progress object - * on the host never will get completed! */ - int rc2 = VbglR3GuestCtrlExecSendOut(idClient, uContextID, uPID, uHandleID, uFlags, - pBuf, cbRead); - if (RT_SUCCESS(rc)) - rc = rc2; - else if (rc == VERR_NOT_FOUND) /* It's not critical if guest process (PID) is not found. */ - rc = VINF_SUCCESS; - - RTMemFree(pBuf); } - else - rc = VERR_NO_MEMORY; +#endif + VBoxServiceVerbose(2, "Closing guest session %RU32 returned rc=%Rrc\n", + uSessionID, rc); } - - if (RT_FAILURE(rc)) - VBoxServiceError("[PID %u]: Error handling output command! Error: %Rrc\n", - uPID, rc); + else + VBoxServiceError("Closing guest session %RU32 failed with rc=%Rrc\n", + uSessionID, rc); return rc; } @@ -967,8 +407,8 @@ static DECLCALLBACK(void) VBoxServiceControlStop(void) RTSemEventMultiSignal(g_hControlEvent); /* - * Ask the host service to cancel all pending requests so that we can - * shutdown properly here. + * Ask the host service to cancel all pending requests for the main + * control thread so that we can shutdown properly here. */ if (g_uControlSvcClientID) { @@ -983,124 +423,20 @@ static DECLCALLBACK(void) VBoxServiceControlStop(void) /** - * Reaps all inactive guest process threads. - * - * @return IPRT status code. - */ -static int VBoxServiceControlReapThreads(void) -{ - int rc = RTCritSectEnter(&g_csControlThreads); - if (RT_SUCCESS(rc)) - { - PVBOXSERVICECTRLTHREAD pThread = - RTListGetFirst(&g_lstControlThreadsInactive, VBOXSERVICECTRLTHREAD, Node); - while (pThread) - { - PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node); - bool fLast = RTListNodeIsLast(&g_lstControlThreadsInactive, &pThread->Node); - int rc2 = VBoxServiceControlThreadWait(pThread, 30 * 1000 /* 30 seconds max. */, - NULL /* rc */); - if (RT_SUCCESS(rc2)) - { - RTListNodeRemove(&pThread->Node); - - rc2 = VBoxServiceControlThreadFree(pThread); - if (RT_FAILURE(rc2)) - { - VBoxServiceError("Freeing guest process thread failed with rc=%Rrc\n", rc2); - if (RT_SUCCESS(rc)) /* Keep original failure. */ - rc = rc2; - } - } - else - VBoxServiceError("Waiting on guest process thread failed with rc=%Rrc\n", rc2); - /* Keep going. */ - - if (fLast) - break; - - pThread = pNext; - } - - int rc2 = RTCritSectLeave(&g_csControlThreads); - if (RT_SUCCESS(rc)) - rc = rc2; - } - - VBoxServiceVerbose(4, "Reaping threads returned with rc=%Rrc\n", rc); - return rc; -} - - -/** * Destroys all guest process threads which are still active. */ static void VBoxServiceControlShutdown(void) { VBoxServiceVerbose(2, "Shutting down ...\n"); - /* Signal all threads in the active list that we want to shutdown. */ - PVBOXSERVICECTRLTHREAD pThread; - RTListForEach(&g_lstControlThreadsActive, pThread, VBOXSERVICECTRLTHREAD, Node) - VBoxServiceControlThreadStop(pThread); - - /* Wait for all active threads to shutdown and destroy the active thread list. */ - pThread = RTListGetFirst(&g_lstControlThreadsActive, VBOXSERVICECTRLTHREAD, Node); - while (pThread) - { - PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node); - bool fLast = RTListNodeIsLast(&g_lstControlThreadsActive, &pThread->Node); - - int rc2 = VBoxServiceControlThreadWait(pThread, - 30 * 1000 /* Wait 30 seconds max. */, - NULL /* rc */); - if (RT_FAILURE(rc2)) - VBoxServiceError("Guest process thread failed to stop; rc=%Rrc\n", rc2); - - if (fLast) - break; - - pThread = pNext; - } - - int rc2 = VBoxServiceControlReapThreads(); + int rc2 = GstCntlSessionThreadDestroyAll(&g_lstControlSessionThreads, + 0 /* Flags */); if (RT_FAILURE(rc2)) - VBoxServiceError("Reaping inactive threads failed with rc=%Rrc\n", rc2); - - AssertMsg(RTListIsEmpty(&g_lstControlThreadsActive), - ("Guest process active thread list still contains entries when it should not\n")); - AssertMsg(RTListIsEmpty(&g_lstControlThreadsInactive), - ("Guest process inactive thread list still contains entries when it should not\n")); - - /* Destroy critical section. */ - RTCritSectDelete(&g_csControlThreads); - - /* Close all left guest files. */ - PVBOXSERVICECTRLFILE pFile; - pFile = RTListGetFirst(&g_lstControlFiles, VBOXSERVICECTRLFILE, Node); - while (pFile) - { - PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node); - bool fLast = RTListNodeIsLast(&g_lstControlFiles, &pFile->Node); - - rc2 = RTFileClose(pFile->hFile); - if (RT_FAILURE(rc2)) - { - VBoxServiceError("Unable to close file \"%s\"; rc=%Rrc\n", - pFile->szName, rc2); - /* Keep going. */ - } - - RTListNodeRemove(&pFile->Node); - - if (fLast) - break; + VBoxServiceError("Closing session threads failed with rc=%Rrc\n", rc2); - pFile = pNext; - } - - AssertMsg(RTListIsEmpty(&g_lstControlFiles), - ("Guest file list still contains entries when it should not\n")); + rc2 = GstCntlSessionClose(&g_Session); + if (RT_FAILURE(rc2)) + VBoxServiceError("Closing session failed with rc=%Rrc\n", rc2); VBoxServiceVerbose(2, "Shutting down complete\n"); } @@ -1127,152 +463,6 @@ static DECLCALLBACK(void) VBoxServiceControlTerm(void) /** - * Determines whether starting a new guest process according to the - * maximum number of concurrent guest processes defined is allowed or not. - * - * @return IPRT status code. - * @param pbAllowed True if starting (another) guest process - * is allowed, false if not. - */ -static int VBoxServiceControlStartAllowed(bool *pbAllowed) -{ - AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER); - - int rc = RTCritSectEnter(&g_csControlThreads); - if (RT_SUCCESS(rc)) - { - /* - * Check if we're respecting our memory policy by checking - * how many guest processes are started and served already. - */ - bool fLimitReached = false; - if (g_uControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */ - { - uint32_t uProcsRunning = 0; - PVBOXSERVICECTRLTHREAD pThread; - RTListForEach(&g_lstControlThreadsActive, pThread, VBOXSERVICECTRLTHREAD, Node) - uProcsRunning++; - - VBoxServiceVerbose(3, "Maximum served guest processes set to %u, running=%u\n", - g_uControlProcsMaxKept, uProcsRunning); - - int32_t iProcsLeft = (g_uControlProcsMaxKept - uProcsRunning - 1); - if (iProcsLeft < 0) - { - VBoxServiceVerbose(3, "Maximum running guest processes reached (%u)\n", - g_uControlProcsMaxKept); - fLimitReached = true; - } - } - - *pbAllowed = !fLimitReached; - - int rc2 = RTCritSectLeave(&g_csControlThreads); - if (RT_SUCCESS(rc)) - rc = rc2; - } - - return rc; -} - - -/** - * Finds a (formerly) started process given by its PID and locks it. Must be unlocked - * by the caller with VBoxServiceControlThreadUnlock(). - * - * @return PVBOXSERVICECTRLTHREAD Process structure if found, otherwise NULL. - * @param uPID PID to search for. - */ -PVBOXSERVICECTRLTHREAD VBoxServiceControlLockThread(uint32_t uPID) -{ - PVBOXSERVICECTRLTHREAD pThread = NULL; - int rc = RTCritSectEnter(&g_csControlThreads); - if (RT_SUCCESS(rc)) - { - PVBOXSERVICECTRLTHREAD pThreadCur; - RTListForEach(&g_lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLTHREAD, Node) - { - if (pThreadCur->uPID == uPID) - { - rc = RTCritSectEnter(&pThreadCur->CritSect); - if (RT_SUCCESS(rc)) - pThread = pThreadCur; - break; - } - } - - int rc2 = RTCritSectLeave(&g_csControlThreads); - if (RT_SUCCESS(rc)) - rc = rc2; - } - - return pThread; -} - - -/** - * Unlocks a previously locked guest process thread. - * - * @param pThread Thread to unlock. - */ -void VBoxServiceControlUnlockThread(const PVBOXSERVICECTRLTHREAD pThread) -{ - AssertPtr(pThread); - - int rc = RTCritSectLeave(&pThread->CritSect); - AssertRC(rc); -} - - -/** - * Assigns a valid PID to a guest control thread and also checks if there already was - * another (stale) guest process which was using that PID before and destroys it. - * - * @return IPRT status code. - * @param pThread Thread to assign PID to. - * @param uPID PID to assign to the specified guest control execution thread. - */ -int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - AssertReturn(uPID, VERR_INVALID_PARAMETER); - - int rc = RTCritSectEnter(&g_csControlThreads); - if (RT_SUCCESS(rc)) - { - /* Search old threads using the desired PID and shut them down completely -- it's - * not used anymore. */ - PVBOXSERVICECTRLTHREAD pThreadCur; - bool fTryAgain = false; - do - { - RTListForEach(&g_lstControlThreadsActive, pThreadCur, VBOXSERVICECTRLTHREAD, Node) - { - if (pThreadCur->uPID == uPID) - { - Assert(pThreadCur != pThread); /* can't happen */ - uint32_t uTriedPID = uPID; - uPID += 391939; - VBoxServiceVerbose(2, "PID %u was used before, trying again with %u ...\n", - uTriedPID, uPID); - fTryAgain = true; - break; - } - } - } while (fTryAgain); - - /* Assign PID to current thread. */ - pThread->uPID = uPID; - - rc = RTCritSectLeave(&g_csControlThreads); - AssertRC(rc); - } - - return rc; -} - - -/** * The 'vminfo' service description. */ VBOXSERVICE g_Control = @@ -1285,8 +475,7 @@ VBOXSERVICE g_Control = #ifdef DEBUG " [--control-dump-stderr] [--control-dump-stdout]\n" #endif - " [--control-interval <ms>] [--control-procs-max-kept <x>]\n" - " [--control-procs-mem-std[in|out|err] <KB>]" + " [--control-interval <ms>]" , /* pszOptions. */ #ifdef DEBUG @@ -1297,9 +486,6 @@ VBOXSERVICE g_Control = #endif " --control-interval Specifies the interval at which to check for\n" " new control commands. The default is 1000 ms.\n" - " --control-procs-max-kept\n" - " Specifies how many started guest processes are\n" - " kept into memory to work with. Default is 256.\n" , /* methods */ VBoxServiceControlPreInit, diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h new file mode 100644 index 00000000..7afe7cb4 --- /dev/null +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControl.h @@ -0,0 +1,325 @@ +/* $Id: VBoxServiceControl.h $ */ +/** @file + * VBoxServiceControl.h - Internal guest control definitions. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef ___VBoxServiceControl_h +#define ___VBoxServiceControl_h + +#include <iprt/critsect.h> +#include <iprt/list.h> +#include <iprt/req.h> + +#include <VBox/VBoxGuestLib.h> +#include <VBox/HostServices/GuestControlSvc.h> + + +/** + * Pipe IDs for handling the guest process poll set. + */ +typedef enum VBOXSERVICECTRLPIPEID +{ + VBOXSERVICECTRLPIPEID_UNKNOWN = 0, + VBOXSERVICECTRLPIPEID_STDIN = 10, + VBOXSERVICECTRLPIPEID_STDIN_WRITABLE = 11, + /** Pipe for reading from guest process' stdout. */ + VBOXSERVICECTRLPIPEID_STDOUT = 40, + /** Pipe for reading from guest process' stderr. */ + VBOXSERVICECTRLPIPEID_STDERR = 50, + /** Notification pipe for waking up the guest process + * control thread. */ + VBOXSERVICECTRLPIPEID_IPC_NOTIFY = 100 +} VBOXSERVICECTRLPIPEID; + +/** + * Structure for one (opened) guest file. + */ +typedef struct VBOXSERVICECTRLFILE +{ + /** Pointer to list archor of following + * list node. + * @todo Would be nice to have a RTListGetAnchor(). */ + PRTLISTANCHOR pAnchor; + /** Node to global guest control file list. */ + /** @todo Use a map later? */ + RTLISTNODE Node; + /** The file name. */ + char szName[RTPATH_MAX]; + /** The file handle on the guest. */ + RTFILE hFile; + /** File handle to identify this file. */ + uint32_t uHandle; + /** Context ID. */ + uint32_t uContextID; +} VBOXSERVICECTRLFILE; +/** Pointer to thread data. */ +typedef VBOXSERVICECTRLFILE *PVBOXSERVICECTRLFILE; + +typedef struct VBOXSERVICECTRLSESSIONSTARTUPINFO +{ + /** The session's protocol version to use. */ + uint32_t uProtocol; + /** The session's ID. */ + uint32_t uSessionID; + /** User name (account) to start the guest session under. */ + char szUser[GUESTPROCESS_MAX_USER_LEN]; + /** Password of specified user name (account). */ + char szPassword[GUESTPROCESS_MAX_PASSWORD_LEN]; + /** Domain of the user account. */ + char szDomain[GUESTPROCESS_MAX_DOMAIN_LEN]; + /** Session creation flags. + * @sa VBOXSERVICECTRLSESSIONSTARTUPFLAG_* flags. */ + uint32_t uFlags; +} VBOXSERVICECTRLSESSIONSTARTUPINFO; +/** Pointer to thread data. */ +typedef VBOXSERVICECTRLSESSIONSTARTUPINFO *PVBOXSERVICECTRLSESSIONSTARTUPINFO; + +/** + * Structure for a guest session thread to + * observe/control the forked session instance from + * the VBoxService main executable. + */ +typedef struct VBOXSERVICECTRLSESSIONTHREAD +{ + /** Node to global guest control session list. */ + /** @todo Use a map later? */ + RTLISTNODE Node; + /** The sessions's startup info. */ + VBOXSERVICECTRLSESSIONSTARTUPINFO + StartupInfo; + /** The worker thread. */ + RTTHREAD Thread; + /** Critical section for thread-safe use. */ + RTCRITSECT CritSect; + /** Process handle for forked child. */ + RTPROCESS hProcess; + /** Shutdown indicator; will be set when the thread + * needs (or is asked) to shutdown. */ + bool volatile fShutdown; + /** Indicator set by the service thread exiting. */ + bool volatile fStopped; + /** Whether the thread was started or not. */ + bool fStarted; +#if 0 /* Pipe IPC not used yet. */ + /** Pollset containing all the pipes. */ + RTPOLLSET hPollSet; + RTPIPE hStdInW; + RTPIPE hStdOutR; + RTPIPE hStdErrR; + struct StdPipe + { + RTHANDLE hChild; + PRTHANDLE phChild; + } StdIn, + StdOut, + StdErr; + /** The notification pipe associated with this guest session. + * This is NIL_RTPIPE for output pipes. */ + RTPIPE hNotificationPipeW; + /** The other end of hNotificationPipeW. */ + RTPIPE hNotificationPipeR; +#endif +} VBOXSERVICECTRLSESSIONTHREAD; +/** Pointer to thread data. */ +typedef VBOXSERVICECTRLSESSIONTHREAD *PVBOXSERVICECTRLSESSIONTHREAD; + +/** Flag indicating that this session has been forked from + * the main executable. */ +#define VBOXSERVICECTRLSESSION_FLAG_FORK RT_BIT(0) +/** Flag indicating that this session is anonymous, that is, + * it will run start guest processes with the same credentials + * as the main executable. */ +#define VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS RT_BIT(1) +/** Flag indicating that started guest processes will dump their + * stdout output to a separate file on disk. For debugging. */ +#define VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT RT_BIT(2) +/** Flag indicating that started guest processes will dump their + * stderr output to a separate file on disk. For debugging. */ +#define VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR RT_BIT(3) + +/** + * Structure for maintaining a guest session. This also + * contains all started threads (e.g. for guest processes). + * + * This structure can act in two different ways: + * - For legacy guest control handling (protocol version < 2) + * this acts as a per-guest process structure containing all + * the information needed to get a guest process up and running. + * - For newer guest control protocols (>= 2) this structure is + * part of the forked session child, maintaining all guest + * control objects under it. + */ +typedef struct VBOXSERVICECTRLSESSION +{ + /* The session's startup information. */ + VBOXSERVICECTRLSESSIONSTARTUPINFO + StartupInfo; + /** List of active guest process threads + * (VBOXSERVICECTRLPROCESS). */ + RTLISTANCHOR lstProcesses; + /** List of guest control files (VBOXSERVICECTRLFILE). */ + RTLISTANCHOR lstFiles; + /** The session's critical section. */ + RTCRITSECT CritSect; + /** Internal session flags, not related + * to StartupInfo stuff. + * @sa VBOXSERVICECTRLSESSION_FLAG_* flags. */ + uint32_t uFlags; + /** How many processes do we allow keeping around at a time? */ + uint32_t uProcsMaxKept; +} VBOXSERVICECTRLSESSION; +/** Pointer to guest session. */ +typedef VBOXSERVICECTRLSESSION *PVBOXSERVICECTRLSESSION; + +/** + * Structure holding information for starting a guest + * process. + */ +typedef struct VBOXSERVICECTRLPROCSTARTUPINFO +{ + /** Full qualified path of process to start (without arguments). */ + char szCmd[GUESTPROCESS_MAX_CMD_LEN]; + /** Process execution flags. @sa */ + uint32_t uFlags; + /** Command line arguments. */ + char szArgs[GUESTPROCESS_MAX_ARGS_LEN]; + /** Number of arguments specified in pszArgs. */ + uint32_t uNumArgs; + /** String of environment variables ("FOO=BAR") to pass to the process + * to start. */ + char szEnv[GUESTPROCESS_MAX_ENV_LEN]; + /** Size (in bytes) of environment variables block. */ + uint32_t cbEnv; + /** Number of environment variables specified in pszEnv. */ + uint32_t uNumEnvVars; + /** User name (account) to start the process under. */ + char szUser[GUESTPROCESS_MAX_USER_LEN]; + /** Password of specified user name (account). */ + char szPassword[GUESTPROCESS_MAX_PASSWORD_LEN]; + /** Time limit (in ms) of the process' life time. */ + uint32_t uTimeLimitMS; + /** Process priority. */ + uint32_t uPriority; + /** Process affinity. At the moment we support + * up to 4 * 64 = 256 CPUs. */ + uint64_t uAffinity[4]; + /** Number of used process affinity blocks. */ + uint32_t uNumAffinity; +} VBOXSERVICECTRLPROCSTARTUPINFO; +/** Pointer to a guest process block. */ +typedef VBOXSERVICECTRLPROCSTARTUPINFO *PVBOXSERVICECTRLPROCSTARTUPINFO; + +/** + * Structure for holding data for one (started) guest process. + */ +typedef struct VBOXSERVICECTRLPROCESS +{ + /** Node. */ + RTLISTNODE Node; + /** Process handle. */ + RTPROCESS hProcess; + /** Number of references using this struct. */ + uint32_t cRefs; + /** The worker thread. */ + RTTHREAD Thread; + /** The session this guest process + * is bound to. */ + PVBOXSERVICECTRLSESSION pSession; + /** Shutdown indicator; will be set when the thread + * needs (or is asked) to shutdown. */ + bool volatile fShutdown; + /** Whether the guest process thread was stopped + * or not. */ + bool volatile fStopped; + /** Whether the guest process thread was started + * or not. */ + bool fStarted; + /** Client ID. */ + uint32_t uClientID; + /** Context ID. */ + uint32_t uContextID; + /** Critical section for thread-safe use. */ + RTCRITSECT CritSect; + /** Process startup information. */ + VBOXSERVICECTRLPROCSTARTUPINFO + StartupInfo; + /** The process' PID assigned by the guest OS. */ + uint32_t uPID; + /** The process' request queue to handle requests + * from the outside, e.g. the session. */ + RTREQQUEUE hReqQueue; + /** Our pollset, used for accessing the process' + * std* pipes + the notification pipe. */ + RTPOLLSET hPollSet; + /** StdIn pipe for addressing writes to the + * guest process' stdin.*/ + RTPIPE hPipeStdInW; + /** StdOut pipe for addressing reads from + * guest process' stdout.*/ + RTPIPE hPipeStdOutR; + /** StdOut pipe for addressing reads from + * guest process' stdout.*/ + RTPIPE hPipeStdErrR; + /** The notification pipe associated with this guest process. + * This is NIL_RTPIPE for output pipes. */ + RTPIPE hNotificationPipeW; + /** The other end of hNotificationPipeW. */ + RTPIPE hNotificationPipeR; +} VBOXSERVICECTRLPROCESS; +/** Pointer to thread data. */ +typedef VBOXSERVICECTRLPROCESS *PVBOXSERVICECTRLPROCESS; + +RT_C_DECLS_BEGIN + +/** + * Note on naming conventions: + * - VBoxServiceControl* is named everything sub service module related, e.g. + * everything which is callable by main() and/or the service dispatcher(s). + * - GstCntl* is named everything which declared extern and thus can be called + * by different guest control modules as needed. + * - gstcntl (all lowercase) is a purely static function to split up functionality + * inside a module. + */ + +/* Guest session thread handling. */ +extern int GstCntlSessionThreadCreate(PRTLISTANCHOR pList, const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo, PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread); +extern int GstCntlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pSession, uint32_t uFlags); +extern int GstCntlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags); +extern int GstCntlSessionThreadTerminate(PVBOXSERVICECTRLSESSIONTHREAD pSession); +extern RTEXITCODE VBoxServiceControlSessionForkInit(int argc, char **argv); +/* Per-session functions. */ +extern PVBOXSERVICECTRLPROCESS GstCntlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID); +extern int GstCntlSessionClose(PVBOXSERVICECTRLSESSION pSession); +extern int GstCntlSessionDestroy(PVBOXSERVICECTRLSESSION pSession); +extern int GstCntlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t uFlags); +extern int GstCntlSessionHandler(PVBOXSERVICECTRLSESSION pSession, uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx, void *pvScratchBuf, size_t cbScratchBuf, volatile bool *pfShutdown); +extern int GstCntlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess); +extern int GstCntlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLPROCESS pProcess); +extern int GstCntlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, bool *pbAllowed); +extern int GstCntlSessionReapProcesses(PVBOXSERVICECTRLSESSION pSession); +/* Per-guest process functions. */ +extern int GstCntlProcessFree(PVBOXSERVICECTRLPROCESS pProcess); +extern int GstCntlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, bool fPendingClose, void *pvBuf, uint32_t cbBuf); +extern int GstCntlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags); +extern int GstCntlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess); +extern void GstCntlProcessRelease(PVBOXSERVICECTRLPROCESS pProcess); +extern int GstCntlProcessStart(const PVBOXSERVICECTRLSESSION pSession, const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, uint32_t uContext); +extern int GstCntlProcessStop(PVBOXSERVICECTRLPROCESS pProcess); +extern int GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, RTMSINTERVAL msTimeout, int *pRc); + +RT_C_DECLS_END + +#endif /* ___VBoxServiceControl_h */ + diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp new file mode 100644 index 00000000..c355b581 --- /dev/null +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControlProcess.cpp @@ -0,0 +1,2141 @@ +/* $Id: VBoxServiceControlProcess.cpp $ */ +/** @file + * VBoxServiceControlThread - Guest process handling. + */ + +/* + * Copyright (C) 2012-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/env.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/handle.h> +#include <iprt/mem.h> +#include <iprt/path.h> +#include <iprt/pipe.h> +#include <iprt/poll.h> +#include <iprt/process.h> +#include <iprt/semaphore.h> +#include <iprt/string.h> +#include <iprt/thread.h> + +#include <VBox/VBoxGuestLib.h> +#include <VBox/HostServices/GuestControlSvc.h> + +#include "VBoxServiceInternal.h" +#include "VBoxServiceControl.h" + +using namespace guestControl; + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int gstcntlProcessAssignPID(PVBOXSERVICECTRLPROCESS pThread, uint32_t uPID); +static int gstcntlProcessLock(PVBOXSERVICECTRLPROCESS pProcess); +static int gstcntlProcessRequest(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, PFNRT pfnFunction, unsigned cArgs, ...); +static int gstcntlProcessSetupPipe(const char *pszHowTo, int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe); +static int gstcntlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess); +/* Request handlers. */ +static DECLCALLBACK(int) gstcntlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, bool fPendingClose, void *pvBuf, uint32_t cbBuf); +static DECLCALLBACK(int) gstcntlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags); +static DECLCALLBACK(int) gstcntlProcessOnTerm(PVBOXSERVICECTRLPROCESS pThis); + +/** + * Initialies the passed in thread data structure with the parameters given. + * + * @return IPRT status code. + * @param pProcess Process to initialize. + * @param pSession Guest session the process is bound to. + * @param pStartupInfo Startup information. + * @param u32ContextID The context ID bound to this request / command. + */ +static int gstcntlProcessInit(PVBOXSERVICECTRLPROCESS pProcess, + const PVBOXSERVICECTRLSESSION pSession, + const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, + uint32_t u32ContextID) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + + /* General stuff. */ + pProcess->hProcess = NIL_RTPROCESS; + pProcess->pSession = pSession; + pProcess->Node.pPrev = NULL; + pProcess->Node.pNext = NULL; + + pProcess->fShutdown = false; + pProcess->fStarted = false; + pProcess->fStopped = false; + + pProcess->uPID = 0; /* Don't have a PID yet. */ + pProcess->cRefs = 0; + /* + * Use the initial context ID we got for starting + * the process to report back its status with the + * same context ID. + */ + pProcess->uContextID = u32ContextID; + /* + * Note: pProcess->ClientID will be assigned when thread is started; + * every guest process has its own client ID to detect crashes on + * a per-guest-process level. + */ + + int rc = RTCritSectInit(&pProcess->CritSect); + if (RT_FAILURE(rc)) + return rc; + + pProcess->hPollSet = NIL_RTPOLLSET; + pProcess->hPipeStdInW = NIL_RTPIPE; + pProcess->hPipeStdOutR = NIL_RTPIPE; + pProcess->hPipeStdErrR = NIL_RTPIPE; + pProcess->hNotificationPipeW = NIL_RTPIPE; + pProcess->hNotificationPipeR = NIL_RTPIPE; + + rc = RTReqQueueCreate(&pProcess->hReqQueue); + AssertReleaseRC(rc); + + /* Copy over startup info. */ + memcpy(&pProcess->StartupInfo, pStartupInfo, sizeof(VBOXSERVICECTRLPROCSTARTUPINFO)); + + /* Adjust timeout value. */ + if ( pProcess->StartupInfo.uTimeLimitMS == UINT32_MAX + || pProcess->StartupInfo.uTimeLimitMS == 0) + pProcess->StartupInfo.uTimeLimitMS = RT_INDEFINITE_WAIT; + + if (RT_FAILURE(rc)) /* Clean up on failure. */ + GstCntlProcessFree(pProcess); + return rc; +} + + +/** + * Frees a guest process. On success, pProcess will be + * free'd and thus won't be available anymore. + * + * @return IPRT status code. + * @param pProcess Guest process to free. + */ +int GstCntlProcessFree(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + VBoxServiceVerbose(3, "[PID %RU32]: Freeing (cRefs=%RU32)...\n", + pProcess->uPID, pProcess->cRefs); + Assert(pProcess->cRefs == 0); + + /* + * Destroy other thread data. + */ + if (RTCritSectIsInitialized(&pProcess->CritSect)) + RTCritSectDelete(&pProcess->CritSect); + + int rc = RTReqQueueDestroy(pProcess->hReqQueue); + AssertRC(rc); + + /* + * Remove from list. + */ + AssertPtr(pProcess->pSession); + rc = GstCntlSessionProcessRemove(pProcess->pSession, pProcess); + AssertRC(rc); + + /* + * Destroy thread structure as final step. + */ + RTMemFree(pProcess); + pProcess = NULL; + + return VINF_SUCCESS; +} + + +/** + * Signals a guest process thread that we want it to shut down in + * a gentle way. + * + * @return IPRT status code. + * @param pProcess Process to stop. + */ +int GstCntlProcessStop(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + VBoxServiceVerbose(3, "[PID %RU32]: Stopping ...\n", + pProcess->uPID); + + /* Do *not* set pThread->fShutdown or other stuff here! + * The guest thread loop will clean up itself. */ + + return GstCntlProcessHandleTerm(pProcess); +} + + +/** + * Releases a previously acquired guest process (decreases the refcount). + * + * @param pProcess Process to unlock. + */ +void GstCntlProcessRelease(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturnVoid(pProcess); + + bool fShutdown = false; + + int rc = RTCritSectEnter(&pProcess->CritSect); + if (RT_SUCCESS(rc)) + { + Assert(pProcess->cRefs); + pProcess->cRefs--; + fShutdown = pProcess->fStopped; /* Has the process' thread been stopped? */ + + rc = RTCritSectLeave(&pProcess->CritSect); + AssertRC(rc); + } + + if (fShutdown) + GstCntlProcessFree(pProcess); +} + + +/** + * Wait for a guest process thread to shut down. + * + * @return IPRT status code. + * @param pProcess Process to wait shutting down for. + * @param RTMSINTERVAL Timeout in ms to wait for shutdown. + * @param pRc Where to store the thread's return code. Optional. + */ +int GstCntlProcessWait(const PVBOXSERVICECTRLPROCESS pProcess, + RTMSINTERVAL msTimeout, int *pRc) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + /* pRc is optional. */ + + int rc = gstcntlProcessLock(pProcess); + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(2, "[PID %RU32]: Waiting for shutdown (%RU32ms) ...\n", + pProcess->uPID, msTimeout); + + AssertMsgReturn(pProcess->fStarted, + ("Tried to wait on guest process=%p (PID %RU32) which has not been started yet\n", + pProcess, pProcess->uPID), VERR_INVALID_PARAMETER); + + /* Guest process already has been stopped, no need to wait. */ + if (!pProcess->fStopped) + { + /* Unlock process before waiting. */ + rc = gstcntlProcessUnlock(pProcess); + AssertRC(rc); + + /* Do the actual waiting. */ + int rcThread; + Assert(pProcess->Thread != NIL_RTTHREAD); + rc = RTThreadWait(pProcess->Thread, msTimeout, &rcThread); + if (RT_FAILURE(rc)) + { + VBoxServiceError("[PID %RU32]: Waiting for shutting down thread returned error rc=%Rrc\n", + pProcess->uPID, rc); + } + else + { + VBoxServiceVerbose(3, "[PID %RU32]: Thread shutdown complete, thread rc=%Rrc\n", + pProcess->uPID, rcThread); + if (pRc) + *pRc = rcThread; + } + } + else + { + VBoxServiceVerbose(3, "[PID %RU32]: Thread already shut down, no waiting needed\n", + pProcess->uPID); + + int rc2 = gstcntlProcessUnlock(pProcess); + AssertRC(rc2); + } + } + + VBoxServiceVerbose(3, "[PID %RU32]: Waiting resulted in rc=%Rrc\n", + pProcess->uPID, rc); + return rc; +} + + +/** + * Closes the stdin pipe of a guest process. + * + * @return IPRT status code. + * @param hPollSet The polling set. + * @param phStdInW The standard input pipe handle. + */ +static int gstcntlProcessPollsetCloseInput(PVBOXSERVICECTRLPROCESS pProcess, + PRTPIPE phStdInW) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + AssertPtrReturn(phStdInW, VERR_INVALID_POINTER); + + int rc = RTPollSetRemove(pProcess->hPollSet, VBOXSERVICECTRLPIPEID_STDIN); + if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND) + AssertRC(rc); + + if (*phStdInW != NIL_RTPIPE) + { + rc = RTPipeClose(*phStdInW); + AssertRC(rc); + *phStdInW = NIL_RTPIPE; + } + + return rc; +} + + +static const char* gstcntlProcessPollHandleToString(uint32_t idPollHnd) +{ + switch (idPollHnd) + { + case VBOXSERVICECTRLPIPEID_UNKNOWN: + return "unknown"; + case VBOXSERVICECTRLPIPEID_STDIN: + return "stdin"; + case VBOXSERVICECTRLPIPEID_STDIN_WRITABLE: + return "stdin_writable"; + case VBOXSERVICECTRLPIPEID_STDOUT: + return "stdout"; + case VBOXSERVICECTRLPIPEID_STDERR: + return "stderr"; + case VBOXSERVICECTRLPIPEID_IPC_NOTIFY: + return "ipc_notify"; + default: + break; + } + + return "unknown"; +} + + +/** + * Handle an error event on standard input. + * + * @return IPRT status code. + * @param pProcess Process to handle pollset for. + * @param fPollEvt The event mask returned by RTPollNoResume. + * @param phStdInW The standard input pipe handle. + */ +static int gstcntlProcessPollsetOnInput(PVBOXSERVICECTRLPROCESS pProcess, + uint32_t fPollEvt, PRTPIPE phStdInW) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + NOREF(fPollEvt); + + return gstcntlProcessPollsetCloseInput(pProcess, phStdInW); +} + + +/** + * Handle pending output data or error on standard out or standard error. + * + * @returns IPRT status code from client send. + * @param pProcess Process to handle pollset for. + * @param fPollEvt The event mask returned by RTPollNoResume. + * @param phPipeR The pipe handle. + * @param idPollHnd The pipe ID to handle. + * + */ +static int gstcntlProcessHandleOutputError(PVBOXSERVICECTRLPROCESS pProcess, + uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + if (!phPipeR) + return VINF_SUCCESS; + +#ifdef DEBUG + VBoxServiceVerbose(4, "[PID %RU32]: Output error: idPollHnd=%s, fPollEvt=0x%x\n", + pProcess->uPID, gstcntlProcessPollHandleToString(idPollHnd), fPollEvt); +#endif + + /* Remove pipe from poll set. */ + int rc2 = RTPollSetRemove(pProcess->hPollSet, idPollHnd); + AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2)); + + bool fClosePipe = true; /* By default close the pipe. */ + + /* Check if there's remaining data to read from the pipe. */ + if (*phPipeR != NIL_RTPIPE) + { + size_t cbReadable; + rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable); + if ( RT_SUCCESS(rc2) + && cbReadable) + { +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: idPollHnd=%s has %zu bytes left, vetoing close\n", + pProcess->uPID, gstcntlProcessPollHandleToString(idPollHnd), cbReadable); +#endif + /* Veto closing the pipe yet because there's still stuff to read + * from the pipe. This can happen on UNIX-y systems where on + * error/hangup there still can be data to be read out. */ + fClosePipe = false; + } + } +#ifdef DEBUG + else + VBoxServiceVerbose(3, "[PID %RU32]: idPollHnd=%s will be closed\n", + pProcess->uPID, gstcntlProcessPollHandleToString(idPollHnd)); +#endif + + if ( *phPipeR != NIL_RTPIPE + && fClosePipe) + { + rc2 = RTPipeClose(*phPipeR); + AssertRC(rc2); + *phPipeR = NIL_RTPIPE; + } + + return VINF_SUCCESS; +} + + +/** + * Handle pending output data or error on standard out or standard error. + * + * @returns IPRT status code from client send. + * @param pProcess Process to handle pollset for. + * @param fPollEvt The event mask returned by RTPollNoResume. + * @param phPipeR The pipe handle. + * @param idPollHnd The pipe ID to handle. + * + */ +static int gstcntlProcessPollsetOnOutput(PVBOXSERVICECTRLPROCESS pProcess, + uint32_t fPollEvt, PRTPIPE phPipeR, uint32_t idPollHnd) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + +#ifdef DEBUG + VBoxServiceVerbose(4, "[PID %RU32]: Output event phPipeR=%p, idPollHnd=%s, fPollEvt=0x%x\n", + pProcess->uPID, phPipeR, gstcntlProcessPollHandleToString(idPollHnd), fPollEvt); +#endif + + if (!phPipeR) + return VINF_SUCCESS; + + int rc = VINF_SUCCESS; + +#ifdef DEBUG + if (*phPipeR != NIL_RTPIPE) + { + size_t cbReadable; + rc = RTPipeQueryReadable(*phPipeR, &cbReadable); + if ( RT_SUCCESS(rc) + && cbReadable) + { + VBoxServiceVerbose(4, "[PID %RU32]: Output event cbReadable=%zu\n", + pProcess->uPID, cbReadable); + } + } +#endif + +#if 0 + /* Push output to the host. */ + if (fPollEvt & RTPOLL_EVT_READ) + { + size_t cbRead = 0; + uint8_t byData[_64K]; + rc = RTPipeRead(*phPipeR, + byData, sizeof(byData), &cbRead); + VBoxServiceVerbose(4, "GstCntlProcessHandleOutputEvent cbRead=%u, rc=%Rrc\n", + cbRead, rc); + + /* Make sure we go another poll round in case there was too much data + for the buffer to hold. */ + fPollEvt &= RTPOLL_EVT_ERROR; + } +#endif + + if (fPollEvt & RTPOLL_EVT_ERROR) + rc = gstcntlProcessHandleOutputError(pProcess, + fPollEvt, phPipeR, idPollHnd); + return rc; +} + + +/** + * Execution loop which runs in a dedicated per-started-process thread and + * handles all pipe input/output and signalling stuff. + * + * @return IPRT status code. + * @param pProcess The guest process to handle. + */ +static int gstcntlProcessProcLoop(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + int rc; + int rc2; + uint64_t const uMsStart = RTTimeMilliTS(); + RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND }; + bool fProcessAlive = true; + bool fProcessTimedOut = false; + uint64_t MsProcessKilled = UINT64_MAX; + RTMSINTERVAL const cMsPollBase = pProcess->hPipeStdInW != NIL_RTPIPE + ? 100 /* Need to poll for input. */ + : 1000; /* Need only poll for process exit and aborts. */ + RTMSINTERVAL cMsPollCur = 0; + + /* + * Assign PID to thread data. + * Also check if there already was a thread with the same PID and shut it down -- otherwise + * the first (stale) entry will be found and we get really weird results! + */ + rc = gstcntlProcessAssignPID(pProcess, pProcess->hProcess /* Opaque PID handle */); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Unable to assign PID=%u, to new thread, rc=%Rrc\n", + pProcess->hProcess, rc); + return rc; + } + + /* + * Before entering the loop, tell the host that we've started the guest + * and that it's now OK to send input to the process. + */ + VBoxServiceVerbose(2, "[PID %RU32]: Process \"%s\" started, CID=%u, User=%s, cMsTimeout=%RU32\n", + pProcess->uPID, pProcess->StartupInfo.szCmd, pProcess->uContextID, + pProcess->StartupInfo.szUser, pProcess->StartupInfo.uTimeLimitMS); + VBGLR3GUESTCTRLCMDCTX ctxStart = { pProcess->uClientID, pProcess->uContextID }; + rc = VbglR3GuestCtrlProcCbStatus(&ctxStart, + pProcess->uPID, PROC_STS_STARTED, 0 /* u32Flags */, + NULL /* pvData */, 0 /* cbData */); + if (RT_FAILURE(rc)) + VBoxServiceError("[PID %RU32]: Error reporting starting status to host, rc=%Rrc\n", + pProcess->uPID, rc); + + /* + * Process input, output, the test pipe and client requests. + */ + while ( RT_SUCCESS(rc) + && RT_UNLIKELY(!pProcess->fShutdown)) + { + /* + * Wait/Process all pending events. + */ + uint32_t idPollHnd; + uint32_t fPollEvt; + rc2 = RTPollNoResume(pProcess->hPollSet, cMsPollCur, &fPollEvt, &idPollHnd); + if (pProcess->fShutdown) + continue; + + cMsPollCur = 0; /* No rest until we've checked everything. */ + + if (RT_SUCCESS(rc2)) + { + switch (idPollHnd) + { + case VBOXSERVICECTRLPIPEID_STDIN: + rc = gstcntlProcessPollsetOnInput(pProcess, fPollEvt, + &pProcess->hPipeStdInW); + break; + + case VBOXSERVICECTRLPIPEID_STDOUT: + rc = gstcntlProcessPollsetOnOutput(pProcess, fPollEvt, + &pProcess->hPipeStdOutR, idPollHnd); + break; + + case VBOXSERVICECTRLPIPEID_STDERR: + rc = gstcntlProcessPollsetOnOutput(pProcess, fPollEvt, + &pProcess->hPipeStdOutR, idPollHnd); + break; + + case VBOXSERVICECTRLPIPEID_IPC_NOTIFY: +#ifdef DEBUG_andy + VBoxServiceVerbose(4, "[PID %RU32]: IPC notify\n", pProcess->uPID); +#endif + rc2 = gstcntlProcessLock(pProcess); + if (RT_SUCCESS(rc2)) + { + /* Drain the notification pipe. */ + uint8_t abBuf[8]; + size_t cbIgnore; + rc2 = RTPipeRead(pProcess->hNotificationPipeR, + abBuf, sizeof(abBuf), &cbIgnore); + if (RT_FAILURE(rc2)) + VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc2); + + /* Process all pending requests. */ + VBoxServiceVerbose(4, "[PID %RU32]: Processing pending requests ...\n", + pProcess->uPID); + Assert(pProcess->hReqQueue != NIL_RTREQQUEUE); + rc2 = RTReqQueueProcess(pProcess->hReqQueue, + 0 /* Only process all pending requests, don't wait for new ones */); + if ( RT_FAILURE(rc2) + && rc2 != VERR_TIMEOUT) + VBoxServiceError("Processing requests failed with with rc=%Rrc\n", rc2); + + int rc3 = gstcntlProcessUnlock(pProcess); + AssertRC(rc3); +#ifdef DEBUG + VBoxServiceVerbose(4, "[PID %RU32]: Processing pending requests done, rc=%Rrc\n", + pProcess->uPID, rc2); +#endif + } + + break; + + default: + AssertMsgFailed(("Unknown idPollHnd=%RU32\n", idPollHnd)); + break; + } + + if (RT_FAILURE(rc) || rc == VINF_EOF) + break; /* Abort command, or client dead or something. */ + } +#if 0 + VBoxServiceVerbose(4, "[PID %RU32]: Polling done, pollRc=%Rrc, pollCnt=%RU32, idPollHnd=%s, rc=%Rrc, fProcessAlive=%RTbool, fShutdown=%RTbool\n", + pProcess->uPID, rc2, RTPollSetGetCount(hPollSet), gstcntlProcessPollHandleToString(idPollHnd), rc, fProcessAlive, pProcess->fShutdown); + VBoxServiceVerbose(4, "[PID %RU32]: stdOut=%s, stdErrR=%s\n", + pProcess->uPID, + *phStdOutR == NIL_RTPIPE ? "closed" : "open", + *phStdErrR == NIL_RTPIPE ? "closed" : "open"); +#endif + if (RT_UNLIKELY(pProcess->fShutdown)) + break; /* We were asked to shutdown. */ + + /* + * Check for process death. + */ + if (fProcessAlive) + { + rc2 = RTProcWaitNoResume(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); +#if 0 + VBoxServiceVerbose(4, "[PID %RU32]: RTProcWaitNoResume=%Rrc\n", + pProcess->uPID, rc2); +#endif + if (RT_SUCCESS_NP(rc2)) + { + fProcessAlive = false; + /* Note: Don't bail out here yet. First check in the next block below + * if all needed pipe outputs have been consumed. */ + } + else + { + if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED)) + continue; + if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND)) + { + fProcessAlive = false; + ProcessStatus.enmReason = RTPROCEXITREASON_ABEND; + ProcessStatus.iStatus = 255; + AssertFailed(); + } + else + AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2)); + } + } + + /* + * If the process has terminated and all output has been consumed, + * we should be heading out. + */ + if (!fProcessAlive) + { + if ( fProcessTimedOut + || ( pProcess->hPipeStdOutR == NIL_RTPIPE + && pProcess->hPipeStdErrR == NIL_RTPIPE) + ) + { + break; + } + } + + /* + * Check for timed out, killing the process. + */ + uint32_t cMilliesLeft = RT_INDEFINITE_WAIT; + if ( pProcess->StartupInfo.uTimeLimitMS != RT_INDEFINITE_WAIT + && pProcess->StartupInfo.uTimeLimitMS != 0) + { + uint64_t u64Now = RTTimeMilliTS(); + uint64_t cMsElapsed = u64Now - uMsStart; + if (cMsElapsed >= pProcess->StartupInfo.uTimeLimitMS) + { + fProcessTimedOut = true; + if ( MsProcessKilled == UINT64_MAX + || u64Now - MsProcessKilled > 1000) + { + if (u64Now - MsProcessKilled > 20*60*1000) + break; /* Give up after 20 mins. */ + + VBoxServiceVerbose(3, "[PID %RU32]: Timed out (%RU64ms elapsed > %RU32ms timeout), killing ...\n", + pProcess->uPID, cMsElapsed, pProcess->StartupInfo.uTimeLimitMS); + + rc2 = RTProcTerminate(pProcess->hProcess); + VBoxServiceVerbose(3, "[PID %RU32]: Killing process resulted in rc=%Rrc\n", + pProcess->uPID, rc2); + MsProcessKilled = u64Now; + continue; + } + cMilliesLeft = 10000; + } + else + cMilliesLeft = pProcess->StartupInfo.uTimeLimitMS - (uint32_t)cMsElapsed; + } + + /* Reset the polling interval since we've done all pending work. */ + cMsPollCur = fProcessAlive + ? cMsPollBase + : RT_MS_1MIN; + if (cMilliesLeft < cMsPollCur) + cMsPollCur = cMilliesLeft; + } + + VBoxServiceVerbose(3, "[PID %RU32]: Loop ended: rc=%Rrc, fShutdown=%RTbool, fProcessAlive=%RTbool, fProcessTimedOut=%RTbool, MsProcessKilled=%RU64\n", + pProcess->uPID, rc, pProcess->fShutdown, fProcessAlive, fProcessTimedOut, MsProcessKilled, MsProcessKilled); + VBoxServiceVerbose(3, "[PID %RU32]: *phStdOutR=%s, *phStdErrR=%s\n", + pProcess->uPID, + pProcess->hPipeStdOutR == NIL_RTPIPE ? "closed" : "open", + pProcess->hPipeStdErrR == NIL_RTPIPE ? "closed" : "open"); + + /* Signal that this thread is in progress of shutting down. */ + ASMAtomicXchgBool(&pProcess->fShutdown, true); + + /* + * Try killing the process if it's still alive at this point. + */ + if (fProcessAlive) + { + if (MsProcessKilled == UINT64_MAX) + { + VBoxServiceVerbose(2, "[PID %RU32]: Is still alive and not killed yet\n", + pProcess->uPID); + + MsProcessKilled = RTTimeMilliTS(); + rc2 = RTProcTerminate(pProcess->hProcess); + if (rc2 == VERR_NOT_FOUND) + { + fProcessAlive = false; + } + else if (RT_FAILURE(rc2)) + VBoxServiceError("PID %RU32]: Killing process failed with rc=%Rrc\n", + pProcess->uPID, rc2); + RTThreadSleep(500); + } + + for (int i = 0; i < 10 && fProcessAlive; i++) + { + VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Waiting to exit ...\n", + pProcess->uPID, i + 1); + rc2 = RTProcWait(pProcess->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); + if (RT_SUCCESS(rc2)) + { + VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Exited\n", + pProcess->uPID, i + 1); + fProcessAlive = false; + break; + } + if (i >= 5) + { + VBoxServiceVerbose(4, "[PID %RU32]: Kill attempt %d/10: Trying to terminate ...\n", + pProcess->uPID, i + 1); + rc2 = RTProcTerminate(pProcess->hProcess); + if ( RT_FAILURE(rc) + && rc2 != VERR_NOT_FOUND) + VBoxServiceError("PID %RU32]: Killing process failed with rc=%Rrc\n", + pProcess->uPID, rc2); + } + RTThreadSleep(i >= 5 ? 2000 : 500); + } + + if (fProcessAlive) + VBoxServiceError("[PID %RU32]: Could not be killed\n", pProcess->uPID); + } + + /* + * Shutdown procedure: + * - Set the pProcess->fShutdown indicator to let others know we're + * not accepting any new requests anymore. + * - After setting the indicator, try to process all outstanding + * requests to make sure they're getting delivered. + * + * Note: After removing the process from the session's list it's not + * even possible for the session anymore to control what's + * happening to this thread, so be careful and don't mess it up. + */ + + rc2 = gstcntlProcessLock(pProcess); + if (RT_SUCCESS(rc2)) + { + VBoxServiceVerbose(3, "[PID %RU32]: Processing outstanding requests ...\n", + pProcess->uPID); + + /* Process all pending requests (but don't wait for new ones). */ + Assert(pProcess->hReqQueue != NIL_RTREQQUEUE); + rc2 = RTReqQueueProcess(pProcess->hReqQueue, 0 /* No timeout */); + if ( RT_FAILURE(rc2) + && rc2 != VERR_TIMEOUT) + VBoxServiceError("[PID %RU32]: Processing outstanding requests failed with with rc=%Rrc\n", + pProcess->uPID, rc2); + + VBoxServiceVerbose(3, "[PID %RU32]: Processing outstanding requests done, rc=%Rrc\n", + pProcess->uPID, rc2); + + rc2 = gstcntlProcessUnlock(pProcess); + AssertRC(rc2); + } + + /* + * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the + * clients exec packet now. + */ + if (RT_SUCCESS(rc)) + { + uint32_t uStatus = PROC_STS_UNDEFINED; + uint32_t uFlags = 0; + + if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX) + { + VBoxServiceVerbose(3, "[PID %RU32]: Timed out and got killed\n", + pProcess->uPID); + uStatus = PROC_STS_TOK; + } + else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX) + { + VBoxServiceVerbose(3, "[PID %RU32]: Timed out and did *not* get killed\n", + pProcess->uPID); + uStatus = PROC_STS_TOA; + } + else if (pProcess->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX)) + { + VBoxServiceVerbose(3, "[PID %RU32]: Got terminated because system/service is about to shutdown\n", + pProcess->uPID); + uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */ + uFlags = pProcess->StartupInfo.uFlags; /* Return handed-in execution flags back to the host. */ + } + else if (fProcessAlive) + { + VBoxServiceError("[PID %RU32]: Is alive when it should not!\n", + pProcess->uPID); + } + else if (MsProcessKilled != UINT64_MAX) + { + VBoxServiceError("[PID %RU32]: Has been killed when it should not!\n", + pProcess->uPID); + } + else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL) + { + VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %d)\n", + pProcess->uPID, ProcessStatus.iStatus); + + uStatus = PROC_STS_TEN; + uFlags = ProcessStatus.iStatus; + } + else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL) + { + VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n", + pProcess->uPID, ProcessStatus.iStatus); + + uStatus = PROC_STS_TES; + uFlags = ProcessStatus.iStatus; + } + else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND) + { + /* ProcessStatus.iStatus will be undefined. */ + VBoxServiceVerbose(3, "[PID %RU32]: Ended with RTPROCEXITREASON_ABEND\n", + pProcess->uPID); + + uStatus = PROC_STS_TEA; + uFlags = ProcessStatus.iStatus; + } + else + VBoxServiceVerbose(1, "[PID %RU32]: Handling process status %u not implemented\n", + pProcess->uPID, ProcessStatus.enmReason); + + VBoxServiceVerbose(2, "[PID %RU32]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n", + pProcess->uPID, pProcess->uClientID, pProcess->uContextID, uStatus, uFlags); + + VBGLR3GUESTCTRLCMDCTX ctxEnd = { pProcess->uClientID, pProcess->uContextID }; + rc2 = VbglR3GuestCtrlProcCbStatus(&ctxEnd, + pProcess->uPID, uStatus, uFlags, + NULL /* pvData */, 0 /* cbData */); + if ( RT_FAILURE(rc2) + && rc2 == VERR_NOT_FOUND) + VBoxServiceError("[PID %RU32]: Error reporting final status to host; rc=%Rrc\n", + pProcess->uPID, rc2); + } + + VBoxServiceVerbose(3, "[PID %RU32]: Process loop returned with rc=%Rrc\n", + pProcess->uPID, rc); + return rc; +} + + +/** + * Initializes a pipe's handle and pipe object. + * + * @return IPRT status code. + * @param ph The pipe's handle to initialize. + * @param phPipe The pipe's object to initialize. + */ +static int gstcntlProcessInitPipe(PRTHANDLE ph, PRTPIPE phPipe) +{ + AssertPtrReturn(ph, VERR_INVALID_PARAMETER); + AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER); + + ph->enmType = RTHANDLETYPE_PIPE; + ph->u.hPipe = NIL_RTPIPE; + *phPipe = NIL_RTPIPE; + + return VINF_SUCCESS; +} + + +/** + * Sets up the redirection / pipe / nothing for one of the standard handles. + * + * @returns IPRT status code. No client replies made. + * @param pszHowTo How to set up this standard handle. + * @param fd Which standard handle it is (0 == stdin, 1 == + * stdout, 2 == stderr). + * @param ph The generic handle that @a pph may be set + * pointing to. Always set. + * @param pph Pointer to the RTProcCreateExec argument. + * Always set. + * @param phPipe Where to return the end of the pipe that we + * should service. + */ +static int gstcntlProcessSetupPipe(const char *pszHowTo, int fd, + PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe) +{ + AssertPtrReturn(ph, VERR_INVALID_POINTER); + AssertPtrReturn(pph, VERR_INVALID_POINTER); + AssertPtrReturn(phPipe, VERR_INVALID_POINTER); + + int rc; + + ph->enmType = RTHANDLETYPE_PIPE; + ph->u.hPipe = NIL_RTPIPE; + *pph = NULL; + *phPipe = NIL_RTPIPE; + + if (!strcmp(pszHowTo, "|")) + { + /* + * Setup a pipe for forwarding to/from the client. + * The ph union struct will be filled with a pipe read/write handle + * to represent the "other" end to phPipe. + */ + if (fd == 0) /* stdin? */ + { + /* Connect a wrtie pipe specified by phPipe to stdin. */ + rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ); + } + else /* stdout or stderr? */ + { + /* Connect a read pipe specified by phPipe to stdout or stderr. */ + rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE); + } + + if (RT_FAILURE(rc)) + return rc; + + ph->enmType = RTHANDLETYPE_PIPE; + *pph = ph; + } + else if (!strcmp(pszHowTo, "/dev/null")) + { + /* + * Redirect to/from /dev/null. + */ + RTFILE hFile; + rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE); + if (RT_FAILURE(rc)) + return rc; + + ph->enmType = RTHANDLETYPE_FILE; + ph->u.hFile = hFile; + *pph = ph; + } + else /* Add other piping stuff here. */ + rc = VINF_SUCCESS; /* Same as parent (us). */ + + return rc; +} + + +/** + * Expands a file name / path to its real content. This only works on Windows + * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting + * with system / administrative rights). + * + * @return IPRT status code. + * @param pszPath Path to resolve. + * @param pszExpanded Pointer to string to store the resolved path in. + * @param cbExpanded Size (in bytes) of string to store the resolved path. + */ +static int gstcntlProcessMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded) +{ + int rc = VINF_SUCCESS; +#ifdef RT_OS_WINDOWS + if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded)) + rc = RTErrConvertFromWin32(GetLastError()); +#else + /* No expansion for non-Windows yet. */ + rc = RTStrCopy(pszExpanded, cbExpanded, pszPath); +#endif +#ifdef DEBUG + VBoxServiceVerbose(3, "VBoxServiceControlExecMakeFullPath: %s -> %s\n", + pszPath, pszExpanded); +#endif + return rc; +} + + +/** + * Resolves the full path of a specified executable name. This function also + * resolves internal VBoxService tools to its appropriate executable path + name if + * VBOXSERVICE_NAME is specified as pszFileName. + * + * @return IPRT status code. + * @param pszFileName File name to resolve. + * @param pszResolved Pointer to a string where the resolved file name will be stored. + * @param cbResolved Size (in bytes) of resolved file name string. + */ +static int gstcntlProcessResolveExecutable(const char *pszFileName, + char *pszResolved, size_t cbResolved) +{ + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertPtrReturn(pszResolved, VERR_INVALID_POINTER); + AssertReturn(cbResolved, VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + + char szPathToResolve[RTPATH_MAX]; + if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0)) + || !RTStrICmp(pszFileName, VBOXSERVICE_NAME)) + { + /* Resolve executable name of this process. */ + if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve))) + rc = VERR_FILE_NOT_FOUND; + } + else + { + /* Take the raw argument to resolve. */ + rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName); + } + + if (RT_SUCCESS(rc)) + { + rc = gstcntlProcessMakeFullPath(szPathToResolve, pszResolved, cbResolved); + if (RT_SUCCESS(rc)) + VBoxServiceVerbose(3, "Looked up executable: %s -> %s\n", + pszFileName, pszResolved); + } + + if (RT_FAILURE(rc)) + VBoxServiceError("Failed to lookup executable \"%s\" with rc=%Rrc\n", + pszFileName, rc); + return rc; +} + + +/** + * Constructs the argv command line by resolving environment variables + * and relative paths. + * + * @return IPRT status code. + * @param pszArgv0 First argument (argv0), either original or modified version. Optional. + * @param papszArgs Original argv command line from the host, starting at argv[1]. + * @param ppapszArgv Pointer to a pointer with the new argv command line. + * Needs to be freed with RTGetOptArgvFree. + */ +static int gstcntlProcessAllocateArgv(const char *pszArgv0, + const char * const *papszArgs, + bool fExpandArgs, char ***ppapszArgv) +{ + AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER); + + VBoxServiceVerbose(3, "GstCntlProcessPrepareArgv: pszArgv0=%p, papszArgs=%p, fExpandArgs=%RTbool, ppapszArgv=%p\n", + pszArgv0, papszArgs, fExpandArgs, ppapszArgv); + + int rc = VINF_SUCCESS; + uint32_t cArgs; + for (cArgs = 0; papszArgs[cArgs]; cArgs++) + { + if (cArgs >= UINT32_MAX - 2) + return VERR_BUFFER_OVERFLOW; + } + + /* Allocate new argv vector (adding + 2 for argv0 + termination). */ + size_t cbSize = (cArgs + 2) * sizeof(char*); + char **papszNewArgv = (char**)RTMemAlloc(cbSize); + if (!papszNewArgv) + return VERR_NO_MEMORY; + +#ifdef DEBUG + VBoxServiceVerbose(3, "GstCntlProcessAllocateArgv: cbSize=%RU32, cArgs=%RU32\n", + cbSize, cArgs); +#endif + + size_t i = 0; /* Keep the argument counter in scope for cleaning up on failure. */ + + rc = RTStrDupEx(&papszNewArgv[0], pszArgv0); + if (RT_SUCCESS(rc)) + { + for (; i < cArgs; i++) + { + char *pszArg; +#if 0 /* Arguments expansion -- untested. */ + if (fExpandArgs) + { + /* According to MSDN the limit on older Windows version is 32K, whereas + * Vista+ there are no limits anymore. We still stick to 4K. */ + char szExpanded[_4K]; +# ifdef RT_OS_WINDOWS + if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded))) + rc = RTErrConvertFromWin32(GetLastError()); +# else + /* No expansion for non-Windows yet. */ + rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded); +# endif + if (RT_SUCCESS(rc)) + rc = RTStrDupEx(&pszArg, szExpanded); + } + else +#endif + rc = RTStrDupEx(&pszArg, papszArgs[i]); + + if (RT_FAILURE(rc)) + break; + + papszNewArgv[i + 1] = pszArg; + } + + if (RT_SUCCESS(rc)) + { + /* Terminate array. */ + papszNewArgv[cArgs + 1] = NULL; + + *ppapszArgv = papszNewArgv; + } + } + + if (RT_FAILURE(rc)) + { + for (i; i > 0; i--) + RTStrFree(papszNewArgv[i]); + RTMemFree(papszNewArgv); + } + + return rc; +} + + +/** + * Assigns a valid PID to a guest control thread and also checks if there already was + * another (stale) guest process which was using that PID before and destroys it. + * + * @return IPRT status code. + * @param pProcess Process to assign PID to. + * @param uPID PID to assign to the specified guest control execution thread. + */ +int gstcntlProcessAssignPID(PVBOXSERVICECTRLPROCESS pProcess, uint32_t uPID) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + AssertReturn(uPID, VERR_INVALID_PARAMETER); + + AssertPtr(pProcess->pSession); + int rc = RTCritSectEnter(&pProcess->pSession->CritSect); + if (RT_SUCCESS(rc)) + { + /* Search old threads using the desired PID and shut them down completely -- it's + * not used anymore. */ + PVBOXSERVICECTRLPROCESS pProcessCur; + bool fTryAgain; + do + { + fTryAgain = false; + RTListForEach(&pProcess->pSession->lstProcesses, pProcessCur, VBOXSERVICECTRLPROCESS, Node) + { + if (pProcessCur->uPID == uPID) + { + Assert(pProcessCur != pProcess); /* can't happen */ + uint32_t uTriedPID = uPID; + uPID += 391939; + VBoxServiceVerbose(2, "PID %RU32 was used before (process %p), trying again with %RU32 ...\n", + uTriedPID, pProcessCur, uPID); + fTryAgain = true; + break; + } + } + } while (fTryAgain); + + /* Assign PID to current thread. */ + pProcess->uPID = uPID; + + rc = RTCritSectLeave(&pProcess->pSession->CritSect); + AssertRC(rc); + } + + return rc; +} + + +void gstcntlProcessFreeArgv(char **papszArgv) +{ + if (papszArgv) + { + size_t i = 0; + while (papszArgv[i]) + RTStrFree(papszArgv[i++]); + RTMemFree(papszArgv); + } +} + + +/** + * Helper function to create/start a process on the guest. + * + * @return IPRT status code. + * @param pszExec Full qualified path of process to start (without arguments). + * @param papszArgs Pointer to array of command line arguments. + * @param hEnv Handle to environment block to use. + * @param fFlags Process execution flags. + * @param phStdIn Handle for the process' stdin pipe. + * @param phStdOut Handle for the process' stdout pipe. + * @param phStdErr Handle for the process' stderr pipe. + * @param pszAsUser User name (account) to start the process under. + * @param pszPassword Password of the specified user. + * @param phProcess Pointer which will receive the process handle after + * successful process start. + */ +static int gstcntlProcessCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, + PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, + const char *pszPassword, PRTPROCESS phProcess) +{ + AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER); + AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); + AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER); + + int rc = VINF_SUCCESS; + char szExecExp[RTPATH_MAX]; + + /* Do we need to expand environment variables in arguments? */ + bool fExpandArgs = (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS) ? true : false; + +#ifdef RT_OS_WINDOWS + /* + * If sysprep should be executed do this in the context of VBoxService, which + * (usually, if started by SCM) has administrator rights. Because of that a UI + * won't be shown (doesn't have a desktop). + */ + if (!RTStrICmp(pszExec, "sysprep")) + { + /* Use a predefined sysprep path as default. */ + char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe"; + /** @todo Check digital signature of file above before executing it? */ + + /* + * On Windows Vista (and up) sysprep is located in "system32\\Sysprep\\sysprep.exe", + * so detect the OS and use a different path. + */ + OSVERSIONINFOEX OSInfoEx; + RT_ZERO(OSInfoEx); + OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + BOOL fRet = GetVersionEx((LPOSVERSIONINFO) &OSInfoEx); + if ( fRet + && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT + && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */) + { + rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL); +#ifndef RT_ARCH_AMD64 + /* Don't execute 64-bit sysprep from a 32-bit service host! */ + char szSysWow64[RTPATH_MAX]; + if (RTStrPrintf(szSysWow64, sizeof(szSysWow64), "%s", szSysprepCmd)) + { + rc = RTPathAppend(szSysWow64, sizeof(szSysWow64), "SysWow64"); + AssertRC(rc); + } + if ( RT_SUCCESS(rc) + && RTPathExists(szSysWow64)) + VBoxServiceVerbose(0, "Warning: This service is 32-bit; could not execute sysprep on 64-bit OS!\n"); +#endif + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\Sysprep\\sysprep.exe"); + if (RT_SUCCESS(rc)) + RTPathChangeToDosSlashes(szSysprepCmd, false /* No forcing necessary */); + + if (RT_FAILURE(rc)) + VBoxServiceError("Failed to detect sysrep location, rc=%Rrc\n", rc); + } + else if (!fRet) + VBoxServiceError("Failed to retrieve OS information, last error=%ld\n", GetLastError()); + + VBoxServiceVerbose(3, "Sysprep executable is: %s\n", szSysprepCmd); + + if (RT_SUCCESS(rc)) + { + char **papszArgsExp; + rc = gstcntlProcessAllocateArgv(szSysprepCmd /* argv0 */, papszArgs, + fExpandArgs, &papszArgsExp); + if (RT_SUCCESS(rc)) + { + /* As we don't specify credentials for the sysprep process, it will + * run under behalf of the account VBoxService was started under, most + * likely local system. */ + rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */, + phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */, + NULL /* pszPassword */, phProcess); + gstcntlProcessFreeArgv(papszArgsExp); + } + } + + if (RT_FAILURE(rc)) + VBoxServiceVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc); + + return rc; + } +#endif /* RT_OS_WINDOWS */ + +#ifdef VBOXSERVICE_TOOLBOX + if (RTStrStr(pszExec, "vbox_") == pszExec) + { + /* We want to use the internal toolbox (all internal + * tools are starting with "vbox_" (e.g. "vbox_cat"). */ + rc = gstcntlProcessResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp)); + } + else + { +#endif + /* + * Do the environment variables expansion on executable and arguments. + */ + rc = gstcntlProcessResolveExecutable(pszExec, szExecExp, sizeof(szExecExp)); +#ifdef VBOXSERVICE_TOOLBOX + } +#endif + if (RT_SUCCESS(rc)) + { + char **papszArgsExp; + rc = gstcntlProcessAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */, + papszArgs /* Append the rest of the argument vector (if any). */, + fExpandArgs, &papszArgsExp); + if (RT_FAILURE(rc)) + { + /* Don't print any arguments -- may contain passwords or other sensible data! */ + VBoxServiceError("Could not prepare arguments, rc=%Rrc\n", rc); + } + else + { + uint32_t uProcFlags = 0; + if (fFlags) + { + if (fFlags & EXECUTEPROCESSFLAG_HIDDEN) + uProcFlags |= RTPROC_FLAGS_HIDDEN; + if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE) + uProcFlags |= RTPROC_FLAGS_NO_PROFILE; + } + + /* If no user name specified run with current credentials (e.g. + * full service/system rights). This is prohibited via official Main API! + * + * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication + * code (at least on Windows) for running processes as different users + * started from our system service. */ + if (pszAsUser && *pszAsUser) + uProcFlags |= RTPROC_FLAGS_SERVICE; +#ifdef DEBUG + VBoxServiceVerbose(3, "Command: %s\n", szExecExp); + for (size_t i = 0; papszArgsExp[i]; i++) + VBoxServiceVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]); +#endif + VBoxServiceVerbose(3, "Starting process \"%s\" ...\n", szExecExp); + + /* Do normal execution. */ + rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags, + phStdIn, phStdOut, phStdErr, + pszAsUser && *pszAsUser ? pszAsUser : NULL, + pszPassword && *pszPassword ? pszPassword : NULL, + phProcess); + + VBoxServiceVerbose(3, "Starting process \"%s\" returned rc=%Rrc\n", + szExecExp, rc); + + gstcntlProcessFreeArgv(papszArgsExp); + } + } + return rc; +} + + +#ifdef DEBUG +static int gstcntlProcessDumpToFile(const char *pszFileName, void *pvBuf, size_t cbBuf) +{ + AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); + AssertPtrReturn(pvBuf, VERR_INVALID_POINTER); + + if (!cbBuf) + return VINF_SUCCESS; + + char szFile[RTPATH_MAX]; + + int rc = RTPathTemp(szFile, sizeof(szFile)); + if (RT_SUCCESS(rc)) + rc = RTPathAppend(szFile, sizeof(szFile), pszFileName); + + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(4, "Dumping %ld bytes to \"%s\"\n", cbBuf, szFile); + + RTFILE fh; + rc = RTFileOpen(&fh, szFile, RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_WRITE); + if (RT_SUCCESS(rc)) + { + rc = RTFileWrite(fh, pvBuf, cbBuf, NULL /* pcbWritten */); + RTFileClose(fh); + } + } + + return rc; +} +#endif + + +/** + * The actual worker routine (loop) for a started guest process. + * + * @return IPRT status code. + * @param PVBOXSERVICECTRLPROCESS Guest process. + */ +static int gstcntlProcessProcessWorker(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n", + pProcess, pProcess->StartupInfo.szCmd); + + int rc = VbglR3GuestCtrlConnect(&pProcess->uClientID); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Process thread \"%s\" (%p) failed to connect to the guest control service, rc=%Rrc\n", + pProcess->StartupInfo.szCmd, pProcess, rc); + RTThreadUserSignal(RTThreadSelf()); + return rc; + } + + VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n", + pProcess->StartupInfo.szCmd, pProcess->uClientID, pProcess->StartupInfo.uFlags); + + /* The process thread is not interested in receiving any commands; + * tell the host service. */ + rc = VbglR3GuestCtrlMsgFilterSet(pProcess->uClientID, 0 /* Skip all */, + 0 /* Filter mask to add */, 0 /* Filter mask to remove */); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Unable to set message filter, rc=%Rrc\n", rc); + /* Non-critical. */ + } + + rc = GstCntlSessionProcessAdd(pProcess->pSession, pProcess); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Errorwhile adding guest process \"%s\" (%p) to session process list, rc=%Rrc\n", + pProcess->StartupInfo.szCmd, pProcess, rc); + RTThreadUserSignal(RTThreadSelf()); + return rc; + } + + bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */ + + /* + * Prepare argument list. + */ + char **papszArgs; + uint32_t uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */ + rc = RTGetOptArgvFromString(&papszArgs, (int*)&uNumArgs, + (pProcess->StartupInfo.uNumArgs > 0) ? pProcess->StartupInfo.szArgs : "", NULL); + /* Did we get the same result? */ + Assert(pProcess->StartupInfo.uNumArgs == uNumArgs); + + /* + * Prepare environment variables list. + */ + char **papszEnv = NULL; + uint32_t uNumEnvVars = 0; /* Initialize in case of failing ... */ + if (RT_SUCCESS(rc)) + { + /* Prepare environment list. */ + if (pProcess->StartupInfo.uNumEnvVars) + { + papszEnv = (char **)RTMemAlloc(pProcess->StartupInfo.uNumEnvVars * sizeof(char*)); + AssertPtr(papszEnv); + uNumEnvVars = pProcess->StartupInfo.uNumEnvVars; + + const char *pszCur = pProcess->StartupInfo.szEnv; + uint32_t i = 0; + uint32_t cbLen = 0; + while (cbLen < pProcess->StartupInfo.cbEnv) + { + /* sanity check */ + if (i >= pProcess->StartupInfo.uNumEnvVars) + { + rc = VERR_INVALID_PARAMETER; + break; + } + int cbStr = RTStrAPrintf(&papszEnv[i++], "%s", pszCur); + if (cbStr < 0) + { + rc = VERR_NO_STR_MEMORY; + break; + } + pszCur += cbStr + 1; /* Skip terminating '\0' */ + cbLen += cbStr + 1; /* Skip terminating '\0' */ + } + Assert(i == pProcess->StartupInfo.uNumEnvVars); + } + } + + /* + * Create the environment. + */ + RTENV hEnv; + if (RT_SUCCESS(rc)) + rc = RTEnvClone(&hEnv, RTENV_DEFAULT); + if (RT_SUCCESS(rc)) + { + size_t i; + for (i = 0; i < uNumEnvVars && papszEnv; i++) + { + rc = RTEnvPutEx(hEnv, papszEnv[i]); + if (RT_FAILURE(rc)) + break; + } + if (RT_SUCCESS(rc)) + { + /* + * Setup the redirection of the standard stuff. + */ + /** @todo consider supporting: gcc stuff.c >file 2>&1. */ + RTHANDLE hStdIn; + PRTHANDLE phStdIn; + rc = gstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/, + &hStdIn, &phStdIn, &pProcess->hPipeStdInW); + if (RT_SUCCESS(rc)) + { + RTHANDLE hStdOut; + PRTHANDLE phStdOut; + rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT) + ? "|" : "/dev/null", + 1 /*STDOUT_FILENO*/, + &hStdOut, &phStdOut, &pProcess->hPipeStdOutR); + if (RT_SUCCESS(rc)) + { + RTHANDLE hStdErr; + PRTHANDLE phStdErr; + rc = gstcntlProcessSetupPipe( (pProcess->StartupInfo.uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR) + ? "|" : "/dev/null", + 2 /*STDERR_FILENO*/, + &hStdErr, &phStdErr, &pProcess->hPipeStdErrR); + if (RT_SUCCESS(rc)) + { + /* + * Create a poll set for the pipes and let the + * transport layer add stuff to it as well. + */ + rc = RTPollSetCreate(&pProcess->hPollSet); + if (RT_SUCCESS(rc)) + { + uint32_t uFlags = RTPOLL_EVT_ERROR; +#if 0 + /* Add reading event to pollset to get some more information. */ + uFlags |= RTPOLL_EVT_READ; +#endif + /* Stdin. */ + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pProcess->hPollSet, + pProcess->hPipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN); + /* Stdout. */ + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pProcess->hPollSet, + pProcess->hPipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT); + /* Stderr. */ + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pProcess->hPollSet, + pProcess->hPipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR); + /* IPC notification pipe. */ + if (RT_SUCCESS(rc)) + rc = RTPipeCreate(&pProcess->hNotificationPipeR, &pProcess->hNotificationPipeW, 0 /* Flags */); + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pProcess->hPollSet, + pProcess->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY); + if (RT_SUCCESS(rc)) + { + AssertPtr(pProcess->pSession); + bool fNeedsImpersonation = !(pProcess->pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_FORK); + + rc = gstcntlProcessCreateProcess(pProcess->StartupInfo.szCmd, papszArgs, hEnv, pProcess->StartupInfo.uFlags, + phStdIn, phStdOut, phStdErr, + fNeedsImpersonation ? pProcess->StartupInfo.szUser : NULL, + fNeedsImpersonation ? pProcess->StartupInfo.szPassword : NULL, + &pProcess->hProcess); + if (RT_FAILURE(rc)) + VBoxServiceError("Error starting process, rc=%Rrc\n", rc); + /* + * Tell the session thread that it can continue + * spawning guest processes. This needs to be done after the new + * process has been started because otherwise signal handling + * on (Open) Solaris does not work correctly (see @bugref{5068}). + */ + int rc2 = RTThreadUserSignal(RTThreadSelf()); + if (RT_SUCCESS(rc)) + rc = rc2; + fSignalled = true; + + if (RT_SUCCESS(rc)) + { + /* + * Close the child ends of any pipes and redirected files. + */ + rc2 = RTHandleClose(phStdIn); AssertRC(rc2); + phStdIn = NULL; + rc2 = RTHandleClose(phStdOut); AssertRC(rc2); + phStdOut = NULL; + rc2 = RTHandleClose(phStdErr); AssertRC(rc2); + phStdErr = NULL; + + /* Enter the process main loop. */ + rc = gstcntlProcessProcLoop(pProcess); + + /* + * The handles that are no longer in the set have + * been closed by the above call in order to prevent + * the guest from getting stuck accessing them. + * So, NIL the handles to avoid closing them again. + */ + if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet, + VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL))) + { + pProcess->hNotificationPipeR = NIL_RTPIPE; + pProcess->hNotificationPipeW = NIL_RTPIPE; + } + if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet, + VBOXSERVICECTRLPIPEID_STDERR, NULL))) + pProcess->hPipeStdErrR = NIL_RTPIPE; + if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet, + VBOXSERVICECTRLPIPEID_STDOUT, NULL))) + pProcess->hPipeStdOutR = NIL_RTPIPE; + if (RT_FAILURE(RTPollSetQueryHandle(pProcess->hPollSet, + VBOXSERVICECTRLPIPEID_STDIN, NULL))) + pProcess->hPipeStdInW = NIL_RTPIPE; + } + } + RTPollSetDestroy(pProcess->hPollSet); + + RTPipeClose(pProcess->hNotificationPipeR); + pProcess->hNotificationPipeR = NIL_RTPIPE; + RTPipeClose(pProcess->hNotificationPipeW); + pProcess->hNotificationPipeW = NIL_RTPIPE; + } + RTPipeClose(pProcess->hPipeStdErrR); + pProcess->hPipeStdErrR = NIL_RTPIPE; + RTHandleClose(phStdErr); + if (phStdErr) + RTHandleClose(phStdErr); + } + RTPipeClose(pProcess->hPipeStdOutR); + pProcess->hPipeStdOutR = NIL_RTPIPE; + RTHandleClose(&hStdOut); + if (phStdOut) + RTHandleClose(phStdOut); + } + RTPipeClose(pProcess->hPipeStdInW); + pProcess->hPipeStdInW = NIL_RTPIPE; + RTHandleClose(phStdIn); + } + } + RTEnvDestroy(hEnv); + } + + if (pProcess->uClientID) + { + if (RT_FAILURE(rc)) + { + VBGLR3GUESTCTRLCMDCTX ctx = { pProcess->uClientID, pProcess->uContextID }; + int rc2 = VbglR3GuestCtrlProcCbStatus(&ctx, + pProcess->uPID, PROC_STS_ERROR, rc, + NULL /* pvData */, 0 /* cbData */); + if ( RT_FAILURE(rc2) + && rc2 != VERR_NOT_FOUND) + VBoxServiceError("[PID %RU32]: Could not report process failure error; rc=%Rrc (process error %Rrc)\n", + pProcess->uPID, rc2, rc); + } + + /* Disconnect this client from the guest control service. This also cancels all + * outstanding host requests. */ + VBoxServiceVerbose(3, "[PID %RU32]: Disconnecting (client ID=%u) ...\n", + pProcess->uPID, pProcess->uClientID); + VbglR3GuestCtrlDisconnect(pProcess->uClientID); + pProcess->uClientID = 0; + } + + /* Free argument + environment variable lists. */ + if (uNumEnvVars) + { + for (uint32_t i = 0; i < uNumEnvVars; i++) + RTStrFree(papszEnv[i]); + RTMemFree(papszEnv); + } + if (uNumArgs) + RTGetOptArgvFree(papszArgs); + + /* + * If something went wrong signal the user event so that others don't wait + * forever on this thread. + */ + if (RT_FAILURE(rc) && !fSignalled) + RTThreadUserSignal(RTThreadSelf()); + + VBoxServiceVerbose(3, "[PID %RU32]: Thread of process \"%s\" ended with rc=%Rrc\n", + pProcess->uPID, pProcess->StartupInfo.szCmd, rc); + + /* Finally, update stopped status. */ + ASMAtomicXchgBool(&pProcess->fStopped, true); + + return rc; +} + + +static int gstcntlProcessLock(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + int rc = RTCritSectEnter(&pProcess->CritSect); + AssertRC(rc); + return rc; +} + + +/** + * Thread main routine for a started process. + * + * @return IPRT status code. + * @param RTTHREAD Pointer to the thread's data. + * @param void* User-supplied argument pointer. + * + */ +static DECLCALLBACK(int) gstcntlProcessThread(RTTHREAD ThreadSelf, void *pvUser) +{ + PVBOXSERVICECTRLPROCESS pProcess = (VBOXSERVICECTRLPROCESS*)pvUser; + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + return gstcntlProcessProcessWorker(pProcess); +} + + +static int gstcntlProcessUnlock(PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + int rc = RTCritSectLeave(&pProcess->CritSect); + AssertRC(rc); + return rc; +} + + +/** + * Executes (starts) a process on the guest. This causes a new thread to be created + * so that this function will not block the overall program execution. + * + * @return IPRT status code. + * @param pSession Guest session. + * @param pStartupInfo Startup info. + * @param uContextID Context ID to associate the process to start with. + + */ +int GstCntlProcessStart(const PVBOXSERVICECTRLSESSION pSession, + const PVBOXSERVICECTRLPROCSTARTUPINFO pStartupInfo, + uint32_t uContextID) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pStartupInfo, VERR_INVALID_POINTER); + + /* + * Allocate new thread data and assign it to our thread list. + */ + PVBOXSERVICECTRLPROCESS pProcess = (PVBOXSERVICECTRLPROCESS)RTMemAlloc(sizeof(VBOXSERVICECTRLPROCESS)); + if (!pProcess) + return VERR_NO_MEMORY; + + int rc = gstcntlProcessInit(pProcess, pSession, pStartupInfo, uContextID); + if (RT_SUCCESS(rc)) + { + static uint32_t s_uCtrlExecThread = 0; + if (s_uCtrlExecThread++ == UINT32_MAX) + s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */ + rc = RTThreadCreateF(&pProcess->Thread, gstcntlProcessThread, + pProcess /*pvUser*/, 0 /*cbStack*/, + RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Creating thread for guest process \"%s\" failed: rc=%Rrc, pProcess=%p\n", + pStartupInfo->szCmd, rc, pProcess); + + GstCntlProcessFree(pProcess); + } + else + { + VBoxServiceVerbose(4, "Waiting for thread to initialize ...\n"); + + /* Wait for the thread to initialize. */ + rc = RTThreadUserWait(pProcess->Thread, 60 * 1000 /* 60 seconds max. */); + AssertRC(rc); + if ( ASMAtomicReadBool(&pProcess->fShutdown) + || RT_FAILURE(rc)) + { + VBoxServiceError("Thread for process \"%s\" failed to start, rc=%Rrc\n", + pStartupInfo->szCmd, rc); + + GstCntlProcessFree(pProcess); + } + else + { + ASMAtomicXchgBool(&pProcess->fStarted, true); + } + } + } + + return rc; +} + + +static DECLCALLBACK(int) gstcntlProcessOnInput(PVBOXSERVICECTRLPROCESS pThis, + const PVBGLR3GUESTCTRLCMDCTX pHostCtx, + bool fPendingClose, void *pvBuf, uint32_t cbBuf) +{ + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + int rc; + + size_t cbWritten = 0; + if (pvBuf && cbBuf) + { + if (pThis->hPipeStdInW != NIL_RTPIPE) + { + rc = RTPipeWrite(pThis->hPipeStdInW, + pvBuf, cbBuf, &cbWritten); + } + else + rc = VINF_EOF; + } + else + rc = VERR_INVALID_PARAMETER; + + /* + * If this is the last write + we have really have written all data + * we need to close the stdin pipe on our end and remove it from + * the poll set. + */ + if ( fPendingClose + && (cbBuf == cbWritten)) + { + int rc2 = gstcntlProcessPollsetCloseInput(pThis, &pThis->hPipeStdInW); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status to send back to the host. */ + uint32_t uFlags = 0; /* No flags at the moment. */ + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(4, "[PID %RU32]: Written %RU32 bytes input, CID=%RU32, fPendingClose=%RTbool\n", + pThis->uPID, cbWritten, pHostCtx->uContextID, fPendingClose); + uStatus = INPUT_STS_WRITTEN; + } + else + { + if (rc == VERR_BAD_PIPE) + uStatus = INPUT_STS_TERMINATED; + else if (rc == VERR_BUFFER_OVERFLOW) + uStatus = INPUT_STS_OVERFLOW; + /* else undefined */ + } + + /* + * If there was an error and we did not set the host status + * yet, then do it now. + */ + if ( RT_FAILURE(rc) + && uStatus == INPUT_STS_UNDEFINED) + { + uStatus = INPUT_STS_ERROR; + uFlags = rc; + } + Assert(uStatus > INPUT_STS_UNDEFINED); + +#ifdef DEBUG + +#endif + int rc2 = VbglR3GuestCtrlProcCbStatusInput(pHostCtx, pThis->uPID, + uStatus, uFlags, (uint32_t)cbWritten); + if (RT_SUCCESS(rc)) + rc = rc2; + +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessOnInput returned with rc=%Rrc\n", + pThis->uPID, rc); +#endif + return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */ +} + + +static DECLCALLBACK(int) gstcntlProcessOnOutput(PVBOXSERVICECTRLPROCESS pThis, + const PVBGLR3GUESTCTRLCMDCTX pHostCtx, + uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags) +{ + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + const PVBOXSERVICECTRLSESSION pSession = pThis->pSession; + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + int rc; + + uint32_t cbBuf = cbToRead; + uint8_t *pvBuf = (uint8_t *)RTMemAlloc(cbBuf); + if (pvBuf) + { + PRTPIPE phPipe = uHandle == OUTPUT_HANDLE_ID_STDOUT + ? &pThis->hPipeStdOutR + : &pThis->hPipeStdErrR; + AssertPtr(phPipe); + + size_t cbRead = 0; + if (*phPipe != NIL_RTPIPE) + { + rc = RTPipeRead(*phPipe, pvBuf, cbBuf, &cbRead); + if (RT_FAILURE(rc)) + { + RTPollSetRemove(pThis->hPollSet, uHandle == OUTPUT_HANDLE_ID_STDERR + ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT); + RTPipeClose(*phPipe); + *phPipe = NIL_RTPIPE; + if (rc == VERR_BROKEN_PIPE) + rc = VINF_EOF; + } + } + else + rc = VINF_EOF; + +#ifdef DEBUG + if (RT_SUCCESS(rc)) + { + if ( pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT + && ( uHandle == OUTPUT_HANDLE_ID_STDOUT + || uHandle == OUTPUT_HANDLE_ID_STDOUT_DEPRECATED) + ) + { + char szDumpFile[RTPATH_MAX]; + if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdOut.txt", + pSession->StartupInfo.uSessionID, pThis->uPID)) rc = VERR_BUFFER_UNDERFLOW; + if (RT_SUCCESS(rc)) + rc = gstcntlProcessDumpToFile(szDumpFile, pvBuf, cbRead); + AssertRC(rc); + } + else if ( pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR + && uHandle == OUTPUT_HANDLE_ID_STDERR) + { + char szDumpFile[RTPATH_MAX]; + if (!RTStrPrintf(szDumpFile, sizeof(szDumpFile), "VBoxService_Session%RU32_PID%RU32_StdErr.txt", + pSession->StartupInfo.uSessionID, pThis->uPID)) + rc = VERR_BUFFER_UNDERFLOW; + if (RT_SUCCESS(rc)) + rc = gstcntlProcessDumpToFile(szDumpFile, pvBuf, cbRead); + AssertRC(rc); + } + } +#endif + + if (RT_SUCCESS(rc)) + { +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: Read %RU32 bytes output: uHandle=%RU32, CID=%RU32, uFlags=%x\n", + pThis->uPID, cbRead, uHandle, pHostCtx->uContextID, uFlags); +#endif + /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary + * data which the host needs to work with -- so just pass through all data unfiltered! */ + + /* Note: Since the context ID is unique the request *has* to be completed here, + * regardless whether we got data or not! Otherwise the waiting events + * on the host never will get completed! */ + rc = VbglR3GuestCtrlProcCbOutput(pHostCtx, pThis->uPID, uHandle, uFlags, + pvBuf, cbRead); + if ( RT_FAILURE(rc) + && rc == VERR_NOT_FOUND) /* Not critical if guest PID is not found on the host (anymore). */ + rc = VINF_SUCCESS; + } + + RTMemFree(pvBuf); + } + else + rc = VERR_NO_MEMORY; + +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: Reading output returned with rc=%Rrc\n", + pThis->uPID, rc); +#endif + return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */ +} + + +static DECLCALLBACK(int) gstcntlProcessOnTerm(PVBOXSERVICECTRLPROCESS pThis) +{ + AssertPtrReturn(pThis, VERR_INVALID_POINTER); + + if (!ASMAtomicXchgBool(&pThis->fShutdown, true)) + { + VBoxServiceVerbose(3, "[PID %RU32]: Setting shutdown flag ...\n", + pThis->uPID); + } + + return VINF_SUCCESS; /** @todo Return rc here as soon as RTReqQueue todos are fixed. */ +} + + +int gstcntlProcessRequestExV(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, + bool fAsync, RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction, + unsigned cArgs, va_list Args) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + /* pHostCtx is optional. */ + AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER); + if (!fAsync) + AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER); + + int rc = gstcntlProcessLock(pProcess); + if (RT_SUCCESS(rc)) + { +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessRequestExV fAsync=%RTbool, uTimeoutMS=%RU32, cArgs=%u\n", + pProcess->uPID, fAsync, uTimeoutMS, cArgs); +#endif + uint32_t uFlags = RTREQFLAGS_IPRT_STATUS; + if (fAsync) + { + Assert(uTimeoutMS == 0); + uFlags |= RTREQFLAGS_NO_WAIT; + } + + rc = RTReqQueueCallV(pProcess->hReqQueue, &pReq, uTimeoutMS, uFlags, + pfnFunction, cArgs, Args); + if (RT_SUCCESS(rc)) + { + /* Wake up the process' notification pipe to get + * the request being processed. */ + Assert(pProcess->hNotificationPipeW != NIL_RTPIPE); + size_t cbWritten = 0; + rc = RTPipeWrite(pProcess->hNotificationPipeW, "i", 1, &cbWritten); + if ( RT_SUCCESS(rc) + && cbWritten != 1) + { + VBoxServiceError("[PID %RU32]: Notification pipe got %zu bytes instead of 1\n", + pProcess->uPID, cbWritten); + } + else if (RT_UNLIKELY(RT_FAILURE(rc))) + VBoxServiceError("[PID %RU32]: Writing to notification pipe failed, rc=%Rrc\n", + pProcess->uPID, rc); + } + else + VBoxServiceError("[PID %RU32]: RTReqQueueCallV failed, rc=%Rrc\n", + pProcess->uPID, rc); + + int rc2 = gstcntlProcessUnlock(pProcess); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(3, "[PID %RU32]: gstcntlProcessRequestExV returned rc=%Rrc\n", + pProcess->uPID, rc); +#endif + return rc; +} + + +int gstcntlProcessRequestAsync(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, + PFNRT pfnFunction, unsigned cArgs, ...) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + /* pHostCtx is optional. */ + AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER); + + va_list va; + va_start(va, cArgs); + int rc = gstcntlProcessRequestExV(pProcess, pHostCtx, true /* fAsync */, 0 /* uTimeoutMS */, + NULL /* pReq */, pfnFunction, cArgs, va); + va_end(va); + + return rc; +} + + +int gstcntlProcessRequestWait(PVBOXSERVICECTRLPROCESS pProcess, const PVBGLR3GUESTCTRLCMDCTX pHostCtx, + RTMSINTERVAL uTimeoutMS, PRTREQ pReq, PFNRT pfnFunction, unsigned cArgs, ...) +{ + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + /* pHostCtx is optional. */ + AssertPtrReturn(pfnFunction, VERR_INVALID_POINTER); + + va_list va; + va_start(va, cArgs); + int rc = gstcntlProcessRequestExV(pProcess, pHostCtx, false /* fAsync */, uTimeoutMS, + pReq, pfnFunction, cArgs, va); + va_end(va); + + return rc; +} + + +int GstCntlProcessHandleInput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, + bool fPendingClose, void *pvBuf, uint32_t cbBuf) +{ + if (!ASMAtomicReadBool(&pProcess->fShutdown)) + return gstcntlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)gstcntlProcessOnInput, + 5 /* cArgs */, pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf); + + return gstcntlProcessOnInput(pProcess, pHostCtx, fPendingClose, pvBuf, cbBuf); +} + + +int GstCntlProcessHandleOutput(PVBOXSERVICECTRLPROCESS pProcess, PVBGLR3GUESTCTRLCMDCTX pHostCtx, + uint32_t uHandle, uint32_t cbToRead, uint32_t uFlags) +{ + if (!ASMAtomicReadBool(&pProcess->fShutdown)) + return gstcntlProcessRequestAsync(pProcess, pHostCtx, (PFNRT)gstcntlProcessOnOutput, + 5 /* cArgs */, pProcess, pHostCtx, uHandle, cbToRead, uFlags); + + return gstcntlProcessOnOutput(pProcess, pHostCtx, uHandle, cbToRead, uFlags); +} + + +int GstCntlProcessHandleTerm(PVBOXSERVICECTRLPROCESS pProcess) +{ + if (!ASMAtomicReadBool(&pProcess->fShutdown)) + return gstcntlProcessRequestAsync(pProcess, NULL /* pHostCtx */, (PFNRT)gstcntlProcessOnTerm, + 1 /* cArgs */, pProcess); + + return gstcntlProcessOnTerm(pProcess); +} + diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp new file mode 100644 index 00000000..d96b13ad --- /dev/null +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceControlSession.cpp @@ -0,0 +1,2364 @@ +/* $Id: VBoxServiceControlSession.cpp $ */ +/** @file + * VBoxServiceControlSession - Guest session handling. Also handles + * the forked session processes. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + + +/******************************************************************************* +* Header Files * +*******************************************************************************/ +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/dir.h> +#include <iprt/env.h> +#include <iprt/file.h> +#include <iprt/getopt.h> +#include <iprt/handle.h> +#include <iprt/mem.h> +#include <iprt/message.h> +#include <iprt/path.h> +#include <iprt/pipe.h> +#include <iprt/poll.h> +#include <iprt/process.h> + +#include "VBoxServiceInternal.h" +#include "VBoxServiceUtils.h" +#include "VBoxServiceControl.h" + +using namespace guestControl; + +/******************************************************************************* +* Externals * +*******************************************************************************/ +extern RTLISTANCHOR g_lstControlSessionThreads; +extern VBOXSERVICECTRLSESSION g_Session; + +extern int VBoxServiceLogCreate(const char *pszLogFile); +extern void VBoxServiceLogDestroy(void); + +/******************************************************************************* +* Internal Functions * +*******************************************************************************/ +static int gstcntlSessionFileDestroy(PVBOXSERVICECTRLFILE pFile); +static int gstcntlSessionFileAdd(PVBOXSERVICECTRLSESSION pSession, PVBOXSERVICECTRLFILE pFile); +static PVBOXSERVICECTRLFILE gstcntlSessionFileGetLocked(const PVBOXSERVICECTRLSESSION pSession, uint32_t uHandle); +static DECLCALLBACK(int) gstcntlSessionThread(RTTHREAD ThreadSelf, void *pvUser); +/* Host -> Guest handlers. */ +static int gstcntlSessionHandleDirRemove(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleFileClose(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleFileRead(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleFileWrite(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx, void *pvScratchBuf, size_t cbScratchBuf); +static int gstcntlSessionHandleFileSeek(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleFileTell(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandlePathRename(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleProcExec(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleProcInput(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx, void *pvScratchBuf, size_t cbScratchBuf); +static int gstcntlSessionHandleProcOutput(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); +static int gstcntlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, PVBGLR3GUESTCTRLCMDCTX pHostCtx); + + +/** Generic option indices for session fork arguments. */ +enum +{ + VBOXSERVICESESSIONOPT_FIRST = 1000, /* For initialization. */ +#ifdef DEBUG + VBOXSERVICESESSIONOPT_DUMP_STDOUT, + VBOXSERVICESESSIONOPT_DUMP_STDERR, +#endif + VBOXSERVICESESSIONOPT_LOG_FILE, + VBOXSERVICESESSIONOPT_USERNAME, + VBOXSERVICESESSIONOPT_SESSION_ID, + VBOXSERVICESESSIONOPT_SESSION_PROTO, + VBOXSERVICESESSIONOPT_THREAD_ID +}; + + +static int gstcntlSessionFileDestroy(PVBOXSERVICECTRLFILE pFile) +{ + AssertPtrReturn(pFile, VERR_INVALID_POINTER); + + int rc = RTFileClose(pFile->hFile); + if (RT_SUCCESS(rc)) + { + /* Remove file entry in any case. */ + RTListNodeRemove(&pFile->Node); + /* Destroy this object. */ + RTMemFree(pFile); + } + + return rc; +} + + +/** @todo No locking done yet! */ +static PVBOXSERVICECTRLFILE gstcntlSessionFileGetLocked(const PVBOXSERVICECTRLSESSION pSession, + uint32_t uHandle) +{ + AssertPtrReturn(pSession, NULL); + + PVBOXSERVICECTRLFILE pFileCur = NULL; + /** @todo Use a map later! */ + RTListForEach(&pSession->lstFiles, pFileCur, VBOXSERVICECTRLFILE, Node) + { + if (pFileCur->uHandle == uHandle) + return pFileCur; + } + + return NULL; +} + + +static int gstcntlSessionHandleDirRemove(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + char szDir[RTPATH_MAX]; + uint32_t uFlags = 0; + + int rc = VbglR3GuestCtrlDirGetRemove(pHostCtx, + /* Directory to remove. */ + szDir, sizeof(szDir), + /* Flags of type DIRREMOVE_FLAG_. */ + &uFlags); + if (RT_SUCCESS(rc)) + { + uint32_t uFlagsRemRec = 0; + bool fRecursive = false; + + if (!(uFlags & ~DIRREMOVE_FLAG_VALID_MASK)) + { + if (uFlags & DIRREMOVE_FLAG_RECURSIVE) + { + /* Note: DIRREMOVE_FLAG_RECURSIVE must be set explicitly. + * Play safe here. */ + fRecursive = true; + } + + if (uFlags & DIRREMOVE_FLAG_CONTENT_AND_DIR) + { + /* Setting direct value is intentional. */ + uFlagsRemRec = RTDIRRMREC_F_CONTENT_AND_DIR; + } + + if (uFlags & DIRREMOVE_FLAG_CONTENT_ONLY) + { + /* Setting direct value is intentional. */ + uFlagsRemRec |= RTDIRRMREC_F_CONTENT_ONLY; + } + } + else + rc = VERR_NOT_SUPPORTED; + + VBoxServiceVerbose(4, "[Dir %s]: Removing with uFlags=0x%x, fRecursive=%RTbool\n", + szDir, uFlags, fRecursive); + + if (RT_SUCCESS(rc)) + { + /** @todo Add own recursive function (or a new IPRT function w/ callback?) to + * provide guest-to-host progress reporting. */ + if (fRecursive) + rc = RTDirRemoveRecursive(szDir, uFlagsRemRec); + else + rc = RTDirRemove(szDir); + } + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc); + if (RT_FAILURE(rc2)) + VBoxServiceError("[Dir %s]: Failed to report removing status, rc=%Rrc\n", + szDir, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Removing directory \"%s\" returned rc=%Rrc\n", + szDir, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileOpen(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + char szFile[RTPATH_MAX]; + char szAccess[64]; + char szDisposition[64]; + char szSharing[64]; + uint32_t uCreationMode = 0; + uint64_t uOffset = 0; + uint32_t uHandle = 0; + + int rc = VbglR3GuestCtrlFileGetOpen(pHostCtx, + /* File to open. */ + szFile, sizeof(szFile), + /* Open mode. */ + szAccess, sizeof(szAccess), + /* Disposition. */ + szDisposition, sizeof(szDisposition), + /* Sharing. */ + szSharing, sizeof(szSharing), + /* Creation mode. */ + &uCreationMode, + /* Offset. */ + &uOffset); + VBoxServiceVerbose(4, "[File %s]: szAccess=%s, szDisposition=%s, szSharing=%s, uOffset=%RU64, rc=%Rrc\n", + szFile, szAccess, szDisposition, szSharing, uOffset, rc); + + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLFILE pFile = (PVBOXSERVICECTRLFILE)RTMemAllocZ(sizeof(VBOXSERVICECTRLFILE)); + if (pFile) + { + if (!strlen(szFile)) + rc = VERR_INVALID_PARAMETER; + + if ( RT_SUCCESS(rc) + && !RTStrPrintf(pFile->szName, sizeof(pFile->szName), "%s", szFile)) + rc = VERR_NO_MEMORY; + + if (RT_SUCCESS(rc)) + { + uint64_t fFlags; + rc = RTFileModeToFlagsEx(szAccess, szDisposition, + NULL /* pszSharing, not used yet */, &fFlags); + VBoxServiceVerbose(4, "[File %s]: Opening flags=0x%x, rc=%Rrc\n", + pFile->szName, fFlags, rc); + if (RT_SUCCESS(rc)) + rc = RTFileOpen(&pFile->hFile, pFile->szName, fFlags); + if ( RT_SUCCESS(rc) + && uOffset) + { + /* Seeking is optional. However, the whole operation + * will fail if we don't succeed seeking to the wanted position. */ + rc = RTFileSeek(pFile->hFile, (int64_t)uOffset, RTFILE_SEEK_BEGIN, NULL /* Current offset */); + if (RT_FAILURE(rc)) + VBoxServiceError("[File %s]: Seeking to offset %RU64 failed; rc=%Rrc\n", + pFile->szName, uOffset, rc); + } + else if (RT_FAILURE(rc)) + VBoxServiceError("[File %s]: Opening failed; rc=%Rrc\n", + pFile->szName, rc); + } + + if (RT_SUCCESS(rc)) + { + uHandle = VBOX_GUESTCTRL_CONTEXTID_GET_OBJECT(pHostCtx->uContextID); + pFile->uHandle = uHandle; + + /* rc = */ RTListAppend(&pSession->lstFiles, &pFile->Node); + + VBoxServiceVerbose(3, "[File %s]: Opened (ID=%RU32)\n", + pFile->szName, pFile->uHandle); + } + + if (RT_FAILURE(rc)) + { + if (pFile->hFile) + RTFileClose(pFile->hFile); + RTMemFree(pFile); + } + } + else + rc = VERR_NO_MEMORY; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbOpen(pHostCtx, rc, uHandle); + if (RT_FAILURE(rc2)) + VBoxServiceError("[File %s]: Failed to report file open status, rc=%Rrc\n", + szFile, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Opening file \"%s\" (open mode=\"%s\", disposition=\"%s\", creation mode=0x%x returned rc=%Rrc\n", + szFile, szAccess, szDisposition, uCreationMode, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileClose(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + int rc = VbglR3GuestCtrlFileGetClose(pHostCtx, &uHandle /* File handle to close */); + if (RT_SUCCESS(rc)) + { + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + rc = gstcntlSessionFileDestroy(pFile); + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbClose(pHostCtx, rc); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file close status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Closing file \"%s\" (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileRead(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint32_t cbToRead; + int rc = VbglR3GuestCtrlFileGetRead(pHostCtx, &uHandle, &cbToRead); + if (RT_SUCCESS(rc)) + { + void *pvDataRead = pvScratchBuf; + size_t cbRead = 0; + + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + if (cbToRead) + { + if (cbToRead > cbScratchBuf) + { + pvDataRead = RTMemAlloc(cbToRead); + if (!pvDataRead) + rc = VERR_NO_MEMORY; + } + + if (RT_LIKELY(RT_SUCCESS(rc))) + rc = RTFileRead(pFile->hFile, pvDataRead, cbToRead, &cbRead); + } + else + rc = VERR_BUFFER_UNDERFLOW; + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, pvDataRead, (uint32_t)cbRead); + if ( cbToRead > cbScratchBuf + && pvDataRead) + RTMemFree(pvDataRead); + + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file read status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Reading file \"%s\" (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileReadAt(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint32_t cbToRead; int64_t iOffset; + + int rc = VbglR3GuestCtrlFileGetReadAt(pHostCtx, + &uHandle, &cbToRead, (uint64_t *)&iOffset); + if (RT_SUCCESS(rc)) + { + void *pvDataRead = pvScratchBuf; + size_t cbRead = 0; + + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + if (cbToRead) + { + if (cbToRead > cbScratchBuf) + { + pvDataRead = RTMemAlloc(cbToRead); + if (!pvDataRead) + rc = VERR_NO_MEMORY; + } + + if (RT_LIKELY(RT_SUCCESS(rc))) + rc = RTFileReadAt(pFile->hFile, iOffset, pvDataRead, cbToRead, &cbRead); + } + else + rc = VERR_BUFFER_UNDERFLOW; + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbRead(pHostCtx, rc, pvDataRead, (uint32_t)cbRead); + if ( cbToRead > cbScratchBuf + && pvDataRead) + RTMemFree(pvDataRead); + + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file read status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Reading file \"%s\" at offset (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileWrite(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); + AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint32_t cbToWrite; + + int rc = VbglR3GuestCtrlFileGetWrite(pHostCtx, &uHandle, + pvScratchBuf, cbScratchBuf, + &cbToWrite); + if (RT_SUCCESS(rc)) + { + size_t cbWritten = 0; + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + rc = RTFileWrite(pFile->hFile, pvScratchBuf, cbToWrite, &cbWritten); +#ifdef DEBUG + VBoxServiceVerbose(4, "[File %s]: Writing pvScratchBuf=%p, cbToWrite=%RU32, cbWritten=%zu, rc=%Rrc\n", + pFile->szName, pvScratchBuf, cbToWrite, cbWritten, rc); +#endif + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file write status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Writing file \"%s\" (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileWriteAt(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); + AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint32_t cbToWrite; int64_t iOffset; + + int rc = VbglR3GuestCtrlFileGetWriteAt(pHostCtx, &uHandle, + pvScratchBuf, cbScratchBuf, + &cbToWrite, (uint64_t *)&iOffset); + if (RT_SUCCESS(rc)) + { + size_t cbWritten = 0; + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + rc = RTFileWriteAt(pFile->hFile, iOffset, + pvScratchBuf, cbToWrite, &cbWritten); +#ifdef DEBUG + VBoxServiceVerbose(4, "[File %s]: Writing iOffset=%RI64, pvScratchBuf=%p, cbToWrite=%RU32, cbWritten=%zu, rc=%Rrc\n", + pFile->szName, iOffset, pvScratchBuf, cbToWrite, cbWritten, rc); +#endif + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbWrite(pHostCtx, rc, (uint32_t)cbWritten); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file write status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Writing file \"%s\" at offset (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileSeek(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint32_t uSeekMethod; + uint64_t uOffset; /* Will be converted to int64_t. */ + + uint64_t uOffsetActual = 0; + + int rc = VbglR3GuestCtrlFileGetSeek(pHostCtx, &uHandle, + &uSeekMethod, &uOffset); + if (RT_SUCCESS(rc)) + { + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + unsigned uSeekMethodIPRT; + switch (uSeekMethod) + { + case GUEST_FILE_SEEKTYPE_BEGIN: + uSeekMethodIPRT = RTFILE_SEEK_BEGIN; + break; + + case GUEST_FILE_SEEKTYPE_CURRENT: + uSeekMethodIPRT = RTFILE_SEEK_CURRENT; + break; + + case GUEST_FILE_SEEKTYPE_END: + uSeekMethodIPRT = RTFILE_SEEK_END; + break; + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(rc)) + { + rc = RTFileSeek(pFile->hFile, (int64_t)uOffset, + uSeekMethodIPRT, &uOffsetActual); +#ifdef DEBUG + VBoxServiceVerbose(4, "[File %s]: Seeking to iOffset=%RI64, uSeekMethodIPRT=%RU16, rc=%Rrc\n", + pFile->szName, (int64_t)uOffset, uSeekMethodIPRT, rc); +#endif + } + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbSeek(pHostCtx, rc, uOffsetActual); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file seek status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Seeking file \"%s\" (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandleFileTell(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + PVBOXSERVICECTRLFILE pFile = NULL; + + uint32_t uHandle = 0; + uint64_t uOffsetActual = 0; + + int rc = VbglR3GuestCtrlFileGetTell(pHostCtx, &uHandle); + if (RT_SUCCESS(rc)) + { + pFile = gstcntlSessionFileGetLocked(pSession, uHandle); + if (pFile) + { + uOffsetActual = RTFileTell(pFile->hFile); +#ifdef DEBUG + VBoxServiceVerbose(4, "[File %s]: Telling uOffsetActual=%RU64\n", + pFile->szName, uOffsetActual); +#endif + } + else + rc = VERR_NOT_FOUND; + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlFileCbTell(pHostCtx, rc, uOffsetActual); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report file tell status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Telling file \"%s\" (handle=%RU32) returned rc=%Rrc\n", + pFile ? pFile->szName : "<Not found>", uHandle, rc); +#endif + return rc; +} + + +static int gstcntlSessionHandlePathRename(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + char szSource[RTPATH_MAX]; + char szDest[RTPATH_MAX]; + uint32_t uFlags = 0; + + int rc = VbglR3GuestCtrlPathGetRename(pHostCtx, + szSource, sizeof(szSource), + szDest, sizeof(szDest), + /* Flags of type PATHRENAME_FLAG_. */ + &uFlags); + if (RT_SUCCESS(rc)) + { + if (uFlags & ~PATHRENAME_FLAG_VALID_MASK) + rc = VERR_NOT_SUPPORTED; + + VBoxServiceVerbose(4, "Renaming \"%s\" to \"%s\", uFlags=0x%x, rc=%Rrc\n", + szSource, szDest, uFlags, rc); + + if (RT_SUCCESS(rc)) + { + if (uFlags & PATHRENAME_FLAG_NO_REPLACE) + uFlags |= RTPATHRENAME_FLAGS_NO_REPLACE; + + if (uFlags & PATHRENAME_FLAG_REPLACE) + uFlags |= RTPATHRENAME_FLAGS_REPLACE; + + if (uFlags & PATHRENAME_FLAG_NO_SYMLINKS) + uFlags |= RTPATHRENAME_FLAGS_NO_SYMLINKS; + + rc = RTPathRename(szSource, szDest, uFlags); + } + + /* Report back in any case. */ + int rc2 = VbglR3GuestCtrlMsgReply(pHostCtx, rc); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to report renaming status, rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Renaming \"%s\" to \"%s\" returned rc=%Rrc\n", + szSource, szDest, rc); +#endif + return rc; +} + + +/** + * Handles starting a guest processes. + * + * @returns IPRT status code. + * @param pSession Guest session. + * @param pHostCtx Host context. + */ +int gstcntlSessionHandleProcExec(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + bool fStartAllowed = false; /* Flag indicating whether starting a process is allowed or not. */ + + switch (pHostCtx->uProtocol) + { + case 1: /* Guest Additions < 4.3. */ + if (pHostCtx->uNumParms != 11) + rc = VERR_NOT_SUPPORTED; + break; + + case 2: /* Guest Additions >= 4.3. */ + if (pHostCtx->uNumParms != 12) + rc = VERR_NOT_SUPPORTED; + break; + + default: + rc = VERR_NOT_SUPPORTED; + break; + } + + if (RT_SUCCESS(rc)) + { + VBOXSERVICECTRLPROCSTARTUPINFO startupInfo; + RT_ZERO(startupInfo); + + /* Initialize maximum environment block size -- needed as input + * parameter to retrieve the stuff from the host. On output this then + * will contain the actual block size. */ + startupInfo.cbEnv = sizeof(startupInfo.szEnv); + + rc = VbglR3GuestCtrlProcGetStart(pHostCtx, + /* Command */ + startupInfo.szCmd, sizeof(startupInfo.szCmd), + /* Flags */ + &startupInfo.uFlags, + /* Arguments */ + startupInfo.szArgs, sizeof(startupInfo.szArgs), &startupInfo.uNumArgs, + /* Environment */ + startupInfo.szEnv, &startupInfo.cbEnv, &startupInfo.uNumEnvVars, + /* Credentials; for hosts with VBox < 4.3 (protocol version 1). + * For protocl v2 and up the credentials are part of the session + * opening call. */ + startupInfo.szUser, sizeof(startupInfo.szUser), + startupInfo.szPassword, sizeof(startupInfo.szPassword), + /* Timeout (in ms) */ + &startupInfo.uTimeLimitMS, + /* Process priority */ + &startupInfo.uPriority, + /* Process affinity */ + startupInfo.uAffinity, sizeof(startupInfo.uAffinity), &startupInfo.uNumAffinity); + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(3, "Request to start process szCmd=%s, uFlags=0x%x, szArgs=%s, szEnv=%s, uTimeout=%RU32\n", + startupInfo.szCmd, startupInfo.uFlags, + startupInfo.uNumArgs ? startupInfo.szArgs : "<None>", + startupInfo.uNumEnvVars ? startupInfo.szEnv : "<None>", + startupInfo.uTimeLimitMS); + + rc = GstCntlSessionProcessStartAllowed(pSession, &fStartAllowed); + if (RT_SUCCESS(rc)) + { + if (fStartAllowed) + { + rc = GstCntlProcessStart(pSession, &startupInfo, pHostCtx->uContextID); + } + else + rc = VERR_MAX_PROCS_REACHED; /* Maximum number of processes reached. */ + } + } + } + + /* In case of an error we need to notify the host to not wait forever for our response. */ + if (RT_FAILURE(rc)) + { + VBoxServiceError("Starting process failed with rc=%Rrc, protocol=%RU32, parameters=%RU32\n", + rc, pHostCtx->uProtocol, pHostCtx->uNumParms); + + /* Don't report back if we didn't supply sufficient buffer for getting + * the actual command -- we don't have the matching context ID. */ + if (rc != VERR_TOO_MUCH_DATA) + { + /* + * Note: The context ID can be 0 because we mabye weren't able to fetch the command + * from the host. The host in case has to deal with that! + */ + int rc2 = VbglR3GuestCtrlProcCbStatus(pHostCtx, 0 /* PID, invalid */, + PROC_STS_ERROR, rc, + NULL /* pvData */, 0 /* cbData */); + if (RT_FAILURE(rc2)) + VBoxServiceError("Error sending start process status to host, rc=%Rrc\n", rc2); + } + } + + return rc; +} + + +/** + * Sends stdin input to a specific guest process. + * + * @returns IPRT status code. + * @param pSession The session which is in charge. + * @param pHostCtx The host context to use. + * @param pvScratchBuf The scratch buffer. + * @param cbScratchBuf The scratch buffer size for retrieving the input data. + */ +int gstcntlSessionHandleProcInput(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + AssertPtrReturn(cbScratchBuf, VERR_INVALID_PARAMETER); + AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); + + uint32_t uPID; + uint32_t uFlags; + uint32_t cbSize; + + uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */ + uint32_t cbWritten = 0; /* Number of bytes written to the guest. */ + + /* + * Ask the host for the input data. + */ + int rc = VbglR3GuestCtrlProcGetInput(pHostCtx, &uPID, &uFlags, + pvScratchBuf, cbScratchBuf, &cbSize); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Failed to retrieve process input command for PID=%RU32, rc=%Rrc\n", + uPID, rc); + } + else if (cbSize > cbScratchBuf) + { + VBoxServiceError("Too much process input received, rejecting: uPID=%RU32, cbSize=%RU32, cbScratchBuf=%RU32\n", + uPID, cbSize, cbScratchBuf); + rc = VERR_TOO_MUCH_DATA; + } + else + { + /* + * Is this the last input block we need to deliver? Then let the pipe know ... + */ + bool fPendingClose = false; + if (uFlags & INPUT_FLAG_EOF) + { + fPendingClose = true; +#ifdef DEBUG + VBoxServiceVerbose(4, "Got last process input block for PID=%RU32 (%RU32 bytes) ...\n", + uPID, cbSize); +#endif + } + + PVBOXSERVICECTRLPROCESS pProcess = GstCntlSessionRetainProcess(pSession, uPID); + if (pProcess) + { + rc = GstCntlProcessHandleInput(pProcess, pHostCtx, fPendingClose, + pvScratchBuf, cbSize); + if (RT_FAILURE(rc)) + VBoxServiceError("Error handling input command for PID=%RU32, rc=%Rrc\n", + uPID, rc); + GstCntlProcessRelease(pProcess); + } + else + rc = VERR_NOT_FOUND; + } + +#ifdef DEBUG + VBoxServiceVerbose(4, "Setting input for PID=%RU32 resulted in rc=%Rrc\n", + uPID, rc); +#endif + return rc; +} + + +/** + * Gets stdout/stderr output of a specific guest process. + * + * @return IPRT status code. + * @param pSession The session which is in charge. + * @param pHostCtx The host context to use. + */ +int gstcntlSessionHandleProcOutput(PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + uint32_t uPID; + uint32_t uHandleID; + uint32_t uFlags; + + int rc = VbglR3GuestCtrlProcGetOutput(pHostCtx, &uPID, &uHandleID, &uFlags); +#ifdef DEBUG_andy + VBoxServiceVerbose(4, "Getting output for PID=%RU32, CID=%RU32, uHandleID=%RU32, uFlags=%RU32\n", + uPID, pHostCtx->uContextID, uHandleID, uFlags); +#endif + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLPROCESS pProcess = GstCntlSessionRetainProcess(pSession, uPID); + if (pProcess) + { + rc = GstCntlProcessHandleOutput(pProcess, pHostCtx, + uHandleID, _64K /* cbToRead */, uFlags); + if (RT_FAILURE(rc)) + VBoxServiceError("Error getting output for PID=%RU32, rc=%Rrc\n", + uPID, rc); + GstCntlProcessRelease(pProcess); + } + else + rc = VERR_NOT_FOUND; + } + +#ifdef DEBUG_andy + VBoxServiceVerbose(4, "Getting output for PID=%RU32 resulted in rc=%Rrc\n", + uPID, rc); +#endif + return rc; +} + + +/** + * Tells a guest process to terminate. + * + * @return IPRT status code. + * @param pSession The session which is in charge. + * @param pHostCtx The host context to use. + */ +int gstcntlSessionHandleProcTerminate(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + uint32_t uPID; + int rc = VbglR3GuestCtrlProcGetTerminate(pHostCtx, &uPID); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLPROCESS pProcess = GstCntlSessionRetainProcess(pSession, uPID); + if (pProcess) + { + rc = GstCntlProcessHandleTerm(pProcess); + + GstCntlProcessRelease(pProcess); + } + else + rc = VERR_NOT_FOUND; + } + +#ifdef DEBUG_andy + VBoxServiceVerbose(4, "Terminating PID=%RU32 resulted in rc=%Rrc\n", + uPID, rc); +#endif + return rc; +} + + +int gstcntlSessionHandleProcWaitFor(const PVBOXSERVICECTRLSESSION pSession, + PVBGLR3GUESTCTRLCMDCTX pHostCtx) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + + uint32_t uPID; + uint32_t uWaitFlags; uint32_t uTimeoutMS; + + int rc = VbglR3GuestCtrlProcGetWaitFor(pHostCtx, &uPID, &uWaitFlags, &uTimeoutMS); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLPROCESS pProcess = GstCntlSessionRetainProcess(pSession, uPID); + if (pProcess) + { + rc = VERR_NOT_IMPLEMENTED; /** @todo */ + GstCntlProcessRelease(pProcess); + } + else + rc = VERR_NOT_FOUND; + } + + return rc; +} + + +int GstCntlSessionHandler(PVBOXSERVICECTRLSESSION pSession, + uint32_t uMsg, PVBGLR3GUESTCTRLCMDCTX pHostCtx, + void *pvScratchBuf, size_t cbScratchBuf, + volatile bool *pfShutdown) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pHostCtx, VERR_INVALID_POINTER); + AssertPtrReturn(pvScratchBuf, VERR_INVALID_POINTER); + AssertPtrReturn(pfShutdown, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + /** + * Only anonymous sessions (that is, sessions which run with local + * service privileges) or forked session processes can do certain + * operations. + */ + bool fImpersonated = ( pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_FORK + || pSession->uFlags & VBOXSERVICECTRLSESSION_FLAG_ANONYMOUS); + + switch (uMsg) + { + case HOST_SESSION_CLOSE: + /* Shutdown (this fork). */ + rc = GstCntlSessionClose(pSession); + *pfShutdown = true; /* Shutdown in any case. */ + break; + + case HOST_DIR_REMOVE: + rc = fImpersonated + ? gstcntlSessionHandleDirRemove(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + case HOST_EXEC_CMD: + rc = gstcntlSessionHandleProcExec(pSession, pHostCtx); + break; + + case HOST_EXEC_SET_INPUT: + rc = gstcntlSessionHandleProcInput(pSession, pHostCtx, + pvScratchBuf, cbScratchBuf); + break; + + case HOST_EXEC_GET_OUTPUT: + rc = gstcntlSessionHandleProcOutput(pSession, pHostCtx); + break; + + case HOST_EXEC_TERMINATE: + rc = gstcntlSessionHandleProcTerminate(pSession, pHostCtx); + break; + + case HOST_EXEC_WAIT_FOR: + rc = gstcntlSessionHandleProcWaitFor(pSession, pHostCtx); + break; + + case HOST_FILE_OPEN: + rc = fImpersonated + ? gstcntlSessionHandleFileOpen(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_CLOSE: + rc = fImpersonated + ? gstcntlSessionHandleFileClose(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_READ: + rc = fImpersonated + ? gstcntlSessionHandleFileRead(pSession, pHostCtx, + pvScratchBuf, cbScratchBuf) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_READ_AT: + rc = fImpersonated + ? gstcntlSessionHandleFileReadAt(pSession, pHostCtx, + pvScratchBuf, cbScratchBuf) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_WRITE: + rc = fImpersonated + ? gstcntlSessionHandleFileWrite(pSession, pHostCtx, + pvScratchBuf, cbScratchBuf) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_WRITE_AT: + rc = fImpersonated + ? gstcntlSessionHandleFileWriteAt(pSession, pHostCtx, + pvScratchBuf, cbScratchBuf) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_SEEK: + rc = fImpersonated + ? gstcntlSessionHandleFileSeek(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + case HOST_FILE_TELL: + rc = fImpersonated + ? gstcntlSessionHandleFileTell(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + case HOST_PATH_RENAME: + rc = fImpersonated + ? gstcntlSessionHandlePathRename(pSession, pHostCtx) + : VERR_NOT_SUPPORTED; + break; + + default: + rc = VbglR3GuestCtrlMsgSkip(pHostCtx->uClientID); + VBoxServiceVerbose(3, "Unsupported message (uMsg=%RU32, cParms=%RU32) from host, skipping\n", + uMsg, pHostCtx->uNumParms); + break; + } + + if (RT_FAILURE(rc)) + VBoxServiceError("Error while handling message (uMsg=%RU32, cParms=%RU32), rc=%Rrc\n", + uMsg, pHostCtx->uNumParms, rc); + + return rc; +} + + +/** + * Thread main routine for a forked guest session process. + * This thread runs in the main executable to control the forked + * session process. + * + * @return IPRT status code. + * @param RTTHREAD Pointer to the thread's data. + * @param void* User-supplied argument pointer. + * + */ +static DECLCALLBACK(int) gstcntlSessionThread(RTTHREAD ThreadSelf, void *pvUser) +{ + PVBOXSERVICECTRLSESSIONTHREAD pThread = (PVBOXSERVICECTRLSESSIONTHREAD)pvUser; + AssertPtrReturn(pThread, VERR_INVALID_POINTER); + + uint32_t uSessionID = pThread->StartupInfo.uSessionID; + + uint32_t uClientID; + int rc = VbglR3GuestCtrlConnect(&uClientID); + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(3, "Session ID=%RU32 thread running, client ID=%RU32\n", + uSessionID, uClientID); + + /* The session thread is not interested in receiving any commands; + * tell the host service. */ + rc = VbglR3GuestCtrlMsgFilterSet(uClientID, 0 /* Skip all */, + 0 /* Filter mask to add */, 0 /* Filter mask to remove */); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Unable to set message filter, rc=%Rrc\n", rc); + /* Non-critical. */ + rc = VINF_SUCCESS; + } + } + else + VBoxServiceError("Error connecting to guest control service, rc=%Rrc\n", rc); + + if (RT_FAILURE(rc)) + pThread->fShutdown = true; + + /* Let caller know that we're done initializing, regardless of the result. */ + int rc2 = RTThreadUserSignal(RTThreadSelf()); + AssertRC(rc2); + + if (RT_FAILURE(rc)) + return rc; + + bool fProcessAlive = true; + RTPROCSTATUS ProcessStatus; + RT_ZERO(ProcessStatus); + + int rcWait; + if (RT_SUCCESS(rc)) + { + uint32_t uTimeoutsMS = 30 * 1000; /** @todo Make this configurable. Later. */ + uint64_t u64TimeoutStart = 0; + + for (;;) + { + rcWait = RTProcWaitNoResume(pThread->hProcess, RTPROCWAIT_FLAGS_NOBLOCK, + &ProcessStatus); + if (RT_UNLIKELY(rcWait == VERR_INTERRUPTED)) + continue; + else if ( rcWait == VINF_SUCCESS + || rcWait == VERR_PROCESS_NOT_FOUND) + { + fProcessAlive = false; + break; + } + else + AssertMsgBreak(rcWait == VERR_PROCESS_RUNNING, + ("Got unexpected rc=%Rrc while waiting for session process termination\n", rcWait)); + + if (ASMAtomicReadBool(&pThread->fShutdown)) + { + if (!u64TimeoutStart) + { + VBoxServiceVerbose(3, "Notifying guest session process (PID=%RU32, session ID=%RU32) ...\n", + pThread->hProcess, uSessionID); + + VBGLR3GUESTCTRLCMDCTX hostCtx = { uClientID, + VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(uSessionID), + pThread->StartupInfo.uProtocol, 2 /* uNumParms */ }; + rc = VbglR3GuestCtrlSessionClose(&hostCtx, 0 /* uFlags */); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Unable to notify guest session process (PID=%RU32, session ID=%RU32), rc=%Rrc\n", + pThread->hProcess, uSessionID, rc); + + if (rc == VERR_NOT_SUPPORTED) + { + /* Terminate guest session process in case it's not supported by a too old host. */ + rc = RTProcTerminate(pThread->hProcess); + VBoxServiceVerbose(3, "Terminating guest session process (PID=%RU32) ended with rc=%Rrc\n", + pThread->hProcess, rc); + } + break; + } + + VBoxServiceVerbose(3, "Guest session ID=%RU32 thread was asked to terminate, waiting for session process to exit (%RU32ms timeout) ...\n", + uSessionID, uTimeoutsMS); + u64TimeoutStart = RTTimeMilliTS(); + + continue; /* Don't waste time on waiting. */ + } + if (RTTimeMilliTS() - u64TimeoutStart > uTimeoutsMS) + { + VBoxServiceVerbose(3, "Guest session ID=%RU32 process did not shut down within time\n", + uSessionID); + break; + } + } + + RTThreadSleep(100); /* Wait a bit. */ + } + + if (!fProcessAlive) + { + VBoxServiceVerbose(2, "Guest session ID=%RU32 process terminated with rc=%Rrc, reason=%ld, status=%d\n", + uSessionID, rcWait, + ProcessStatus.enmReason, ProcessStatus.iStatus); + } + } + + uint32_t uSessionStatus = GUEST_SESSION_NOTIFYTYPE_UNDEFINED; + uint32_t uSessionRc = VINF_SUCCESS; /** uint32_t vs. int. */ + + if (fProcessAlive) + { + for (int i = 0; i < 3; i++) + { + VBoxServiceVerbose(2, "Guest session ID=%RU32 process still alive, killing attempt %d/3\n", + uSessionID, i + 1); + + rc = RTProcTerminate(pThread->hProcess); + if (RT_SUCCESS(rc)) + break; + RTThreadSleep(3000); + } + + VBoxServiceVerbose(2, "Guest session ID=%RU32 process termination resulted in rc=%Rrc\n", + uSessionID, rc); + + uSessionStatus = RT_SUCCESS(rc) + ? GUEST_SESSION_NOTIFYTYPE_TOK : GUEST_SESSION_NOTIFYTYPE_TOA; + } + else + { + if (RT_SUCCESS(rcWait)) + { + switch (ProcessStatus.enmReason) + { + case RTPROCEXITREASON_NORMAL: + uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN; + break; + + case RTPROCEXITREASON_ABEND: + uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA; + break; + + case RTPROCEXITREASON_SIGNAL: + uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TES; + break; + + default: + AssertMsgFailed(("Unhandled process termination reason (%ld)\n", + ProcessStatus.enmReason)); + uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEA; + break; + } + } + else + { + /* If we didn't find the guest process anymore, just assume it + * terminated normally. */ + uSessionStatus = GUEST_SESSION_NOTIFYTYPE_TEN; + } + } + + VBoxServiceVerbose(3, "Guest session ID=%RU32 thread ended with sessionStatus=%RU32, sessionRc=%Rrc\n", + uSessionID, uSessionStatus, uSessionRc); + + /* Report final status. */ + Assert(uSessionStatus != GUEST_SESSION_NOTIFYTYPE_UNDEFINED); + VBGLR3GUESTCTRLCMDCTX ctx = { uClientID, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(uSessionID) }; + rc2 = VbglR3GuestCtrlSessionNotify(&ctx, + uSessionStatus, uSessionRc); + if (RT_FAILURE(rc2)) + VBoxServiceError("Reporting session ID=%RU32 final status failed with rc=%Rrc\n", + uSessionID, rc2); + + VbglR3GuestCtrlDisconnect(uClientID); + + VBoxServiceVerbose(3, "Session ID=%RU32 thread ended with rc=%Rrc\n", + uSessionID, rc); + return rc; +} + + +RTEXITCODE gstcntlSessionForkWorker(PVBOXSERVICECTRLSESSION pSession) +{ + AssertPtrReturn(pSession, RTEXITCODE_FAILURE); + + bool fSessionFilter = true; + + VBoxServiceVerbose(0, "Hi, this is guest session ID=%RU32\n", + pSession->StartupInfo.uSessionID); + + uint32_t uClientID; + int rc = VbglR3GuestCtrlConnect(&uClientID); + if (RT_SUCCESS(rc)) + { + /* Set session filter. This prevents the guest control + * host service to send messages which belong to another + * session we don't want to handle. */ + uint32_t uFilterAdd = + VBOX_GUESTCTRL_FILTER_BY_SESSION(pSession->StartupInfo.uSessionID); + rc = VbglR3GuestCtrlMsgFilterSet(uClientID, + VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID), + uFilterAdd, 0 /* Filter remove */); + VBoxServiceVerbose(3, "Setting message filterAdd=0x%x returned %Rrc\n", + uFilterAdd, rc); + + if ( RT_FAILURE(rc) + && rc == VERR_NOT_SUPPORTED) + { + /* No session filter available. Skip. */ + fSessionFilter = false; + + rc = VINF_SUCCESS; + } + + VBoxServiceVerbose(1, "Using client ID=%RU32\n", uClientID); + } + else + VBoxServiceError("Error connecting to guest control service, rc=%Rrc\n", rc); + + /* Report started status. */ + VBGLR3GUESTCTRLCMDCTX ctx = { uClientID, VBOX_GUESTCTRL_CONTEXTID_MAKE_SESSION(pSession->StartupInfo.uSessionID) }; + int rc2 = VbglR3GuestCtrlSessionNotify(&ctx, + GUEST_SESSION_NOTIFYTYPE_STARTED, VINF_SUCCESS); + if (RT_FAILURE(rc2)) + { + VBoxServiceError("Reporting session ID=%RU32 started status failed with rc=%Rrc\n", + pSession->StartupInfo.uSessionID, rc2); + + /* + * If session status cannot be posted to the host for + * some reason, bail out. + */ + if (RT_SUCCESS(rc)) + rc = rc2; + } + + /* Allocate a scratch buffer for commands which also send + * payload data with them. */ + uint32_t cbScratchBuf = _64K; /** @todo Make buffer size configurable via guest properties/argv! */ + AssertReturn(RT_IS_POWER_OF_TWO(cbScratchBuf), RTEXITCODE_FAILURE); + uint8_t *pvScratchBuf = NULL; + + if (RT_SUCCESS(rc)) + { + pvScratchBuf = (uint8_t*)RTMemAlloc(cbScratchBuf); + if (!pvScratchBuf) + rc = VERR_NO_MEMORY; + } + + if (RT_SUCCESS(rc)) + { + bool fShutdown = false; + + VBGLR3GUESTCTRLCMDCTX ctxHost = { uClientID, 0 /* Context ID, zeroed */, + pSession->StartupInfo.uProtocol }; + for (;;) + { + VBoxServiceVerbose(3, "Waiting for host msg ...\n"); + uint32_t uMsg = 0; + uint32_t cParms = 0; + rc = VbglR3GuestCtrlMsgWaitFor(uClientID, &uMsg, &cParms); + if (rc == VERR_TOO_MUCH_DATA) + { +#ifdef DEBUG + VBoxServiceVerbose(4, "Message requires %RU32 parameters, but only 2 supplied -- retrying request (no error!)...\n", cParms); +#endif + rc = VINF_SUCCESS; /* Try to get "real" message in next block below. */ + } + else if (RT_FAILURE(rc)) + VBoxServiceVerbose(3, "Getting host message failed with %Rrc\n", rc); /* VERR_GEN_IO_FAILURE seems to be normal if ran into timeout. */ + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(4, "Msg=%RU32 (%RU32 parms) retrieved\n", uMsg, cParms); + + /* Set number of parameters for current host context. */ + ctxHost.uNumParms = cParms; + + /* ... and pass it on to the session handler. */ + rc = GstCntlSessionHandler(pSession, uMsg, &ctxHost, + pvScratchBuf, cbScratchBuf, &fShutdown); + } + + if (fShutdown) + break; + + /* Let's sleep for a bit and let others run ... */ + RTThreadYield(); + } + } + + VBoxServiceVerbose(0, "Session %RU32 ended\n", pSession->StartupInfo.uSessionID); + + if (pvScratchBuf) + RTMemFree(pvScratchBuf); + + if (uClientID) + { + VBoxServiceVerbose(3, "Disconnecting client ID=%RU32 ...\n", uClientID); + VbglR3GuestCtrlDisconnect(uClientID); + } + + VBoxServiceVerbose(3, "Session worker returned with rc=%Rrc\n", rc); + return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; +} + + +/** + * Finds a (formerly) started guest process given by its PID and increases + * its reference count. Must be decreased by the caller with GstCntlProcessRelease(). + * Note: This does *not lock the process! + * + * @return PVBOXSERVICECTRLTHREAD Guest process if found, otherwise NULL. + * @param PVBOXSERVICECTRLSESSION Pointer to guest session where to search process in. + * @param uPID PID to search for. + */ +PVBOXSERVICECTRLPROCESS GstCntlSessionRetainProcess(PVBOXSERVICECTRLSESSION pSession, uint32_t uPID) +{ + AssertPtrReturn(pSession, NULL); + + PVBOXSERVICECTRLPROCESS pProcess = NULL; + int rc = RTCritSectEnter(&pSession->CritSect); + if (RT_SUCCESS(rc)) + { + PVBOXSERVICECTRLPROCESS pCurProcess; + RTListForEach(&pSession->lstProcesses, pCurProcess, VBOXSERVICECTRLPROCESS, Node) + { + if (pCurProcess->uPID == uPID) + { + rc = RTCritSectEnter(&pCurProcess->CritSect); + if (RT_SUCCESS(rc)) + { + pCurProcess->cRefs++; + rc = RTCritSectLeave(&pCurProcess->CritSect); + AssertRC(rc); + } + + if (RT_SUCCESS(rc)) + pProcess = pCurProcess; + break; + } + } + + rc = RTCritSectLeave(&pSession->CritSect); + AssertRC(rc); + } + + return pProcess; +} + + +int GstCntlSessionClose(PVBOXSERVICECTRLSESSION pSession) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + VBoxServiceVerbose(0, "Session %RU32 is about to close ...\n", + pSession->StartupInfo.uSessionID); + + int rc = RTCritSectEnter(&pSession->CritSect); + if (RT_SUCCESS(rc)) + { + /* + * Close all guest processes. + */ + VBoxServiceVerbose(0, "Stopping all guest processes ...\n"); + + /* Signal all guest processes in the active list that we want to shutdown. */ + size_t cProcesses = 0; + PVBOXSERVICECTRLPROCESS pProcess; + RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node) + { + GstCntlProcessStop(pProcess); + cProcesses++; + } + + VBoxServiceVerbose(1, "%zu guest processes were signalled to stop\n", cProcesses); + + /* Wait for all active threads to shutdown and destroy the active thread list. */ + pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node); + while (pProcess) + { + PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node); + bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node); + + int rc2 = RTCritSectLeave(&pSession->CritSect); + AssertRC(rc2); + + rc2 = GstCntlProcessWait(pProcess, + 30 * 1000 /* Wait 30 seconds max. */, + NULL /* rc */); + + int rc3 = RTCritSectEnter(&pSession->CritSect); + AssertRC(rc3); + + if (RT_SUCCESS(rc2)) + GstCntlProcessFree(pProcess); + + if (fLast) + break; + + pProcess = pNext; + } + +#ifdef DEBUG + pProcess = RTListGetFirst(&pSession->lstProcesses, VBOXSERVICECTRLPROCESS, Node); + while (pProcess) + { + PVBOXSERVICECTRLPROCESS pNext = RTListNodeGetNext(&pProcess->Node, VBOXSERVICECTRLPROCESS, Node); + bool fLast = RTListNodeIsLast(&pSession->lstProcesses, &pProcess->Node); + + VBoxServiceVerbose(1, "Process %p (PID %RU32) still in list\n", + pProcess, pProcess->uPID); + if (fLast) + break; + + pProcess = pNext; + } +#endif + AssertMsg(RTListIsEmpty(&pSession->lstProcesses), + ("Guest process list still contains entries when it should not\n")); + + /* + * Close all left guest files. + */ + VBoxServiceVerbose(0, "Closing all guest files ...\n"); + + PVBOXSERVICECTRLFILE pFile; + pFile = RTListGetFirst(&pSession->lstFiles, VBOXSERVICECTRLFILE, Node); + while (pFile) + { + PVBOXSERVICECTRLFILE pNext = RTListNodeGetNext(&pFile->Node, VBOXSERVICECTRLFILE, Node); + bool fLast = RTListNodeIsLast(&pSession->lstFiles, &pFile->Node); + + int rc2 = gstcntlSessionFileDestroy(pFile); + if (RT_FAILURE(rc2)) + { + VBoxServiceError("Unable to close file \"%s\"; rc=%Rrc\n", + pFile->szName, rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + /* Keep going. */ + } + + if (fLast) + break; + + pFile = pNext; + } + + AssertMsg(RTListIsEmpty(&pSession->lstFiles), + ("Guest file list still contains entries when it should not\n")); + + int rc2 = RTCritSectLeave(&pSession->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + return rc; +} + + +int GstCntlSessionDestroy(PVBOXSERVICECTRLSESSION pSession) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + int rc = GstCntlSessionClose(pSession); + + /* Destroy critical section. */ + RTCritSectDelete(&pSession->CritSect); + + return rc; +} + + +int GstCntlSessionInit(PVBOXSERVICECTRLSESSION pSession, uint32_t uFlags) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + + RTListInit(&pSession->lstProcesses); + RTListInit(&pSession->lstFiles); + + pSession->uFlags = uFlags; + + /* Init critical section for protecting the thread lists. */ + int rc = RTCritSectInit(&pSession->CritSect); + AssertRC(rc); + + return rc; +} + + +/** + * Adds a guest process to a session's process list. + * + * @return IPRT status code. + * @param pSession Guest session to add process to. + * @param pProcess Guest process to add. + */ +int GstCntlSessionProcessAdd(PVBOXSERVICECTRLSESSION pSession, + PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + int rc = RTCritSectEnter(&pSession->CritSect); + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(3, "Adding process (PID %RU32) to session ID=%RU32\n", + pProcess->uPID, pSession->StartupInfo.uSessionID); + + /* Add process to session list. */ + /* rc = */ RTListAppend(&pSession->lstProcesses, &pProcess->Node); + + int rc2 = RTCritSectLeave(&pSession->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + return VINF_SUCCESS; +} + + +/** + * Removes a guest process from a session's process list. + * + * @return IPRT status code. + * @param pSession Guest session to remove process from. + * @param pProcess Guest process to remove. + */ +int GstCntlSessionProcessRemove(PVBOXSERVICECTRLSESSION pSession, + PVBOXSERVICECTRLPROCESS pProcess) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pProcess, VERR_INVALID_POINTER); + + int rc = RTCritSectEnter(&pSession->CritSect); + if (RT_SUCCESS(rc)) + { + VBoxServiceVerbose(3, "Removing process (PID %RU32) from session ID=%RU32\n", + pProcess->uPID, pSession->StartupInfo.uSessionID); + Assert(pProcess->cRefs == 0); + + RTListNodeRemove(&pProcess->Node); + + int rc2 = RTCritSectLeave(&pSession->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + return VINF_SUCCESS; +} + + +/** + * Determines whether starting a new guest process according to the + * maximum number of concurrent guest processes defined is allowed or not. + * + * @return IPRT status code. + * @param pbAllowed True if starting (another) guest process + * is allowed, false if not. + */ +int GstCntlSessionProcessStartAllowed(const PVBOXSERVICECTRLSESSION pSession, + bool *pbAllowed) +{ + AssertPtrReturn(pSession, VERR_INVALID_POINTER); + AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER); + + int rc = RTCritSectEnter(&pSession->CritSect); + if (RT_SUCCESS(rc)) + { + /* + * Check if we're respecting our memory policy by checking + * how many guest processes are started and served already. + */ + bool fLimitReached = false; + if (pSession->uProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */ + { + uint32_t uProcsRunning = 0; + PVBOXSERVICECTRLPROCESS pProcess; + RTListForEach(&pSession->lstProcesses, pProcess, VBOXSERVICECTRLPROCESS, Node) + uProcsRunning++; + + VBoxServiceVerbose(3, "Maximum served guest processes set to %u, running=%u\n", + pSession->uProcsMaxKept, uProcsRunning); + + int32_t iProcsLeft = (pSession->uProcsMaxKept - uProcsRunning - 1); + if (iProcsLeft < 0) + { + VBoxServiceVerbose(3, "Maximum running guest processes reached (%u)\n", + pSession->uProcsMaxKept); + fLimitReached = true; + } + } + + *pbAllowed = !fLimitReached; + + int rc2 = RTCritSectLeave(&pSession->CritSect); + if (RT_SUCCESS(rc)) + rc = rc2; + } + + return rc; +} + + +/** + * Creates a guest session. This will spawn a new VBoxService.exe instance under + * behalf of the given user which then will act as a session host. On successful + * open, the session will be added to the given session thread list. + * + * @return IPRT status code. + * @param pList Which list to use to store the session thread in. + * @param pSessionStartupInfo Session startup info. + * @param ppSessionThread Returns newly created session thread on success. + * Optional. + */ +int GstCntlSessionThreadCreate(PRTLISTANCHOR pList, + const PVBOXSERVICECTRLSESSIONSTARTUPINFO pSessionStartupInfo, + PVBOXSERVICECTRLSESSIONTHREAD *ppSessionThread) +{ + AssertPtrReturn(pList, VERR_INVALID_POINTER); + AssertPtrReturn(pSessionStartupInfo, VERR_INVALID_POINTER); + /* ppSessionThread is optional. */ + +#ifdef DEBUG + PVBOXSERVICECTRLSESSIONTHREAD pSessionCur; + /* Check for existing session in debug mode. Should never happen because of + * Main consistency. */ + RTListForEach(pList, pSessionCur, VBOXSERVICECTRLSESSIONTHREAD, Node) + { + if (pSessionCur->StartupInfo.uSessionID == pSessionStartupInfo->uSessionID) + { + AssertMsgFailed(("Guest session thread ID=%RU32 (%p) already exists when it should not\n", + pSessionCur->StartupInfo.uSessionID, pSessionCur)); + return VERR_ALREADY_EXISTS; + } + } +#endif + int rc = VINF_SUCCESS; + + /* Static counter to help tracking session thread <-> process relations. */ + static uint32_t s_uCtrlSessionThread = 0; + if (s_uCtrlSessionThread++ == UINT32_MAX) + s_uCtrlSessionThread = 0; /* Wrap around to not let IPRT freak out. */ + + PVBOXSERVICECTRLSESSIONTHREAD pSessionThread = + (PVBOXSERVICECTRLSESSIONTHREAD)RTMemAllocZ(sizeof(VBOXSERVICECTRLSESSIONTHREAD)); + if (pSessionThread) + { + /* Copy over session startup info. */ + memcpy(&pSessionThread->StartupInfo, pSessionStartupInfo, + sizeof(VBOXSERVICECTRLSESSIONSTARTUPINFO)); + + pSessionThread->fShutdown = false; + pSessionThread->fStarted = false; + pSessionThread->fStopped = false; + + /* Is this an anonymous session? */ + /* Anonymous sessions run with the same privileges as the main VBoxService executable. */ + bool fAnonymous = !RT_BOOL(strlen(pSessionThread->StartupInfo.szUser)); + if (fAnonymous) + { + Assert(!strlen(pSessionThread->StartupInfo.szPassword)); + Assert(!strlen(pSessionThread->StartupInfo.szDomain)); + + VBoxServiceVerbose(3, "New anonymous guest session ID=%RU32 created, uFlags=%x, using protocol %RU32\n", + pSessionStartupInfo->uSessionID, + pSessionStartupInfo->uFlags, + pSessionStartupInfo->uProtocol); + } + else + { + VBoxServiceVerbose(3, "Forking new guest session ID=%RU32, szUser=%s, szPassword=%s, szDomain=%s, uFlags=%x, using protocol %RU32\n", + pSessionStartupInfo->uSessionID, + pSessionStartupInfo->szUser, +#ifdef DEBUG + pSessionStartupInfo->szPassword, +#else + "XXX", /* Never show passwords in release mode. */ +#endif + pSessionStartupInfo->szDomain, + pSessionStartupInfo->uFlags, + pSessionStartupInfo->uProtocol); + } + + rc = RTCritSectInit(&pSessionThread->CritSect); + AssertRC(rc); + + /* Fork child doing the actual session handling. */ + char szExeName[RTPATH_MAX]; + char *pszExeName = RTProcGetExecutablePath(szExeName, sizeof(szExeName)); + if (pszExeName) + { + char szParmUserName[GUESTPROCESS_MAX_USER_LEN + 32]; + if (!fAnonymous) + { + if (!RTStrPrintf(szParmUserName, sizeof(szParmUserName), "--user=%s", pSessionThread->StartupInfo.szUser)) + rc = VERR_BUFFER_OVERFLOW; + } + char szParmSessionID[32]; + if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionID, sizeof(szParmSessionID), "--session-id=%RU32", + pSessionThread->StartupInfo.uSessionID)) + { + rc = VERR_BUFFER_OVERFLOW; + } + char szParmSessionProto[32]; + if (RT_SUCCESS(rc) && !RTStrPrintf(szParmSessionProto, sizeof(szParmSessionProto), "--session-proto=%RU32", + pSessionThread->StartupInfo.uProtocol)) + { + rc = VERR_BUFFER_OVERFLOW; + } +#ifdef DEBUG + char szParmThreadId[32]; + if (RT_SUCCESS(rc) && !RTStrPrintf(szParmThreadId, sizeof(szParmThreadId), "--thread-id=%RU32", + s_uCtrlSessionThread)) + { + rc = VERR_BUFFER_OVERFLOW; + } +#endif /* DEBUG */ + if (RT_SUCCESS(rc)) + { + int iOptIdx = 0; /* Current index in argument vector. */ + + char const *papszArgs[16]; + papszArgs[iOptIdx++] = pszExeName; + papszArgs[iOptIdx++] = "guestsession"; + papszArgs[iOptIdx++] = szParmSessionID; + papszArgs[iOptIdx++] = szParmSessionProto; +#ifdef DEBUG + papszArgs[iOptIdx++] = szParmThreadId; +#endif /* DEBUG */ + if (!fAnonymous) + papszArgs[iOptIdx++] = szParmUserName; + + /* Add same verbose flags as parent process. */ + int rc2 = VINF_SUCCESS; + char szParmVerbose[32] = { 0 }; + for (int i = 0; i < g_cVerbosity && RT_SUCCESS(rc2); i++) + { + if (i == 0) + rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "-"); + if (RT_FAILURE(rc2)) + break; + rc2 = RTStrCat(szParmVerbose, sizeof(szParmVerbose), "v"); + } + if (RT_SUCCESS(rc2)) + papszArgs[iOptIdx++] = szParmVerbose; + + /* Add log file handling. Each session will have an own + * log file, naming based on the parent log file. */ + char szParmLogFile[RTPATH_MAX]; + if ( RT_SUCCESS(rc2) + && strlen(g_szLogFile)) + { + char *pszLogFile = RTStrDup(g_szLogFile); + if (pszLogFile) + { + char *pszLogExt = NULL; + if (RTPathHasExt(pszLogFile)) + pszLogExt = RTStrDup(RTPathExt(pszLogFile)); + RTPathStripExt(pszLogFile); + char *pszLogSuffix; +#ifndef DEBUG + if (RTStrAPrintf(&pszLogSuffix, "-%RU32-%s", + pSessionStartupInfo->uSessionID, + pSessionStartupInfo->szUser) < 0) + { + rc2 = VERR_NO_MEMORY; + } +#else + if (RTStrAPrintf(&pszLogSuffix, "-%RU32-%RU32-%s", + pSessionStartupInfo->uSessionID, + s_uCtrlSessionThread, + pSessionStartupInfo->szUser) < 0) + { + rc2 = VERR_NO_MEMORY; + } +#endif /* DEBUG */ + else + { + rc2 = RTStrAAppend(&pszLogFile, pszLogSuffix); + if (RT_SUCCESS(rc2) && pszLogExt) + rc2 = RTStrAAppend(&pszLogFile, pszLogExt); + if (RT_SUCCESS(rc2)) + { + if (!RTStrPrintf(szParmLogFile, sizeof(szParmLogFile), + "--logfile=%s", pszLogFile)) + { + rc2 = VERR_BUFFER_OVERFLOW; + } + } + RTStrFree(pszLogSuffix); + } + if (RT_FAILURE(rc2)) + VBoxServiceError("Error building session logfile string for session %RU32 (user %s), rc=%Rrc\n", + pSessionStartupInfo->uSessionID, pSessionStartupInfo->szUser, rc2); + if (pszLogExt) + RTStrFree(pszLogExt); + RTStrFree(pszLogFile); + } + if (RT_SUCCESS(rc2)) + papszArgs[iOptIdx++] = szParmLogFile; + + rc = rc2; + } + else if (RT_FAILURE(rc2)) + rc = rc2; +#ifdef DEBUG + VBoxServiceVerbose(4, "Argv building rc=%Rrc, session flags=%x\n", + rc, g_Session.uFlags); + char szParmDumpStdOut[32]; + if ( RT_SUCCESS(rc) + && g_Session.uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT) + { + if (!RTStrPrintf(szParmDumpStdOut, sizeof(szParmDumpStdOut), "--dump-stdout")) + rc = VERR_BUFFER_OVERFLOW; + if (RT_SUCCESS(rc)) + papszArgs[iOptIdx++] = szParmDumpStdOut; + } + char szParmDumpStdErr[32]; + if ( RT_SUCCESS(rc) + && g_Session.uFlags & VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR) + { + if (!RTStrPrintf(szParmDumpStdErr, sizeof(szParmDumpStdErr), "--dump-stderr")) + rc = VERR_BUFFER_OVERFLOW; + if (RT_SUCCESS(rc)) + papszArgs[iOptIdx++] = szParmDumpStdErr; + } +#endif + papszArgs[iOptIdx++] = NULL; + + if (g_cVerbosity > 3) + { + VBoxServiceVerbose(4, "Forking parameters:\n"); + + iOptIdx = 0; + while (papszArgs[iOptIdx]) + VBoxServiceVerbose(4, "\t%s\n", papszArgs[iOptIdx++]); + } + + uint32_t uProcFlags = RTPROC_FLAGS_SERVICE + | RTPROC_FLAGS_HIDDEN; /** @todo More flags from startup info? */ + +#if 0 /* Pipe handling not needed (yet). */ + /* Setup pipes. */ + rc = GstcntlProcessSetupPipe("|", 0 /*STDIN_FILENO*/, + &pSession->StdIn.hChild, &pSession->StdIn.phChild, &pSession->hStdInW); + if (RT_SUCCESS(rc)) + { + rc = GstcntlProcessSetupPipe("|", 1 /*STDOUT_FILENO*/, + &pSession->StdOut.hChild, &pSession->StdOut.phChild, &pSession->hStdOutR); + if (RT_SUCCESS(rc)) + { + rc = GstcntlProcessSetupPipe("|", 2 /*STDERR_FILENO*/, + &pSession->StdErr.hChild, &pSession->StdErr.phChild, &pSession->hStdErrR); + if (RT_SUCCESS(rc)) + { + rc = RTPollSetCreate(&pSession->hPollSet); + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdInW, RTPOLL_EVT_ERROR, + VBOXSERVICECTRLPIPEID_STDIN); + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdOutR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, + VBOXSERVICECTRLPIPEID_STDOUT); + if (RT_SUCCESS(rc)) + rc = RTPollSetAddPipe(pSession->hPollSet, pSession->hStdErrR, RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, + VBOXSERVICECTRLPIPEID_STDERR); + } + + if (RT_SUCCESS(rc)) + { + /* Fork the thing. */ + /** @todo Do we need a custom environment block? */ + rc = RTProcCreateEx(pszExeName, papszArgs, RTENV_DEFAULT, uProcFlags, + pSession->StdIn.phChild, pSession->StdOut.phChild, pSession->StdErr.phChild, + !fAnonymous ? pSession->StartupInfo.szUser : NULL, + !fAnonymous ? pSession->StartupInfo.szPassword : NULL, + &pSession->hProcess); + } + + if (RT_SUCCESS(rc)) + { + /* + * Close the child ends of any pipes and redirected files. + */ + int rc2 = RTHandleClose(pSession->StdIn.phChild); AssertRC(rc2); + pSession->StdIn.phChild = NULL; + rc2 = RTHandleClose(pSession->StdOut.phChild); AssertRC(rc2); + pSession->StdOut.phChild = NULL; + rc2 = RTHandleClose(pSession->StdErr.phChild); AssertRC(rc2); + pSession->StdErr.phChild = NULL; + } + } + } +#else + RTHANDLE hStdIn; + if (RT_SUCCESS(rc)) + rc = RTFileOpenBitBucket(&hStdIn.u.hFile, RTFILE_O_READ); + if (RT_SUCCESS(rc)) + { + hStdIn.enmType = RTHANDLETYPE_FILE; + + RTHANDLE hStdOutAndErr; + rc = RTFileOpenBitBucket(&hStdOutAndErr.u.hFile, RTFILE_O_WRITE); + if (RT_SUCCESS(rc)) + { + hStdOutAndErr.enmType = RTHANDLETYPE_FILE; + + /** @todo Set custom/cloned guest session environment block. */ + rc = RTProcCreateEx(pszExeName, papszArgs, RTENV_DEFAULT, uProcFlags, + &hStdIn, &hStdOutAndErr, &hStdOutAndErr, + !fAnonymous ? pSessionThread->StartupInfo.szUser : NULL, + !fAnonymous ? pSessionThread->StartupInfo.szPassword : NULL, + &pSessionThread->hProcess); + + RTFileClose(hStdOutAndErr.u.hFile); + } + + RTFileClose(hStdIn.u.hFile); + } +#endif + } + } + else + rc = VERR_FILE_NOT_FOUND; + + if (RT_SUCCESS(rc)) + { + /* Start session thread. */ + rc = RTThreadCreateF(&pSessionThread->Thread, gstcntlSessionThread, + pSessionThread /*pvUser*/, 0 /*cbStack*/, + RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "sess%u", s_uCtrlSessionThread); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Creating session thread failed, rc=%Rrc\n", rc); + } + else + { + /* Wait for the thread to initialize. */ + rc = RTThreadUserWait(pSessionThread->Thread, 60 * 1000 /* 60s timeout */); + if ( ASMAtomicReadBool(&pSessionThread->fShutdown) + || RT_FAILURE(rc)) + { + VBoxServiceError("Thread for session ID=%RU32 failed to start, rc=%Rrc\n", + pSessionThread->StartupInfo.uSessionID, rc); + if (RT_SUCCESS(rc)) + rc = VERR_CANT_CREATE; /** @todo Find a better rc. */ + } + else + { + VBoxServiceVerbose(2, "Thread for session ID=%RU32 started\n", + pSessionThread->StartupInfo.uSessionID); + + ASMAtomicXchgBool(&pSessionThread->fStarted, true); + + /* Add session to list. */ + /* rc = */ RTListAppend(pList, &pSessionThread->Node); + if (ppSessionThread) /* Return session if wanted. */ + *ppSessionThread = pSessionThread; + } + } + } + + if (RT_FAILURE(rc)) + { + RTMemFree(pSessionThread); + } + } + else + rc = VERR_NO_MEMORY; + + VBoxServiceVerbose(3, "Forking session thread returned returned rc=%Rrc\n", rc); + return rc; +} + + +/** + * Waits for a formerly opened guest session process to close. + * + * @return IPRT status code. + * @param pThread Guest session thread to wait for. + * @param uTimeoutMS Waiting timeout (in ms). + * @param uFlags Closing flags. + */ +int GstCntlSessionThreadWait(PVBOXSERVICECTRLSESSIONTHREAD pThread, + uint32_t uTimeoutMS, uint32_t uFlags) +{ + AssertPtrReturn(pThread, VERR_INVALID_POINTER); + /** @todo Validate closing flags. */ + + if (pThread->Thread == NIL_RTTHREAD) + { + AssertMsgFailed(("Guest session thread of session %p does not exist when it should\n", + pThread)); + return VERR_NOT_FOUND; + } + + int rc = VINF_SUCCESS; + + /* + * The fork should have received the same closing request, + * so just wait for the process to close. + */ + if (ASMAtomicReadBool(&pThread->fStarted)) + { + /* Ask the thread to shutdown. */ + ASMAtomicXchgBool(&pThread->fShutdown, true); + + VBoxServiceVerbose(3, "Waiting for session thread ID=%RU32 to close (%RU32ms) ...\n", + pThread->StartupInfo.uSessionID, uTimeoutMS); + + int rcThread; + rc = RTThreadWait(pThread->Thread, uTimeoutMS, &rcThread); + if (RT_FAILURE(rc)) + { + VBoxServiceError("Waiting for session thread ID=%RU32 to close failed with rc=%Rrc\n", + pThread->StartupInfo.uSessionID, rc); + } + else + VBoxServiceVerbose(3, "Session thread ID=%RU32 ended with rc=%Rrc\n", + pThread->StartupInfo.uSessionID, rcThread); + } + + return rc; +} + +/** + * Waits for the specified session thread to end and remove + * it from the session thread list. + * + * @return IPRT status code. + * @param pThread Session thread to destroy. + * @param uFlags Closing flags. + */ +int GstCntlSessionThreadDestroy(PVBOXSERVICECTRLSESSIONTHREAD pThread, uint32_t uFlags) +{ + AssertPtrReturn(pThread, VERR_INVALID_POINTER); + + int rc = GstCntlSessionThreadWait(pThread, + 5 * 60 * 1000 /* 5 minutes timeout */, uFlags); + + /* Remove session from list and destroy object. */ + RTListNodeRemove(&pThread->Node); + + RTMemFree(pThread); + pThread = NULL; + + return rc; +} + +/** + * Close all formerly opened guest session threads. + * Note: Caller is responsible for locking! + * + * @return IPRT status code. + * @param pList Which list to close the session threads for. + * @param uFlags Closing flags. + */ +int GstCntlSessionThreadDestroyAll(PRTLISTANCHOR pList, uint32_t uFlags) +{ + AssertPtrReturn(pList, VERR_INVALID_POINTER); + + int rc = VINF_SUCCESS; + + /*int rc = VbglR3GuestCtrlClose + if (RT_FAILURE(rc)) + VBoxServiceError("Cancelling pending waits failed; rc=%Rrc\n", rc);*/ + + PVBOXSERVICECTRLSESSIONTHREAD pSessionThread + = RTListGetFirst(pList, VBOXSERVICECTRLSESSIONTHREAD, Node); + while (pSessionThread) + { + PVBOXSERVICECTRLSESSIONTHREAD pSessionThreadNext = + RTListGetNext(pList, pSessionThread, VBOXSERVICECTRLSESSIONTHREAD, Node); + bool fLast = RTListNodeIsLast(pList, &pSessionThread->Node); + + int rc2 = GstCntlSessionThreadDestroy(pSessionThread, uFlags); + if (RT_FAILURE(rc2)) + { + VBoxServiceError("Closing session thread failed with rc=%Rrc\n", rc2); + if (RT_SUCCESS(rc)) + rc = rc2; + /* Keep going. */ + } + + if (fLast) + break; + + pSessionThread = pSessionThreadNext; + } + + return rc; +} + +RTEXITCODE VBoxServiceControlSessionForkInit(int argc, char **argv) +{ + static const RTGETOPTDEF s_aOptions[] = + { +#ifdef DEBUG + { "--dump-stdout", VBOXSERVICESESSIONOPT_DUMP_STDOUT, RTGETOPT_REQ_NOTHING }, + { "--dump-stderr", VBOXSERVICESESSIONOPT_DUMP_STDERR, RTGETOPT_REQ_NOTHING }, +#endif + { "--logfile", VBOXSERVICESESSIONOPT_LOG_FILE, RTGETOPT_REQ_STRING }, + { "--user", VBOXSERVICESESSIONOPT_USERNAME, RTGETOPT_REQ_STRING }, + { "--session-id", VBOXSERVICESESSIONOPT_SESSION_ID, RTGETOPT_REQ_UINT32 }, + { "--session-proto", VBOXSERVICESESSIONOPT_SESSION_PROTO, RTGETOPT_REQ_UINT32 }, +#ifdef DEBUG + { "--thread-id", VBOXSERVICESESSIONOPT_THREAD_ID, RTGETOPT_REQ_UINT32 }, +#endif /* DEBUG */ + { "--verbose", 'v', RTGETOPT_REQ_NOTHING } + }; + + int ch; + RTGETOPTUNION ValueUnion; + RTGETOPTSTATE GetState; + RTGetOptInit(&GetState, argc, argv, + s_aOptions, RT_ELEMENTS(s_aOptions), + 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST); + + uint32_t uSessionFlags = VBOXSERVICECTRLSESSION_FLAG_FORK; + + /* Protocol and session ID must be specified explicitly. */ + g_Session.StartupInfo.uProtocol = UINT32_MAX; + g_Session.StartupInfo.uSessionID = UINT32_MAX; + + int rc = VINF_SUCCESS; + + while ( (ch = RTGetOpt(&GetState, &ValueUnion)) + && RT_SUCCESS(rc)) + { + /* For options that require an argument, ValueUnion has received the value. */ + switch (ch) + { + case VBOXSERVICESESSIONOPT_LOG_FILE: + if (!RTStrPrintf(g_szLogFile, sizeof(g_szLogFile), "%s", ValueUnion.psz)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unable to set logfile name to '%s'", + ValueUnion.psz); + break; +#ifdef DEBUG + case VBOXSERVICESESSIONOPT_DUMP_STDOUT: + uSessionFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDOUT; + break; + + case VBOXSERVICESESSIONOPT_DUMP_STDERR: + uSessionFlags |= VBOXSERVICECTRLSESSION_FLAG_DUMPSTDERR; + break; +#endif + case VBOXSERVICESESSIONOPT_USERNAME: + /** @todo Information not needed right now, skip. */ + break; + + case VBOXSERVICESESSIONOPT_SESSION_ID: + g_Session.StartupInfo.uSessionID = ValueUnion.u32; + break; + + case VBOXSERVICESESSIONOPT_SESSION_PROTO: + g_Session.StartupInfo.uProtocol = ValueUnion.u32; + break; + +#ifdef DEBUG + case VBOXSERVICESESSIONOPT_THREAD_ID: + /* Not handled. */ + break; +#endif + /** @todo Implement help? */ + + case 'v': + g_cVerbosity++; + break; + + case VINF_GETOPT_NOT_OPTION: + /* Ignore; might be "guestsession" main command. */ + break; + + default: + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Unknown command '%s'", ValueUnion.psz); + break; /* Never reached. */ + } + } + + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Initialization failed with rc=%Rrc", rc); + + if (g_Session.StartupInfo.uProtocol == UINT32_MAX) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No protocol version specified"); + + if (g_Session.StartupInfo.uSessionID == UINT32_MAX) + return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No session ID specified"); + + /* Init the session object. */ + rc = GstCntlSessionInit(&g_Session, uSessionFlags); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize session object, rc=%Rrc\n", rc); + + rc = VBoxServiceLogCreate(strlen(g_szLogFile) ? g_szLogFile : NULL); + if (RT_FAILURE(rc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)", + strlen(g_szLogFile) ? g_szLogFile : "<None>", rc); + + RTEXITCODE rcExit = gstcntlSessionForkWorker(&g_Session); + + VBoxServiceLogDestroy(); + return rcExit; +} + diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp deleted file mode 100644 index 78ec20a8..00000000 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp +++ /dev/null @@ -1,1794 +0,0 @@ -/* $Id: VBoxServiceControlThread.cpp $ */ -/** @file - * VBoxServiceControlExecThread - Thread for every started guest process. - */ - -/* - * Copyright (C) 2012 Oracle Corporation - * - * This file is part of VirtualBox Open Source Edition (OSE), as - * available from http://www.virtualbox.org. This file is free software; - * you can redistribute it and/or modify it under the terms of the GNU - * General Public License (GPL) as published by the Free Software - * Foundation, in version 2 as it comes in the "COPYING" file of the - * VirtualBox OSE distribution. VirtualBox OSE is distributed in the - * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. - */ - - -/******************************************************************************* -* Header Files * -*******************************************************************************/ -#include <iprt/asm.h> -#include <iprt/assert.h> -#include <iprt/env.h> -#include <iprt/file.h> -#include <iprt/getopt.h> -#include <iprt/handle.h> -#include <iprt/mem.h> -#include <iprt/path.h> -#include <iprt/pipe.h> -#include <iprt/poll.h> -#include <iprt/process.h> -#include <iprt/semaphore.h> -#include <iprt/string.h> -#include <iprt/thread.h> - -#include <VBox/VBoxGuestLib.h> -#include <VBox/HostServices/GuestControlSvc.h> - -#include "VBoxServiceInternal.h" - -using namespace guestControl; - -/* Internal functions. */ -static int vboxServiceControlThreadRequestCancel(PVBOXSERVICECTRLREQUEST pThread); - -/** - * Initialies the passed in thread data structure with the parameters given. - * - * @return IPRT status code. - * @param pThread The thread's handle to allocate the data for. - * @param u32ContextID The context ID bound to this request / command. - @ @param pProcess Process information. - */ -static int gstsvcCntlExecThreadInit(PVBOXSERVICECTRLTHREAD pThread, - PVBOXSERVICECTRLPROCESS pProcess, - uint32_t u32ContextID) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - AssertPtrReturn(pProcess, VERR_INVALID_POINTER); - - /* General stuff. */ - pThread->pAnchor = NULL; - pThread->Node.pPrev = NULL; - pThread->Node.pNext = NULL; - - pThread->fShutdown = false; - pThread->fStarted = false; - pThread->fStopped = false; - - pThread->uContextID = u32ContextID; - /* ClientID will be assigned when thread is started; every guest - * process has its own client ID to detect crashes on a per-guest-process - * level. */ - - int rc = RTCritSectInit(&pThread->CritSect); - if (RT_FAILURE(rc)) - return rc; - - pThread->uPID = 0; /* Don't have a PID yet. */ - pThread->pRequest = NULL; /* No request assigned yet. */ - pThread->uFlags = pProcess->uFlags; - pThread->uTimeLimitMS = ( pProcess->uTimeLimitMS == UINT32_MAX - || pProcess->uTimeLimitMS == 0) - ? RT_INDEFINITE_WAIT : pProcess->uTimeLimitMS; - - /* Prepare argument list. */ - pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */ - rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs, - (pProcess->uNumArgs > 0) ? pProcess->szArgs : "", NULL); - /* Did we get the same result? */ - Assert(pProcess->uNumArgs == pThread->uNumArgs); - - if (RT_SUCCESS(rc)) - { - /* Prepare environment list. */ - pThread->uNumEnvVars = 0; - if (pProcess->uNumEnvVars) - { - pThread->papszEnv = (char **)RTMemAlloc(pProcess->uNumEnvVars * sizeof(char*)); - AssertPtr(pThread->papszEnv); - pThread->uNumEnvVars = pProcess->uNumEnvVars; - - const char *pszCur = pProcess->szEnv; - uint32_t i = 0; - uint32_t cbLen = 0; - while (cbLen < pProcess->cbEnv) - { - /* sanity check */ - if (i >= pProcess->uNumEnvVars) - { - rc = VERR_INVALID_PARAMETER; - break; - } - int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur); - if (cbStr < 0) - { - rc = VERR_NO_STR_MEMORY; - break; - } - pszCur += cbStr + 1; /* Skip terminating '\0' */ - cbLen += cbStr + 1; /* Skip terminating '\0' */ - } - Assert(i == pThread->uNumEnvVars); - } - - /* The actual command to execute. */ - pThread->pszCmd = RTStrDup(pProcess->szCmd); - AssertPtr(pThread->pszCmd); - - /* User management. */ - pThread->pszUser = RTStrDup(pProcess->szUser); - AssertPtr(pThread->pszUser); - pThread->pszPassword = RTStrDup(pProcess->szPassword); - AssertPtr(pThread->pszPassword); - } - - if (RT_FAILURE(rc)) /* Clean up on failure. */ - VBoxServiceControlThreadFree(pThread); - return rc; -} - - -/** - * Frees a guest thread. - * - * @return IPRT status code. - * @param pThread Thread to shut down. - */ -int VBoxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - - VBoxServiceVerbose(3, "[PID %u]: Freeing ...\n", - pThread->uPID); - - int rc = RTCritSectEnter(&pThread->CritSect); - if (RT_SUCCESS(rc)) - { - if (pThread->uNumEnvVars) - { - for (uint32_t i = 0; i < pThread->uNumEnvVars; i++) - RTStrFree(pThread->papszEnv[i]); - RTMemFree(pThread->papszEnv); - } - RTGetOptArgvFree(pThread->papszArgs); - - RTStrFree(pThread->pszCmd); - RTStrFree(pThread->pszUser); - RTStrFree(pThread->pszPassword); - - VBoxServiceVerbose(3, "[PID %u]: Setting stopped state\n", - pThread->uPID); - - rc = RTCritSectLeave(&pThread->CritSect); - AssertRC(rc); - } - - /* - * Destroy other thread data. - */ - if (RTCritSectIsInitialized(&pThread->CritSect)) - RTCritSectDelete(&pThread->CritSect); - - /* - * Destroy thread structure as final step. - */ - RTMemFree(pThread); - pThread = NULL; - - return rc; -} - - -/** - * Signals a guest process thread that we want it to shut down in - * a gentle way. - * - * @return IPRT status code. - * @param pThread Thread to shut down. - */ -int VBoxServiceControlThreadStop(const PVBOXSERVICECTRLTHREAD pThread) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - - VBoxServiceVerbose(3, "[PID %u]: Stopping ...\n", - pThread->uPID); - - int rc = vboxServiceControlThreadRequestCancel(pThread->pRequest); - if (RT_FAILURE(rc)) - VBoxServiceError("[PID %u]: Signalling request event failed, rc=%Rrc\n", - pThread->uPID, rc); - - /* Do *not* set pThread->fShutdown or other stuff here! - * The guest thread loop will do that as soon as it processes the quit message. */ - - PVBOXSERVICECTRLREQUEST pRequest; - rc = VBoxServiceControlThreadRequestAlloc(&pRequest, VBOXSERVICECTRLREQUEST_QUIT); - if (RT_SUCCESS(rc)) - { - rc = VBoxServiceControlThreadPerform(pThread->uPID, pRequest); - if (RT_FAILURE(rc)) - VBoxServiceVerbose(3, "[PID %u]: Sending quit request failed with rc=%Rrc\n", - pThread->uPID, rc); - - VBoxServiceControlThreadRequestFree(pRequest); - } - return rc; -} - - -/** - * Wait for a guest process thread to shut down. - * - * @return IPRT status code. - * @param pThread Thread to wait shutting down for. - * @param RTMSINTERVAL Timeout in ms to wait for shutdown. - * @param prc Where to store the thread's return code. Optional. - */ -int VBoxServiceControlThreadWait(const PVBOXSERVICECTRLTHREAD pThread, - RTMSINTERVAL msTimeout, int *prc) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - /* prc is optional. */ - - int rc = VINF_SUCCESS; - if ( pThread->Thread != NIL_RTTHREAD - && ASMAtomicReadBool(&pThread->fStarted)) - { - VBoxServiceVerbose(2, "[PID %u]: Waiting for shutdown of pThread=0x%p = \"%s\"...\n", - pThread->uPID, pThread, pThread->pszCmd); - - /* Wait a bit ... */ - int rcThread; - rc = RTThreadWait(pThread->Thread, msTimeout, &rcThread); - if (RT_FAILURE(rc)) - { - VBoxServiceError("[PID %u]: Waiting for shutting down thread returned error rc=%Rrc\n", - pThread->uPID, rc); - } - else - { - VBoxServiceVerbose(3, "[PID %u]: Thread reported exit code=%Rrc\n", - pThread->uPID, rcThread); - if (prc) - *prc = rcThread; - } - } - return rc; -} - - -/** - * Closes the stdin pipe of a guest process. - * - * @return IPRT status code. - * @param hPollSet The polling set. - * @param phStdInW The standard input pipe handle. - */ -static int VBoxServiceControlThreadCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW) -{ - AssertPtrReturn(phStdInW, VERR_INVALID_POINTER); - - int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN); - if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND) - AssertRC(rc); - - if (*phStdInW != NIL_RTPIPE) - { - rc = RTPipeClose(*phStdInW); - AssertRC(rc); - *phStdInW = NIL_RTPIPE; - } - - return rc; -} - - -/** - * Handle an error event on standard input. - * - * @return IPRT status code. - * @param hPollSet The polling set. - * @param fPollEvt The event mask returned by RTPollNoResume. - * @param phStdInW The standard input pipe handle. - */ -static int VBoxServiceControlThreadHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW) -{ - NOREF(fPollEvt); - - return VBoxServiceControlThreadCloseStdIn(hPollSet, phStdInW); -} - - -/** - * Handle pending output data or error on standard out or standard error. - * - * @returns IPRT status code from client send. - * @param hPollSet The polling set. - * @param fPollEvt The event mask returned by RTPollNoResume. - * @param phPipeR The pipe handle. - * @param idPollHnd The pipe ID to handle. - * - */ -static int VBoxServiceControlThreadHandleOutputError(RTPOLLSET hPollSet, uint32_t fPollEvt, - PRTPIPE phPipeR, uint32_t idPollHnd) -{ - AssertPtrReturn(phPipeR, VERR_INVALID_POINTER); - -#ifdef DEBUG - VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputError: fPollEvt=0x%x, idPollHnd=%u\n", - fPollEvt, idPollHnd); -#endif - - /* Remove pipe from poll set. */ - int rc2 = RTPollSetRemove(hPollSet, idPollHnd); - AssertMsg(RT_SUCCESS(rc2) || rc2 == VERR_POLL_HANDLE_ID_NOT_FOUND, ("%Rrc\n", rc2)); - - bool fClosePipe = true; /* By default close the pipe. */ - - /* Check if there's remaining data to read from the pipe. */ - size_t cbReadable; - rc2 = RTPipeQueryReadable(*phPipeR, &cbReadable); - if ( RT_SUCCESS(rc2) - && cbReadable) - { - VBoxServiceVerbose(3, "VBoxServiceControlThreadHandleOutputError: idPollHnd=%u has %ld bytes left, vetoing close\n", - idPollHnd, cbReadable); - - /* Veto closing the pipe yet because there's still stuff to read - * from the pipe. This can happen on UNIX-y systems where on - * error/hangup there still can be data to be read out. */ - fClosePipe = false; - } - else - VBoxServiceVerbose(3, "VBoxServiceControlThreadHandleOutputError: idPollHnd=%u will be closed\n", - idPollHnd); - - if ( *phPipeR != NIL_RTPIPE - && fClosePipe) - { - rc2 = RTPipeClose(*phPipeR); - AssertRC(rc2); - *phPipeR = NIL_RTPIPE; - } - - return VINF_SUCCESS; -} - - -/** - * Handle pending output data or error on standard out or standard error. - * - * @returns IPRT status code from client send. - * @param hPollSet The polling set. - * @param fPollEvt The event mask returned by RTPollNoResume. - * @param phPipeR The pipe handle. - * @param idPollHnd The pipe ID to handle. - * - */ -static int VBoxServiceControlThreadHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, - PRTPIPE phPipeR, uint32_t idPollHnd) -{ -#if 0 - VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent: fPollEvt=0x%x, idPollHnd=%u\n", - fPollEvt, idPollHnd); -#endif - - int rc = VINF_SUCCESS; - -#ifdef DEBUG - size_t cbReadable; - rc = RTPipeQueryReadable(*phPipeR, &cbReadable); - if ( RT_SUCCESS(rc) - && cbReadable) - { - VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent: cbReadable=%ld\n", - cbReadable); - } -#endif - -#if 0 - //if (fPollEvt & RTPOLL_EVT_READ) - { - size_t cbRead = 0; - uint8_t byData[_64K]; - rc = RTPipeRead(*phPipeR, - byData, sizeof(byData), &cbRead); - VBoxServiceVerbose(4, "VBoxServiceControlThreadHandleOutputEvent cbRead=%u, rc=%Rrc\n", - cbRead, rc); - - /* Make sure we go another poll round in case there was too much data - for the buffer to hold. */ - fPollEvt &= RTPOLL_EVT_ERROR; - } -#endif - - if (fPollEvt & RTPOLL_EVT_ERROR) - rc = VBoxServiceControlThreadHandleOutputError(hPollSet, fPollEvt, - phPipeR, idPollHnd); - return rc; -} - - -static int VBoxServiceControlThreadHandleRequest(RTPOLLSET hPollSet, uint32_t fPollEvt, - PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR, - PVBOXSERVICECTRLTHREAD pThread) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - AssertPtrReturn(phStdInW, VERR_INVALID_POINTER); - AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER); - AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER); - - /* Drain the notification pipe. */ - uint8_t abBuf[8]; - size_t cbIgnore; - int rc = RTPipeRead(pThread->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore); - if (RT_FAILURE(rc)) - VBoxServiceError("Draining IPC notification pipe failed with rc=%Rrc\n", rc); - - int rcReq = VINF_SUCCESS; /* Actual request result. */ - - PVBOXSERVICECTRLREQUEST pRequest = pThread->pRequest; - if (!pRequest) - { - VBoxServiceError("IPC request is invalid\n"); - return VERR_INVALID_POINTER; - } - - switch (pRequest->enmType) - { - case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */ - { - /** @todo Check for some conditions to check to - * veto quitting. */ - ASMAtomicXchgBool(&pThread->fShutdown, true); - rcReq = VERR_CANCELLED; - break; - } - - case VBOXSERVICECTRLREQUEST_STDIN_WRITE: - case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF: - { - size_t cbWritten = 0; - if (pRequest->cbData) - { - AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER); - if (*phStdInW != NIL_RTPIPE) - { - rcReq = RTPipeWrite(*phStdInW, - pRequest->pvData, pRequest->cbData, &cbWritten); - } - else - rcReq = VINF_EOF; - } - - /* - * If this is the last write + we have really have written all data - * we need to close the stdin pipe on our end and remove it from - * the poll set. - */ - if ( pRequest->enmType == VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF - && pRequest->cbData == cbWritten) - { - rc = VBoxServiceControlThreadCloseStdIn(hPollSet, phStdInW); - } - - /* Report back actual data written (if any). */ - pRequest->cbData = cbWritten; - break; - } - - case VBOXSERVICECTRLREQUEST_STDOUT_READ: - case VBOXSERVICECTRLREQUEST_STDERR_READ: - { - AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER); - AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER); - - PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ - ? phStdErrR : phStdOutR; - AssertPtr(pPipeR); - - size_t cbRead = 0; - if (*pPipeR != NIL_RTPIPE) - { - rcReq = RTPipeRead(*pPipeR, - pRequest->pvData, pRequest->cbData, &cbRead); - if (RT_FAILURE(rcReq)) - { - RTPollSetRemove(hPollSet, pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ - ? VBOXSERVICECTRLPIPEID_STDERR : VBOXSERVICECTRLPIPEID_STDOUT); - RTPipeClose(*pPipeR); - *pPipeR = NIL_RTPIPE; - if (rcReq == VERR_BROKEN_PIPE) - rcReq = VINF_EOF; - } - } - else - rcReq = VINF_EOF; - - /* Report back actual data read (if any). */ - pRequest->cbData = cbRead; - break; - } - - default: - rcReq = VERR_NOT_IMPLEMENTED; - break; - } - - /* Assign overall result. */ - pRequest->rc = RT_SUCCESS(rc) - ? rcReq : rc; - - VBoxServiceVerbose(2, "[PID %u]: Handled req=%u, CID=%u, rc=%Rrc, cbData=%u\n", - pThread->uPID, pRequest->enmType, pRequest->uCID, pRequest->rc, pRequest->cbData); - - /* In any case, regardless of the result, we notify - * the main guest control to unblock it. */ - int rc2 = RTSemEventMultiSignal(pRequest->Event); - AssertRC(rc2); - - /* No access to pRequest here anymore -- could be out of scope - * or modified already! */ - pThread->pRequest = pRequest = NULL; - - return rc; -} - - -/** - * Execution loop which runs in a dedicated per-started-process thread and - * handles all pipe input/output and signalling stuff. - * - * @return IPRT status code. - * @param pThread The process' thread handle. - * @param hProcess The actual process handle. - * @param cMsTimeout Time limit (in ms) of the process' life time. - * @param hPollSet The poll set to use. - * @param hStdInW Handle to the process' stdin write end. - * @param hStdOutR Handle to the process' stdout read end. - * @param hStdErrR Handle to the process' stderr read end. - */ -static int VBoxServiceControlThreadProcLoop(PVBOXSERVICECTRLTHREAD pThread, - RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet, - PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER); - /* Rest is optional. */ - - int rc; - int rc2; - uint64_t const MsStart = RTTimeMilliTS(); - RTPROCSTATUS ProcessStatus = { 254, RTPROCEXITREASON_ABEND }; - bool fProcessAlive = true; - bool fProcessTimedOut = false; - uint64_t MsProcessKilled = UINT64_MAX; - RTMSINTERVAL const cMsPollBase = *phStdInW != NIL_RTPIPE - ? 100 /* Need to poll for input. */ - : 1000; /* Need only poll for process exit and aborts. */ - RTMSINTERVAL cMsPollCur = 0; - - /* - * Assign PID to thread data. - * Also check if there already was a thread with the same PID and shut it down -- otherwise - * the first (stale) entry will be found and we get really weird results! - */ - rc = VBoxServiceControlAssignPID(pThread, hProcess); - if (RT_FAILURE(rc)) - { - VBoxServiceError("Unable to assign PID=%u, to new thread, rc=%Rrc\n", - hProcess, rc); - return rc; - } - - /* - * Before entering the loop, tell the host that we've started the guest - * and that it's now OK to send input to the process. - */ - VBoxServiceVerbose(2, "[PID %u]: Process \"%s\" started, CID=%u, User=%s\n", - pThread->uPID, pThread->pszCmd, pThread->uContextID, pThread->pszUser); - rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, - pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */, - NULL /* pvData */, 0 /* cbData */); - - /* - * Process input, output, the test pipe and client requests. - */ - while ( RT_SUCCESS(rc) - && RT_UNLIKELY(!pThread->fShutdown)) - { - /* - * Wait/Process all pending events. - */ - uint32_t idPollHnd; - uint32_t fPollEvt; - rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd); - if (pThread->fShutdown) - continue; - - cMsPollCur = 0; /* No rest until we've checked everything. */ - - if (RT_SUCCESS(rc2)) - { - /*VBoxServiceVerbose(4, "[PID %u}: RTPollNoResume idPollHnd=%u\n", - pThread->uPID, idPollHnd);*/ - switch (idPollHnd) - { - case VBOXSERVICECTRLPIPEID_STDIN: - rc = VBoxServiceControlThreadHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW); - break; - - case VBOXSERVICECTRLPIPEID_STDOUT: - rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt, - phStdOutR, idPollHnd); - break; - - case VBOXSERVICECTRLPIPEID_STDERR: - rc = VBoxServiceControlThreadHandleOutputEvent(hPollSet, fPollEvt, - phStdErrR, idPollHnd); - break; - - case VBOXSERVICECTRLPIPEID_IPC_NOTIFY: - rc = VBoxServiceControlThreadHandleRequest(hPollSet, fPollEvt, - phStdInW, phStdOutR, phStdErrR, pThread); - break; - } - - if (RT_FAILURE(rc) || rc == VINF_EOF) - break; /* Abort command, or client dead or something. */ - - if (RT_UNLIKELY(pThread->fShutdown)) - break; /* We were asked to shutdown. */ - - continue; - } - -#if 0 - VBoxServiceVerbose(4, "[PID %u]: Polling done, pollRC=%Rrc, pollCnt=%u, rc=%Rrc, fShutdown=%RTbool\n", - pThread->uPID, rc2, RTPollSetGetCount(hPollSet), rc, pThread->fShutdown); -#endif - /* - * Check for process death. - */ - if (fProcessAlive) - { - rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); - if (RT_SUCCESS_NP(rc2)) - { - fProcessAlive = false; - continue; - } - if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED)) - continue; - if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND)) - { - fProcessAlive = false; - ProcessStatus.enmReason = RTPROCEXITREASON_ABEND; - ProcessStatus.iStatus = 255; - AssertFailed(); - } - else - AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2)); - } - - /* - * If the process has terminated and all output has been consumed, - * we should be heading out. - */ - if ( !fProcessAlive - && *phStdOutR == NIL_RTPIPE - && *phStdErrR == NIL_RTPIPE) - break; - - /* - * Check for timed out, killing the process. - */ - uint32_t cMilliesLeft = RT_INDEFINITE_WAIT; - if (cMsTimeout != RT_INDEFINITE_WAIT) - { - uint64_t u64Now = RTTimeMilliTS(); - uint64_t cMsElapsed = u64Now - MsStart; - if (cMsElapsed >= cMsTimeout) - { - VBoxServiceVerbose(3, "[PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...", - pThread->uPID, cMsElapsed, cMsTimeout); - - fProcessTimedOut = true; - if ( MsProcessKilled == UINT64_MAX - || u64Now - MsProcessKilled > 1000) - { - if (u64Now - MsProcessKilled > 20*60*1000) - break; /* Give up after 20 mins. */ - RTProcTerminate(hProcess); - MsProcessKilled = u64Now; - continue; - } - cMilliesLeft = 10000; - } - else - cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed; - } - - /* Reset the polling interval since we've done all pending work. */ - cMsPollCur = fProcessAlive - ? cMsPollBase - : RT_MS_1MIN; - if (cMilliesLeft < cMsPollCur) - cMsPollCur = cMilliesLeft; - - /* - * Need to exit? - */ - if (pThread->fShutdown) - break; - } - - rc2 = RTCritSectEnter(&pThread->CritSect); - if (RT_SUCCESS(rc2)) - { - ASMAtomicXchgBool(&pThread->fShutdown, true); - - rc2 = RTCritSectLeave(&pThread->CritSect); - AssertRC(rc2); - } - - /* - * Try kill the process if it's still alive at this point. - */ - if (fProcessAlive) - { - if (MsProcessKilled == UINT64_MAX) - { - VBoxServiceVerbose(3, "[PID %u]: Is still alive and not killed yet\n", - pThread->uPID); - - MsProcessKilled = RTTimeMilliTS(); - RTProcTerminate(hProcess); - RTThreadSleep(500); - } - - for (size_t i = 0; i < 10; i++) - { - VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Waiting to exit ...\n", - pThread->uPID, i + 1); - rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus); - if (RT_SUCCESS(rc2)) - { - VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Exited\n", - pThread->uPID, i + 1); - fProcessAlive = false; - break; - } - if (i >= 5) - { - VBoxServiceVerbose(4, "[PID %u]: Kill attempt %d/10: Trying to terminate ...\n", - pThread->uPID, i + 1); - RTProcTerminate(hProcess); - } - RTThreadSleep(i >= 5 ? 2000 : 500); - } - - if (fProcessAlive) - VBoxServiceVerbose(3, "[PID %u]: Could not be killed\n", pThread->uPID); - } - - /* - * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the - * clients exec packet now. - */ - if (RT_SUCCESS(rc)) - { - uint32_t uStatus = PROC_STS_UNDEFINED; - uint32_t uFlags = 0; - - if ( fProcessTimedOut && !fProcessAlive && MsProcessKilled != UINT64_MAX) - { - VBoxServiceVerbose(3, "[PID %u]: Timed out and got killed\n", - pThread->uPID); - uStatus = PROC_STS_TOK; - } - else if (fProcessTimedOut && fProcessAlive && MsProcessKilled != UINT64_MAX) - { - VBoxServiceVerbose(3, "[PID %u]: Timed out and did *not* get killed\n", - pThread->uPID); - uStatus = PROC_STS_TOA; - } - else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX)) - { - VBoxServiceVerbose(3, "[PID %u]: Got terminated because system/service is about to shutdown\n", - pThread->uPID); - uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */ - uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */ - } - else if (fProcessAlive) - { - VBoxServiceError("[PID %u]: Is alive when it should not!\n", - pThread->uPID); - } - else if (MsProcessKilled != UINT64_MAX) - { - VBoxServiceError("[PID %u]: Has been killed when it should not!\n", - pThread->uPID); - } - else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL) - { - VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_NORMAL (Exit code: %u)\n", - pThread->uPID, ProcessStatus.iStatus); - - uStatus = PROC_STS_TEN; - uFlags = ProcessStatus.iStatus; - } - else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL) - { - VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_SIGNAL (Signal: %u)\n", - pThread->uPID, ProcessStatus.iStatus); - - uStatus = PROC_STS_TES; - uFlags = ProcessStatus.iStatus; - } - else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND) - { - /* ProcessStatus.iStatus will be undefined. */ - VBoxServiceVerbose(3, "[PID %u]: Ended with RTPROCEXITREASON_ABEND\n", - pThread->uPID); - - uStatus = PROC_STS_TEA; - uFlags = ProcessStatus.iStatus; - } - else - VBoxServiceVerbose(1, "[PID %u]: Handling process status %u not implemented\n", - pThread->uPID, ProcessStatus.enmReason); - - VBoxServiceVerbose(2, "[PID %u]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n", - pThread->uPID, pThread->uClientID, pThread->uContextID, uStatus, uFlags); - rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, - pThread->uPID, uStatus, uFlags, - NULL /* pvData */, 0 /* cbData */); - if (RT_FAILURE(rc)) - VBoxServiceError("[PID %u]: Error reporting final status to host; rc=%Rrc\n", - pThread->uPID, rc); - - VBoxServiceVerbose(3, "[PID %u]: Process loop ended with rc=%Rrc\n", - pThread->uPID, rc); - } - else - VBoxServiceError("[PID %u]: Loop failed with rc=%Rrc\n", - pThread->uPID, rc); - return rc; -} - - -/** - * Initializes a pipe's handle and pipe object. - * - * @return IPRT status code. - * @param ph The pipe's handle to initialize. - * @param phPipe The pipe's object to initialize. - */ -static int vboxServiceControlThreadInitPipe(PRTHANDLE ph, PRTPIPE phPipe) -{ - AssertPtrReturn(ph, VERR_INVALID_PARAMETER); - AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER); - - ph->enmType = RTHANDLETYPE_PIPE; - ph->u.hPipe = NIL_RTPIPE; - *phPipe = NIL_RTPIPE; - - return VINF_SUCCESS; -} - - -/** - * Allocates a guest thread request with the specified request data. - * - * @return IPRT status code. - * @param ppReq Pointer that will receive the newly allocated request. - * Must be freed later with VBoxServiceControlThreadRequestFree(). - * @param enmType Request type. - * @param pbData Payload data, based on request type. - * @param cbData Size of payload data (in bytes). - * @param uCID Context ID to which this request belongs to. - */ -int VBoxServiceControlThreadRequestAllocEx(PVBOXSERVICECTRLREQUEST *ppReq, - VBOXSERVICECTRLREQUESTTYPE enmType, - void* pbData, - size_t cbData, - uint32_t uCID) -{ - AssertPtrReturn(ppReq, VERR_INVALID_POINTER); - - PVBOXSERVICECTRLREQUEST pReq = (PVBOXSERVICECTRLREQUEST) - RTMemAlloc(sizeof(VBOXSERVICECTRLREQUEST)); - AssertPtrReturn(pReq, VERR_NO_MEMORY); - - RT_ZERO(*pReq); - pReq->enmType = enmType; - pReq->uCID = uCID; - pReq->cbData = cbData; - pReq->pvData = (uint8_t*)pbData; - - /* Set request result to some defined state in case - * it got cancelled. */ - pReq->rc = VERR_CANCELLED; - - int rc = RTSemEventMultiCreate(&pReq->Event); - AssertRC(rc); - - if (RT_SUCCESS(rc)) - { - *ppReq = pReq; - return VINF_SUCCESS; - } - - RTMemFree(pReq); - return rc; -} - - -/** - * Allocates a guest thread request with the specified request data. - * - * @return IPRT status code. - * @param ppReq Pointer that will receive the newly allocated request. - * Must be freed later with VBoxServiceControlThreadRequestFree(). - * @param enmType Request type. - */ -int VBoxServiceControlThreadRequestAlloc(PVBOXSERVICECTRLREQUEST *ppReq, - VBOXSERVICECTRLREQUESTTYPE enmType) -{ - return VBoxServiceControlThreadRequestAllocEx(ppReq, enmType, - NULL /* pvData */, 0 /* cbData */, - 0 /* ContextID */); -} - - -/** - * Cancels a previously fired off guest thread request. - * - * Note: Does *not* do locking since VBoxServiceControlThreadRequestWait() - * holds the lock (critsect); so only trigger the signal; the owner - * needs to clean up afterwards. - * - * @return IPRT status code. - * @param pReq Request to cancel. - */ -static int vboxServiceControlThreadRequestCancel(PVBOXSERVICECTRLREQUEST pReq) -{ - if (!pReq) /* Silently skip non-initialized requests. */ - return VINF_SUCCESS; - - VBoxServiceVerbose(4, "Cancelling request=0x%p\n", pReq); - - return RTSemEventMultiSignal(pReq->Event); -} - - -/** - * Frees a formerly allocated guest thread request. - * - * @return IPRT status code. - * @param pReq Request to free. - */ -void VBoxServiceControlThreadRequestFree(PVBOXSERVICECTRLREQUEST pReq) -{ - AssertPtrReturnVoid(pReq); - - VBoxServiceVerbose(4, "Freeing request=0x%p (event=%RTsem)\n", - pReq, &pReq->Event); - - int rc = RTSemEventMultiDestroy(pReq->Event); - AssertRC(rc); - - RTMemFree(pReq); - pReq = NULL; -} - - -/** - * Waits for a guest thread's event to get triggered. - * - * @return IPRT status code. - * @param pReq Request to wait for. - */ -int VBoxServiceControlThreadRequestWait(PVBOXSERVICECTRLREQUEST pReq) -{ - AssertPtrReturn(pReq, VERR_INVALID_POINTER); - - /* Wait on the request to get completed (or we are asked to abort/shutdown). */ - int rc = RTSemEventMultiWait(pReq->Event, RT_INDEFINITE_WAIT); - if (RT_SUCCESS(rc)) - { - VBoxServiceVerbose(4, "Performed request with rc=%Rrc, cbData=%u\n", - pReq->rc, pReq->cbData); - - /* Give back overall request result. */ - rc = pReq->rc; - } - else - VBoxServiceError("Waiting for request failed, rc=%Rrc\n", rc); - - return rc; -} - - -/** - * Sets up the redirection / pipe / nothing for one of the standard handles. - * - * @returns IPRT status code. No client replies made. - * @param pszHowTo How to set up this standard handle. - * @param fd Which standard handle it is (0 == stdin, 1 == - * stdout, 2 == stderr). - * @param ph The generic handle that @a pph may be set - * pointing to. Always set. - * @param pph Pointer to the RTProcCreateExec argument. - * Always set. - * @param phPipe Where to return the end of the pipe that we - * should service. - */ -static int VBoxServiceControlThreadSetupPipe(const char *pszHowTo, int fd, - PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe) -{ - AssertPtrReturn(ph, VERR_INVALID_POINTER); - AssertPtrReturn(pph, VERR_INVALID_POINTER); - AssertPtrReturn(phPipe, VERR_INVALID_POINTER); - - int rc; - - ph->enmType = RTHANDLETYPE_PIPE; - ph->u.hPipe = NIL_RTPIPE; - *pph = NULL; - *phPipe = NIL_RTPIPE; - - if (!strcmp(pszHowTo, "|")) - { - /* - * Setup a pipe for forwarding to/from the client. - * The ph union struct will be filled with a pipe read/write handle - * to represent the "other" end to phPipe. - */ - if (fd == 0) /* stdin? */ - { - /* Connect a wrtie pipe specified by phPipe to stdin. */ - rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ); - } - else /* stdout or stderr? */ - { - /* Connect a read pipe specified by phPipe to stdout or stderr. */ - rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE); - } - - if (RT_FAILURE(rc)) - return rc; - - ph->enmType = RTHANDLETYPE_PIPE; - *pph = ph; - } - else if (!strcmp(pszHowTo, "/dev/null")) - { - /* - * Redirect to/from /dev/null. - */ - RTFILE hFile; - rc = RTFileOpenBitBucket(&hFile, fd == 0 ? RTFILE_O_READ : RTFILE_O_WRITE); - if (RT_FAILURE(rc)) - return rc; - - ph->enmType = RTHANDLETYPE_FILE; - ph->u.hFile = hFile; - *pph = ph; - } - else /* Add other piping stuff here. */ - rc = VINF_SUCCESS; /* Same as parent (us). */ - - return rc; -} - - -/** - * Expands a file name / path to its real content. This only works on Windows - * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting - * with system / administrative rights). - * - * @return IPRT status code. - * @param pszPath Path to resolve. - * @param pszExpanded Pointer to string to store the resolved path in. - * @param cbExpanded Size (in bytes) of string to store the resolved path. - */ -static int VBoxServiceControlThreadMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded) -{ - int rc = VINF_SUCCESS; -#ifdef RT_OS_WINDOWS - if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded)) - rc = RTErrConvertFromWin32(GetLastError()); -#else - /* No expansion for non-Windows yet. */ - rc = RTStrCopy(pszExpanded, cbExpanded, pszPath); -#endif -#ifdef DEBUG - VBoxServiceVerbose(3, "VBoxServiceControlExecMakeFullPath: %s -> %s\n", - pszPath, pszExpanded); -#endif - return rc; -} - - -/** - * Resolves the full path of a specified executable name. This function also - * resolves internal VBoxService tools to its appropriate executable path + name if - * VBOXSERVICE_NAME is specified as pszFileName. - * - * @return IPRT status code. - * @param pszFileName File name to resolve. - * @param pszResolved Pointer to a string where the resolved file name will be stored. - * @param cbResolved Size (in bytes) of resolved file name string. - */ -static int VBoxServiceControlThreadResolveExecutable(const char *pszFileName, - char *pszResolved, size_t cbResolved) -{ - AssertPtrReturn(pszFileName, VERR_INVALID_POINTER); - AssertPtrReturn(pszResolved, VERR_INVALID_POINTER); - AssertReturn(cbResolved, VERR_INVALID_PARAMETER); - - int rc = VINF_SUCCESS; - - char szPathToResolve[RTPATH_MAX]; - if ( (g_pszProgName && (RTStrICmp(pszFileName, g_pszProgName) == 0)) - || !RTStrICmp(pszFileName, VBOXSERVICE_NAME)) - { - /* Resolve executable name of this process. */ - if (!RTProcGetExecutablePath(szPathToResolve, sizeof(szPathToResolve))) - rc = VERR_FILE_NOT_FOUND; - } - else - { - /* Take the raw argument to resolve. */ - rc = RTStrCopy(szPathToResolve, sizeof(szPathToResolve), pszFileName); - } - - if (RT_SUCCESS(rc)) - { - rc = VBoxServiceControlThreadMakeFullPath(szPathToResolve, pszResolved, cbResolved); - if (RT_SUCCESS(rc)) - VBoxServiceVerbose(3, "Looked up executable: %s -> %s\n", - pszFileName, pszResolved); - } - - if (RT_FAILURE(rc)) - VBoxServiceError("Failed to lookup executable \"%s\" with rc=%Rrc\n", - pszFileName, rc); - return rc; -} - - -/** - * Constructs the argv command line by resolving environment variables - * and relative paths. - * - * @return IPRT status code. - * @param pszArgv0 First argument (argv0), either original or modified version. Optional. - * @param papszArgs Original argv command line from the host, starting at argv[1]. - * @param ppapszArgv Pointer to a pointer with the new argv command line. - * Needs to be freed with RTGetOptArgvFree. - */ -static int VBoxServiceControlThreadAllocateArgv(const char *pszArgv0, - const char * const *papszArgs, - bool fExpandArgs, char ***ppapszArgv) -{ - AssertPtrReturn(ppapszArgv, VERR_INVALID_POINTER); - - VBoxServiceVerbose(3, "VBoxServiceControlThreadPrepareArgv: pszArgv0=%p, papszArgs=%p, fExpandArgs=%RTbool, ppapszArgv=%p\n", - pszArgv0, papszArgs, fExpandArgs, ppapszArgv); - - int rc = VINF_SUCCESS; - uint32_t cArgs; - for (cArgs = 0; papszArgs[cArgs]; cArgs++) - { - if (cArgs >= UINT32_MAX - 2) - return VERR_BUFFER_OVERFLOW; - } - - /* Allocate new argv vector (adding + 2 for argv0 + termination). */ - size_t cbSize = (cArgs + 2) * sizeof(char*); - char **papszNewArgv = (char**)RTMemAlloc(cbSize); - if (!papszNewArgv) - return VERR_NO_MEMORY; - -#ifdef DEBUG - VBoxServiceVerbose(3, "VBoxServiceControlThreadPrepareArgv: cbSize=%RU32, cArgs=%RU32\n", - cbSize, cArgs); -#endif - - size_t i = 0; /* Keep the argument counter in scope for cleaning up on failure. */ - - rc = RTStrDupEx(&papszNewArgv[0], pszArgv0); - if (RT_SUCCESS(rc)) - { - for (; i < cArgs; i++) - { - char *pszArg; -#if 0 /* Arguments expansion -- untested. */ - if (fExpandArgs) - { - /* According to MSDN the limit on older Windows version is 32K, whereas - * Vista+ there are no limits anymore. We still stick to 4K. */ - char szExpanded[_4K]; -# ifdef RT_OS_WINDOWS - if (!ExpandEnvironmentStrings(papszArgs[i], szExpanded, sizeof(szExpanded))) - rc = RTErrConvertFromWin32(GetLastError()); -# else - /* No expansion for non-Windows yet. */ - rc = RTStrCopy(papszArgs[i], sizeof(szExpanded), szExpanded); -# endif - if (RT_SUCCESS(rc)) - rc = RTStrDupEx(&pszArg, szExpanded); - } - else -#endif - rc = RTStrDupEx(&pszArg, papszArgs[i]); - - if (RT_FAILURE(rc)) - break; - - papszNewArgv[i + 1] = pszArg; - } - - if (RT_SUCCESS(rc)) - { - /* Terminate array. */ - papszNewArgv[cArgs + 1] = NULL; - - *ppapszArgv = papszNewArgv; - } - } - - if (RT_FAILURE(rc)) - { - for (i; i > 0; i--) - RTStrFree(papszNewArgv[i]); - RTMemFree(papszNewArgv); - } - - return rc; -} - - -void VBoxServiceControlThreadFreeArgv(char **papszArgv) -{ - if (papszArgv) - { - size_t i = 0; - while (papszArgv[i]) - RTStrFree(papszArgv[i++]); - RTMemFree(papszArgv); - } -} - - -/** - * Helper function to create/start a process on the guest. - * - * @return IPRT status code. - * @param pszExec Full qualified path of process to start (without arguments). - * @param papszArgs Pointer to array of command line arguments. - * @param hEnv Handle to environment block to use. - * @param fFlags Process execution flags. - * @param phStdIn Handle for the process' stdin pipe. - * @param phStdOut Handle for the process' stdout pipe. - * @param phStdErr Handle for the process' stderr pipe. - * @param pszAsUser User name (account) to start the process under. - * @param pszPassword Password of the specified user. - * @param phProcess Pointer which will receive the process handle after - * successful process start. - */ -static int VBoxServiceControlThreadCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags, - PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser, - const char *pszPassword, PRTPROCESS phProcess) -{ - AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER); - AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER); - AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER); - - int rc = VINF_SUCCESS; - char szExecExp[RTPATH_MAX]; - - /* Do we need to expand environment variables in arguments? */ - bool fExpandArgs = (fFlags & EXECUTEPROCESSFLAG_EXPAND_ARGUMENTS) ? true : false; - -#ifdef RT_OS_WINDOWS - /* - * If sysprep should be executed do this in the context of VBoxService, which - * (usually, if started by SCM) has administrator rights. Because of that a UI - * won't be shown (doesn't have a desktop). - */ - if (!RTStrICmp(pszExec, "sysprep")) - { - /* Use a predefined sysprep path as default. */ - char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe"; - - /* - * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe", - * so detect the OS and use a different path. - */ - OSVERSIONINFOEX OSInfoEx; - RT_ZERO(OSInfoEx); - OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); - if ( GetVersionEx((LPOSVERSIONINFO) &OSInfoEx) - && OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT - && OSInfoEx.dwMajorVersion >= 6 /* Vista or later */) - { - rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL); - if (RT_SUCCESS(rc)) - rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe"); - } - - if (RT_SUCCESS(rc)) - { - char **papszArgsExp; - rc = VBoxServiceControlThreadAllocateArgv(szSysprepCmd /* argv0 */, papszArgs, - fExpandArgs, &papszArgsExp); - if (RT_SUCCESS(rc)) - { - rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */, - phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */, - NULL /* pszPassword */, phProcess); - VBoxServiceControlThreadFreeArgv(papszArgsExp); - } - } - - if (RT_FAILURE(rc)) - VBoxServiceVerbose(3, "Starting sysprep returned rc=%Rrc\n", rc); - - return rc; - } -#endif /* RT_OS_WINDOWS */ - -#ifdef VBOXSERVICE_TOOLBOX - if (RTStrStr(pszExec, "vbox_") == pszExec) - { - /* We want to use the internal toolbox (all internal - * tools are starting with "vbox_" (e.g. "vbox_cat"). */ - rc = VBoxServiceControlThreadResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp)); - } - else - { -#endif - /* - * Do the environment variables expansion on executable and arguments. - */ - rc = VBoxServiceControlThreadResolveExecutable(pszExec, szExecExp, sizeof(szExecExp)); -#ifdef VBOXSERVICE_TOOLBOX - } -#endif - if (RT_SUCCESS(rc)) - { - char **papszArgsExp; - rc = VBoxServiceControlThreadAllocateArgv(pszExec /* Always use the unmodified executable name as argv0. */, - papszArgs /* Append the rest of the argument vector (if any). */, - fExpandArgs, &papszArgsExp); - if (RT_FAILURE(rc)) - { - /* Don't print any arguments -- may contain passwords or other sensible data! */ - VBoxServiceError("Could not prepare arguments, rc=%Rrc\n", rc); - } - else - { - uint32_t uProcFlags = 0; - if (fFlags) - { - if (fFlags & EXECUTEPROCESSFLAG_HIDDEN) - uProcFlags |= RTPROC_FLAGS_HIDDEN; - if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE) - uProcFlags |= RTPROC_FLAGS_NO_PROFILE; - } - - /* If no user name specified run with current credentials (e.g. - * full service/system rights). This is prohibited via official Main API! - * - * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication - * code (at least on Windows) for running processes as different users - * started from our system service. */ - if (*pszAsUser) - uProcFlags |= RTPROC_FLAGS_SERVICE; -#ifdef DEBUG - VBoxServiceVerbose(3, "Command: %s\n", szExecExp); - for (size_t i = 0; papszArgsExp[i]; i++) - VBoxServiceVerbose(3, "\targv[%ld]: %s\n", i, papszArgsExp[i]); -#endif - VBoxServiceVerbose(3, "Starting process \"%s\" ...\n", szExecExp); - - /* Do normal execution. */ - rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags, - phStdIn, phStdOut, phStdErr, - *pszAsUser ? pszAsUser : NULL, - *pszPassword ? pszPassword : NULL, - phProcess); - - VBoxServiceVerbose(3, "Starting process \"%s\" returned rc=%Rrc\n", - szExecExp, rc); - - VBoxServiceControlThreadFreeArgv(papszArgsExp); - } - } - return rc; -} - -/** - * The actual worker routine (loop) for a started guest process. - * - * @return IPRT status code. - * @param PVBOXSERVICECTRLTHREAD Thread data associated with a started process. - */ -static int VBoxServiceControlThreadProcessWorker(PVBOXSERVICECTRLTHREAD pThread) -{ - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - VBoxServiceVerbose(3, "Thread of process pThread=0x%p = \"%s\" started\n", - pThread, pThread->pszCmd); - - int rc = VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLIST_RUNNING, pThread); - AssertRC(rc); - - rc = VbglR3GuestCtrlConnect(&pThread->uClientID); - if (RT_FAILURE(rc)) - { - VBoxServiceError("Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc); - RTThreadUserSignal(RTThreadSelf()); - return rc; - } - VBoxServiceVerbose(3, "Guest process \"%s\" got client ID=%u, flags=0x%x\n", - pThread->pszCmd, pThread->uClientID, pThread->uFlags); - - bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */ - - /* - * Create the environment. - */ - RTENV hEnv; - rc = RTEnvClone(&hEnv, RTENV_DEFAULT); - if (RT_SUCCESS(rc)) - { - size_t i; - for (i = 0; i < pThread->uNumEnvVars && pThread->papszEnv; i++) - { - rc = RTEnvPutEx(hEnv, pThread->papszEnv[i]); - if (RT_FAILURE(rc)) - break; - } - if (RT_SUCCESS(rc)) - { - /* - * Setup the redirection of the standard stuff. - */ - /** @todo consider supporting: gcc stuff.c >file 2>&1. */ - RTHANDLE hStdIn; - PRTHANDLE phStdIn; - rc = VBoxServiceControlThreadSetupPipe("|", 0 /*STDIN_FILENO*/, - &hStdIn, &phStdIn, &pThread->pipeStdInW); - if (RT_SUCCESS(rc)) - { - RTHANDLE hStdOut; - PRTHANDLE phStdOut; - RTPIPE pipeStdOutR; - rc = VBoxServiceControlThreadSetupPipe( (pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT) - ? "|" : "/dev/null", - 1 /*STDOUT_FILENO*/, - &hStdOut, &phStdOut, &pipeStdOutR); - if (RT_SUCCESS(rc)) - { - RTHANDLE hStdErr; - PRTHANDLE phStdErr; - RTPIPE pipeStdErrR; - rc = VBoxServiceControlThreadSetupPipe( (pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR) - ? "|" : "/dev/null", - 2 /*STDERR_FILENO*/, - &hStdErr, &phStdErr, &pipeStdErrR); - if (RT_SUCCESS(rc)) - { - /* - * Create a poll set for the pipes and let the - * transport layer add stuff to it as well. - */ - RTPOLLSET hPollSet; - rc = RTPollSetCreate(&hPollSet); - if (RT_SUCCESS(rc)) - { - uint32_t uFlags = RTPOLL_EVT_ERROR; -#if 0 - /* Add reading event to pollset to get some more information. */ - uFlags |= RTPOLL_EVT_READ; -#endif - /* Stdin. */ - if (RT_SUCCESS(rc)) - rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN); - /* Stdout. */ - if (RT_SUCCESS(rc)) - rc = RTPollSetAddPipe(hPollSet, pipeStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT); - /* Stderr. */ - if (RT_SUCCESS(rc)) - rc = RTPollSetAddPipe(hPollSet, pipeStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR); - /* IPC notification pipe. */ - if (RT_SUCCESS(rc)) - rc = RTPipeCreate(&pThread->hNotificationPipeR, &pThread->hNotificationPipeW, 0 /* Flags */); - if (RT_SUCCESS(rc)) - rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY); - - if (RT_SUCCESS(rc)) - { - RTPROCESS hProcess; - rc = VBoxServiceControlThreadCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags, - phStdIn, phStdOut, phStdErr, - pThread->pszUser, pThread->pszPassword, - &hProcess); - if (RT_FAILURE(rc)) - VBoxServiceError("Error starting process, rc=%Rrc\n", rc); - /* - * Tell the control thread that it can continue - * spawning services. This needs to be done after the new - * process has been started because otherwise signal handling - * on (Open) Solaris does not work correctly (see @bugref{5068}). - */ - int rc2 = RTThreadUserSignal(RTThreadSelf()); - if (RT_SUCCESS(rc)) - rc = rc2; - fSignalled = true; - - if (RT_SUCCESS(rc)) - { - /* - * Close the child ends of any pipes and redirected files. - */ - rc2 = RTHandleClose(phStdIn); AssertRC(rc2); - phStdIn = NULL; - rc2 = RTHandleClose(phStdOut); AssertRC(rc2); - phStdOut = NULL; - rc2 = RTHandleClose(phStdErr); AssertRC(rc2); - phStdErr = NULL; - - /* Enter the process loop. */ - rc = VBoxServiceControlThreadProcLoop(pThread, - hProcess, pThread->uTimeLimitMS, hPollSet, - &pThread->pipeStdInW, &pipeStdOutR, &pipeStdErrR); - - /* - * The handles that are no longer in the set have - * been closed by the above call in order to prevent - * the guest from getting stuck accessing them. - * So, NIL the handles to avoid closing them again. - */ - if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL))) - { - pThread->hNotificationPipeR = NIL_RTPIPE; - pThread->hNotificationPipeW = NIL_RTPIPE; - } - if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL))) - pipeStdErrR = NIL_RTPIPE; - if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL))) - pipeStdOutR = NIL_RTPIPE; - if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL))) - pThread->pipeStdInW = NIL_RTPIPE; - } - } - RTPollSetDestroy(hPollSet); - - RTPipeClose(pThread->hNotificationPipeR); - pThread->hNotificationPipeR = NIL_RTPIPE; - RTPipeClose(pThread->hNotificationPipeW); - pThread->hNotificationPipeW = NIL_RTPIPE; - } - RTPipeClose(pipeStdErrR); - pipeStdErrR = NIL_RTPIPE; - RTHandleClose(phStdErr); - if (phStdErr) - RTHandleClose(phStdErr); - } - RTPipeClose(pipeStdOutR); - pipeStdOutR = NIL_RTPIPE; - RTHandleClose(&hStdOut); - if (phStdOut) - RTHandleClose(phStdOut); - } - RTPipeClose(pThread->pipeStdInW); - pThread->pipeStdInW = NIL_RTPIPE; - RTHandleClose(phStdIn); - } - } - RTEnvDestroy(hEnv); - } - - /* Move thread to stopped thread list. */ - int rc2 = VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLIST_STOPPED, pThread); - AssertRC(rc2); - - if (pThread->uClientID) - { - if (RT_FAILURE(rc)) - { - rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID, - PROC_STS_ERROR, rc, - NULL /* pvData */, 0 /* cbData */); - if (RT_FAILURE(rc2)) - VBoxServiceError("Could not report process failure error; rc=%Rrc (process error %Rrc)\n", - rc2, rc); - } - - VBoxServiceVerbose(3, "[PID %u]: Cancelling pending host requests (client ID=%u)\n", - pThread->uPID, pThread->uClientID); - rc2 = VbglR3GuestCtrlCancelPendingWaits(pThread->uClientID); - if (RT_FAILURE(rc2)) - { - VBoxServiceError("[PID %u]: Cancelling pending host requests failed; rc=%Rrc\n", - pThread->uPID, rc2); - if (RT_SUCCESS(rc)) - rc = rc2; - } - - /* Disconnect from guest control service. */ - VBoxServiceVerbose(3, "[PID %u]: Disconnecting (client ID=%u) ...\n", - pThread->uPID, pThread->uClientID); - VbglR3GuestCtrlDisconnect(pThread->uClientID); - pThread->uClientID = 0; - } - - VBoxServiceVerbose(3, "[PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n", - pThread->uPID, pThread->pszCmd, rc); - - /* Update started/stopped status. */ - ASMAtomicXchgBool(&pThread->fStopped, true); - ASMAtomicXchgBool(&pThread->fStarted, false); - - /* - * If something went wrong signal the user event so that others don't wait - * forever on this thread. - */ - if (RT_FAILURE(rc) && !fSignalled) - RTThreadUserSignal(RTThreadSelf()); - - return rc; -} - - -/** - * Thread main routine for a started process. - * - * @return IPRT status code. - * @param RTTHREAD Pointer to the thread's data. - * @param void* User-supplied argument pointer. - * - */ -static DECLCALLBACK(int) VBoxServiceControlThread(RTTHREAD ThreadSelf, void *pvUser) -{ - PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser; - AssertPtrReturn(pThread, VERR_INVALID_POINTER); - return VBoxServiceControlThreadProcessWorker(pThread); -} - - -/** - * Executes (starts) a process on the guest. This causes a new thread to be created - * so that this function will not block the overall program execution. - * - * @return IPRT status code. - * @param uContextID Context ID to associate the process to start with. - * @param pProcess Process info. - */ -int VBoxServiceControlThreadStart(uint32_t uContextID, - PVBOXSERVICECTRLPROCESS pProcess) -{ - AssertPtrReturn(pProcess, VERR_INVALID_POINTER); - - /* - * Allocate new thread data and assign it to our thread list. - */ - PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD)); - if (!pThread) - return VERR_NO_MEMORY; - - int rc = gstsvcCntlExecThreadInit(pThread, pProcess, uContextID); - if (RT_SUCCESS(rc)) - { - static uint32_t s_uCtrlExecThread = 0; - if (s_uCtrlExecThread++ == UINT32_MAX) - s_uCtrlExecThread = 0; /* Wrap around to not let IPRT freak out. */ - rc = RTThreadCreateF(&pThread->Thread, VBoxServiceControlThread, - pThread /*pvUser*/, 0 /*cbStack*/, - RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "gctl%u", s_uCtrlExecThread); - if (RT_FAILURE(rc)) - { - VBoxServiceError("RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n", - rc, pThread); - } - else - { - VBoxServiceVerbose(4, "Waiting for thread to initialize ...\n"); - - /* Wait for the thread to initialize. */ - rc = RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */); - AssertRC(rc); - if ( ASMAtomicReadBool(&pThread->fShutdown) - || RT_FAILURE(rc)) - { - VBoxServiceError("Thread for process \"%s\" failed to start, rc=%Rrc\n", - pProcess->szCmd, rc); - } - else - { - ASMAtomicXchgBool(&pThread->fStarted, true); - } - } - } - - if (RT_FAILURE(rc)) - RTMemFree(pThread); - - return rc; -} - - -/** - * Performs a request to a specific (formerly started) guest process and waits - * for its response. - * - * @return IPRT status code. - * @param uPID PID of guest process to perform a request to. - * @param pRequest Pointer to request to perform. - */ -int VBoxServiceControlThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest) -{ - AssertPtrReturn(pRequest, VERR_INVALID_POINTER); - AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER); - /* Rest in pRequest is optional (based on the request type). */ - - int rc = VINF_SUCCESS; - PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlLockThread(uPID); - if (pThread) - { - if (ASMAtomicReadBool(&pThread->fShutdown)) - { - rc = VERR_CANCELLED; - } - else - { - /* Set request structure pointer. */ - pThread->pRequest = pRequest; - - /** @todo To speed up simultaneous guest process handling we could add a worker threads - * or queue in order to wait for the request to happen. Later. */ - /* Wake up guest thrad by sending a wakeup byte to the notification pipe so - * that RTPoll unblocks (returns) and we then can do our requested operation. */ - Assert(pThread->hNotificationPipeW != NIL_RTPIPE); - size_t cbWritten; - if (RT_SUCCESS(rc)) - rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten); - - if ( RT_SUCCESS(rc) - && cbWritten) - { - VBoxServiceVerbose(3, "[PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n", - uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData); - - rc = VBoxServiceControlThreadRequestWait(pRequest); - } - } - - VBoxServiceControlUnlockThread(pThread); - } - else /* PID not found! */ - rc = VERR_NOT_FOUND; - - VBoxServiceVerbose(3, "[PID %u]: Performed enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n", - uPID, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc); - return rc; -} - diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp index 53dc1675..ba3a64cf 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceCpuHotPlug.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -120,6 +120,18 @@ SYSFSCPUPATH g_aAcpiCpuPath[] = /** Level 4 */ {ACPI_CPU_PATH_NOT_PROBED, g_aAcpiCpuPathLvl4, RT_ELEMENTS(g_aAcpiCpuPathLvl4), NULL, NULL}, }; + +/** + * Possible directories to get to the topology directory for reading core and package id. + * + * @remark: This is not part of the path above because the eject file is not in one of the directories + * below and would make the hot unplug code fail. + */ +const char *g_apszTopologyPath[] = +{ + "sysdev", + "physical_node" +}; #endif #ifdef RT_OS_LINUX @@ -277,10 +289,25 @@ static int VBoxServiceCpuHotPlugGetACPIDevicePath(char **ppszPath, uint32_t idCp if (iLvlCurr == RT_ELEMENTS(g_aAcpiCpuPath) - 1) { /* Get the sysdev */ - uint32_t idCore = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/core_id", - pszPathCurr); - uint32_t idPackage = RTLinuxSysFsReadIntFile(10, "%s/sysdev/topology/physical_package_id", - pszPathCurr); + uint32_t idCore = 0; + uint32_t idPackage = 0; + + for (unsigned i = 0; i < RT_ELEMENTS(g_apszTopologyPath); i++) + { + int64_t i64Core = RTLinuxSysFsReadIntFile(10, "%s/%s/topology/core_id", + pszPathCurr, g_apszTopologyPath[i]); + int64_t i64Package = RTLinuxSysFsReadIntFile(10, "%s/%s/topology/physical_package_id", + pszPathCurr, g_apszTopologyPath[i]); + + if ( i64Core != -1 + && i64Package != -1) + { + idCore = (uint32_t)i64Core; + idPackage = (uint32_t)i64Package; + break; + } + } + if ( idCore == idCpuCore && idPackage == idCpuPackage) { diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h b/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h index 6a21cad9..a3f09fb6 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007-2012 Oracle Corporation + * Copyright (C) 2007-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -107,213 +107,7 @@ typedef VBOXSERVICE const *PCVBOXSERVICE; # define STATUS_SUCCESS ((NTSTATUS)0x00000000L) #endif /* RT_OS_WINDOWS */ -#ifdef VBOX_WITH_GUEST_CONTROL -/** - * Pipe IDs for handling the guest process poll set. - */ -typedef enum VBOXSERVICECTRLPIPEID -{ - VBOXSERVICECTRLPIPEID_UNKNOWN = 0, - VBOXSERVICECTRLPIPEID_STDIN = 10, - VBOXSERVICECTRLPIPEID_STDIN_WRITABLE = 11, - /** Pipe for reading from guest process' stdout. */ - VBOXSERVICECTRLPIPEID_STDOUT = 40, - /** Pipe for reading from guest process' stderr. */ - VBOXSERVICECTRLPIPEID_STDERR = 50, - /** Notification pipe for waking up the guest process - * control thread. */ - VBOXSERVICECTRLPIPEID_IPC_NOTIFY = 100 -} VBOXSERVICECTRLPIPEID; - -/** - * Request types to perform on a started guest process. - */ -typedef enum VBOXSERVICECTRLREQUESTTYPE -{ - /** Unknown request. */ - VBOXSERVICECTRLREQUEST_UNKNOWN = 0, - /** Main control thread asked used to quit. */ - VBOXSERVICECTRLREQUEST_QUIT = 1, - /** Performs reading from stdout. */ - VBOXSERVICECTRLREQUEST_STDOUT_READ = 50, - /** Performs reading from stderr. */ - VBOXSERVICECTRLREQUEST_STDERR_READ = 60, - /** Performs writing to stdin. */ - VBOXSERVICECTRLREQUEST_STDIN_WRITE = 70, - /** Same as VBOXSERVICECTRLREQUEST_STDIN_WRITE, but - * marks the end of input. */ - VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF = 71, - /** Kill/terminate process. - * @todo Implement this! */ - VBOXSERVICECTRLREQUEST_KILL = 90, - /** Gently ask process to terminate. - * @todo Implement this! */ - VBOXSERVICECTRLREQUEST_HANGUP = 91, - /** Ask the process in which status it - * currently is. - * @todo Implement this! */ - VBOXSERVICECTRLREQUEST_STATUS = 100 -} VBOXSERVICECTRLREQUESTTYPE; - -/** - * Thread list types. - */ -typedef enum VBOXSERVICECTRLTHREADLISTTYPE -{ - /** Unknown list -- uncool to use. */ - VBOXSERVICECTRLTHREADLIST_UNKNOWN = 0, - /** Stopped list: Here all guest threads end up - * when they reached the stopped state and can - * be shut down / free'd safely. */ - VBOXSERVICECTRLTHREADLIST_STOPPED = 1, - /** - * Started list: Here all threads are registered - * when they're up and running (that is, accepting - * commands). - */ - VBOXSERVICECTRLTHREADLIST_RUNNING = 2 -} VBOXSERVICECTRLTHREADLISTTYPE; - -/** - * Structure to perform a request on a started guest - * process. Needed for letting the main guest control thread - * to communicate (and wait) for a certain operation which - * will be done in context of the started guest process thread. - */ -typedef struct VBOXSERVICECTRLREQUEST -{ - /** Event semaphore to serialize access. */ - RTSEMEVENTMULTI Event; - /** The request type to handle. */ - VBOXSERVICECTRLREQUESTTYPE enmType; - /** Payload size; on input, this contains the (maximum) amount - * of data the caller wants to write or to read. On output, - * this show the actual amount of data read/written. */ - size_t cbData; - /** Payload data; a pre-allocated data buffer for input/output. */ - void *pvData; - /** The context ID which is required to complete the - * request. Not used at the moment. */ - uint32_t uCID; - /** The overall result of the operation. */ - int rc; -} VBOXSERVICECTRLREQUEST; -/** Pointer to request. */ -typedef VBOXSERVICECTRLREQUEST *PVBOXSERVICECTRLREQUEST; - -/** - * Structure holding information for starting a guest - * process. - */ -typedef struct VBOXSERVICECTRLPROCESS -{ - /** Full qualified path of process to start (without arguments). */ - char szCmd[GUESTPROCESS_MAX_CMD_LEN]; - /** Process execution flags. @sa */ - uint32_t uFlags; - /** Command line arguments. */ - char szArgs[GUESTPROCESS_MAX_ARGS_LEN]; - /** Number of arguments specified in pszArgs. */ - uint32_t uNumArgs; - /** String of environment variables ("FOO=BAR") to pass to the process - * to start. */ - char szEnv[GUESTPROCESS_MAX_ENV_LEN]; - /** Size (in bytes) of environment variables block. */ - uint32_t cbEnv; - /** Number of environment variables specified in pszEnv. */ - uint32_t uNumEnvVars; - /** User name (account) to start the process under. */ - char szUser[GUESTPROCESS_MAX_USER_LEN]; - /** Password of specified user name (account). */ - char szPassword[GUESTPROCESS_MAX_PASSWORD_LEN]; - /** Time limit (in ms) of the process' life time. */ - uint32_t uTimeLimitMS; -} VBOXSERVICECTRLPROCESS; -/** Pointer to a guest process block. */ -typedef VBOXSERVICECTRLPROCESS *PVBOXSERVICECTRLPROCESS; - -/** - * Structure for holding data for one (started) guest process. - */ -typedef struct VBOXSERVICECTRLTHREAD -{ - /** Pointer to list archor of following - * list node. - * @todo Would be nice to have a RTListGetAnchor(). */ - PRTLISTANCHOR pAnchor; - /** Node. */ - RTLISTNODE Node; - /** The worker thread. */ - RTTHREAD Thread; - /** Shutdown indicator; will be set when the thread - * needs (or is asked) to shutdown. */ - bool volatile fShutdown; - /** Indicator set by the service thread exiting. */ - bool volatile fStopped; - /** Whether the service was started or not. */ - bool fStarted; - /** Client ID. */ - uint32_t uClientID; - /** Context ID. */ - uint32_t uContextID; - /** Critical section for thread-safe use. */ - RTCRITSECT CritSect; - /** @todo Document me! */ - uint32_t uPID; - char *pszCmd; - uint32_t uFlags; - char **papszArgs; - uint32_t uNumArgs; - char **papszEnv; - uint32_t uNumEnvVars; - /** Name of specified user account to run the - * guest process under. */ - char *pszUser; - /** Password of specified user account. */ - char *pszPassword; - /** Overall time limit (in ms) that the guest process - * is allowed to run. 0 for indefinite time. */ - uint32_t uTimeLimitMS; - /** Pointer to the current IPC request being - * processed. */ - PVBOXSERVICECTRLREQUEST pRequest; - /** StdIn pipe for addressing writes to the - * guest process' stdin.*/ - RTPIPE pipeStdInW; - /** The notification pipe associated with this guest process. - * This is NIL_RTPIPE for output pipes. */ - RTPIPE hNotificationPipeW; - /** The other end of hNotificationPipeW. */ - RTPIPE hNotificationPipeR; -} VBOXSERVICECTRLTHREAD; -/** Pointer to thread data. */ -typedef VBOXSERVICECTRLTHREAD *PVBOXSERVICECTRLTHREAD; - -/** - * Structure for one (opened) guest file. - */ -typedef struct VBOXSERVICECTRLFILE -{ - /** Pointer to list archor of following - * list node. - * @todo Would be nice to have a RTListGetAnchor(). */ - PRTLISTANCHOR pAnchor; - /** Node. */ - RTLISTNODE Node; - /** The file name. */ - char szName[RTPATH_MAX]; - /** The file handle on the guest. */ - RTFILE hFile; - /** File handle to identify this file. */ - uint32_t uHandle; - /** Context ID. */ - uint32_t uContextID; -} VBOXSERVICECTRLFILE; -/** Pointer to thread data. */ -typedef VBOXSERVICECTRLFILE *PVBOXSERVICECTRLFILE; -#endif /* VBOX_WITH_GUEST_CONTROL */ #ifdef VBOX_WITH_GUEST_PROPS - /** * A guest property cache. */ @@ -358,6 +152,7 @@ RT_C_DECLS_BEGIN extern char *g_pszProgName; extern int g_cVerbosity; +extern char g_szLogFile[RTPATH_MAX + 128]; extern uint32_t g_DefaultInterval; extern VBOXSERVICE g_TimeSync; extern VBOXSERVICE g_Clipboard; @@ -368,7 +163,7 @@ extern VBOXSERVICE g_CpuHotPlug; extern VBOXSERVICE g_MemBalloon; extern VBOXSERVICE g_VMStatistics; #endif -#ifdef VBOX_WITH_PAGE_SHARING +#ifdef VBOXSERVICE_PAGE_SHARING extern VBOXSERVICE g_PageSharing; #endif #ifdef VBOX_WITH_SHARED_FOLDERS @@ -378,64 +173,37 @@ extern VBOXSERVICE g_AutoMount; extern RTCRITSECT g_csLog; /* For guest process stdout dumping. */ #endif -extern RTEXITCODE VBoxServiceSyntax(const char *pszFormat, ...); -extern RTEXITCODE VBoxServiceError(const char *pszFormat, ...); -extern void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...); -extern int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, - uint32_t u32Min, uint32_t u32Max); -extern int VBoxServiceStartServices(void); -extern int VBoxServiceStopServices(void); -extern void VBoxServiceMainWait(void); -extern int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus); +extern RTEXITCODE VBoxServiceSyntax(const char *pszFormat, ...); +extern RTEXITCODE VBoxServiceError(const char *pszFormat, ...); +extern void VBoxServiceVerbose(int iLevel, const char *pszFormat, ...); +extern int VBoxServiceArgUInt32(int argc, char **argv, const char *psz, int *pi, uint32_t *pu32, + uint32_t u32Min, uint32_t u32Max); +extern int VBoxServiceStartServices(void); +extern int VBoxServiceStopServices(void); +extern void VBoxServiceMainWait(void); +extern int VBoxServiceReportStatus(VBoxGuestFacilityStatus enmStatus); #ifdef RT_OS_WINDOWS -extern RTEXITCODE VBoxServiceWinInstall(void); -extern RTEXITCODE VBoxServiceWinUninstall(void); -extern RTEXITCODE VBoxServiceWinEnterCtrlDispatcher(void); -extern void VBoxServiceWinSetStopPendingStatus(uint32_t uCheckPoint); +extern RTEXITCODE VBoxServiceWinInstall(void); +extern RTEXITCODE VBoxServiceWinUninstall(void); +extern RTEXITCODE VBoxServiceWinEnterCtrlDispatcher(void); +extern void VBoxServiceWinSetStopPendingStatus(uint32_t uCheckPoint); #endif #ifdef VBOXSERVICE_TOOLBOX -extern bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit); +extern bool VBoxServiceToolboxMain(int argc, char **argv, RTEXITCODE *prcExit); #endif #ifdef RT_OS_WINDOWS # ifdef VBOX_WITH_GUEST_PROPS -extern int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList); -extern int VBoxServiceWinGetComponentVersions(uint32_t uiClientID); +extern int VBoxServiceVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache, char **ppszUserList, uint32_t *pcUsersInList); +extern int VBoxServiceWinGetComponentVersions(uint32_t uiClientID); # endif /* VBOX_WITH_GUEST_PROPS */ #endif /* RT_OS_WINDOWS */ -#ifdef VBOX_WITH_GUEST_CONTROL -/* Guest control main thread functions. */ -extern int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID); -extern int VBoxServiceControlListSet(VBOXSERVICECTRLTHREADLISTTYPE enmList, - PVBOXSERVICECTRLTHREAD pThread); -extern PVBOXSERVICECTRLTHREAD VBoxServiceControlLockThread(uint32_t uPID); -extern void VBoxServiceControlUnlockThread(const PVBOXSERVICECTRLTHREAD pThread); -extern int VBoxServiceControlSetInactive(PVBOXSERVICECTRLTHREAD pThread); -/* Per-thread guest process functions. */ -extern int VBoxServiceControlThreadStart(uint32_t uContext, - PVBOXSERVICECTRLPROCESS pProcess); -extern int VBoxServiceControlThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest); -extern int VBoxServiceControlThreadStop(const PVBOXSERVICECTRLTHREAD pThread); -extern int VBoxServiceControlThreadWait(const PVBOXSERVICECTRLTHREAD pThread, - RTMSINTERVAL msTimeout, int *prc); -extern int VBoxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread); -/* Request handling. */ -extern int VBoxServiceControlThreadRequestAlloc(PVBOXSERVICECTRLREQUEST *ppReq, - VBOXSERVICECTRLREQUESTTYPE enmType); -extern int VBoxServiceControlThreadRequestAllocEx(PVBOXSERVICECTRLREQUEST *ppReq, - VBOXSERVICECTRLREQUESTTYPE enmType, - void* pbData, - size_t cbData, - uint32_t uCID); -extern void VBoxServiceControlThreadRequestFree(PVBOXSERVICECTRLREQUEST pReq); -#endif /* VBOX_WITH_GUEST_CONTROL */ - #ifdef VBOXSERVICE_MANAGEMENT extern uint32_t VBoxServiceBalloonQueryPages(uint32_t cbPage); #endif -#if defined(VBOX_WITH_PAGE_SHARING) && defined(RT_OS_WINDOWS) +#if defined(VBOXSERVICE_PAGE_SHARING) extern RTEXITCODE VBoxServicePageSharingInitFork(void); #endif extern int VBoxServiceVMInfoSignal(void); diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp b/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp index 39c9912d..c954d5c9 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServicePageSharing.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,6 +23,7 @@ #include <iprt/avl.h> #include <iprt/asm.h> #include <iprt/mem.h> +#include <iprt/ldr.h> #include <iprt/process.h> #include <iprt/env.h> #include <iprt/stream.h> @@ -80,8 +81,7 @@ typedef struct _RTL_PROCESS_MODULES } RTL_PROCESS_MODULES, *PRTL_PROCESS_MODULES; typedef NTSTATUS (WINAPI *PFNZWQUERYSYSTEMINFORMATION)(ULONG, PVOID, ULONG, PULONG); -static PFNZWQUERYSYSTEMINFORMATION ZwQuerySystemInformation = NULL; -static HMODULE hNtdll = 0; +static PFNZWQUERYSYSTEMINFORMATION g_pfnZwQuerySystemInformation = NULL; static DECLCALLBACK(int) VBoxServicePageSharingEmptyTreeCallback(PAVLPVNODECORE pNode, void *pvUser); @@ -365,13 +365,13 @@ void VBoxServicePageSharingInspectGuest() CloseHandle(hSnapshot); /* Check all loaded kernel modules. */ - if (ZwQuerySystemInformation) + if (g_pfnZwQuerySystemInformation) { ULONG cbBuffer = 0; PVOID pBuffer = NULL; PRTL_PROCESS_MODULES pSystemModules; - NTSTATUS ret = ZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer); + NTSTATUS ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, (PVOID)&cbBuffer, 0, &cbBuffer); if (!cbBuffer) { VBoxServiceVerbose(1, "ZwQuerySystemInformation returned length 0\n"); @@ -382,7 +382,7 @@ void VBoxServicePageSharingInspectGuest() if (!pBuffer) goto skipkernelmodules; - ret = ZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer); + ret = g_pfnZwQuerySystemInformation(SystemModuleInformation, pBuffer, cbBuffer, &cbBuffer); if (ret != STATUS_SUCCESS) { VBoxServiceVerbose(1, "ZwQuerySystemInformation returned %x (1)\n", ret); @@ -552,12 +552,9 @@ static DECLCALLBACK(int) VBoxServicePageSharingInit(void) AssertRCReturn(rc, rc); #if defined(RT_OS_WINDOWS) && !defined(TARGET_NT4) - hNtdll = LoadLibrary("ntdll.dll"); + g_pfnZwQuerySystemInformation = (PFNZWQUERYSYSTEMINFORMATION)RTLdrGetSystemSymbol("ntdll.dll", "ZwQuerySystemInformation"); - if (hNtdll) - ZwQuerySystemInformation = (PFNZWQUERYSYSTEMINFORMATION)GetProcAddress(hNtdll, "ZwQuerySystemInformation"); - - rc = VbglR3GetSessionId(&g_idSession); + rc = VbglR3GetSessionId(&g_idSession); if (RT_FAILURE(rc)) { if (rc == VERR_IO_GEN_FAILURE) @@ -695,7 +692,7 @@ DECLCALLBACK(int) VBoxServicePageSharingWorkerProcess(bool volatile *pfShutdown) { char const *papszArgs[3]; papszArgs[0] = pszExeName; - papszArgs[1] = "--pagefusionfork"; + papszArgs[1] = "pagefusion"; papszArgs[2] = NULL; rc = RTProcCreate(pszExeName, papszArgs, RTENV_DEFAULT, 0 /* normal child */, &hProcess); if (RT_FAILURE(rc)) @@ -737,12 +734,6 @@ DECLCALLBACK(int) VBoxServicePageSharingWorkerProcess(bool volatile *pfShutdown) static DECLCALLBACK(void) VBoxServicePageSharingTerm(void) { VBoxServiceVerbose(3, "VBoxServicePageSharingTerm\n"); - -#if defined(RT_OS_WINDOWS) && !defined(TARGET_NT4) - if (hNtdll) - FreeLibrary(hNtdll); -#endif - return; } diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp index 347faa95..1e691c95 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -38,8 +38,9 @@ PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVIC /** @todo Docs */ PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t uFlags) { - AssertPtr(pCache); - AssertPtr(pszName); + AssertPtrReturn(pCache, NULL); + AssertPtrReturn(pszName, NULL); + /** @todo This is a O(n) lookup, maybe improve this later to O(1) using a * map. * r=bird: Use a string space (RTstrSpace*). That is O(log n) in its current @@ -65,11 +66,18 @@ PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheFindInternal(PVBOXSERVICEVEPROP /** @todo Docs */ PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName) { - AssertPtr(pszName); + AssertPtrReturn(pCache, NULL); + AssertPtrReturn(pszName, NULL); + PVBOXSERVICEVEPROPCACHEENTRY pNode = (PVBOXSERVICEVEPROPCACHEENTRY)RTMemAlloc(sizeof(VBOXSERVICEVEPROPCACHEENTRY)); if (pNode) { pNode->pszName = RTStrDup(pszName); + if (!pNode->pszName) + { + RTMemFree(pNode); + return NULL; + } pNode->pszValue = NULL; pNode->fFlags = 0; pNode->pszValueReset = NULL; @@ -88,7 +96,8 @@ PVBOXSERVICEVEPROPCACHEENTRY vboxServicePropCacheInsertEntryInternal(PVBOXSERVIC /** @todo Docs */ int vboxServicePropCacheWritePropF(uint32_t u32ClientId, const char *pszName, uint32_t fFlags, const char *pszValueFormat, ...) { - AssertPtr(pszName); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + int rc; if (pszValueFormat != NULL) { @@ -141,7 +150,7 @@ int vboxServicePropCacheWritePropF(uint32_t u32ClientId, const char *pszName, ui */ int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientId) { - AssertPtr(pCache); + AssertPtrReturn(pCache, VERR_INVALID_POINTER); /** @todo Prevent init the cache twice! * r=bird: Use a magic. */ RTListInit(&pCache->NodeHead); @@ -164,8 +173,8 @@ int VBoxServicePropCacheCreate(PVBOXSERVICEVEPROPCACHE pCache, uint32_t uClientI int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, uint32_t fFlags, const char *pszValueReset) { - AssertPtr(pCache); - AssertPtr(pszName); + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); PVBOXSERVICEVEPROPCACHEENTRY pNode = vboxServicePropCacheFindInternal(pCache, pszName, 0); if (pNode == NULL) pNode = vboxServicePropCacheInsertEntryInternal(pCache, pszName); @@ -182,6 +191,7 @@ int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, if (pNode->pszValueReset) RTStrFree(pNode->pszValueReset); pNode->pszValueReset = RTStrDup(pszValueReset); + AssertPtr(pNode->pszValueReset); } rc = RTCritSectLeave(&pCache->CritSect); } @@ -205,9 +215,10 @@ int VBoxServicePropCacheUpdateEntry(PVBOXSERVICEVEPROPCACHE pCache, */ int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszName, const char *pszValueFormat, ...) { - AssertPtr(pCache); + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(pszName, VERR_INVALID_POINTER); + Assert(pCache->uClientID); - AssertPtr(pszName); /* * Format the value first. @@ -250,8 +261,15 @@ int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszNa { /* Write the update. */ rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, pNode->fFlags, pszValue); - RTStrFree(pNode->pszValue); - pNode->pszValue = RTStrDup(pszValue); + VBoxServiceVerbose(4, "[PropCache %p]: Written \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n", + pCache, pNode->pszName, pszValue, pNode->fFlags, rc); + if (RT_SUCCESS(rc)) /* Only update the node's value on successful write. */ + { + RTStrFree(pNode->pszValue); + pNode->pszValue = RTStrDup(pszValue); + if (!pNode->pszValue) + rc = VERR_NO_MEMORY; + } } else rc = VINF_NO_CHANGE; /* No update needed. */ @@ -261,11 +279,16 @@ int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszNa /* No value specified. Deletion (or no action required). */ if (pNode->pszValue) /* Did we have a value before? Then the value needs to be deleted. */ { - /* Delete property (but do not remove from cache) if not deleted yet. */ - RTStrFree(pNode->pszValue); - pNode->pszValue = NULL; rc = vboxServicePropCacheWritePropF(pCache->uClientID, pNode->pszName, 0, /* Flags */ NULL /* Value */); + VBoxServiceVerbose(4, "[PropCache %p]: Deleted \"%s\"=\"%s\" (flags: %x), rc=%Rrc\n", + pCache, pNode->pszName, pNode->pszValue, pNode->fFlags, rc); + if (RT_SUCCESS(rc)) /* Only delete property value on successful Vbgl deletion. */ + { + /* Delete property (but do not remove from cache) if not deleted yet. */ + RTStrFree(pNode->pszValue); + pNode->pszValue = NULL; + } } else rc = VINF_NO_CHANGE; /* No update needed. */ @@ -275,6 +298,9 @@ int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszNa RTCritSectLeave(&pCache->CritSect); } + VBoxServiceVerbose(4, "[PropCache %p]: Updating \"%s\" resulted in rc=%Rrc\n", + pCache, pszName, rc); + /* Delete temp stuff. */ RTStrFree(pszValue); return rc; @@ -295,8 +321,9 @@ int VBoxServicePropCacheUpdate(PVBOXSERVICEVEPROPCACHE pCache, const char *pszNa */ int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char *pszValue, uint32_t fFlags, const char *pszPathFormat, ...) { - AssertPtr(pCache); - AssertPtr(pszPathFormat); + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(pszPathFormat, VERR_INVALID_POINTER); + int rc = VERR_NOT_FOUND; PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL; if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect))) @@ -341,7 +368,8 @@ int VBoxServicePropCacheUpdateByPath(PVBOXSERVICEVEPROPCACHE pCache, const char */ int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache) { - AssertPtr(pCache); + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + int rc = VINF_SUCCESS; PVBOXSERVICEVEPROPCACHEENTRY pNodeIt = NULL; if (RT_SUCCESS(RTCritSectEnter(&pCache->CritSect))) @@ -366,7 +394,7 @@ int VBoxServicePropCacheFlush(PVBOXSERVICEVEPROPCACHE pCache) */ void VBoxServicePropCacheDestroy(PVBOXSERVICEVEPROPCACHE pCache) { - AssertPtr(pCache); + AssertPtrReturnVoid(pCache); Assert(pCache->uClientID); /* Lock the cache. */ diff --git a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h index 430f9a7d..eb3ac648 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h +++ b/src/VBox/Additions/common/VBoxService/VBoxServicePropCache.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h b/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h index cf932cbe..60296c55 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceResource-win.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp index c7c259ae..8083e84a 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceStats.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -43,6 +43,7 @@ #include <iprt/assert.h> #include <iprt/mem.h> +#include <iprt/ldr.h> #include <VBox/param.h> #include <iprt/semaphore.h> #include <iprt/string.h> @@ -123,45 +124,32 @@ static DECLCALLBACK(int) VBoxServiceVMStatsInit(void) VBoxServiceVerbose(3, "VBoxStatsInit: DeviceIoControl failed with %d\n", rc); #ifdef RT_OS_WINDOWS - /** @todo Use RTLdr instead of LoadLibrary/GetProcAddress here! */ - - /* NtQuerySystemInformation might be dropped in future releases, so load it dynamically as per Microsoft's recommendation */ - HMODULE hMod = LoadLibrary("NTDLL.DLL"); - if (hMod) + /* NtQuerySystemInformation might be dropped in future releases, so load + it dynamically as per Microsoft's recommendation. */ + *(void **)&gCtx.pfnNtQuerySystemInformation = RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation"); + if (gCtx.pfnNtQuerySystemInformation) + VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation); + else { - *(uintptr_t *)&gCtx.pfnNtQuerySystemInformation = (uintptr_t)GetProcAddress(hMod, "NtQuerySystemInformation"); - if (gCtx.pfnNtQuerySystemInformation) - VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnNtQuerySystemInformation = %x\n", gCtx.pfnNtQuerySystemInformation); - else - { - VBoxServiceVerbose(3, "VBoxStatsInit: NTDLL.NtQuerySystemInformation not found!\n"); - return VERR_SERVICE_DISABLED; - } + VBoxServiceVerbose(3, "VBoxStatsInit: ntdll.NtQuerySystemInformation not found!\n"); + return VERR_SERVICE_DISABLED; } /* GlobalMemoryStatus is win2k and up, so load it dynamically */ - hMod = LoadLibrary("KERNEL32.DLL"); - if (hMod) + *(void **)&gCtx.pfnGlobalMemoryStatusEx = RTLdrGetSystemSymbol("kernel32.dll", "GlobalMemoryStatusEx"); + if (gCtx.pfnGlobalMemoryStatusEx) + VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx); + else { - *(uintptr_t *)&gCtx.pfnGlobalMemoryStatusEx = (uintptr_t)GetProcAddress(hMod, "GlobalMemoryStatusEx"); - if (gCtx.pfnGlobalMemoryStatusEx) - VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.GlobalMemoryStatusEx = %x\n", gCtx.pfnGlobalMemoryStatusEx); - else - { - /** @todo Now fails in NT4; do we care? */ - VBoxServiceVerbose(3, "VBoxStatsInit: KERNEL32.GlobalMemoryStatusEx not found!\n"); - return VERR_SERVICE_DISABLED; - } + /** @todo Now fails in NT4; do we care? */ + VBoxServiceVerbose(3, "VBoxStatsInit: kernel32.GlobalMemoryStatusEx not found!\n"); + return VERR_SERVICE_DISABLED; } + /* GetPerformanceInfo is xp and up, so load it dynamically */ - hMod = LoadLibrary("PSAPI.DLL"); - if (hMod) - { - *(uintptr_t *)&gCtx.pfnGetPerformanceInfo = (uintptr_t)GetProcAddress(hMod, "GetPerformanceInfo"); - if (gCtx.pfnGetPerformanceInfo) - VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo); - /* failure is not fatal */ - } + *(void **)&gCtx.pfnGetPerformanceInfo = RTLdrGetSystemSymbol("psapi.dll", "GetPerformanceInfo"); + if (gCtx.pfnGetPerformanceInfo) + VBoxServiceVerbose(3, "VBoxStatsInit: gCtx.pfnGetPerformanceInfo= %x\n", gCtx.pfnGetPerformanceInfo); #endif /* RT_OS_WINDOWS */ return VINF_SUCCESS; @@ -354,6 +342,7 @@ static void VBoxServiceVMStatsReport(void) req.guestStats.u32PageSize = getpagesize(); req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL + | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); @@ -464,30 +453,30 @@ static void VBoxServiceVMStatsReport(void) */ uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0; int rc = -1; - kstat_t *pStatPages = kstat_lookup(pStatKern, "unix", 0 /* instance */, "system_pages"); + kstat_t *pStatPages = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"system_pages"); if (pStatPages) { rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */); if (rc != -1) { kstat_named_t *pStat = NULL; - pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "pagestotal"); + pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"pagestotal"); if (pStat) u64Total = pStat->value.ul; - pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, "freemem"); + pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"freemem"); if (pStat) u64Free = pStat->value.ul; } } - kstat_t *pStatZFS = kstat_lookup(pStatKern, "zfs", 0 /* instance */, "arcstats"); + kstat_t *pStatZFS = kstat_lookup(pStatKern, (char *)"zfs", 0 /* instance */, (char *)"arcstats"); if (pStatZFS) { rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */); if (rc != -1) { - kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, "size"); + kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, (char *)"size"); if (pStat) u64Cached = pStat->value.ul; } @@ -497,14 +486,14 @@ static void VBoxServiceVMStatsReport(void) * The vminfo are accumulative counters updated every "N" ticks. Let's get the * number of stat updates so far and use that to divide the swap counter. */ - kstat_t *pStatInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "sysinfo"); + kstat_t *pStatInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"sysinfo"); if (pStatInfo) { sysinfo_t SysInfo; rc = kstat_read(pStatKern, pStatInfo, &SysInfo); if (rc != -1) { - kstat_t *pStatVMInfo = kstat_lookup(pStatKern, "unix", 0 /* instance */, "vminfo"); + kstat_t *pStatVMInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"vminfo"); if (pStatVMInfo) { vminfo_t VMInfo; @@ -534,6 +523,7 @@ static void VBoxServiceVMStatsReport(void) req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL + | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); @@ -726,3 +716,4 @@ VBOXSERVICE g_VMStatistics = VBoxServiceVMStatsStop, VBoxServiceVMStatsTerm }; + diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp index 3371cb2f..5c9ba83e 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceTimeSync.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2007 Oracle Corporation + * Copyright (C) 2007-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -410,7 +410,7 @@ static bool VBoxServiceTimeSyncAdjust(PCRTTIMESPEC pDrift) else if (g_cTimeSyncErrors++ < 10) VBoxServiceError("VBoxServiceTimeSyncAdjust: GetSystemTimeAdjustment failed, error=%ld\n", GetLastError()); -#elif defined(RT_OS_OS2) +#elif defined(RT_OS_OS2) || defined(RT_OS_HAIKU) /* No API for doing gradual time adjustments. */ #else /* PORTME */ diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp index 0110dea3..6dbb74bd 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceToolBox.cpp @@ -632,10 +632,10 @@ static int VBoxServiceToolboxPrintFsInfo(const char *pszName, uint16_t cbName, pObjInfo->Attr.u.Unix.gid, pObjInfo->cbObject, pObjInfo->cbAllocated, - pObjInfo->BirthTime, - pObjInfo->ChangeTime, - pObjInfo->ModificationTime, - pObjInfo->AccessTime); + RTTimeSpecGetNano(&pObjInfo->BirthTime), /** @todo really ns? */ + RTTimeSpecGetNano(&pObjInfo->ChangeTime), /** @todo really ns? */ + RTTimeSpecGetNano(&pObjInfo->ModificationTime), /** @todo really ns? */ + RTTimeSpecGetNano(&pObjInfo->AccessTime)); /** @todo really ns? */ RTPrintf(" %2d %s\n", cbName, pszName); } } @@ -1525,7 +1525,7 @@ static RTEXITCODE VBoxServiceToolboxStat(int argc, char **argv) RTListInit(&fileList); while ( (ch = RTGetOpt(&GetState, &ValueUnion)) - && RT_SUCCESS(rc)) + && RT_SUCCESS(rc)) { /* For options that require an argument, ValueUnion has received the value. */ switch (ch) diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp index 6dcff3a8..9302a843 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -47,8 +47,12 @@ * @param puTimestamp Where to return the timestamp. This is only set * on success. Optional. */ -int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) +int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, + char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) { + AssertPtrReturn(pszPropName, VERR_INVALID_POINTER); + AssertPtrReturn(ppszValue, VERR_INVALID_POINTER); + uint32_t cbBuf = _1K; void *pvBuf = NULL; int rc; @@ -106,7 +110,8 @@ int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, char **pp break; /* done */ } - RTMemFree(pvBuf); + if (pvBuf) + RTMemFree(pvBuf); return rc; } @@ -121,14 +126,14 @@ int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, char **pp * @param pu32 Where to store the 32-bit value. * */ -int VBoxServiceReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max) +int VBoxServiceReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, + uint32_t *pu32, uint32_t u32Min, uint32_t u32Max) { char *pszValue; int rc = VBoxServiceReadProp(u32ClientId, pszPropName, &pszValue, - NULL /* ppszFlags */, NULL /* puTimestamp */); + NULL /* ppszFlags */, NULL /* puTimestamp */); if (RT_SUCCESS(rc)) { - AssertPtr(pu32); char *pszNext; rc = RTStrToUInt32Ex(pszValue, &pszNext, 0, pu32); if ( RT_SUCCESS(rc) @@ -144,6 +149,63 @@ int VBoxServiceReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uin /** + * Reads a guest property from the host side. + * + * @returns IPRT status code, fully bitched. + * @param u32ClientId The HGCM client ID for the guest property session. + * @param pszPropName The property name. + * @param fReadOnly Whether or not this property needs to be read only + * by the guest side. Otherwise VERR_ACCESS_DENIED will + * be returned. + * @param ppszValue Where to return the value. This is always set + * to NULL. Free it using RTStrFree(). + * @param ppszFlags Where to return the value flags. Free it + * using RTStrFree(). Optional. + * @param puTimestamp Where to return the timestamp. This is only set + * on success. Optional. + */ +int VBoxServiceReadHostProp(uint32_t u32ClientId, const char *pszPropName, bool fReadOnly, + char **ppszValue, char **ppszFlags, uint64_t *puTimestamp) +{ + AssertPtrReturn(ppszValue, VERR_INVALID_PARAMETER); + + char *pszValue = NULL; + char *pszFlags = NULL; + int rc = VBoxServiceReadProp(u32ClientId, pszPropName, &pszValue, &pszFlags, puTimestamp); + if (RT_SUCCESS(rc)) + { + /* Check security bits. */ + if ( fReadOnly /* Do we except a guest read-only property */ + && !RTStrStr(pszFlags, "RDONLYGUEST")) + { + /* If we want a property which is read-only on the guest + * and it is *not* marked as such, deny access! */ + rc = VERR_ACCESS_DENIED; + } + + if (RT_SUCCESS(rc)) + { + *ppszValue = pszValue; + + if (ppszFlags) + *ppszFlags = pszFlags; + else if (pszFlags) + RTStrFree(pszFlags); + } + else + { + if (pszValue) + RTStrFree(pszValue); + if (pszFlags) + RTStrFree(pszFlags); + } + } + + return rc; +} + + +/** * Wrapper around VbglR3GuestPropWriteValue that does value formatting and * logging. * diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h index 7cd6dfc9..69ea9a9f 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceUtils.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,6 +23,7 @@ #ifdef VBOX_WITH_GUEST_PROPS int VBoxServiceReadProp(uint32_t u32ClientId, const char *pszPropName, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp); int VBoxServiceReadPropUInt32(uint32_t u32ClientId, const char *pszPropName, uint32_t *pu32, uint32_t u32Min, uint32_t u32Max); +int VBoxServiceReadHostProp(uint32_t u32ClientId, const char *pszPropName, bool fReadOnly, char **ppszValue, char **ppszFlags, uint64_t *puTimestamp); int VBoxServiceWritePropF(uint32_t u32ClientId, const char *pszName, const char *pszValueFormat, ...); #endif diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp index a23128a5..e9191a10 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,12 +24,13 @@ # define _WIN32_WINNT 0x0502 /* CachedRemoteInteractive in recent SDKs. */ #endif #include <Windows.h> -#include <wtsapi32.h> /* For WTS* calls. */ -#include <psapi.h> /* EnumProcesses. */ -#include <Ntsecapi.h> /* Needed for process security information. */ +#include <wtsapi32.h> /* For WTS* calls. */ +#include <psapi.h> /* EnumProcesses. */ +#include <Ntsecapi.h> /* Needed for process security information. */ #include <iprt/assert.h> #include <iprt/ldr.h> +#include <iprt/localipc.h> #include <iprt/mem.h> #include <iprt/thread.h> #include <iprt/string.h> @@ -39,38 +40,47 @@ #include <VBox/VBoxGuestLib.h> #include "VBoxServiceInternal.h" #include "VBoxServiceUtils.h" +#include "VBoxServiceVMInfo.h" +#include "../../WINNT/VBoxTray/VBoxTrayMsg.h" /* For IPC. */ +static uint32_t s_uDebugGuestPropClientID = 0; +static uint32_t s_uDebugIter = 0; +/** Whether to skip the logged-in user detection over RDP or not. + * See notes in this section why we might want to skip this. */ +static bool s_fSkipRDPDetection = false; /******************************************************************************* * Structures and Typedefs * *******************************************************************************/ /** Structure for storing the looked up user information. */ -typedef struct +typedef struct VBOXSERVICEVMINFOUSER { WCHAR wszUser[_MAX_PATH]; WCHAR wszAuthenticationPackage[_MAX_PATH]; WCHAR wszLogonDomain[_MAX_PATH]; /** Number of assigned user processes. */ ULONG ulNumProcs; - /** Last (highest) session number. This + /** Last (highest) session ID. This * is needed for distinguishing old session * process counts from new (current) session * ones. */ - ULONG ulSession; + ULONG ulLastSession; } VBOXSERVICEVMINFOUSER, *PVBOXSERVICEVMINFOUSER; /** Structure for the file information lookup. */ -typedef struct +typedef struct VBOXSERVICEVMINFOFILE { char *pszFilePath; char *pszFileName; } VBOXSERVICEVMINFOFILE, *PVBOXSERVICEVMINFOFILE; /** Structure for process information lookup. */ -typedef struct +typedef struct VBOXSERVICEVMINFOPROC { /** The PID. */ DWORD id; + /** The SID. */ + PSID pSid; /** The LUID. */ LUID luid; /** Interactive process. */ @@ -84,7 +94,8 @@ typedef struct uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs); bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER a_pUserInfo, PLUID a_pSession); int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppProc, DWORD *pdwCount); -void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs); +void VBoxServiceVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs); +int vboxServiceVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain); typedef BOOL WINAPI FNQUERYFULLPROCESSIMAGENAME(HANDLE, DWORD, LPTSTR, PDWORD); typedef FNQUERYFULLPROCESSIMAGENAME *PFNQUERYFULLPROCESSIMAGENAME; @@ -92,6 +103,28 @@ typedef FNQUERYFULLPROCESSIMAGENAME *PFNQUERYFULLPROCESSIMAGENAME; #ifndef TARGET_NT4 +static bool vboxServiceVMInfoSession0Separation(void) +{ + /** @todo Only do this once. Later. */ + OSVERSIONINFOEX OSInfoEx; + RT_ZERO(OSInfoEx); + OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if ( !GetVersionEx((LPOSVERSIONINFO) &OSInfoEx) + || OSInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT) + { + /* Platform other than NT (e.g. Win9x) not supported. */ + return false; + } + + if ( OSInfoEx.dwMajorVersion >= 6 + && OSInfoEx.dwMinorVersion >= 0) + { + return true; + } + + return false; +} + /** * Retrieves the module name of a given process. * @@ -107,6 +140,7 @@ static int VBoxServiceVMInfoWinProcessesGetModuleName(PVBOXSERVICEVMINFOPROC con AssertPtrReturn(pszName, VERR_INVALID_POINTER); AssertReturn(cbName, VERR_INVALID_PARAMETER); + /** @todo Only do this once. Later. */ OSVERSIONINFOEX OSInfoEx; RT_ZERO(OSInfoEx); OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); @@ -142,20 +176,19 @@ static int VBoxServiceVMInfoWinProcessesGetModuleName(PVBOXSERVICEVMINFOPROC con { /* Loading the module and getting the symbol for each and every process is expensive * -- since this function (at the moment) only is used for debugging purposes it's okay. */ - RTLDRMOD hMod; - rc = RTLdrLoad("kernel32.dll", &hMod); - if (RT_SUCCESS(rc)) + PFNQUERYFULLPROCESSIMAGENAME pfnQueryFullProcessImageName; + pfnQueryFullProcessImageName = (PFNQUERYFULLPROCESSIMAGENAME) + RTLdrGetSystemSymbol("kernel32.dll", "QueryFullProcessImageNameA"); + /** @todo r=bird: WTF don't we query the UNICODE name? */ + if (pfnQueryFullProcessImageName) { - PFNQUERYFULLPROCESSIMAGENAME pfnQueryFullProcessImageName; - rc = RTLdrGetSymbol(hMod, "QueryFullProcessImageNameA", (void **)&pfnQueryFullProcessImageName); - if (RT_SUCCESS(rc)) - { - DWORD dwLen = cbName / sizeof(TCHAR); - if (!pfnQueryFullProcessImageName(h, 0 /*PROCESS_NAME_NATIVE*/, pszName, &dwLen)) - rc = VERR_ACCESS_DENIED; - } - - RTLdrClose(hMod); + /** @todo r=bird: Completely bogus use of TCHAR. + * !!ALL USE OF TCHAR IS HEREWITH BANNED IN ALL VBOX SOURCES!! + * We use WCHAR when talking to windows, everything else is WRONG. (We don't + * want Chinese MBCS being treated as UTF-8.) */ + DWORD dwLen = cbName / sizeof(TCHAR); + if (!pfnQueryFullProcessImageName(h, 0 /*PROCESS_NAME_NATIVE*/, pszName, &dwLen)) + rc = VERR_ACCESS_DENIED; } } else @@ -211,10 +244,13 @@ static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pPro case TokenGroups: dwTokenInfoSize = 0; - /* Allocating will follow in a second step. */ + /* Allocation will follow in a second step. */ break; - /** @todo Implement more token classes here. */ + case TokenUser: + dwTokenInfoSize = 0; + /* Allocation will follow in a second step. */ + break; default: VBoxServiceError("Token class not implemented: %ld", tkClass); @@ -242,6 +278,14 @@ static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pPro dwTokenInfoSize = dwRetLength; break; + case TokenUser: + pvTokenInfo = (PTOKEN_USER)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, dwRetLength); + if (!pvTokenInfo) + dwErr = GetLastError(); + dwTokenInfoSize = dwRetLength; + break; + default: AssertMsgFailed(("Re-allocating of token information for token class not implemented\n")); break; @@ -264,6 +308,7 @@ static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pPro case TokenStatistics: { PTOKEN_STATISTICS pStats = (PTOKEN_STATISTICS)pvTokenInfo; + AssertPtr(pStats); memcpy(&pProc->luid, &pStats->AuthenticationId, sizeof(LUID)); /** @todo Add more information of TOKEN_STATISTICS as needed. */ break; @@ -309,6 +354,42 @@ static int VBoxServiceVMInfoWinProcessesGetTokenInfo(PVBOXSERVICEVMINFOPROC pPro break; } + case TokenUser: + { + PTOKEN_USER pUser = (PTOKEN_USER)pvTokenInfo; + AssertPtr(pUser); + + DWORD dwLength = GetLengthSid(pUser->User.Sid); + Assert(dwLength); + if (dwLength) + { + pProc->pSid = (PSID)HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, dwLength); + AssertPtr(pProc->pSid); + if (CopySid(dwLength, pProc->pSid, pUser->User.Sid)) + { + if (!IsValidSid(pProc->pSid)) + dwErr = ERROR_INVALID_NAME; + } + else + dwErr = GetLastError(); + } + else + dwErr = ERROR_NO_DATA; + + if (dwErr != ERROR_SUCCESS) + { + VBoxServiceError("Error retrieving SID of process PID=%ld: %ld\n", + pProc->id, dwErr); + if (pProc->pSid) + { + HeapFree(GetProcessHeap(), 0 /* Flags */, pProc->pSid); + pProc->pSid = NULL; + } + } + break; + } + default: AssertMsgFailed(("Unhandled token information class\n")); break; @@ -383,6 +464,7 @@ int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDW break; } } while (cProcesses <= _32K); /* Should be enough; see: http://blogs.technet.com/markrussinovich/archive/2009/07/08/3261309.aspx */ + if (RT_SUCCESS(rc)) { /* @@ -396,23 +478,22 @@ int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDW for (DWORD i = 0; i < cProcesses; i++) { paProcs[i].id = paPID[i]; - rc = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenGroups); - if (RT_FAILURE(rc)) - { - /* Because some processes cannot be opened/parsed on - Windows, we should not consider to be this an error here. */ - rc = VINF_SUCCESS; - } - else - { - rc = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics); - if (RT_FAILURE(rc)) - { - /* Because some processes cannot be opened/parsed on - Windows, we should not consider to be this an error here. */ - rc = VINF_SUCCESS; - } - } + paProcs[i].pSid = NULL; + + int rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenUser); + if (RT_FAILURE(rc2) && g_cVerbosity) + VBoxServiceError("Get token class \"user\" for process %ld failed, rc=%Rrc\n", + paProcs[i].id, rc2); + + rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenGroups); + if (RT_FAILURE(rc2) && g_cVerbosity) + VBoxServiceError("Get token class \"groups\" for process %ld failed, rc=%Rrc\n", + paProcs[i].id, rc2); + + rc2 = VBoxServiceVMInfoWinProcessesGetTokenInfo(&paProcs[i], TokenStatistics); + if (RT_FAILURE(rc2) && g_cVerbosity) + VBoxServiceError("Get token class \"statistics\" for process %ld failed, rc=%Rrc\n", + paProcs[i].id, rc2); } /* Save number of processes */ @@ -422,7 +503,7 @@ int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDW *ppaProcs = paProcs; } else - RTMemFree(paProcs); + VBoxServiceVMInfoWinProcessesFree(cProcesses, paProcs); } else rc = VERR_NO_MEMORY; @@ -438,8 +519,17 @@ int VBoxServiceVMInfoWinProcessesEnumerate(PVBOXSERVICEVMINFOPROC *ppaProcs, PDW * * @param paProcs What */ -void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs) +void VBoxServiceVMInfoWinProcessesFree(DWORD cProcs, PVBOXSERVICEVMINFOPROC paProcs) { + for (DWORD i = 0; i < cProcs; i++) + { + if (paProcs[i].pSid) + { + HeapFree(GetProcessHeap(), 0 /* Flags */, paProcs[i].pSid); + paProcs[i].pSid = NULL; + } + + } RTMemFree(paProcs); } @@ -447,14 +537,14 @@ void VBoxServiceVMInfoWinProcessesFree(PVBOXSERVICEVMINFOPROC paProcs) * Determines whether the specified session has processes on the system. * * @returns Number of processes found for a specified session. - * @param pSession The session. + * @param pSession The current user's SID. * @param paProcs The process snapshot. * @param cProcs The number of processes in the snaphot. * @param puSession Looked up session number. Optional. */ uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, PVBOXSERVICEVMINFOPROC const paProcs, DWORD cProcs, - PULONG puSession) + PULONG puTerminalSession) { if (!pSession) { @@ -466,10 +556,20 @@ uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, NTSTATUS rcNt = LsaGetLogonSessionData(pSession, &pSessionData); if (rcNt != STATUS_SUCCESS) { - VBoxServiceError("Could not get logon session data! rcNt=%#x", rcNt); + VBoxServiceError("Could not get logon session data! rcNt=%#x\n", rcNt); return 0; } + if (!IsValidSid(pSessionData->Sid)) + { + VBoxServiceError("User SID=%p is not valid\n", pSessionData->Sid); + if (pSessionData) + LsaFreeReturnBuffer(pSessionData); + return 0; + } + + int rc = VINF_SUCCESS; + /* * Even if a user seems to be logged in, it could be a stale/orphaned logon * session. So check if we have some processes bound to it by comparing the @@ -478,40 +578,35 @@ uint32_t VBoxServiceVMInfoWinSessionHasProcesses(PLUID pSession, uint32_t cNumProcs = 0; for (DWORD i = 0; i < cProcs; i++) { - VBoxServiceVerbose(4, "PID=%ld: (Interactive: %RTbool) %ld:%ld <-> %ld:%ld\n", - paProcs[i].id, paProcs[i].fInteractive, - paProcs[i].luid.HighPart, paProcs[i].luid.LowPart, - pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart); if (g_cVerbosity) { TCHAR szModule[_1K]; - int rc2 = VBoxServiceVMInfoWinProcessesGetModuleName(&paProcs[i], szModule, sizeof(szModule)); - if (RT_SUCCESS(rc2)) + rc = VBoxServiceVMInfoWinProcessesGetModuleName(&paProcs[i], szModule, sizeof(szModule)); + if (RT_SUCCESS(rc)) VBoxServiceVerbose(4, "PID=%ld: %s\n", paProcs[i].id, szModule); } - if ( paProcs[i].fInteractive - && ( paProcs[i].luid.HighPart == pSessionData->LogonId.HighPart - && paProcs[i].luid.LowPart == pSessionData->LogonId.LowPart)) + PSID pProcSID = paProcs[i].pSid; + if ( RT_SUCCESS(rc) + && pProcSID + && IsValidSid(pProcSID)) { - cNumProcs++; - if (!g_cVerbosity) /* We want a bit more info on higher verbosity. */ - break; + if ( EqualSid(pSessionData->Sid, paProcs[i].pSid) + && paProcs[i].fInteractive) + { + cNumProcs++; + if (!g_cVerbosity) /* We want a bit more info on higher verbosity. */ + break; + } } } - if (g_cVerbosity) - VBoxServiceVerbose(3, "Session %u has %u processes total\n", - pSessionData->Session, cNumProcs); - else - VBoxServiceVerbose(3, "Session %u has at least one process\n", - pSessionData->Session); - - if (puSession) - *puSession = pSessionData->Session; + if (puTerminalSession) + *puTerminalSession = pSessionData->Session; LsaFreeReturnBuffer(pSessionData); + return cNumProcs; } @@ -586,12 +681,25 @@ bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSes return false; } - VBoxServiceVerbose(3, "Session data: Name=%ls, Session=%u, LogonID=%ld,%ld, LogonType=%ld\n", + VBoxServiceVerbose(3, "Session data: Name=%ls, SessionID=%RU32, LogonID=%ld,%ld, LogonType=%ld\n", pSessionData->UserName.Buffer, pSessionData->Session, pSessionData->LogonId.HighPart, pSessionData->LogonId.LowPart, pSessionData->LogonType); + if (vboxServiceVMInfoSession0Separation()) + { + /* Starting at Windows Vista user sessions begin with session 1, so + * ignore (stale) session 0 users. */ + if ( pSessionData->Session == 0 + /* Also check the logon time. */ + || pSessionData->LogonTime.QuadPart == 0) + { + LsaFreeReturnBuffer(pSessionData); + return false; + } + } + /* * Only handle users which can login interactively or logged in * remotely over native RDP. @@ -651,55 +759,90 @@ bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSes pSessionData->LogonId.LowPart, pUserInfo->wszAuthenticationPackage, pUserInfo->wszLogonDomain); - /* Detect RDP sessions as well. */ - LPTSTR pBuffer = NULL; - DWORD cbRet = 0; - int iState = -1; - if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, - pSessionData->Session, - WTSConnectState, - &pBuffer, - &cbRet)) + /** + * Note: On certain Windows OSes WTSQuerySessionInformation leaks memory when used + * under a heavy stress situation. There are hotfixes available from Microsoft. + * + * See: http://support.microsoft.com/kb/970910 + */ + if (!s_fSkipRDPDetection) { - if (cbRet) - iState = *pBuffer; - VBoxServiceVerbose(3, "Account User=%ls, WTSConnectState=%d (%ld)\n", - pUserInfo->wszUser, iState, cbRet); - if ( iState == WTSActive /* User logged on to WinStation. */ - || iState == WTSShadow /* Shadowing another WinStation. */ - || iState == WTSDisconnected) /* WinStation logged on without client. */ + /** @todo Only do this once. Later. */ + OSVERSIONINFOEX OSInfoEx; + RT_ZERO(OSInfoEx); + OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + + /* Skip RDP detection on non-NT systems. */ + if ( !GetVersionEx((LPOSVERSIONINFO) &OSInfoEx) + || OSInfoEx.dwPlatformId != VER_PLATFORM_WIN32_NT) { - /** @todo On Vista and W2K, always "old" user name are still - * there. Filter out the old one! */ - VBoxServiceVerbose(3, "Account User=%ls using TCS/RDP, state=%d \n", - pUserInfo->wszUser, iState); - fFoundUser = true; + s_fSkipRDPDetection = true; } - if (pBuffer) - WTSFreeMemory(pBuffer); + /* Skip RDP detection on Windows 2000. + * For Windows 2000 however we don't have any hotfixes, so just skip the + * RDP detection in any case. */ + if ( OSInfoEx.dwMajorVersion == 5 + && OSInfoEx.dwMinorVersion == 0) + { + s_fSkipRDPDetection = true; + } + + if (s_fSkipRDPDetection) + VBoxServiceVerbose(0, "Detection of logged-in users via RDP is disabled\n"); } - else + + if (!s_fSkipRDPDetection) { - DWORD dwLastErr = GetLastError(); - switch (dwLastErr) + /* Detect RDP sessions as well. */ + LPTSTR pBuffer = NULL; + DWORD cbRet = 0; + int iState = -1; + if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, + pSessionData->Session, + WTSConnectState, + &pBuffer, + &cbRet)) { - /* - * Terminal services don't run (for example in W2K, - * nothing to worry about ...). ... or is on the Vista - * fast user switching page! - */ - case ERROR_CTX_WINSTATION_NOT_FOUND: - VBoxServiceVerbose(3, "No WinStation found for user=%ls\n", - pUserInfo->wszUser); - break; - - default: - VBoxServiceVerbose(3, "Cannot query WTS connection state for user=%ls, error=%ld\n", - pUserInfo->wszUser, dwLastErr); - break; + if (cbRet) + iState = *pBuffer; + VBoxServiceVerbose(3, "Account User=%ls, WTSConnectState=%d (%ld)\n", + pUserInfo->wszUser, iState, cbRet); + if ( iState == WTSActive /* User logged on to WinStation. */ + || iState == WTSShadow /* Shadowing another WinStation. */ + || iState == WTSDisconnected) /* WinStation logged on without client. */ + { + /** @todo On Vista and W2K, always "old" user name are still + * there. Filter out the old one! */ + VBoxServiceVerbose(3, "Account User=%ls using TCS/RDP, state=%d \n", + pUserInfo->wszUser, iState); + fFoundUser = true; + } + if (pBuffer) + WTSFreeMemory(pBuffer); } + else + { + DWORD dwLastErr = GetLastError(); + switch (dwLastErr) + { + /* + * Terminal services don't run (for example in W2K, + * nothing to worry about ...). ... or is on the Vista + * fast user switching page! + */ + case ERROR_CTX_WINSTATION_NOT_FOUND: + VBoxServiceVerbose(3, "No WinStation found for user=%ls\n", + pUserInfo->wszUser); + break; + + default: + VBoxServiceVerbose(3, "Cannot query WTS connection state for user=%ls, error=%ld\n", + pUserInfo->wszUser, dwLastErr); + break; + } - fFoundUser = true; + fFoundUser = true; + } } } else @@ -711,27 +854,163 @@ bool VBoxServiceVMInfoWinIsLoggedIn(PVBOXSERVICEVMINFOUSER pUserInfo, PLUID pSes pUserInfo->wszUser, fFoundUser ? "is" : "is not"); } + if (fFoundUser) + pUserInfo->ulLastSession = pSessionData->Session; + LsaFreeReturnBuffer(pSessionData); return fFoundUser; } +static int vboxServiceVMInfoWinWriteLastInput(PVBOXSERVICEVEPROPCACHE pCache, + const char *pszUser, const char *pszDomain) +{ + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + /* pszDomain is optional. */ + + int rc = VINF_SUCCESS; + + char szPipeName[255]; + if (RTStrPrintf(szPipeName, sizeof(szPipeName), "%s%s", + VBOXTRAY_IPC_PIPE_PREFIX, pszUser)) + { + bool fReportToHost = false; + VBoxGuestUserState userState = VBoxGuestUserState_Unknown; + + RTLOCALIPCSESSION hSession; + rc = RTLocalIpcSessionConnect(&hSession, szPipeName, 0 /* Flags */); + if (RT_SUCCESS(rc)) + { + VBOXTRAYIPCHEADER ipcHdr = { VBOXTRAY_IPC_HDR_MAGIC, 0 /* Header version */, + VBOXTRAYIPCMSGTYPE_USERLASTINPUT, 0 /* No msg */ }; + + rc = RTLocalIpcSessionWrite(hSession, &ipcHdr, sizeof(ipcHdr)); + + VBOXTRAYIPCRES_USERLASTINPUT ipcRes; + if (RT_SUCCESS(rc)) + rc = RTLocalIpcSessionRead(hSession, &ipcRes, sizeof(ipcRes), + NULL /* Exact read */); + if ( RT_SUCCESS(rc) + /* If uLastInput is set to UINT32_MAX VBoxTray was not able to retrieve the + * user's last input time. This might happen when running on Windows NT4 or older. */ + && ipcRes.uLastInput != UINT32_MAX) + { + userState = (ipcRes.uLastInput * 1000) < g_uVMInfoUserIdleThresholdMS + ? VBoxGuestUserState_InUse + : VBoxGuestUserState_Idle; + + rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "UsageState", + userState == VBoxGuestUserState_InUse + ? "InUse" : "Idle"); + + /* + * Note: vboxServiceUserUpdateF can return VINF_NO_CHANGE in case there wasn't anything + * to update. So only report the user's status to host when we really got something + * new. + */ + fReportToHost = rc == VINF_SUCCESS; + VBoxServiceVerbose(4, "User \"%s\" (domain \"%s\") is idle for %RU32, fReportToHost=%RTbool\n", + pszUser, pszDomain ? pszDomain : "<None>", ipcRes.uLastInput, fReportToHost); + +#if 0 /* Do we want to write the idle time as well? */ + /* Also write the user's current idle time, if there is any. */ + if (userState == VBoxGuestUserState_Idle) + rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", + "%RU32", ipcRes.uLastInputMs); + else + rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, "IdleTimeMs", + NULL /* Delete property */); + + if (RT_SUCCESS(rc)) +#endif + } +#ifdef DEBUG + else if (ipcRes.uLastInput == UINT32_MAX) + VBoxServiceVerbose(4, "Last input for user \"%s\" is not supported, skipping\n", + pszUser, rc); + + VBoxServiceVerbose(4, "Getting last input for user \"%s\" ended with rc=%Rrc\n", + pszUser, rc); +#endif + int rc2 = RTLocalIpcSessionClose(hSession); + if (RT_SUCCESS(rc)) + rc = rc2; + } + else + { + switch (rc) + { + case VERR_FILE_NOT_FOUND: + { + /* No VBoxTray (or too old version which does not support IPC) running + for the given user. Not much we can do then. */ + VBoxServiceVerbose(4, "VBoxTray for user \"%s\" not running (anymore), no last input available\n", + pszUser); + + /* Overwrite rc from above. */ + rc = vboxServiceUserUpdateF(pCache, pszUser, pszDomain, + "UsageState", "Idle"); + + fReportToHost = rc == VINF_SUCCESS; + if (fReportToHost) + userState = VBoxGuestUserState_Idle; + break; + } + + default: + VBoxServiceError("Error querying last input for user \"%s\", rc=%Rrc\n", + pszUser, rc); + break; + } + } + + if (fReportToHost) + { + Assert(userState != VBoxGuestUserState_Unknown); + int rc2 = VbglR3GuestUserReportState(pszUser, pszDomain, userState, + NULL /* No details */, 0); + if (RT_FAILURE(rc2)) + VBoxServiceError("Error reporting usage state %ld for user \"%s\" to host, rc=%Rrc\n", + userState, pszUser, rc2); + + if (RT_SUCCESS(rc)) + rc = rc2; + } + } + + return rc; +} + + /** * Retrieves the currently logged in users and stores their names along with the * user count. * * @returns VBox status code. + * @param pCachce Property cache to use for storing some of the lookup + * data in between calls. * @param ppszUserList Where to store the user list (separated by commas). * Must be freed with RTStrFree(). * @param pcUsersInList Where to store the number of users in the list. */ -int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList) +int VBoxServiceVMInfoWinWriteUsers(PVBOXSERVICEVEPROPCACHE pCache, + char **ppszUserList, uint32_t *pcUsersInList) { - PLUID paSessions = NULL; - ULONG cSessions = 0; + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(ppszUserList, VERR_INVALID_POINTER); + AssertPtrReturn(pcUsersInList, VERR_INVALID_POINTER); + + int rc2 = VbglR3GuestPropConnect(&s_uDebugGuestPropClientID); + AssertRC(rc2); + + char *pszUserList = NULL; + uint32_t cUsersInList = 0; /* This function can report stale or orphaned interactive logon sessions of already logged off users (especially in Windows 2000). */ + PLUID paSessions = NULL; + ULONG cSessions = 0; NTSTATUS rcNt = LsaEnumerateLogonSessions(&cSessions, &paSessions); if (rcNt != STATUS_SUCCESS) { @@ -750,10 +1029,13 @@ int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList) break; default: - VBoxServiceError("LsaEnumerate failed with error %u\n", ulError); + VBoxServiceError("LsaEnumerate failed with error %RU32\n", ulError); break; } + if (paSessions) + LsaFreeReturnBuffer(paSessions); + return RTErrConvertFromWin32(ulError); } VBoxServiceVerbose(3, "Found %ld sessions\n", cSessions); @@ -777,56 +1059,73 @@ int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList) else { ULONG cUniqueUsers = 0; + + /** + * Note: The cSessions loop variable does *not* correlate with + * the Windows session ID! + */ for (ULONG i = 0; i < cSessions; i++) { - VBoxServiceVerbose(3, "Handling session %u\n", i); + VBoxServiceVerbose(3, "Handling session %RU32 (of %RU32)\n", i + 1, cSessions); - VBOXSERVICEVMINFOUSER UserInfo; - if (VBoxServiceVMInfoWinIsLoggedIn(&UserInfo, &paSessions[i])) + VBOXSERVICEVMINFOUSER userSession; + if (VBoxServiceVMInfoWinIsLoggedIn(&userSession, &paSessions[i])) { - VBoxServiceVerbose(4, "Handling user=%ls, domain=%ls, package=%ls\n", - UserInfo.wszUser, UserInfo.wszLogonDomain, UserInfo.wszAuthenticationPackage); + VBoxServiceVerbose(4, "Handling user=%ls, domain=%ls, package=%ls, session=%RU32\n", + userSession.wszUser, userSession.wszLogonDomain, userSession.wszAuthenticationPackage, + userSession.ulLastSession); /* Retrieve assigned processes of current session. */ - ULONG ulSession; - uint32_t cSessionProcs = VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs, &ulSession); + uint32_t cCurSessionProcs = VBoxServiceVMInfoWinSessionHasProcesses(&paSessions[i], paProcs, cProcs, + NULL /* Terminal session ID */); /* Don't return here when current session does not have assigned processes * anymore -- in that case we have to search through the unique users list below * and see if got a stale user/session entry. */ + if (g_cVerbosity > 3) + { + char szDebugSessionPath[255]; RTStrPrintf(szDebugSessionPath, sizeof(szDebugSessionPath), "/VirtualBox/GuestInfo/Debug/LSA/Session/%RU32", + userSession.ulLastSession); + VBoxServiceWritePropF(s_uDebugGuestPropClientID, szDebugSessionPath, + "#%RU32: cSessionProcs=%RU32 (of %RU32 procs total)", s_uDebugIter, cCurSessionProcs, cProcs); + } + bool fFoundUser = false; - for (ULONG i = 0; i < cUniqueUsers; i++) + for (ULONG a = 0; a < cUniqueUsers; a++) { - if ( !wcscmp(UserInfo.wszUser, pUserInfo[i].wszUser) - && !wcscmp(UserInfo.wszLogonDomain, pUserInfo[i].wszLogonDomain) - && !wcscmp(UserInfo.wszAuthenticationPackage, pUserInfo[i].wszAuthenticationPackage) - && cSessionProcs) + PVBOXSERVICEVMINFOUSER pCurUser = &pUserInfo[a]; + AssertPtr(pCurUser); + + if ( !wcscmp(userSession.wszUser, pCurUser->wszUser) + && !wcscmp(userSession.wszLogonDomain, pCurUser->wszLogonDomain) + && !wcscmp(userSession.wszAuthenticationPackage, pCurUser->wszAuthenticationPackage)) { /* * Only respect the highest session for the current user. */ - if (ulSession > pUserInfo[i].ulSession) + if (userSession.ulLastSession > pCurUser->ulLastSession) { - VBoxServiceVerbose(4, "Updating user=%ls to %u processes (last session: %u)\n", - UserInfo.wszUser, cSessionProcs, ulSession); + VBoxServiceVerbose(4, "Updating user=%ls to %u processes (last used session: %RU32)\n", + pCurUser->wszUser, cCurSessionProcs, userSession.ulLastSession); - pUserInfo[i].ulNumProcs = cSessionProcs; - pUserInfo[i].ulSession = ulSession; + if (!cCurSessionProcs) + VBoxServiceVerbose(3, "Stale session for user=%ls detected! Processes: %RU32 -> %RU32, Session: %RU32 -> %RU32\n", + pCurUser->wszUser, + pCurUser->ulNumProcs, cCurSessionProcs, + pCurUser->ulLastSession, userSession.ulLastSession); - if (!cSessionProcs) - VBoxServiceVerbose(3, "Stale session for user=%ls detected! Old processes: %u, new: %u\n", - pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, cSessionProcs); + pCurUser->ulNumProcs = cCurSessionProcs; + pCurUser->ulLastSession = userSession.ulLastSession; } /* There can be multiple session objects using the same session ID for the * current user -- so when we got the same session again just add the found * processes to it. */ - else if (pUserInfo[i].ulSession == ulSession) + else if (pCurUser->ulLastSession == userSession.ulLastSession) { - VBoxServiceVerbose(4, "Adding %u processes to user=%ls (session %u)\n", - cSessionProcs, UserInfo.wszUser, ulSession); + VBoxServiceVerbose(4, "Updating processes for user=%ls (old procs=%RU32, new procs=%RU32, session=%RU32)\n", + pCurUser->wszUser, pCurUser->ulNumProcs, cCurSessionProcs, pCurUser->ulLastSession); - pUserInfo[i].ulNumProcs += cSessionProcs; - pUserInfo[i].ulSession = ulSession; + pCurUser->ulNumProcs = cCurSessionProcs; } fFoundUser = true; @@ -836,55 +1135,93 @@ int VBoxServiceVMInfoWinWriteUsers(char **ppszUserList, uint32_t *pcUsersInList) if (!fFoundUser) { - VBoxServiceVerbose(4, "Adding new user=%ls (session %u) with %u processes\n", - UserInfo.wszUser, ulSession, cSessionProcs); + VBoxServiceVerbose(4, "Adding new user=%ls (session=%RU32) with %RU32 processes\n", + userSession.wszUser, userSession.ulLastSession, cCurSessionProcs); - memcpy(&pUserInfo[cUniqueUsers], &UserInfo, sizeof(VBOXSERVICEVMINFOUSER)); - pUserInfo[cUniqueUsers].ulNumProcs = cSessionProcs; - pUserInfo[cUniqueUsers].ulSession = ulSession; + memcpy(&pUserInfo[cUniqueUsers], &userSession, sizeof(VBOXSERVICEVMINFOUSER)); + pUserInfo[cUniqueUsers].ulNumProcs = cCurSessionProcs; cUniqueUsers++; Assert(cUniqueUsers <= cSessions); } } } + if (g_cVerbosity > 3) + VBoxServiceWritePropF(s_uDebugGuestPropClientID, "/VirtualBox/GuestInfo/Debug/LSA", + "#%RU32: cSessions=%RU32, cProcs=%RU32, cUniqueUsers=%RU32", + s_uDebugIter, cSessions, cProcs, cUniqueUsers); + VBoxServiceVerbose(3, "Found %u unique logged-in user(s)\n", cUniqueUsers); - *pcUsersInList = 0; for (ULONG i = 0; i < cUniqueUsers; i++) { + if (g_cVerbosity > 3) + { + char szDebugUserPath[255]; RTStrPrintf(szDebugUserPath, sizeof(szDebugUserPath), "/VirtualBox/GuestInfo/Debug/LSA/User/%RU32", i); + VBoxServiceWritePropF(s_uDebugGuestPropClientID, szDebugUserPath, + "#%RU32: szName=%ls, sessionID=%RU32, cProcs=%RU32", + s_uDebugIter, pUserInfo[i].wszUser, pUserInfo[i].ulLastSession, pUserInfo[i].ulNumProcs); + } + + bool fAddUser = false; if (pUserInfo[i].ulNumProcs) + fAddUser = true; + + if (fAddUser) { - VBoxServiceVerbose(3, "User %ls has %ld processes (session %u)\n", - pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulSession); + VBoxServiceVerbose(3, "User \"%ls\" has %RU32 interactive processes (session=%RU32)\n", + pUserInfo[i].wszUser, pUserInfo[i].ulNumProcs, pUserInfo[i].ulLastSession); - if (*pcUsersInList > 0) + if (cUsersInList > 0) { - rc = RTStrAAppend(ppszUserList, ","); - AssertRCBreakStmt(rc, RTStrFree(*ppszUserList)); + rc = RTStrAAppend(&pszUserList, ","); + AssertRCBreakStmt(rc, RTStrFree(pszUserList)); } - *pcUsersInList += 1; + cUsersInList += 1; - char *pszTemp; - int rc2 = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszTemp); - if (RT_SUCCESS(rc2)) + char *pszUser = NULL; + char *pszDomain = NULL; + rc = RTUtf16ToUtf8(pUserInfo[i].wszUser, &pszUser); + if ( RT_SUCCESS(rc) + && pUserInfo[i].wszLogonDomain) + rc = RTUtf16ToUtf8(pUserInfo[i].wszLogonDomain, &pszDomain); + if (RT_SUCCESS(rc)) { - rc = RTStrAAppend(ppszUserList, pszTemp); - RTMemFree(pszTemp); + /* Append user to users list. */ + rc = RTStrAAppend(&pszUserList, pszUser); + + /* Do idle detection. */ + if (RT_SUCCESS(rc)) + rc = vboxServiceVMInfoWinWriteLastInput(pCache, pszUser, pszDomain); } else - rc = RTStrAAppend(ppszUserList, "<string-conversion-error>"); - AssertRCBreakStmt(rc, RTStrFree(*ppszUserList)); + rc = RTStrAAppend(&pszUserList, "<string-conversion-error>"); + + RTStrFree(pszUser); + RTStrFree(pszDomain); + + AssertRCBreakStmt(rc, RTStrFree(pszUserList)); } } RTMemFree(pUserInfo); } - VBoxServiceVMInfoWinProcessesFree(paProcs); + VBoxServiceVMInfoWinProcessesFree(cProcs, paProcs); } - LsaFreeReturnBuffer(paSessions); + if (paSessions) + LsaFreeReturnBuffer(paSessions); + + if (RT_SUCCESS(rc)) + { + *ppszUserList = pszUserList; + *pcUsersInList = cUsersInList; + } + + s_uDebugIter++; + VbglR3GuestPropDisconnect(s_uDebugGuestPropClientID); + return rc; } @@ -918,10 +1255,16 @@ int VBoxServiceWinGetComponentVersions(uint32_t uClientID) { szSysDir, "VBoxTray.exe" }, { szSysDir, "VBoxGINA.dll" }, { szSysDir, "VBoxCredProv.dll" }, +# ifdef VBOX_WITH_MMR + { szSysDir, "VBoxMMR.exe" }, +# endif /* VBOX_WITH_MMR */ /* On 64-bit we don't yet have the OpenGL DLLs in native format. So just enumerate the 32-bit files in the SYSWOW directory. */ # ifdef RT_ARCH_AMD64 +# ifdef VBOX_WITH_MMR + { szSysWowDir, "VBoxMMRHook.dll" }, +# endif /* VBOX_WITH_MMR */ { szSysWowDir, "VBoxOGLarrayspu.dll" }, { szSysWowDir, "VBoxOGLcrutil.dll" }, { szSysWowDir, "VBoxOGLerrorspu.dll" }, @@ -930,6 +1273,9 @@ int VBoxServiceWinGetComponentVersions(uint32_t uClientID) { szSysWowDir, "VBoxOGLfeedbackspu.dll" }, { szSysWowDir, "VBoxOGL.dll" }, # else /* !RT_ARCH_AMD64 */ +# ifdef VBOX_WITH_MMR + { szSysDir, "VBoxMMRHook.dll" }, +# endif /* VBOX_WITH_MMR */ { szSysDir, "VBoxOGLarrayspu.dll" }, { szSysDir, "VBoxOGLcrutil.dll" }, { szSysDir, "VBoxOGLerrorspu.dll" }, diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp index 25fc36c3..d8dbe7c0 100644 --- a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -38,21 +38,23 @@ # include <sys/ioctl.h> # include <sys/socket.h> # include <net/if.h> +# include <pwd.h> /* getpwuid */ # include <unistd.h> -# ifndef RT_OS_OS2 -# ifndef RT_OS_FREEBSD -# include <utmpx.h> /* @todo FreeBSD 9 should have this. */ -# endif +# if !defined(RT_OS_OS2) && !defined(RT_OS_FREEBSD) && !defined(RT_OS_HAIKU) +# include <utmpx.h> /* @todo FreeBSD 9 should have this. */ # endif # ifdef RT_OS_SOLARIS # include <sys/sockio.h> # include <net/if_arp.h> # endif -# ifdef RT_OS_FREEBSD +# if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) # include <ifaddrs.h> /* getifaddrs, freeifaddrs */ # include <net/if_dl.h> /* LLADDR */ # include <netdb.h> /* getnameinfo */ # endif +# ifdef VBOX_WITH_DBUS +# include <VBox/dbus.h> +# endif #endif #include <iprt/mem.h> @@ -69,6 +71,20 @@ #include "VBoxServicePropCache.h" +/** Structure containing information about a location awarness + * client provided by the host. */ +/** @todo Move this (and functions) into VbglR3. */ +typedef struct VBOXSERVICELACLIENTINFO +{ + uint32_t uID; + char *pszName; + char *pszLocation; + char *pszDomain; + bool fAttached; + uint64_t uAttachedTS; +} VBOXSERVICELACLIENTINFO, *PVBOXSERVICELACLIENTINFO; + + /******************************************************************************* * Global Variables * *******************************************************************************/ @@ -78,12 +94,43 @@ static uint32_t g_cMsVMInfoInterval = 0; static RTSEMEVENTMULTI g_hVMInfoEvent = NIL_RTSEMEVENTMULTI; /** The guest property service client ID. */ static uint32_t g_uVMInfoGuestPropSvcClientID = 0; -/** Number of logged in users in OS. */ -static uint32_t g_cVMInfoLoggedInUsers = UINT32_MAX; +/** Number of currently logged in users in OS. */ +static uint32_t g_cVMInfoLoggedInUsers = 0; /** The guest property cache. */ static VBOXSERVICEVEPROPCACHE g_VMInfoPropCache; +static const char *g_pszPropCacheValLoggedInUsersList = "/VirtualBox/GuestInfo/OS/LoggedInUsersList"; +static const char *g_pszPropCacheValLoggedInUsers = "/VirtualBox/GuestInfo/OS/LoggedInUsers"; +static const char *g_pszPropCacheValNoLoggedInUsers = "/VirtualBox/GuestInfo/OS/NoLoggedInUsers"; +static const char *g_pszPropCacheValNetCount = "/VirtualBox/GuestInfo/Net/Count"; +/** A guest user's guest property root key. */ +static const char *g_pszPropCacheValUser = "/VirtualBox/GuestInfo/User/"; /** The VM session ID. Changes whenever the VM is restored or reset. */ static uint64_t g_idVMInfoSession; +/** The last attached locartion awareness (LA) client timestamp. */ +static uint64_t g_LAClientAttachedTS = 0; +/** The current LA client info. */ +static VBOXSERVICELACLIENTINFO g_LAClientInfo; +/** User idle threshold (in ms). This specifies the minimum time a user is considered + * as being idle and then will be reported to the host. Default is 5s. */ +uint32_t g_uVMInfoUserIdleThresholdMS = 5 * 1000; + + +/******************************************************************************* +* Defines * +*******************************************************************************/ +static const char *g_pszLAActiveClient = "/VirtualBox/HostInfo/VRDP/ActiveClient"; + +#ifdef VBOX_WITH_DBUS +/** ConsoleKit defines (taken from 0.4.5). */ +#define CK_NAME "org.freedesktop.ConsoleKit" +#define CK_PATH "/org/freedesktop/ConsoleKit" +#define CK_INTERFACE "org.freedesktop.ConsoleKit" + +#define CK_MANAGER_PATH "/org/freedesktop/ConsoleKit/Manager" +#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager" +#define CK_SEAT_INTERFACE "org.freedesktop.ConsoleKit.Seat" +#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session" +#endif @@ -114,12 +161,17 @@ static DECLCALLBACK(int) VBoxServiceVMInfoPreInit(void) /** @copydoc VBOXSERVICE::pfnOption */ static DECLCALLBACK(int) VBoxServiceVMInfoOption(const char **ppszShort, int argc, char **argv, int *pi) { + /** @todo Use RTGetOpt here. */ + int rc = -1; if (ppszShort) /* no short options */; else if (!strcmp(argv[*pi], "--vminfo-interval")) rc = VBoxServiceArgUInt32(argc, argv, "", pi, &g_cMsVMInfoInterval, 1, UINT32_MAX - 1); + else if (!strcmp(argv[*pi], "--vminfo-user-idle-threshold")) + rc = VBoxServiceArgUInt32(argc, argv, "", pi, + &g_uVMInfoUserIdleThresholdMS, 1, UINT32_MAX - 1); return rc; } @@ -134,7 +186,10 @@ static DECLCALLBACK(int) VBoxServiceVMInfoInit(void) if (!g_cMsVMInfoInterval) g_cMsVMInfoInterval = g_DefaultInterval * 1000; if (!g_cMsVMInfoInterval) - g_cMsVMInfoInterval = 10 * 1000; + { + /* Set it to 5s by default for location awareness checks. */ + g_cMsVMInfoInterval = 5 * 1000; + } int rc = RTSemEventMultiCreate(&g_hVMInfoEvent); AssertRCReturn(rc, rc); @@ -142,20 +197,23 @@ static DECLCALLBACK(int) VBoxServiceVMInfoInit(void) VbglR3GetSessionId(&g_idVMInfoSession); /* The status code is ignored as this information is not available with VBox < 3.2.10. */ + /* Initialize the LA client object. */ + RT_ZERO(g_LAClientInfo); + rc = VbglR3GuestPropConnect(&g_uVMInfoGuestPropSvcClientID); if (RT_SUCCESS(rc)) - VBoxServiceVerbose(3, "VMInfo: Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID); + VBoxServiceVerbose(3, "Property Service Client ID: %#x\n", g_uVMInfoGuestPropSvcClientID); else { /* If the service was not found, we disable this service without causing VBoxService to fail. */ if (rc == VERR_HGCM_SERVICE_NOT_FOUND) /* Host service is not available. */ { - VBoxServiceVerbose(0, "VMInfo: Guest property service is not available, disabling the service\n"); + VBoxServiceVerbose(0, "Guest property service is not available, disabling the service\n"); rc = VERR_SERVICE_DISABLED; } else - VBoxServiceError("VMInfo: Failed to connect to the guest property service! Error: %Rrc\n", rc); + VBoxServiceError("Failed to connect to the guest property service! Error: %Rrc\n", rc); RTSemEventMultiDestroy(g_hVMInfoEvent); g_hVMInfoEvent = NIL_RTSEMEVENTMULTI; } @@ -167,15 +225,204 @@ static DECLCALLBACK(int) VBoxServiceVMInfoInit(void) /* * Declare some guest properties with flags and reset values. */ - VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", - VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */); - VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", - VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0"); - VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers", - VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true"); - VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", - VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */); + int rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, + VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, NULL /* Delete on exit */); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValLoggedInUsersList, rc2); + + rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, + VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "0"); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValLoggedInUsers, rc2); + + rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers, + VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, "true"); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValNoLoggedInUsers, rc2); + + rc2 = VBoxServicePropCacheUpdateEntry(&g_VMInfoPropCache, g_pszPropCacheValNetCount, + VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_ALWAYS_UPDATE, NULL /* Delete on exit */); + if (RT_FAILURE(rc2)) + VBoxServiceError("Failed to init property cache value \"%s\", rc=%Rrc\n", g_pszPropCacheValNetCount, rc2); + + /* + * Get configuration guest properties from the host. + * Note: All properties should have sensible defaults in case the lookup here fails. + */ + char *pszValue; + rc2 = VBoxServiceReadHostProp(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestAdd/VBoxService/--vminfo-user-idle-threshold", true /* Read only */, + &pszValue, NULL /* Flags */, NULL /* Timestamp */); + if (RT_SUCCESS(rc2)) + { + AssertPtr(pszValue); + g_uVMInfoUserIdleThresholdMS = RT_CLAMP(RTStrToUInt32(pszValue), 1000, UINT32_MAX - 1); + RTStrFree(pszValue); + } + } + return rc; +} + + +/** + * Retrieves a specifiy client LA property. + * + * @return IPRT status code. + * @param uClientID LA client ID to retrieve property for. + * @param pszProperty Property (without path) to retrieve. + * @param ppszValue Where to store value of property. + * @param puTimestamp Timestamp of property to retrieve. Optional. + */ +static int vboxServiceGetLAClientValue(uint32_t uClientID, const char *pszProperty, + char **ppszValue, uint64_t *puTimestamp) +{ + AssertReturn(uClientID, VERR_INVALID_PARAMETER); + AssertPtrReturn(pszProperty, VERR_INVALID_POINTER); + + int rc; + + char pszClientPath[255]; + if (RTStrPrintf(pszClientPath, sizeof(pszClientPath), + "/VirtualBox/HostInfo/VRDP/Client/%RU32/%s", uClientID, pszProperty)) + { + rc = VBoxServiceReadHostProp(g_uVMInfoGuestPropSvcClientID, pszClientPath, true /* Read only */, + ppszValue, NULL /* Flags */, puTimestamp); + } + else + rc = VERR_NO_MEMORY; + + return rc; +} + + +/** + * Retrieves LA client information. On success the returned structure will have allocated + * objects which need to be free'd with vboxServiceFreeLAClientInfo. + * + * @return IPRT status code. + * @param uClientID Client ID to retrieve information for. + * @param pClient Pointer where to store the client information. + */ +static int vboxServiceGetLAClientInfo(uint32_t uClientID, PVBOXSERVICELACLIENTINFO pClient) +{ + AssertReturn(uClientID, VERR_INVALID_PARAMETER); + AssertPtrReturn(pClient, VERR_INVALID_POINTER); + + int rc = vboxServiceGetLAClientValue(uClientID, "Name", &pClient->pszName, + NULL /* Timestamp */); + if (RT_SUCCESS(rc)) + { + char *pszAttach; + rc = vboxServiceGetLAClientValue(uClientID, "Attach", &pszAttach, + &pClient->uAttachedTS); + if (RT_SUCCESS(rc)) + { + AssertPtr(pszAttach); + pClient->fAttached = !RTStrICmp(pszAttach, "1") ? true : false; + + RTStrFree(pszAttach); + } + } + if (RT_SUCCESS(rc)) + rc = vboxServiceGetLAClientValue(uClientID, "Location", &pClient->pszLocation, + NULL /* Timestamp */); + if (RT_SUCCESS(rc)) + rc = vboxServiceGetLAClientValue(uClientID, "Domain", &pClient->pszDomain, + NULL /* Timestamp */); + if (RT_SUCCESS(rc)) + pClient->uID = uClientID; + + return rc; +} + + +/** + * Frees all allocated LA client information of a structure. + * + * @param pClient Pointer to client information structure to free. + */ +static void vboxServiceFreeLAClientInfo(PVBOXSERVICELACLIENTINFO pClient) +{ + if (pClient) + { + if (pClient->pszName) + { + RTStrFree(pClient->pszName); + pClient->pszName = NULL; + } + if (pClient->pszLocation) + { + RTStrFree(pClient->pszLocation); + pClient->pszLocation = NULL; + } + if (pClient->pszDomain) + { + RTStrFree(pClient->pszDomain); + pClient->pszDomain = NULL; + } + } +} + + +/** + * Updates a per-guest user guest property inside the given property cache. + * + * @return IPRT status code. + * @param pCache Pointer to guest property cache to update user in. + * @param pszUser Name of guest user to update. + * @param pszDomain Domain of guest user to update. Optional. + * @param pszKey Key name of guest property to update. + * @param pszValueFormat Guest property value to set. Pass NULL for deleting + * the property. + */ +int vboxServiceUserUpdateF(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain, + const char *pszKey, const char *pszValueFormat, ...) +{ + AssertPtrReturn(pCache, VERR_INVALID_POINTER); + AssertPtrReturn(pszUser, VERR_INVALID_POINTER); + /* pszDomain is optional. */ + AssertPtrReturn(pszKey, VERR_INVALID_POINTER); + /* pszValueFormat is optional. */ + + int rc = VINF_SUCCESS; + + char *pszName; + if (pszDomain) + { + if (!RTStrAPrintf(&pszName, "%s%s@%s/%s", g_pszPropCacheValUser, pszUser, pszDomain, pszKey)) + rc = VERR_NO_MEMORY; + } + else + { + if (!RTStrAPrintf(&pszName, "%s%s/%s", g_pszPropCacheValUser, pszUser, pszKey)) + rc = VERR_NO_MEMORY; + } + + char *pszValue = NULL; + if ( RT_SUCCESS(rc) + && pszValueFormat) + { + va_list va; + va_start(va, pszValueFormat); + if (RTStrAPrintfV(&pszValue, pszValueFormat, va) < 0) + rc = VERR_NO_MEMORY; + va_end(va); + if ( RT_SUCCESS(rc) + && !pszValue) + rc = VERR_NO_STR_MEMORY; } + + if (RT_SUCCESS(rc)) + rc = VBoxServicePropCacheUpdate(pCache, pszName, pszValue); + if (rc == VINF_SUCCESS) /* VBoxServicePropCacheUpdate will also return VINF_NO_CHANGE. */ + { + /** @todo Combine updating flags w/ updating the actual value. */ + rc = VBoxServicePropCacheUpdateEntry(pCache, pszName, + VBOXSERVICEPROPCACHEFLAG_TEMPORARY | VBOXSERVICEPROPCACHEFLAG_TRANSIENT, + NULL /* Delete on exit */); + } + + RTStrFree(pszValue); + RTStrFree(pszName); return rc; } @@ -242,6 +489,23 @@ static void vboxserviceVMInfoWriteFixedProperties(void) #endif } +#if defined(VBOX_WITH_DBUS) && defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */ +/* + * Simple wrapper to work around compiler-specific va_list madness. + */ +static dbus_bool_t vboxService_dbus_message_get_args(DBusMessage *message, + DBusError *error, + int first_arg_type, + ...) +{ + va_list va; + va_start(va, first_arg_type); + dbus_bool_t ret = dbus_message_get_args_valist(message, error, + first_arg_type, va); + va_end(va); + return ret; +} +#endif /** * Provide information about active users. @@ -254,7 +518,8 @@ static int vboxserviceVMInfoWriteUsers(void) #ifdef RT_OS_WINDOWS # ifndef TARGET_NT4 - rc = VBoxServiceVMInfoWinWriteUsers(&pszUserList, &cUsersInList); + rc = VBoxServiceVMInfoWinWriteUsers(&g_VMInfoPropCache, + &pszUserList, &cUsersInList); # else rc = VERR_NOT_IMPLEMENTED; # endif @@ -265,6 +530,10 @@ static int vboxserviceVMInfoWriteUsers(void) * block below (?). */ rc = VERR_NOT_IMPLEMENTED; +#elif defined(RT_OS_HAIKU) + /** @todo Haiku: Port logged on user info retrieval. */ + rc = VERR_NOT_IMPLEMENTED; + #elif defined(RT_OS_OS2) /** @todo OS/2: Port logged on (LAN/local/whatever) user info retrieval. */ rc = VERR_NOT_IMPLEMENTED; @@ -279,12 +548,18 @@ static int vboxserviceVMInfoWriteUsers(void) if (papszUsers == NULL) rc = VERR_NO_MEMORY; - /* Process all entries in the utmp file. */ + /* Process all entries in the utmp file. + * Note: This only handles */ while ( (ut_user = getutxent()) && RT_SUCCESS(rc)) { - VBoxServiceVerbose(4, "Found logged in user \"%s\" (type: %d)\n", - ut_user->ut_user, ut_user->ut_type); +#ifdef RT_OS_DARWIN /* No ut_user->ut_session on Darwin */ + VBoxServiceVerbose(4, "Found entry \"%s\" (type: %d, PID: %RU32)\n", + ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid); +#else + VBoxServiceVerbose(4, "Found entry \"%s\" (type: %d, PID: %RU32, session: %RU32)\n", + ut_user->ut_user, ut_user->ut_type, ut_user->ut_pid, ut_user->ut_session); +#endif if (cUsersInList > cListSize) { cListSize += 32; @@ -294,8 +569,8 @@ static int vboxserviceVMInfoWriteUsers(void) } /* Make sure we don't add user names which are not - * part of type USER_PROCESS. */ - if (ut_user->ut_type == USER_PROCESS) + * part of type USER_PROCES. */ + if (ut_user->ut_type == USER_PROCESS) /* Regular user process. */ { bool fFound = false; for (uint32_t i = 0; i < cUsersInList && !fFound; i++) @@ -314,25 +589,230 @@ static int vboxserviceVMInfoWriteUsers(void) } } +#ifdef VBOX_WITH_DBUS +# if defined(RT_OS_LINUX) /* Not yet for Solaris/FreeBSB. */ + DBusError dbErr; + DBusConnection *pConnection = NULL; + int rc2 = RTDBusLoadLib(); + if (RT_SUCCESS(rc2)) + { + /* Handle desktop sessions using ConsoleKit. */ + VBoxServiceVerbose(4, "Checking ConsoleKit sessions ...\n"); + + dbus_error_init(&dbErr); + pConnection = dbus_bus_get(DBUS_BUS_SYSTEM, &dbErr); + } + + if ( pConnection + && !dbus_error_is_set(&dbErr)) + { + /* Get all available sessions. */ + DBusMessage *pMsgSessions = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + "/org/freedesktop/ConsoleKit/Manager", + "org.freedesktop.ConsoleKit.Manager", + "GetSessions"); + if ( pMsgSessions + && (dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL)) + { + DBusMessage *pReplySessions = dbus_connection_send_with_reply_and_block(pConnection, + pMsgSessions, 30 * 1000 /* 30s timeout */, + &dbErr); + if ( pReplySessions + && !dbus_error_is_set(&dbErr)) + { + char **ppszSessions; int cSessions; + if ( (dbus_message_get_type(pMsgSessions) == DBUS_MESSAGE_TYPE_METHOD_CALL) + && vboxService_dbus_message_get_args(pReplySessions, &dbErr, DBUS_TYPE_ARRAY, + DBUS_TYPE_OBJECT_PATH, &ppszSessions, &cSessions, + DBUS_TYPE_INVALID /* Termination */)) + { + VBoxServiceVerbose(4, "ConsoleKit: retrieved %RU16 session(s)\n", cSessions); + + char **ppszCurSession = ppszSessions; + for (ppszCurSession; + ppszCurSession && *ppszCurSession; ppszCurSession++) + { + VBoxServiceVerbose(4, "ConsoleKit: processing session '%s' ...\n", *ppszCurSession); + + /* Only respect active sessions .*/ + bool fActive = false; + DBusMessage *pMsgSessionActive = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + *ppszCurSession, + "org.freedesktop.ConsoleKit.Session", + "IsActive"); + if ( pMsgSessionActive + && dbus_message_get_type(pMsgSessionActive) == DBUS_MESSAGE_TYPE_METHOD_CALL) + { + DBusMessage *pReplySessionActive = dbus_connection_send_with_reply_and_block(pConnection, + pMsgSessionActive, 30 * 1000 /* 30s timeout */, + &dbErr); + if ( pReplySessionActive + && !dbus_error_is_set(&dbErr)) + { + DBusMessageIter itMsg; + if ( dbus_message_iter_init(pReplySessionActive, &itMsg) + && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_BOOLEAN) + { + /* Get uid from message. */ + int val; + dbus_message_iter_get_basic(&itMsg, &val); + fActive = val >= 1; + } + + if (pReplySessionActive) + dbus_message_unref(pReplySessionActive); + } + + if (pMsgSessionActive) + dbus_message_unref(pMsgSessionActive); + } + + VBoxServiceVerbose(4, "ConsoleKit: session '%s' is %s\n", + *ppszCurSession, fActive ? "active" : "not active"); + + /* *ppszCurSession now contains the object path + * (e.g. "/org/freedesktop/ConsoleKit/Session1"). */ + DBusMessage *pMsgUnixUser = dbus_message_new_method_call("org.freedesktop.ConsoleKit", + *ppszCurSession, + "org.freedesktop.ConsoleKit.Session", + "GetUnixUser"); + if ( fActive + && pMsgUnixUser + && dbus_message_get_type(pMsgUnixUser) == DBUS_MESSAGE_TYPE_METHOD_CALL) + { + DBusMessage *pReplyUnixUser = dbus_connection_send_with_reply_and_block(pConnection, + pMsgUnixUser, 30 * 1000 /* 30s timeout */, + &dbErr); + if ( pReplyUnixUser + && !dbus_error_is_set(&dbErr)) + { + DBusMessageIter itMsg; + if ( dbus_message_iter_init(pReplyUnixUser, &itMsg) + && dbus_message_iter_get_arg_type(&itMsg) == DBUS_TYPE_UINT32) + { + /* Get uid from message. */ + uint32_t uid; + dbus_message_iter_get_basic(&itMsg, &uid); + + /** @todo Add support for getting UID_MIN (/etc/login.defs on + * Debian). */ + uint32_t uid_min = 1000; + + /* Look up user name (realname) from uid. */ + setpwent(); + struct passwd *ppwEntry = getpwuid(uid); + if ( ppwEntry + && ppwEntry->pw_uid >= uid_min /* Only respect users, not daemons etc. */ + && ppwEntry->pw_name) + { + VBoxServiceVerbose(4, "ConsoleKit: session '%s' -> %s (uid: %RU32)\n", + *ppszCurSession, ppwEntry->pw_name, uid); + + bool fFound = false; + for (uint32_t i = 0; i < cUsersInList && !fFound; i++) + fFound = strcmp(papszUsers[i], ppwEntry->pw_name) == 0; + + if (!fFound) + { + VBoxServiceVerbose(4, "ConsoleKit: adding user \"%s\" to list\n", + ppwEntry->pw_name); + + rc = RTStrDupEx(&papszUsers[cUsersInList], (const char *)ppwEntry->pw_name); + if (RT_FAILURE(rc)) + break; + cUsersInList++; + } + } + else + VBoxServiceError("ConsoleKit: unable to lookup user name for uid=%RU32\n", uid); + } + else + AssertMsgFailed(("ConsoleKit: GetUnixUser returned a wrong argument type\n")); + } + + if (pReplyUnixUser) + dbus_message_unref(pReplyUnixUser); + } + else + VBoxServiceError("ConsoleKit: unable to retrieve user for session '%s' (msg type=%d): %s", + *ppszCurSession, dbus_message_get_type(pMsgUnixUser), + dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available"); + + if (pMsgUnixUser) + dbus_message_unref(pMsgUnixUser); + } + + dbus_free_string_array(ppszSessions); + } + else + { + VBoxServiceError("ConsoleKit: unable to retrieve session parameters (msg type=%d): %s", + dbus_message_get_type(pMsgSessions), + dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available"); + } + dbus_message_unref(pReplySessions); + } + + if (pMsgSessions) + { + dbus_message_unref(pMsgSessions); + pMsgSessions = NULL; + } + } + else + { + static int s_iBitchedAboutConsoleKit = 0; + if (s_iBitchedAboutConsoleKit++ < 3) + VBoxServiceError("Unable to invoke ConsoleKit (%d/3) -- maybe not installed / used? Error: %s\n", + s_iBitchedAboutConsoleKit, + dbus_error_is_set(&dbErr) ? dbErr.message : "No error information available"); + } + + if (pMsgSessions) + dbus_message_unref(pMsgSessions); + } + else + { + static int s_iBitchedAboutDBus = 0; + if (s_iBitchedAboutDBus++ < 3) + VBoxServiceError("Unable to connect to system D-Bus (%d/3): %s\n", s_iBitchedAboutDBus, + pConnection && dbus_error_is_set(&dbErr) ? dbErr.message : "D-Bus not installed"); + } + + if ( pConnection + && dbus_error_is_set(&dbErr)) + dbus_error_free(&dbErr); +# endif /* RT_OS_LINUX */ +#endif /* VBOX_WITH_DBUS */ + + /** @todo Fedora/others: Handle systemd-loginctl. */ + /* Calc the string length. */ size_t cchUserList = 0; - for (uint32_t i = 0; i < cUsersInList; i++) - cchUserList += (i != 0) + strlen(papszUsers[i]); - - /* Build the user list. */ - rc = RTStrAllocEx(&pszUserList, cchUserList + 1); if (RT_SUCCESS(rc)) { - char *psz = pszUserList; for (uint32_t i = 0; i < cUsersInList; i++) + cchUserList += (i != 0) + strlen(papszUsers[i]); + } + + /* Build the user list. */ + if (cchUserList > 0) + { + if (RT_SUCCESS(rc)) + rc = RTStrAllocEx(&pszUserList, cchUserList + 1); + if (RT_SUCCESS(rc)) { - if (i != 0) - *psz++ = ','; - size_t cch = strlen(papszUsers[i]); - memcpy(psz, papszUsers[i], cch); - psz += cch; + char *psz = pszUserList; + for (uint32_t i = 0; i < cUsersInList; i++) + { + if (i != 0) + *psz++ = ','; + size_t cch = strlen(papszUsers[i]); + memcpy(psz, papszUsers[i], cch); + psz += cch; + } + *psz = '\0'; } - *psz = '\0'; } /* Cleanup. */ @@ -354,30 +834,42 @@ static int vboxserviceVMInfoWriteUsers(void) { static int s_iVMInfoBitchedOOM = 0; if (s_iVMInfoBitchedOOM++ < 3) - VBoxServiceVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%u)\n", + VBoxServiceVerbose(0, "Warning: Not enough memory available to enumerate users! Keeping old value (%RU32)\n", g_cVMInfoLoggedInUsers); cUsersInList = g_cVMInfoLoggedInUsers; } else cUsersInList = 0; } + else /* Preserve logged in users count. */ + g_cVMInfoLoggedInUsers = cUsersInList; VBoxServiceVerbose(4, "cUsersInList=%RU32, pszUserList=%s, rc=%Rrc\n", cUsersInList, pszUserList ? pszUserList : "<NULL>", rc); - if (pszUserList && cUsersInList > 0) - VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", "%s", pszUserList); - else - VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsersList", NULL); - VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/LoggedInUsers", "%u", cUsersInList); - if (g_cVMInfoLoggedInUsers != cUsersInList) + if (pszUserList) { - VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/OS/NoLoggedInUsers", - cUsersInList == 0 ? "true" : "false"); - g_cVMInfoLoggedInUsers = cUsersInList; + AssertMsg(cUsersInList, ("pszUserList contains users whereas cUsersInList is 0\n")); + rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, "%s", pszUserList); } - if (RT_SUCCESS(rc) && pszUserList) + else + rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsersList, NULL); + if (RT_FAILURE(rc)) + VBoxServiceError("Error writing logged in users list, rc=%Rrc\n", rc); + + rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValLoggedInUsers, "%RU32", cUsersInList); + if (RT_FAILURE(rc)) + VBoxServiceError("Error writing logged in users count, rc=%Rrc\n", rc); + + rc = VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNoLoggedInUsers, + cUsersInList == 0 ? "true" : "false"); + if (RT_FAILURE(rc)) + VBoxServiceError("Error writing no logged in users beacon, rc=%Rrc\n", rc); + + if (pszUserList) RTStrFree(pszUserList); + + VBoxServiceVerbose(4, "Writing users returned with rc=%Rrc\n", rc); return rc; } @@ -479,18 +971,18 @@ static int vboxserviceVMInfoWriteNetwork(void) Assert(pAddress); char szIp[32]; RTStrPrintf(szIp, sizeof(szIp), "%s", inet_ntoa(pAddress->sin_addr)); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szIp); pAddress = (sockaddr_in *) & (InterfaceList[i].iiBroadcastAddress); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr)); pAddress = (sockaddr_in *)&(InterfaceList[i].iiNetmask); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr)); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, nFlags & IFF_UP ? "Up" : "Down"); # ifndef TARGET_NT4 @@ -499,7 +991,7 @@ static int vboxserviceVMInfoWriteNetwork(void) if (!strcmp(pAdp->IpAddressList.IpAddress.String, szIp)) break; - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport); if (pAdp) { char szMac[32]; @@ -519,7 +1011,11 @@ static int vboxserviceVMInfoWriteNetwork(void) if (sd >= 0) closesocket(sd); -#elif defined(RT_OS_FREEBSD) +#elif defined(RT_OS_HAIKU) + /** @todo Haiku: implement network info. retreival */ + return VERR_NOT_IMPLEMENTED; + +#elif defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) struct ifaddrs *pIfHead = NULL; /* Get all available interfaces */ @@ -546,19 +1042,19 @@ static int vboxserviceVMInfoWriteNetwork(void) memset(szInetAddr, 0, NI_MAXHOST); getnameinfo(pIfCurr->ifa_addr, sizeof(struct sockaddr_in), szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr); memset(szInetAddr, 0, NI_MAXHOST); getnameinfo(pIfCurr->ifa_broadaddr, sizeof(struct sockaddr_in), szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr); memset(szInetAddr, 0, NI_MAXHOST); getnameinfo(pIfCurr->ifa_netmask, sizeof(struct sockaddr_in), szInetAddr, NI_MAXHOST, NULL, 0, NI_NUMERICHOST); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szInetAddr); /* Search for the AF_LINK interface of the current AF_INET one and get the mac. */ @@ -575,13 +1071,13 @@ static int vboxserviceVMInfoWriteNetwork(void) pu8Mac = (uint8_t *)LLADDR(pLinkAddress); RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X", pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac); break; } } - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, pIfCurr->ifa_flags & IFF_UP ? "Up" : "Down"); cIfacesReport++; @@ -630,7 +1126,7 @@ static int vboxserviceVMInfoWriteNetwork(void) bool fIfUp = !!(ifrequest[i].ifr_flags & IFF_UP); pAddress = ((sockaddr_in *)&ifrequest[i].ifr_addr); Assert(pAddress); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/IP", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/IP", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr)); if (ioctl(sd, SIOCGIFBRDADDR, &ifrequest[i]) < 0) @@ -640,7 +1136,7 @@ static int vboxserviceVMInfoWriteNetwork(void) break; } pAddress = (sockaddr_in *)&ifrequest[i].ifr_broadaddr; - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Broadcast", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Broadcast", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr)); if (ioctl(sd, SIOCGIFNETMASK, &ifrequest[i]) < 0) @@ -655,7 +1151,7 @@ static int vboxserviceVMInfoWriteNetwork(void) pAddress = (sockaddr_in *)&ifrequest[i].ifr_netmask; # endif - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/V4/Netmask", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/V4/Netmask", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", inet_ntoa(pAddress->sin_addr)); # if defined(RT_OS_SOLARIS) @@ -712,18 +1208,18 @@ static int vboxserviceVMInfoWriteNetwork(void) # endif RTStrPrintf(szMac, sizeof(szMac), "%02X%02X%02X%02X%02X%02X", pu8Mac[0], pu8Mac[1], pu8Mac[2], pu8Mac[3], pu8Mac[4], pu8Mac[5]); - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/MAC", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/MAC", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, "%s", szMac); # endif /* !OS/2*/ - RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%u/Status", cIfacesReport); + RTStrPrintf(szPropPath, sizeof(szPropPath), "/VirtualBox/GuestInfo/Net/%RU32/Status", cIfacesReport); VBoxServicePropCacheUpdate(&g_VMInfoPropCache, szPropPath, fIfUp ? "Up" : "Down"); cIfacesReport++; } /* For all interfaces */ close(sd); if (RT_FAILURE(rc)) - VBoxServiceError("VMInfo/Network: Network enumeration for interface %u failed with error %Rrc\n", cIfacesReport, rc); + VBoxServiceError("VMInfo/Network: Network enumeration for interface %RU32 failed with error %Rrc\n", cIfacesReport, rc); #endif /* !RT_OS_WINDOWS */ @@ -735,19 +1231,19 @@ static int vboxserviceVMInfoWriteNetwork(void) /* Get former count. */ uint32_t cIfacesReportOld; - rc = VBoxServiceReadPropUInt32(g_uVMInfoGuestPropSvcClientID, "/VirtualBox/GuestInfo/Net/Count", &cIfacesReportOld, + rc = VBoxServiceReadPropUInt32(g_uVMInfoGuestPropSvcClientID, g_pszPropCacheValNetCount, &cIfacesReportOld, 0 /* Min */, UINT32_MAX /* Max */); if ( RT_SUCCESS(rc) && cIfacesReportOld > cIfacesReport) /* Are some ifaces not around anymore? */ { - VBoxServiceVerbose(3, "VMInfo/Network: Stale interface data detected (%u old vs. %u current)\n", + VBoxServiceVerbose(3, "VMInfo/Network: Stale interface data detected (%RU32 old vs. %RU32 current)\n", cIfacesReportOld, cIfacesReport); uint32_t uIfaceDeleteIdx = cIfacesReport; do { VBoxServiceVerbose(3, "VMInfo/Network: Deleting stale data of interface %d ...\n", uIfaceDeleteIdx); - rc = VBoxServicePropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%u", uIfaceDeleteIdx++); + rc = VBoxServicePropCacheUpdateByPath(&g_VMInfoPropCache, NULL /* Value, delete */, 0 /* Flags */, "/VirtualBox/GuestInfo/Net/%RU32", uIfaceDeleteIdx++); } while (RT_SUCCESS(rc)); } else if ( RT_FAILURE(rc) @@ -762,7 +1258,7 @@ static int vboxserviceVMInfoWriteNetwork(void) * does not change. If this property is missing, the host assumes that all other GuestInfo * properties are no longer valid. */ - VBoxServicePropCacheUpdate(&g_VMInfoPropCache, "/VirtualBox/GuestInfo/Net/Count", "%d", + VBoxServicePropCacheUpdate(&g_VMInfoPropCache, g_pszPropCacheValNetCount, "%RU32", cIfacesReport); /* Don't fail here; just report everything we got. */ @@ -806,6 +1302,83 @@ DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown) if (RT_FAILURE(rc)) break; + /* Whether to wait for event semaphore or not. */ + bool fWait = true; + + /* Check for location awareness. This most likely only + * works with VBox (latest) 4.1 and up. */ + + /* Check for new connection. */ + char *pszLAClientID = NULL; + int rc2 = VBoxServiceReadHostProp(g_uVMInfoGuestPropSvcClientID, g_pszLAActiveClient, true /* Read only */, + &pszLAClientID, NULL /* Flags */, NULL /* Timestamp */); + if (RT_SUCCESS(rc2)) + { + AssertPtr(pszLAClientID); + if (RTStrICmp(pszLAClientID, "0")) /* Is a client connected? */ + { + uint32_t uLAClientID = RTStrToInt32(pszLAClientID); + uint64_t uLAClientAttachedTS; + + /* Peek at "Attach" value to figure out if hotdesking happened. */ + char *pszAttach = NULL; + rc2 = vboxServiceGetLAClientValue(uLAClientID, "Attach", &pszAttach, + &uLAClientAttachedTS); + + if ( RT_SUCCESS(rc2) + && ( !g_LAClientAttachedTS + || (g_LAClientAttachedTS != uLAClientAttachedTS))) + { + vboxServiceFreeLAClientInfo(&g_LAClientInfo); + + /* Note: There is a race between setting the guest properties by the host and getting them by + * the guest. */ + rc2 = vboxServiceGetLAClientInfo(uLAClientID, &g_LAClientInfo); + if (RT_SUCCESS(rc2)) + { + VBoxServiceVerbose(1, "VRDP: Hotdesk client %s with ID=%RU32, Name=%s, Domain=%s\n", + /* If g_LAClientAttachedTS is 0 this means there already was an active + * hotdesk session when VBoxService started. */ + !g_LAClientAttachedTS ? "already active" : g_LAClientInfo.fAttached ? "connected" : "disconnected", + uLAClientID, g_LAClientInfo.pszName, g_LAClientInfo.pszDomain); + + g_LAClientAttachedTS = g_LAClientInfo.uAttachedTS; + + /* Don't wait for event semaphore below anymore because we now know that the client + * changed. This means we need to iterate all VM information again immediately. */ + fWait = false; + } + else + { + static int s_iBitchedAboutLAClientInfo = 0; + if (s_iBitchedAboutLAClientInfo++ < 10) + VBoxServiceError("Error getting active location awareness client info, rc=%Rrc\n", rc2); + } + } + else if (RT_FAILURE(rc2)) + VBoxServiceError("Error getting attached value of location awareness client %RU32, rc=%Rrc\n", + uLAClientID, rc2); + if (pszAttach) + RTStrFree(pszAttach); + } + else + { + VBoxServiceVerbose(1, "VRDP: UTTSC disconnected from VRDP server\n"); + vboxServiceFreeLAClientInfo(&g_LAClientInfo); + } + + RTStrFree(pszLAClientID); + } + else + { + static int s_iBitchedAboutLAClient = 0; + if ( (rc2 != VERR_NOT_FOUND) /* No location awareness installed, skip. */ + && s_iBitchedAboutLAClient++ < 3) + VBoxServiceError("VRDP: Querying connected location awareness client failed with rc=%Rrc\n", rc2); + } + + VBoxServiceVerbose(3, "VRDP: Handling location awareness done\n"); + /* * Flush all properties if we were restored. */ @@ -813,7 +1386,7 @@ DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown) VbglR3GetSessionId(&idNewSession); if (idNewSession != g_idVMInfoSession) { - VBoxServiceVerbose(3, "VMInfo: The VM session ID changed, flushing all properties\n"); + VBoxServiceVerbose(3, "The VM session ID changed, flushing all properties\n"); vboxserviceVMInfoWriteFixedProperties(); VBoxServicePropCacheFlush(&g_VMInfoPropCache); g_idVMInfoSession = idNewSession; @@ -827,12 +1400,13 @@ DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown) */ if (*pfShutdown) break; - int rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval); + if (fWait) + rc2 = RTSemEventMultiWait(g_hVMInfoEvent, g_cMsVMInfoInterval); if (*pfShutdown) break; if (rc2 != VERR_TIMEOUT && RT_FAILURE(rc2)) { - VBoxServiceError("VMInfo: RTSemEventMultiWait failed; rc2=%Rrc\n", rc2); + VBoxServiceError("RTSemEventMultiWait failed; rc2=%Rrc\n", rc2); rc = rc2; break; } @@ -841,7 +1415,7 @@ DECLCALLBACK(int) VBoxServiceVMInfoWorker(bool volatile *pfShutdown) /* Reset event semaphore if it got triggered. */ rc2 = RTSemEventMultiReset(g_hVMInfoEvent); if (RT_FAILURE(rc2)) - rc2 = VBoxServiceError("VMInfo: RTSemEventMultiReset failed; rc2=%Rrc\n", rc2); + rc2 = VBoxServiceError("RTSemEventMultiReset failed; rc2=%Rrc\n", rc2); } } @@ -880,13 +1454,16 @@ static DECLCALLBACK(void) VBoxServiceVMInfoTerm(void) const char *apszPat[1] = { "/VirtualBox/GuestInfo/Net/*" }; int rc = VbglR3GuestPropDelSet(g_uVMInfoGuestPropSvcClientID, &apszPat[0], RT_ELEMENTS(apszPat)); + /* Destroy LA client info. */ + vboxServiceFreeLAClientInfo(&g_LAClientInfo); + /* Destroy property cache. */ VBoxServicePropCacheDestroy(&g_VMInfoPropCache); /* Disconnect from guest properties service. */ rc = VbglR3GuestPropDisconnect(g_uVMInfoGuestPropSvcClientID); if (RT_FAILURE(rc)) - VBoxServiceError("VMInfo: Failed to disconnect from guest property service! Error: %Rrc\n", rc); + VBoxServiceError("Failed to disconnect from guest property service! Error: %Rrc\n", rc); g_uVMInfoGuestPropSvcClientID = 0; RTSemEventMultiDestroy(g_hVMInfoEvent); @@ -905,11 +1482,15 @@ VBOXSERVICE g_VMInfo = /* pszDescription. */ "Virtual Machine Information", /* pszUsage. */ - " [--vminfo-interval <ms>]" + " [--vminfo-interval <ms>] [--vminfo-user-idle-threshold <ms>]" , /* pszOptions. */ " --vminfo-interval Specifies the interval at which to retrieve the\n" " VM information. The default is 10000 ms.\n" + " --vminfo-user-idle-threshold <ms>\n" + " Specifies the user idle threshold (in ms) for\n" + " considering a guest user as being idle. The default\n" + " is 5000 (5 seconds).\n" , /* methods */ VBoxServiceVMInfoPreInit, diff --git a/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h new file mode 100644 index 00000000..38984fa6 --- /dev/null +++ b/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo.h @@ -0,0 +1,31 @@ +/* $Id: VBoxServiceVMInfo.h $ */ +/** @file + * VBoxServiceVMInfo.h - Internal VM info definitions. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef ___VBoxServiceVMInfo_h +#define ___VBoxServiceVMInfo_h + +//RT_C_DECLS_BEGIN + +extern int vboxServiceUserUpdateF(PVBOXSERVICEVEPROPCACHE pCache, const char *pszUser, const char *pszDomain, + const char *pszKey, const char *pszValueFormat, ...); + +//RT_C_DECLS_END + +extern uint32_t g_uVMInfoUserIdleThresholdMS; + +#endif /* ___VBoxServiceVMInfo_h */ + diff --git a/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp b/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp index 84b18dfa..29f2ff88 100644 --- a/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp +++ b/src/VBox/Additions/common/VBoxService/testcase/tstUserInfo.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp index 0e5fa467..72b7ed1b 100644 --- a/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp +++ b/src/VBox/Additions/common/VBoxVideo/HGSMIBase.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp index 5f481049..f0411760 100644 --- a/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp +++ b/src/VBox/Additions/common/VBoxVideo/VBVABase.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -301,7 +301,7 @@ static bool vboxHwBufferWrite(PVBVABUFFERCONTEXT pCtx, pRecord = pCtx->pRecord; Assert(pRecord && (pRecord->cbRecord & VBVA_F_RECORD_PARTIAL)); - LogFunc(("%d\n", cb)); + // LogFunc(("%d\n", cb)); cbHwBufferAvail = vboxHwBufferAvail (pVBVA); diff --git a/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py b/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py index c47f990a..65c4660d 100755 --- a/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py +++ b/src/VBox/Additions/common/crOpenGL/Linux_i386_glxapi_exports.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2009 Oracle Corporation +Copyright (C) 2009-2012 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/Makefile.kmk b/src/VBox/Additions/common/crOpenGL/Makefile.kmk index 4f3b9257..9d21e1a2 100644 --- a/src/VBox/Additions/common/crOpenGL/Makefile.kmk +++ b/src/VBox/Additions/common/crOpenGL/Makefile.kmk @@ -40,9 +40,20 @@ DLLS += \ VBoxOGLfeedbackspu endif +VBOX_OGL_X86_GUEST_DLLS = \ + VBoxOGL-x86 \ + VBoxOGLarrayspu-x86 \ + VBoxOGLpassthroughspu-x86 \ + VBoxOGLpackspu-x86 \ + VBoxOGLfeedbackspu-x86 + +ifdef VBOX_WITH_WDDM + DLLS.win.amd64 += $(VBOX_OGL_X86_GUEST_DLLS) +endif + if1of ($(KBUILD_TARGET), linux solaris freebsd) #VBoxOGL_DRI = 1 - ifn1of ($(KBUILD_TARGET),solaris freebsd) # No DRI on Solaris yet + ifn1of ($(KBUILD_TARGET),solaris) # No DRI on Solaris yet VBoxOGL_FAKEDRI = 1 endif @@ -209,8 +220,13 @@ if1of ($(KBUILD_TARGET), linux solaris freebsd) $(PATH_STAGE_LIB)/libXfixes.so \ $(PATH_STAGE_LIB)/libXext.so ifdef VBoxOGL_FAKEDRI - VBoxOGL_LIBS += \ + ifeq ($(KBUILD_TARGET), freebsd) + VBoxOGL_LIBS += \ + elf + else + VBoxOGL_LIBS += \ dl + endif endif endif ifdef VBOX_WITH_CRHGSMI @@ -224,7 +240,6 @@ ifdef VBOX_WITH_WDDM # # VBoxOGL-x86 - x86 VBoxOGL version built for amd64 build # -DLLS.win.amd64 += VBoxOGL-x86 VBoxOGL-x86_EXTENDS = VBoxOGL VBoxOGL-x86_BLD_TRG_ARCH = x86 VBoxOGL-x86_LIBS = $(VBOX_LIB_IPRT_GUEST_R3_SHARED_X86) \ @@ -242,14 +257,18 @@ endif $(VBOX_PATH_CROGL_GENFILES)/NULLfuncs.c: $(PATH_SUB_CURRENT)/NULLfuncs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + $(VBOX_PATH_CROGL_GENFILES)/tsfuncs.c: $(PATH_SUB_CURRENT)/tsfuncs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + ifeq ($(KBUILD_TARGET),win) + # Windows $(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c: $(PATH_SUB_CURRENT)/windows_getprocaddress.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + ifeq ($(KBUILD_TARGET_ARCH),amd64) $(VBOX_PATH_CROGL_GENFILES)/cropengl.def: $(PATH_SUB_CURRENT)/defs64.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) @@ -259,15 +278,18 @@ $(VBOX_PATH_CROGL_GENFILES)/cropengl.def: $(PATH_SUB_CURRENT)/defs.py $(VBOX_CRO $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) endif + $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + $(VBOX_PATH_CROGL_GENFILES)/windows_exports.asm: \ $(PATH_SUB_CURRENT)/windows_i386_exports.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + ifdef VBOX_WITH_WDDM ifeq ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH),win.amd64) $(VBOX_PATH_CROGL_GENFILES)/cropengl-x86.def: $(PATH_SUB_CURRENT)/defs.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) @@ -275,71 +297,85 @@ $(VBOX_PATH_CROGL_GENFILES)/cropengl-x86.def: $(PATH_SUB_CURRENT)/defs.py $(VBOX $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) endif #ifeq ($(KBUILD_TARGET).$(KBUILD_TARGET_ARCH),win.amd64) endif #ifdef VBOX_WITH_WDDM -else if1of ($(KBUILD_TARGET), linux solaris freebsd) + + +else if1of ($(KBUILD_TARGET), freebsd linux solaris) + # FreeBSD, Linux, Solaris $(VBOX_PATH_CROGL_GENFILES)/getprocaddress.c: $(PATH_SUB_CURRENT)/getprocaddress.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) -if !defined(VBoxOGL_DRI) && !defined(VBoxOGL_FAKEDRI) - ifeq ($(KBUILD_TARGET),solaris) + + if !defined(VBoxOGL_DRI) && !defined(VBoxOGL_FAKEDRI) + ifeq ($(KBUILD_TARGET),solaris) $(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c: \ $(PATH_SUB_CURRENT)/SunOS_exports.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - else ifeq ($(KBUILD_TARGET),freebsd) + + else ifeq ($(KBUILD_TARGET),freebsd) $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c: \ $(PATH_SUB_CURRENT)/FreeBSD_exports.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - else + + else $(VBOX_PATH_CROGL_GENFILES)/linux_exports.asm: \ $(PATH_SUB_CURRENT)/Linux_i386_exports.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - endif -else ifdef VBoxOGL_DRI + endif + + else ifdef VBoxOGL_DRI $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + $(VBOX_PATH_CROGL_GENFILES)/DD_gl.h: $(PATH_SUB_CURRENT)/DD_glh.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + $(VBOX_PATH_CROGL_GENFILES)/DD_gl.c: $(PATH_SUB_CURRENT)/DD_glc.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - ifeq ($(KBUILD_TARGET),solaris) + + ifeq ($(KBUILD_TARGET),solaris) $(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm: \ $(PATH_SUB_CURRENT)/SunOS_i386_exports_dri.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - else ifeq ($(KBUILD_TARGET),freebsd) + + else ifeq ($(KBUILD_TARGET),freebsd) $(VBOX_PATH_CROGL_GENFILES)/freebsd_exports_dri.asm: \ $(PATH_SUB_CURRENT)/FreeBSD_i386_exports_dri.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - else + + else $(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm: \ $(PATH_SUB_CURRENT)/Linux_i386_exports_dri.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - endif -else ifdef VBoxOGL_FAKEDRI + endif + + else ifdef VBoxOGL_FAKEDRI $(VBOX_PATH_CROGL_GENFILES)/cr_gl.h: $(PATH_SUB_CURRENT)/cr_gl.py $(VBOX_CROGL_API_FILES) | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - ifndef VBOX_OGL_GLX_USE_CSTUBS - ifeq ($(KBUILD_TARGET),solaris) + + ifndef VBOX_OGL_GLX_USE_CSTUBS + ifeq ($(KBUILD_TARGET),solaris) $(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm: \ $(PATH_SUB_CURRENT)/SunOS_i386_exports_dri.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ @@ -349,7 +385,8 @@ $(VBOX_PATH_CROGL_GENFILES)/solaris_exports_dri.asm: \ $(VBOX_PATH_CROGL_GENFILES)/solaris_glxapi_exports.asm: $(PATH_SUB_CURRENT)/SunOS_i386_glxapi_exports.py | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< - else + + else $(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm: \ $(PATH_SUB_CURRENT)/Linux_i386_exports_dri.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ @@ -359,15 +396,24 @@ $(VBOX_PATH_CROGL_GENFILES)/linux_exports_dri.asm: \ $(VBOX_PATH_CROGL_GENFILES)/linux_glxapi_exports.asm: $(PATH_SUB_CURRENT)/Linux_i386_glxapi_exports.py | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< - endif - else - ifeq ($(KBUILD_TARGET),solaris) + endif + + else + ifeq ($(KBUILD_TARGET),solaris) $(VBOX_PATH_CROGL_GENFILES)/solaris_exports.c: \ $(PATH_SUB_CURRENT)/SunOS_exports.py \ $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) + + else ifeq ($(KBUILD_TARGET),freebsd) +$(VBOX_PATH_CROGL_GENFILES)/freebsd_exports.c: \ + $(PATH_SUB_CURRENT)/FreeBSD_exports.py \ + $(VBOX_CROGL_API_FILES) $(PATH_SUB_CURRENT)/entrypoints.py \ + | $$(dir $$@) + $(call MSG_GENERATE,python,$@,$<) + $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) else $(VBOX_PATH_CROGL_GENFILES)/linux_exports.c: \ $(PATH_SUB_CURRENT)/Linux_exports.py \ @@ -375,9 +421,10 @@ $(VBOX_PATH_CROGL_GENFILES)/linux_exports.c: \ | $$(dir $$@) $(call MSG_GENERATE,python,$@,$<) $(QUIET)$(call VBOX_CROGL_PYTHON_ENV,$(VBOX_PATH_CROGL_PYTHON_INCLUDE),$@) $(VBOX_BLD_PYTHON) $< $(VBOX_PATH_CROGL_GLAPI) - endif - endif # VBOX_OGL_GLX_USE_CSTUBS -endif + endif + + endif # VBOX_OGL_GLX_USE_CSTUBS + endif endif # @@ -420,7 +467,6 @@ ifdef VBOX_WITH_WDDM # # VBoxOGLarrayspu-x86 - x86 version of VBoxOGLarrayspu built for amd64 build # -DLLS.win.amd64 += VBoxOGLarrayspu-x86 VBoxOGLarrayspu-x86_EXTENDS = VBoxOGLarrayspu VBoxOGLarrayspu-x86_BLD_TRG_ARCH = x86 VBoxOGLarrayspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \ @@ -464,7 +510,6 @@ ifdef VBOX_WITH_WDDM # # VBoxOGLpassthroughspu-x86 - x86 version of VBoxOGLpassthroughspu built for amd64 build # -DLLS.win.amd64 += VBoxOGLpassthroughspu-x86 VBoxOGLpassthroughspu-x86_EXTENDS = VBoxOGLpassthroughspu VBoxOGLpassthroughspu-x86_BLD_TRG_ARCH = x86 VBoxOGLpassthroughspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) @@ -532,6 +577,12 @@ VBoxOGLpackspu_LIBS = \ ifdef VBOX_WITH_CRHGSMI VBoxOGLpackspu_DEFS.win += VBOX_WITH_CRHGSMI endif +ifdef VBOX_WITH_CRDUMPER +VBoxOGLpackspu_DEFS += VBOX_WITH_CRDUMPER +endif +ifdef VBOX_WITH_CRPACKSPU_DUMPER +VBoxOGLpackspu_DEFS += VBOX_WITH_CRPACKSPU_DUMPER +endif ifdef VBOX_WITH_WDDM VBoxOGLpackspu_DEFS.win += VBOX_WITH_WDDM endif @@ -540,7 +591,6 @@ ifdef VBOX_WITH_WDDM # # VBoxOGLpackspu-x86 - x86 version of VBoxOGLpackspu built for amd64 build # -DLLS.win.amd64 += VBoxOGLpackspu-x86 VBoxOGLpackspu-x86_EXTENDS = VBoxOGLpackspu VBoxOGLpackspu-x86_BLD_TRG_ARCH = x86 VBoxOGLpackspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \ @@ -617,7 +667,6 @@ ifdef VBOX_WITH_WDDM # # VBoxOGLfeedbackspu-x86 - x86 version of VBoxOGLfeedbackspu built for amd64 build # -DLLS.win.amd64 += VBoxOGLfeedbackspu-x86 VBoxOGLfeedbackspu-x86_EXTENDS = VBoxOGLfeedbackspu VBoxOGLfeedbackspu-x86_BLD_TRG_ARCH = x86 VBoxOGLfeedbackspu-x86_LIBS = $(VBOX_LIB_OGL_CRUTIL_X86) \ diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py index b7fe6b9f..7fc6cddb 100755 --- a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py +++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2009 Oracle Corporation +Copyright (C) 2009-2012 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py index 7d9378e6..26903439 100755 --- a/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py +++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_exports_dri.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2009 Oracle Corporation +Copyright (C) 2009-2012 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py b/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py index d395732d..e835ecb2 100755 --- a/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py +++ b/src/VBox/Additions/common/crOpenGL/SunOS_i386_glxapi_exports.py @@ -1,5 +1,5 @@ """ -Copyright (C) 2009 Oracle Corporation +Copyright (C) 2009-2012 Oracle Corporation This file is part of VirtualBox Open Source Edition (OSE), as available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/VBoxICDList.h b/src/VBox/Additions/common/crOpenGL/VBoxICDList.h index 54c7e452..cc43c1c6 100644 --- a/src/VBox/Additions/common/crOpenGL/VBoxICDList.h +++ b/src/VBox/Additions/common/crOpenGL/VBoxICDList.h @@ -2,7 +2,7 @@ * * VirtualBox Windows NT/2000/XP guest OpenGL ICD * - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/context.c b/src/VBox/Additions/common/crOpenGL/context.c index 4a077a5d..6c6e4b97 100644 --- a/src/VBox/Additions/common/crOpenGL/context.c +++ b/src/VBox/Additions/common/crOpenGL/context.c @@ -107,6 +107,22 @@ void stubForcedFlush(GLint con) #endif } +void stubConChromiumParameteriCR(GLint con, GLenum param, GLint value) +{ + if (con) + stub.spu->dispatch_table.VBoxConChromiumParameteriCR(con, param, value); + else + crError("VBoxConChromiumParameteriCR called with null connection"); +} + +void stubConFlush(GLint con) +{ + if (con) + stub.spu->dispatch_table.VBoxConFlush(con); + else + crError("stubConFlush called with null connection"); +} + static void stubWindowCleanupForContextsCB(unsigned long key, void *data1, void *data2) { ContextInfo *context = (ContextInfo *) data1; @@ -144,6 +160,7 @@ void stubDestroyWindow( GLint con, GLint window ) } # endif #endif + stubForcedFlush(con); crHashtableWalk(stub.contextTable, stubWindowCleanupForContextsCB, winInfo); @@ -189,7 +206,7 @@ stubNewWindow( const char *dpyName, GLint visBits ) #ifdef VBOX_WITH_WDDM if (stub.bRunningUnderWDDM) { - crError("Should not be here: WindowCreate/Destroy & VBoxPackGetInjectID recuire connection id!"); + crError("Should not be here: WindowCreate/Destroy & VBoxPackGetInjectID require connection id!"); winInfo->mapped = 0; } else @@ -433,10 +450,6 @@ stubDestroyContextLocked( ContextInfo *context ) #ifdef GLX crFreeHashtable(context->pGLXPixmapsHash, crFree); - if (context->damageDpy) - { - XCloseDisplay(context->damageDpy); - } #endif crHashtableDelete(stub.contextTable, contextId, NULL); @@ -535,8 +548,7 @@ stubNewContext( const char *dpyName, GLint visBits, ContextType type, #ifdef GLX context->pGLXPixmapsHash = crAllocHashtable(); - context->damageInitFailed = GL_FALSE; - context->damageDpy = NULL; + context->damageQueryFailed = GL_FALSE; context->damageEventsBase = 0; #endif @@ -1089,6 +1101,50 @@ static void stubWindowCheckOwnerCB(unsigned long key, void *data1, void *data2) } } +GLboolean stubCtxCreate(ContextInfo *context) +{ + /* + * Create a Chromium context. + */ +#if defined(GLX) || defined(DARWIN) + GLint spuShareCtx = context->share ? context->share->spuContext : 0; +#else + GLint spuShareCtx = 0; +#endif + GLint spuConnection = 0; + CRASSERT(stub.spu); + CRASSERT(stub.spu->dispatch_table.CreateContext); + context->type = CHROMIUM; + +#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) + if (context->pHgsmi) + { + spuConnection = stub.spu->dispatch_table.VBoxConCreate(context->pHgsmi); + if (!spuConnection) + { + crWarning("VBoxConCreate failed"); + return GL_FALSE; + } + context->spuConnection = spuConnection; + } +#endif + + context->spuContext + = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, context->dpyName, + context->visBits, + spuShareCtx); + + return GL_TRUE; +} + +GLboolean stubCtxCheckCreate(ContextInfo *context) +{ + if (context->type == UNDECIDED) + return stubCtxCreate(context); + return CHROMIUM == context->type; +} + + GLboolean stubMakeCurrent( WindowInfo *window, ContextInfo *context ) { @@ -1119,42 +1175,22 @@ stubMakeCurrent( WindowInfo *window, ContextInfo *context ) #endif if (stubCheckUseChromium(window)) { - /* - * Create a Chromium context. - */ -#if defined(GLX) || defined(DARWIN) - GLint spuShareCtx = context->share ? context->share->spuContext : 0; -#else - GLint spuShareCtx = 0; -#endif GLint spuConnection = 0; - CRASSERT(stub.spu); - CRASSERT(stub.spu->dispatch_table.CreateContext); - context->type = CHROMIUM; -#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) - if (context->pHgsmi) + if (!stubCtxCreate(context)) { - spuConnection = stub.spu->dispatch_table.VBoxConCreate(context->pHgsmi); - if (!spuConnection) - { - crWarning("VBoxConCreate failed"); - return GL_FALSE; - } - context->spuConnection = spuConnection; + crWarning("stubCtxCreate failed"); + return GL_FALSE; } + +#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) + spuConnection = context->spuConnection; #endif - context->spuContext - = stub.spu->dispatch_table.VBoxCreateContext(spuConnection, context->dpyName, - context->visBits, - spuShareCtx); if (window->spuWindow == -1) { /*crDebug("(1)stubMakeCurrent ctx=%p(%i) window=%p(%i)", context, context->spuContext, window, window->spuWindow);*/ - window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate( - spuConnection, - window->dpyName, context->visBits ); + window->spuWindow = stub.spu->dispatch_table.VBoxWindowCreate(spuConnection, window->dpyName, context->visBits ); #ifdef CR_NEWWINTRACK window->u32ClientID = stub.spu->dispatch_table.VBoxPackGetInjectID(spuConnection); #endif @@ -1311,7 +1347,8 @@ stubMakeCurrent( WindowInfo *window, ContextInfo *context ) stub.spu->dispatch_table.Viewport( 0, 0, winW, winH ); } #ifdef VBOX_WITH_WDDM - stub.spu->dispatch_table.WindowVisibleRegion(window->spuWindow, 0, NULL); + if (stub.trackWindowVisibleRgn) + stub.spu->dispatch_table.WindowVisibleRegion(window->spuWindow, 0, NULL); #endif } diff --git a/src/VBox/Additions/common/crOpenGL/defs.py b/src/VBox/Additions/common/crOpenGL/defs.py index 5129f0db..de0d031e 100644 --- a/src/VBox/Additions/common/crOpenGL/defs.py +++ b/src/VBox/Additions/common/crOpenGL/defs.py @@ -9,8 +9,9 @@ import apiutil apiutil.CopyrightDef() -print "LIBRARY VBoxOGL" -print "DESCRIPTION \"\"" +# NOTE: if we need a LIBRARY statement, we would need to create a defs-x86.py to generate a .def file for VBoxOGL-x86 library +#print "LIBRARY VBoxOGL" +#print "DESCRIPTION \"\"" - warning LNK4017: DESCRIPTION statement not supported for the target platform; ignored print "EXPORTS" # XXX can't these values be automatically computed by analyzing parameters? @@ -454,7 +455,7 @@ for func_name in ( "wglChoosePixelFormat", "wglGetPixelFormatAttribivEXT", "wglGetPixelFormatAttribfvEXT", "wglGetExtensionsStringEXT"): - print "%s@%d = %s_prox" % (func_name,stack_sizes[func_name],func_name) + print "%s = %s_prox" % (func_name,func_name) """ for func_name in ( "CopyContext", @@ -495,5 +496,9 @@ print """crCreateContext crMakeCurrent crSwapBuffers crGetProcAddress -VBoxCreateContext""" +VBoxCreateContext +VBoxCtxChromiumParameteriCR +VBoxGetWindowId +VBoxGetContextId +VBoxFlushToHost""" #print "DllMain" diff --git a/src/VBox/Additions/common/crOpenGL/defs64.py b/src/VBox/Additions/common/crOpenGL/defs64.py index e98bb89b..68b3fd21 100644 --- a/src/VBox/Additions/common/crOpenGL/defs64.py +++ b/src/VBox/Additions/common/crOpenGL/defs64.py @@ -9,8 +9,8 @@ import apiutil apiutil.CopyrightDef() -print "LIBRARY VBoxOGL" -print "DESCRIPTION \"\"" +#print "LIBRARY VBoxOGL" +#print "DESCRIPTION \"\"" - warning LNK4017: DESCRIPTION statement not supported for the target platform; ignored print "EXPORTS" # XXX can't these values be automatically computed by analyzing parameters? @@ -476,5 +476,9 @@ print """crCreateContext crMakeCurrent crSwapBuffers crGetProcAddress -VBoxCreateContext""" +VBoxCreateContext +VBoxCtxChromiumParameteriCR +VBoxGetWindowId +VBoxGetContextId +VBoxFlushToHost""" #print "DllMain" diff --git a/src/VBox/Additions/common/crOpenGL/dri_drv.c b/src/VBox/Additions/common/crOpenGL/dri_drv.c index d19b99b1..37f501e9 100644 --- a/src/VBox/Additions/common/crOpenGL/dri_drv.c +++ b/src/VBox/Additions/common/crOpenGL/dri_drv.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/dri_drv.h b/src/VBox/Additions/common/crOpenGL/dri_drv.h index 9ee57766..7c5807d2 100644 --- a/src/VBox/Additions/common/crOpenGL/dri_drv.h +++ b/src/VBox/Additions/common/crOpenGL/dri_drv.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/dri_glx.h b/src/VBox/Additions/common/crOpenGL/dri_glx.h index a2a38970..5ff29561 100644 --- a/src/VBox/Additions/common/crOpenGL/dri_glx.h +++ b/src/VBox/Additions/common/crOpenGL/dri_glx.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_drv.c b/src/VBox/Additions/common/crOpenGL/fakedri_drv.c index 8e7b6ba5..f6793dab 100644 --- a/src/VBox/Additions/common/crOpenGL/fakedri_drv.c +++ b/src/VBox/Additions/common/crOpenGL/fakedri_drv.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,6 +29,15 @@ #include <dlfcn.h> #include <elf.h> #include <unistd.h> + +#if defined(RT_OS_FREEBSD) +#include <sys/param.h> +#include <fcntl.h> +#include <gelf.h> +#include <libelf.h> +#include <string.h> +#endif + /** X server message type definitions. */ typedef enum { X_PROBED, /* Value was probed */ @@ -50,11 +59,21 @@ typedef enum { //@todo this could be different... #ifdef RT_ARCH_AMD64 -# define DRI_DEFAULT_DRIVER_DIR "/usr/lib64/dri:/usr/lib/dri:/usr/lib/x86_64-linux-gnu/dri" -# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/" +# ifdef RT_OS_FREEBSD +# define DRI_DEFAULT_DRIVER_DIR "/usr/local/lib/dri" +# define DRI_XORG_DRV_DIR "/usr/local/lib/xorg/modules/drivers/" +# else +# define DRI_DEFAULT_DRIVER_DIR "/usr/lib64/dri:/usr/lib/dri:/usr/lib/x86_64-linux-gnu/dri:/usr/lib/xorg/modules/dri" +# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/" +# endif #else -# define DRI_DEFAULT_DRIVER_DIR "/usr/lib/dri:/usr/lib/i386-linux-gnu/dri" -# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/" +# ifdef RT_OS_FREEBSD +# define DRI_DEFAULT_DRIVER_DIR "/usr/local/lib/dri" +# define DRI_XORG_DRV_DIR "/usr/local/lib/xorg/modules/drivers/" +# else +# define DRI_DEFAULT_DRIVER_DIR "/usr/lib/dri:/usr/lib/i386-linux-gnu/dri:/usr/lib/xorg/modules/dri" +# define DRI_XORG_DRV_DIR "/usr/lib/xorg/modules/drivers/" +# endif #endif #ifdef DEBUG_DRI_CALLS @@ -209,6 +228,85 @@ vboxApplyPatch(const char* psFuncName, void *pDst, const void *pSrc, unsigned lo #define FAKEDRI_JMP64_PATCH_SIZE 13 +#if defined(RT_OS_FREEBSD) +/* Provide basic dladdr1 flags */ +enum { + RTLD_DL_SYMENT = 1 +}; + +/* Provide a minimal local version of dladdr1 */ +static int +dladdr1(const void *address, Dl_info *dlip, void **info, int flags) +{ + static DRI_ELFSYM desym; + GElf_Sym sym; + GElf_Shdr shdr; + Elf *elf; + Elf_Scn *scn; + Elf_Data *data; + int ret, fd, count, i; + + /* Initialize variables */ + fd = -1; + elf = NULL; + + /* Call dladdr first */ + ret = dladdr(address, dlip); + if (ret == 0) goto err_exit; + + /* Check for supported flags */ + if (flags != RTLD_DL_SYMENT) return 1; + + /* Open shared library's ELF file */ + if (elf_version(EV_CURRENT) == EV_NONE) goto err_exit; + fd = open(dlip->dli_fname, O_RDONLY); + if (fd < 0) goto err_exit; + elf = elf_begin(fd, ELF_C_READ, NULL); + if (elf == NULL) goto err_exit; + + /* Find the '.dynsym' section */ + scn = elf_nextscn(elf, NULL); + while (scn != NULL) { + if (gelf_getshdr(scn, &shdr) == NULL) goto err_exit; + if (shdr.sh_type == SHT_DYNSYM) break; + scn = elf_nextscn(elf, scn); + } + if (scn == NULL) goto err_exit; + + /* Search for the requested symbol by name and offset */ + data = elf_getdata(scn, NULL); + count = shdr.sh_size / shdr.sh_entsize; + for (i = 0; i < count; i++) { + gelf_getsym(data, i, &sym); + if ((strcmp(dlip->dli_sname, + elf_strptr(elf, shdr.sh_link, sym.st_name)) == 0) && + (sym.st_value == (dlip->dli_saddr - dlip->dli_fbase))) { + break; + } + } + + /* Close ELF file */ + elf_end(elf); + close(fd); + + /* Return symbol entry in native format */ + desym.st_name = sym.st_name; + desym.st_info = sym.st_info; + desym.st_other = sym.st_other; + desym.st_shndx = sym.st_shndx; + desym.st_value = sym.st_value; + desym.st_size = sym.st_size; + *info = &desym; + return 1; + + /* Error handler */ +err_exit: + if (elf != NULL) elf_end(elf); + if (fd >= 0) close(fd); + return 0; +} +#endif + static void vboxPatchMesaExport(const char* psFuncName, const void *pStart, const void *pEnd) { @@ -262,7 +360,7 @@ vboxPatchMesaExport(const char* psFuncName, const void *pStart, const void *pEnd #ifndef VBOX_NO_MESA_PATCH_REPORTS crDebug("Mesa Entry: %p, start: %p(%s:%s), size: %li", pMesaEntry, dlip.dli_saddr, dlip.dli_fname, dlip.dli_sname, sym->st_size); - crDebug("Vbox code: start: %p, end %p, size: %li", pStart, pEnd, pEnd-pStart); + crDebug("VBox code: start: %p, end %p, size: %li", pStart, pEnd, pEnd-pStart); #endif #ifndef VBOX_OGL_GLX_USE_CSTUBS diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_drv.h b/src/VBox/Additions/common/crOpenGL/fakedri_drv.h index 0d94e1c3..acbc7116 100644 --- a/src/VBox/Additions/common/crOpenGL/fakedri_drv.h +++ b/src/VBox/Additions/common/crOpenGL/fakedri_drv.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h b/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h index ce5510f5..a8551de6 100644 --- a/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h +++ b/src/VBox/Additions/common/crOpenGL/fakedri_glfuncsList.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h b/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h index c1592cdf..6c449f85 100644 --- a/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h +++ b/src/VBox/Additions/common/crOpenGL/fakedri_glxfuncsList.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c b/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c index 232a7c67..2dc757ae 100644 --- a/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c +++ b/src/VBox/Additions/common/crOpenGL/feedback/feedback_context.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/glx.c b/src/VBox/Additions/common/crOpenGL/glx.c index cdb43590..328ed208 100644 --- a/src/VBox/Additions/common/crOpenGL/glx.c +++ b/src/VBox/Additions/common/crOpenGL/glx.c @@ -57,7 +57,7 @@ struct VisualInfo { static struct VisualInfo *VisualInfoList = NULL; static void stubXshmUpdateImageRect(Display *dpy, GLXDrawable draw, GLX_Pixmap_t *pGlxPixmap, XRectangle *pRect); -static void stubInitXDamageExtension(ContextInfo *pContext); +static void stubQueryXDamageExtension(Display *dpy, ContextInfo *pContext); static void AddVisualInfo(Display *dpy, int screen, VisualID visualid, int visBits) @@ -624,9 +624,7 @@ VBOXGLXTAG(glXCreateContext)(Display *dpy, XVisualInfo *vis, GLXContext share, B context->visual = vis; context->direct = direct; - /* This means that clients can't hold a server grab during - * glXCreateContext! */ - stubInitXDamageExtension(context); + stubQueryXDamageExtension(dpy, context); return (GLXContext) context->id; } @@ -830,7 +828,7 @@ DECLEXPORT(int) VBOXGLXTAG(glXGetConfig)( Display *dpy, XVisualInfo *vis, int at case GLX_DEPTH_SIZE: visBits |= CR_DEPTH_BIT; - *value = 16; + *value = 24; break; case GLX_STENCIL_SIZE: @@ -969,7 +967,7 @@ DECLEXPORT(int) VBOXGLXTAG(glXGetConfig)( Display *dpy, XVisualInfo *vis, int at break; case GLX_DEPTH_SIZE: - *value = 16; + *value = 24; break; case GLX_STENCIL_SIZE: @@ -1501,7 +1499,7 @@ VBOXGLXTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list break; case GLX_DEPTH_SIZE: - if (attrib[1] > 16) + if (attrib[1] > 24) goto err_exit; attrib++; break; @@ -1533,7 +1531,8 @@ VBOXGLXTAG(glXChooseFBConfig)(Display *dpy, int screen, ATTRIB_TYPE *attrib_list break; case GLX_DRAWABLE_TYPE: - if (attrib[1]!=GLX_WINDOW_BIT) + if ( !(attrib[1] & GLX_WINDOW_BIT) + && !(attrib[1] & GLX_PIXMAP_BIT)) goto err_exit; attrib++; break; @@ -1782,7 +1781,7 @@ DECLEXPORT(void) VBOXGLXTAG(glXDestroyPixmap)(Display *dpy, GLXPixmap pixmap) if (parms.pGlxPixmap->hDamage>0) { //crDebug("Destroy: Damage for drawable 0x%x, handle 0x%x", (unsigned int) pixmap, (unsigned int) parms.pGlxPixmap->damage); - XDamageDestroy(parms.pCtx->damageDpy, parms.pGlxPixmap->hDamage); + XDamageDestroy(dpy, parms.pGlxPixmap->hDamage); } if (parms.pGlxPixmap->pDamageRegion) @@ -1822,7 +1821,7 @@ DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig confi switch (attribute) { case GLX_DRAWABLE_TYPE: - *value = GLX_PIXMAP_BIT; + *value = GLX_PIXMAP_BIT | GLX_WINDOW_BIT; break; case GLX_BIND_TO_TEXTURE_TARGETS_EXT: *value = GLX_TEXTURE_2D_BIT_EXT; @@ -1860,7 +1859,7 @@ DECLEXPORT(int) VBOXGLXTAG(glXGetFBConfigAttrib)(Display *dpy, GLXFBConfig confi *value = 8; break; case GLX_DEPTH_SIZE: - *value = 16; + *value = 24; //crDebug("attribute=GLX_DEPTH_SIZE"); break; case GLX_BIND_TO_MIPMAP_TEXTURE_EXT: @@ -2172,69 +2171,76 @@ static void stubInitXSharedMemory(Display *dpy) #endif } -void stubInitXDamageExtension(ContextInfo *pContext) +void stubQueryXDamageExtension(Display *dpy, ContextInfo *pContext) { int erb, vma, vmi; CRASSERT(pContext); - if (pContext->damageInitFailed || pContext->damageDpy) + if (pContext->damageQueryFailed) return; - pContext->damageInitFailed = True; - - /* Open second xserver connection to make sure we'd receive all the xdamage messages - * and those wouldn't be eaten by application even queue */ - pContext->damageDpy = XOpenDisplay(DisplayString(pContext->dpy)); - - if (!pContext->damageDpy) - { - crWarning("XDamage: Can't connect to display %s", DisplayString(pContext->dpy)); - return; - } + pContext->damageQueryFailed = True; - if (!XDamageQueryExtension(pContext->damageDpy, &pContext->damageEventsBase, &erb) - || !XDamageQueryVersion(pContext->damageDpy, &vma, &vmi)) + if (!XDamageQueryExtension(dpy, &pContext->damageEventsBase, &erb) + || !XDamageQueryVersion(dpy, &vma, &vmi)) { crWarning("XDamage not found or old version (%i.%i), going to run *very* slow", vma, vmi); - XCloseDisplay(pContext->damageDpy); - pContext->damageDpy = NULL; return; } crDebug("XDamage %i.%i", vma, vmi); - pContext->damageInitFailed = False; + pContext->damageQueryFailed = False; } -static void stubCheckXDamageCB(unsigned long key, void *data1, void *data2) +static void stubFetchDamageOnDrawable(Display *dpy, GLX_Pixmap_t *pGlxPixmap) { - GLX_Pixmap_t *pGlxPixmap = (GLX_Pixmap_t *) data1; - XDamageNotifyEvent *e = (XDamageNotifyEvent *) data2; + Damage damage = pGlxPixmap->hDamage; - if (pGlxPixmap->hDamage==e->damage) + if (damage) { - /*crDebug("Event: Damage for pixmap 0x%lx(drawable 0x%x), handle 0x%x (level=%i) [%i,%i,%i,%i]", - key, (unsigned int) e->drawable, (unsigned int) e->damage, (int) e->level, - e->area.x, e->area.y, e->area.width, e->area.height);*/ + XRectangle *returnRects; + int nReturnRects; + + /* Get the damage region as a server region */ + XserverRegion serverDamageRegion = XFixesCreateRegion (dpy, NULL, 0); + + /* Unite damage region with server region and clear damage region */ + XDamageSubtract (dpy, + damage, + None, /* subtract all damage from this region */ + serverDamageRegion /* save in serverDamageRegion */); + + /* Fetch damage rectangles */ + returnRects = XFixesFetchRegion (dpy, serverDamageRegion, &nReturnRects); + + /* Delete region */ + XFixesDestroyRegion (dpy, serverDamageRegion); if (pGlxPixmap->pDamageRegion) { /* If it's dirty and regions are empty, it marked for full update, so do nothing.*/ if (!pGlxPixmap->bPixmapImageDirty || !XEmptyRegion(pGlxPixmap->pDamageRegion)) { - if (CR_MAX_DAMAGE_REGIONS_TRACKED <= pGlxPixmap->pDamageRegion->numRects) + int i = 0; + for (; i < nReturnRects; ++i) { - /* Mark for full update */ - EMPTY_REGION(pGlxPixmap->pDamageRegion); - } - else - { - /* Add to damage regions */ - XUnionRectWithRegion(&e->area, pGlxPixmap->pDamageRegion, pGlxPixmap->pDamageRegion); + if (CR_MAX_DAMAGE_REGIONS_TRACKED <= pGlxPixmap->pDamageRegion->numRects) + { + /* Mark for full update */ + EMPTY_REGION(pGlxPixmap->pDamageRegion); + } + else + { + /* Add to damage regions */ + XUnionRectWithRegion(&returnRects[i], pGlxPixmap->pDamageRegion, pGlxPixmap->pDamageRegion); + } } } } + XFree(returnRects); + pGlxPixmap->bPixmapImageDirty = True; } } @@ -2338,9 +2344,9 @@ static GLX_Pixmap_t* stubInitGlxPixmap(GLX_Pixmap_t* pCreateInfoPixmap, Display XUNLOCK(dpy); /* If there's damage extension, then get handle for damage events related to this pixmap */ - if (pContext->damageDpy) + if (!pContext->damageQueryFailed) { - pGlxPixmap->hDamage = XDamageCreate(pContext->damageDpy, (Pixmap)draw, XDamageReportRawRectangles); + pGlxPixmap->hDamage = XDamageCreate(dpy, (Pixmap)draw, XDamageReportNonEmpty); /*crDebug("Create: Damage for drawable 0x%x, handle 0x%x (level=%i)", (unsigned int) draw, (unsigned int) pGlxPixmap->damage, (int) XDamageReportRawRectangles);*/ pGlxPixmap->pDamageRegion = XCreateRegion(); @@ -2532,24 +2538,14 @@ DECLEXPORT(void) VBOXGLXTAG(glXBindTexImageEXT)(Display *dpy, GLXDrawable draw, } /* If there's damage extension, then process incoming events as we need the information right now */ - if (context->damageDpy) + if (!context->damageQueryFailed) { - /* Sync connections, note that order of syncs is important here. - * First make sure client commands are finished, then make sure we get all the damage events back*/ + /* Sync connection */ XLOCK(dpy); XSync(dpy, False); XUNLOCK(dpy); - XSync(context->damageDpy, False); - while (XPending(context->damageDpy)) - { - XEvent event; - XNextEvent(context->damageDpy, &event); - if (event.type==context->damageEventsBase+XDamageNotify) - { - crHashtableWalk(context->pGLXPixmapsHash, stubCheckXDamageCB, &event); - } - } + stubFetchDamageOnDrawable(dpy, pGlxPixmap); } /* No shared memory? Rollback to use slow x protocol then */ @@ -2597,7 +2593,7 @@ DECLEXPORT(void) VBOXGLXTAG(glXBindTexImageEXT)(Display *dpy, GLXDrawable draw, else /* Use shm to get pixmap data */ { /* Check if we have damage extension */ - if (context->damageDpy) + if (!context->damageQueryFailed) { if (pGlxPixmap->bPixmapImageDirty) { diff --git a/src/VBox/Additions/common/crOpenGL/glx_c_exports.c b/src/VBox/Additions/common/crOpenGL/glx_c_exports.c index 4f94870a..183c0219 100644 --- a/src/VBox/Additions/common/crOpenGL/glx_c_exports.c +++ b/src/VBox/Additions/common/crOpenGL/glx_c_exports.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/glx_proto.h b/src/VBox/Additions/common/crOpenGL/glx_proto.h index 2ab6d05c..61084749 100644 --- a/src/VBox/Additions/common/crOpenGL/glx_proto.h +++ b/src/VBox/Additions/common/crOpenGL/glx_proto.h @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/icd_drv.c b/src/VBox/Additions/common/crOpenGL/icd_drv.c index 22a9eadb..cff7f9ac 100644 --- a/src/VBox/Additions/common/crOpenGL/icd_drv.c +++ b/src/VBox/Additions/common/crOpenGL/icd_drv.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/icd_drv.h b/src/VBox/Additions/common/crOpenGL/icd_drv.h index e610de15..46c0b56c 100644 --- a/src/VBox/Additions/common/crOpenGL/icd_drv.h +++ b/src/VBox/Additions/common/crOpenGL/icd_drv.h @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2006-2008 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/crOpenGL/load.c b/src/VBox/Additions/common/crOpenGL/load.c index a9cf34e1..35d1f72a 100644 --- a/src/VBox/Additions/common/crOpenGL/load.c +++ b/src/VBox/Additions/common/crOpenGL/load.c @@ -47,10 +47,6 @@ #define PYTHON_EXE "python" #endif -#ifdef WINDOWS -static char* gsViewportHackApps[] = {"googleearth.exe", NULL}; -#endif - static bool stub_initialized = 0; #ifdef WINDOWS static CRmutex stub_init_mutex; @@ -213,16 +209,19 @@ static void stubCheckWindowsState(void) return; #endif + /* Try to keep a consistent locking order. */ + crHashtableLock(stub.windowTable); #if defined(CR_NEWWINTRACK) && !defined(WINDOWS) crLockMutex(&stub.mutex); #endif stubCheckWindowState(context->currentDrawable, GL_TRUE); - crHashtableWalk(stub.windowTable, stubCheckWindowsCB, context); + crHashtableWalkUnlocked(stub.windowTable, stubCheckWindowsCB, context); #if defined(CR_NEWWINTRACK) && !defined(WINDOWS) crUnlockMutex(&stub.mutex); #endif + crHashtableUnlock(stub.windowTable); } @@ -246,20 +245,7 @@ static void SPU_APIENTRY trapViewport(GLint x, GLint y, GLsizei w, GLsizei h) { stubCheckWindowsState(); /* call the original SPU glViewport function */ - if (!stub.viewportHack) - { - origViewport(x, y, w, h); - } - else - { - ContextInfo *context = stubGetCurrentContext(); - int winX, winY; - unsigned int winW, winH; - WindowInfo *pWindow; - pWindow = context->currentDrawable; - stubGetWindowGeometry(pWindow, &winX, &winY, &winW, &winH); - origViewport(0, 0, winW, winH); - } + origViewport(x, y, w, h); } static void SPU_APIENTRY trapSwapBuffers(GLint window, GLint flags) @@ -304,8 +290,6 @@ static void stubInitSPUDispatch(SPU *spu) stub.spuDispatch.Clear = trapClear; stub.spuDispatch.Viewport = trapViewport; - if (stub.viewportHack) - stub.spuDispatch.Scissor = trapScissor; /*stub.spuDispatch.SwapBuffers = trapSwapBuffers; stub.spuDispatch.DrawBuffer = trapDrawBuffer;*/ } @@ -415,7 +399,7 @@ static void stubSPUSafeTearDown(void) #if defined(CR_NEWWINTRACK) crUnlockMutex(mutex); # if defined(WINDOWS) - if (RTThreadGetState(stub.hSyncThread)!=RTTHREADSTATE_TERMINATED) + if (stub.hSyncThread && RTThreadGetState(stub.hSyncThread)!=RTTHREADSTATE_TERMINATED) { HANDLE hNative; DWORD ec=0; @@ -752,30 +736,11 @@ void stubSetDefaultConfigurationOptions(void) crNetSetNodeRange("iam0", "iamvis20"); crNetSetKey(key,sizeof(key)); stub.force_pbuffers = 0; - stub.viewportHack = 0; #ifdef WINDOWS - { - char name[1000]; - int i; - # ifdef VBOX_WITH_WDDM - stub.bRunningUnderWDDM = false; + stub.bRunningUnderWDDM = false; # endif - /* Apply viewport hack only if we're running under wine */ - if (NULL!=GetModuleHandle("wined3d.dll") || NULL != GetModuleHandle("wined3dwddm.dll")) - { - crGetProcName(name, 1000); - for (i=0; gsViewportHackApps[i]; ++i) - { - if (!stricmp(name, gsViewportHackApps[i])) - { - stub.viewportHack = 1; - break; - } - } - } - } #endif } @@ -819,161 +784,7 @@ static HRGN stubMakeRegionFromRects(PVBOXVIDEOCM_CMD_RECTS pRegions, uint32_t st return hRgn; } -typedef struct VBOXCR_UPDATEWNDCB -{ - VBOXDISPMP_REGIONS Regions; - bool fSendUpdateMsg; -} VBOXCR_UPDATEWNDCB, *PVBOXCR_UPDATEWNDCB; - -static void stubSyncTrUpdateWindowCB(unsigned long key, void *data1, void *data2) -{ - WindowInfo *pWindow = (WindowInfo *) data1; - PVBOXCR_UPDATEWNDCB pCbData = (PVBOXCR_UPDATEWNDCB) data2; - VBOXDISPMP_REGIONS *pRegions = &pCbData->Regions; - bool bChanged = false, bDoMap = false; - HRGN hNewRgn = INVALID_HANDLE_VALUE; - - if (pRegions->hWnd != pWindow->hWnd) - { - return; - } - - stub.spu->dispatch_table.VBoxPackSetInjectID(pWindow->u32ClientID); - - if (!stubSystemWindowExist(pWindow)) - { - stubDestroyWindow(0, (GLint)pWindow->hWnd); - return; - } - - if (pRegions->pRegions->fFlags.bAddVisibleRects || pRegions->pRegions->fFlags.bSetViewRect) - { - if (!pWindow->mapped) - { - bDoMap = true; - } - - /* ensure data integrity */ - Assert(!pRegions->pRegions->fFlags.bAddHiddenRects); - - if (pRegions->pRegions->fFlags.bSetViewRect) - { - int winX, winY; - unsigned int winW, winH; - BOOL bRc; - - winX = pRegions->pRegions->RectsInfo.aRects[0].left; - winY = pRegions->pRegions->RectsInfo.aRects[0].top; - winW = pRegions->pRegions->RectsInfo.aRects[0].right - winX; - winH = pRegions->pRegions->RectsInfo.aRects[0].bottom - winY; - - if (stub.trackWindowPos && (bDoMap || winX!=pWindow->x || winY!=pWindow->y)) - { - crDebug("Dispatched WindowPosition (%i)", pWindow->spuWindow); - stub.spuDispatch.WindowPosition(pWindow->spuWindow, winX, winY); - pWindow->x = winX; - pWindow->y = winY; - bChanged = true; - } - - if (stub.trackWindowSize && (bDoMap || winW!=pWindow->width || winH!=pWindow->height)) - { - crDebug("Dispatched WindowSize (%i)", pWindow->spuWindow); - stub.spuDispatch.WindowSize(pWindow->spuWindow, winW, winH); - pWindow->width = winW; - pWindow->height = winH; - bChanged = true; - } - - bRc = MoveWindow(pRegions->hWnd, winX, winY, winW, winH, FALSE /*BOOL bRepaint*/); - if (!bRc) - { - DWORD winEr = GetLastError(); - crWarning("stubSyncTrUpdateWindowCB: MoveWindow failed winEr(%d)", winEr); - } - } - - if (pRegions->pRegions->fFlags.bAddVisibleRects) - { - hNewRgn = stubMakeRegionFromRects(pRegions->pRegions, pRegions->pRegions->fFlags.bSetViewRect ? 1 : 0); - } - } - else if (!pRegions->pRegions->fFlags.bHide) - { - Assert(pRegions->pRegions->fFlags.bAddHiddenRects); - hNewRgn = stubMakeRegionFromRects(pRegions->pRegions, 0); - } - else - { - Assert(pRegions->pRegions->fFlags.bAddHiddenRects); - hNewRgn = CreateRectRgn(pWindow->x, pWindow->y, pWindow->x + pWindow->width, pWindow->y + pWindow->height); - } - - if (hNewRgn!=INVALID_HANDLE_VALUE) - { - if (pRegions->pRegions->fFlags.bAddVisibleRects) - { - HRGN hEmptyRgn = CreateRectRgn(0, 0, 0, 0); - - if (hEmptyRgn!=INVALID_HANDLE_VALUE) - { - if (pWindow->hVisibleRegion==INVALID_HANDLE_VALUE || EqualRgn(pWindow->hVisibleRegion, hEmptyRgn)) - { - pCbData->fSendUpdateMsg = true; - } - - DeleteObject(hEmptyRgn); - } - else - { - crWarning("Failed to created empty region!"); - } - } - - OffsetRgn(hNewRgn, -pWindow->x, -pWindow->y); - - if (pWindow->hVisibleRegion!=INVALID_HANDLE_VALUE) - { - CombineRgn(hNewRgn, pWindow->hVisibleRegion, hNewRgn, - pRegions->pRegions->fFlags.bAddHiddenRects ? RGN_DIFF:RGN_OR); - - if (!EqualRgn(pWindow->hVisibleRegion, hNewRgn)) - { - DeleteObject(pWindow->hVisibleRegion); - pWindow->hVisibleRegion = hNewRgn; - stubDispatchVisibleRegions(pWindow); - bChanged = true; - } - else - { - DeleteObject(hNewRgn); - } - } - else - { - if (pRegions->pRegions->fFlags.bAddVisibleRects) - { - pWindow->hVisibleRegion = hNewRgn; - stubDispatchVisibleRegions(pWindow); - bChanged = true; - } - } - } - - if (bDoMap) - { - pWindow->mapped = GL_TRUE; - bChanged = true; - crDebug("Dispatched: WindowShow(%i, %i)", pWindow->spuWindow, pWindow->mapped); - stub.spu->dispatch_table.WindowShow(pWindow->spuWindow, pWindow->mapped); - } - - if (bChanged) - { - stub.spu->dispatch_table.Flush(); - } -} -# endif +# endif /* VBOX_WITH_WDDM */ static void stubSyncTrCheckWindowsCB(unsigned long key, void *data1, void *data2) { @@ -1010,10 +821,7 @@ static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser) #ifdef WINDOWS MSG msg; # ifdef VBOX_WITH_WDDM - static VBOXDISPMP_CALLBACKS VBoxDispMpTstCallbacks = {NULL, NULL, NULL}; HMODULE hVBoxD3D = NULL; - VBOXCR_UPDATEWNDCB RegionsData; - HRESULT hr; GLint spuConnection = 0; # endif #endif @@ -1024,42 +832,18 @@ static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser) #ifdef WINDOWS PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE); # ifdef VBOX_WITH_WDDM - hVBoxD3D = GetModuleHandle(VBOX_MODNAME_DISPD3D); - if (hVBoxD3D) + hVBoxD3D = NULL; + if (!GetModuleHandleEx(0, VBOX_MODNAME_DISPD3D, &hVBoxD3D)) { - hVBoxD3D = LoadLibrary(VBOX_MODNAME_DISPD3D); + crDebug("GetModuleHandleEx failed err %d", GetLastError()); + hVBoxD3D = NULL; } if (hVBoxD3D) { - PFNVBOXDISPMP_GETCALLBACKS pfnVBoxDispMpGetCallbacks; - pfnVBoxDispMpGetCallbacks = (PFNVBOXDISPMP_GETCALLBACKS)GetProcAddress(hVBoxD3D, TEXT("VBoxDispMpGetCallbacks")); - if (pfnVBoxDispMpGetCallbacks) - { - hr = pfnVBoxDispMpGetCallbacks(VBOXDISPMP_VERSION, &VBoxDispMpTstCallbacks); - if (S_OK==hr) - { - CRASSERT(VBoxDispMpTstCallbacks.pfnEnableEvents); - CRASSERT(VBoxDispMpTstCallbacks.pfnDisableEvents); - CRASSERT(VBoxDispMpTstCallbacks.pfnGetRegions); - - hr = VBoxDispMpTstCallbacks.pfnEnableEvents(); - if (hr != S_OK) - { - crWarning("VBoxDispMpTstCallbacks.pfnEnableEvents failed"); - } - else - { crDebug("running with " VBOX_MODNAME_DISPD3D); stub.trackWindowVisibleRgn = 0; stub.bRunningUnderWDDM = true; - } - } - else - { - crWarning("VBoxDispMpGetCallbacks failed"); - } - } } # endif /* VBOX_WITH_WDDM */ #endif /* WINDOWS */ @@ -1085,38 +869,9 @@ static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser) if (!PeekMessage(&msg, 0, 0, 0, PM_REMOVE)) { # ifdef VBOX_WITH_WDDM - if (VBoxDispMpTstCallbacks.pfnGetRegions) + if (stub.bRunningUnderWDDM) { - hr = VBoxDispMpTstCallbacks.pfnGetRegions(&RegionsData.Regions, 50); - if (S_OK==hr) - { - RegionsData.fSendUpdateMsg = false; -# if 0 - uint32_t i; - crDebug(">>>Regions for HWND(0x%x)>>>", RegionsData.Regions.hWnd); - crDebug("Flags(0x%x)", RegionsData.Regions.pRegions->fFlags.Value); - for (i = 0; i < RegionsData.Regions.pRegions->RectsInfo.cRects; ++i) - { - RECT *pRect = &RegionsData.Regions.pRegions->RectsInfo.aRects[i]; - crDebug("Rect(%d): left(%d), top(%d), right(%d), bottom(%d)", i, pRect->left, pRect->top, pRect->right, pRect->bottom); - } - crDebug("<<<<<"); -# endif - /*hacky way to make sure window wouldn't be deleted in another thread as we hold hashtable lock here*/ - crHashtableWalk(stub.windowTable, stubSyncTrUpdateWindowCB, &RegionsData); - if (RegionsData.fSendUpdateMsg) - { - SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, 0, 0, SMTO_NORMAL, 1000, NULL); - } - } - else - { - if (WAIT_TIMEOUT!=hr) - { - crWarning("VBoxDispMpTstCallbacks.pfnGetRegions failed with 0x%x", hr); - } - crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL); - } + } else # endif @@ -1139,18 +894,17 @@ static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser) } } #else + /* Try to keep a consistent locking order. */ + crHashtableLock(stub.windowTable); crLockMutex(&stub.mutex); - crHashtableWalk(stub.windowTable, stubSyncTrCheckWindowsCB, NULL); + crHashtableWalkUnlocked(stub.windowTable, stubSyncTrCheckWindowsCB, NULL); crUnlockMutex(&stub.mutex); + crHashtableUnlock(stub.windowTable); RTThreadSleep(50); #endif } #ifdef VBOX_WITH_WDDM - if (VBoxDispMpTstCallbacks.pfnDisableEvents) - { - VBoxDispMpTstCallbacks.pfnDisableEvents(); - } if (spuConnection) { stub.spu->dispatch_table.VBoxConDestroy(spuConnection); @@ -1163,7 +917,7 @@ static DECLCALLBACK(int) stubSyncThreadProc(RTTHREAD ThreadSelf, void *pvUser) crDebug("Sync thread stopped"); return 0; } -#endif +#endif /* CR_NEWWINTRACK */ /** * Do one-time initializations for the faker. @@ -1188,6 +942,9 @@ stubInitLocked(void) const char *app_id; int i; int disable_sync = 0; +#if defined(WINDOWS) && defined(VBOX_WITH_WDDM) + HMODULE hVBoxD3D = NULL; +#endif stubInitVars(); @@ -1230,15 +987,6 @@ stubInitLocked(void) { crNetFreeConnection(ns.conn); } -#if 0 && defined(CR_NEWWINTRACK) - { - Status st = XInitThreads(); - if (st==0) - { - crWarning("XInitThreads returned %i", (int)st); - } - } -#endif } #endif @@ -1256,6 +1004,27 @@ stubInitLocked(void) stubSetDefaultConfigurationOptions(); +#if defined(WINDOWS) && defined(VBOX_WITH_WDDM) + hVBoxD3D = NULL; + if (!GetModuleHandleEx(0, VBOX_MODNAME_DISPD3D, &hVBoxD3D)) + { + crDebug("GetModuleHandleEx failed err %d", GetLastError()); + hVBoxD3D = NULL; + } + + if (hVBoxD3D) + { + disable_sync = 1; + crDebug("running with %s", VBOX_MODNAME_DISPD3D); + stub.trackWindowVisibleRgn = 0; + /* @todo: should we enable that? */ + stub.trackWindowSize = 0; + stub.trackWindowPos = 0; + stub.trackWindowVisibility = 0; + stub.bRunningUnderWDDM = true; + } +#endif + stub.spu = crSPULoadChain( num_spus, spu_ids, spu_names, stub.spu_dir, NULL ); crFree( spuchain ); @@ -1296,7 +1065,7 @@ raise(SIGINT);*/ { int rc; - RTR3InitDll(0); + RTR3InitDll(RTR3INIT_FLAGS_UNOBTRUSIVE); if (!disable_sync) { @@ -1358,14 +1127,144 @@ stubInit(void) #define WIN32_LEAN_AND_MEAN #include <windows.h> -#ifdef DEBUG_misha +#if 1//def DEBUG_misha /* debugging: this is to be able to catch first-chance notifications * for exceptions other than EXCEPTION_BREAKPOINT in kernel debugger */ # define VDBG_VEHANDLER #endif #ifdef VDBG_VEHANDLER -static PVOID g_VBoxWDbgVEHandler = NULL; +# include <dbghelp.h> +static PVOID g_VBoxVehHandler = NULL; +static DWORD g_VBoxVehEnable = 0; + +/* generate a crash dump on exception */ +#define VBOXVEH_F_DUMP 0x00000001 +/* generate a debugger breakpoint exception */ +#define VBOXVEH_F_BREAK 0x00000002 +/* exit on exception */ +#define VBOXVEH_F_EXIT 0x00000004 + +static DWORD g_VBoxVehFlags = 0 +#ifdef DEBUG_misha + | VBOXVEH_F_BREAK +#else + | VBOXVEH_F_DUMP +#endif + ; + +typedef BOOL WINAPI FNVBOXDBG_MINIDUMPWRITEDUMP(HANDLE hProcess, + DWORD ProcessId, + HANDLE hFile, + MINIDUMP_TYPE DumpType, + PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, + PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, + PMINIDUMP_CALLBACK_INFORMATION CallbackParam); +typedef FNVBOXDBG_MINIDUMPWRITEDUMP *PFNVBOXDBG_MINIDUMPWRITEDUMP; + +static HMODULE g_hVBoxMdDbgHelp = NULL; +static PFNVBOXDBG_MINIDUMPWRITEDUMP g_pfnVBoxMdMiniDumpWriteDump = NULL; +static uint32_t g_cVBoxMdFilePrefixLen = 0; +static WCHAR g_aszwVBoxMdFilePrefix[MAX_PATH]; +static WCHAR g_aszwVBoxMdDumpCount = 0; +static MINIDUMP_TYPE g_enmVBoxMdDumpType = MiniDumpNormal + | MiniDumpWithDataSegs + | MiniDumpWithFullMemory + | MiniDumpWithHandleData +//// | MiniDumpFilterMemory +//// | MiniDumpScanMemory +// | MiniDumpWithUnloadedModules +//// | MiniDumpWithIndirectlyReferencedMemory +//// | MiniDumpFilterModulePaths +// | MiniDumpWithProcessThreadData +// | MiniDumpWithPrivateReadWriteMemory +//// | MiniDumpWithoutOptionalData +// | MiniDumpWithFullMemoryInfo +// | MiniDumpWithThreadInfo +// | MiniDumpWithCodeSegs +// | MiniDumpWithFullAuxiliaryState +// | MiniDumpWithPrivateWriteCopyMemory +// | MiniDumpIgnoreInaccessibleMemory +// | MiniDumpWithTokenInformation +//// | MiniDumpWithModuleHeaders +//// | MiniDumpFilterTriage + ; + + + +#define VBOXMD_DUMP_DIR_PREFIX_DEFAULT L"C:\\dumps\\vboxdmp" + +static HMODULE loadSystemDll(const char *pszName) +{ + char szPath[MAX_PATH]; + UINT cchPath = GetSystemDirectoryA(szPath, sizeof(szPath)); + size_t cbName = strlen(pszName) + 1; + if (cchPath + 1 + cbName > sizeof(szPath)) + { + SetLastError(ERROR_FILENAME_EXCED_RANGE); + return NULL; + } + szPath[cchPath] = '\\'; + memcpy(&szPath[cchPath + 1], pszName, cbName); + return LoadLibraryA(szPath); +} + +static DWORD vboxMdMinidumpCreate(struct _EXCEPTION_POINTERS *pExceptionInfo) +{ + WCHAR aszwMdFileName[MAX_PATH]; + HANDLE hProcess = GetCurrentProcess(); + DWORD ProcessId = GetCurrentProcessId(); + MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo; + HANDLE hFile; + DWORD winErr = ERROR_SUCCESS; + + if (!g_pfnVBoxMdMiniDumpWriteDump) + { + if (!g_hVBoxMdDbgHelp) + { + g_hVBoxMdDbgHelp = loadSystemDll("DbgHelp.dll"); + if (!g_hVBoxMdDbgHelp) + return GetLastError(); + } + + g_pfnVBoxMdMiniDumpWriteDump = (PFNVBOXDBG_MINIDUMPWRITEDUMP)GetProcAddress(g_hVBoxMdDbgHelp, "MiniDumpWriteDump"); + if (!g_pfnVBoxMdMiniDumpWriteDump) + return GetLastError(); + } + + /* @todo: this is a tmp stuff until we get that info from the settings properly */ + if (!g_cVBoxMdFilePrefixLen) + { + g_cVBoxMdFilePrefixLen = sizeof (VBOXMD_DUMP_DIR_PREFIX_DEFAULT)/sizeof (g_aszwVBoxMdFilePrefix[0]) - 1 /* <- don't include nul terminator */; + memcpy(g_aszwVBoxMdFilePrefix, VBOXMD_DUMP_DIR_PREFIX_DEFAULT, sizeof (VBOXMD_DUMP_DIR_PREFIX_DEFAULT)); + } + + + if (RT_ELEMENTS(aszwMdFileName) <= g_cVBoxMdFilePrefixLen) + { + return ERROR_INVALID_STATE; + } + + ++g_aszwVBoxMdDumpCount; + + memcpy(aszwMdFileName, g_aszwVBoxMdFilePrefix, g_cVBoxMdFilePrefixLen * sizeof (g_aszwVBoxMdFilePrefix[0])); + swprintf(aszwMdFileName + g_cVBoxMdFilePrefixLen, RT_ELEMENTS(aszwMdFileName) - g_cVBoxMdFilePrefixLen, L"%d_%d.dmp", ProcessId, g_aszwVBoxMdDumpCount); + + hFile = CreateFileW(aszwMdFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile == INVALID_HANDLE_VALUE) + return GetLastError(); + + ExceptionInfo.ThreadId = GetCurrentThreadId(); + ExceptionInfo.ExceptionPointers = pExceptionInfo; + ExceptionInfo.ClientPointers = FALSE; + + if (!g_pfnVBoxMdMiniDumpWriteDump(hProcess, ProcessId, hFile, g_enmVBoxMdDumpType, &ExceptionInfo, NULL, NULL)) + winErr = GetLastError(); + + CloseHandle(hFile); + return winErr; +} + LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) { PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord; @@ -1380,7 +1279,33 @@ LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) case EXCEPTION_FLT_INVALID_OPERATION: case EXCEPTION_INT_DIVIDE_BY_ZERO: case EXCEPTION_ILLEGAL_INSTRUCTION: - CRASSERT(0); + if (g_VBoxVehFlags & VBOXVEH_F_BREAK) + { + BOOL fBreak = TRUE; +#ifndef DEBUG_misha + if (pExceptionRecord->ExceptionCode == EXCEPTION_BREAKPOINT) + { + HANDLE hProcess = GetCurrentProcess(); + BOOL fDebuggerPresent = FALSE; + /* we do not want to generate breakpoint exceptions recursively, so do it only when running under debugger */ + if (CheckRemoteDebuggerPresent(hProcess, &fDebuggerPresent)) + fBreak = !!fDebuggerPresent; + else + fBreak = FALSE; /* <- the function has failed, don't break for sanity */ + } +#endif + + if (fBreak) + { + RT_BREAKPOINT(); + } + } + + if (g_VBoxVehFlags & VBOXVEH_F_DUMP) + vboxMdMinidumpCreate(pExceptionInfo); + + if (g_VBoxVehFlags & VBOXVEH_F_EXIT) + exit(1); break; default: break; @@ -1390,19 +1315,19 @@ LONG WINAPI vboxVDbgVectoredHandler(struct _EXCEPTION_POINTERS *pExceptionInfo) void vboxVDbgVEHandlerRegister() { - CRASSERT(!g_VBoxWDbgVEHandler); - g_VBoxWDbgVEHandler = AddVectoredExceptionHandler(1,vboxVDbgVectoredHandler); - CRASSERT(g_VBoxWDbgVEHandler); + CRASSERT(!g_VBoxVehHandler); + g_VBoxVehHandler = AddVectoredExceptionHandler(1,vboxVDbgVectoredHandler); + CRASSERT(g_VBoxVehHandler); } void vboxVDbgVEHandlerUnregister() { ULONG uResult; - if (g_VBoxWDbgVEHandler) + if (g_VBoxVehHandler) { - uResult = RemoveVectoredExceptionHandler(g_VBoxWDbgVEHandler); + uResult = RemoveVectoredExceptionHandler(g_VBoxVehHandler); CRASSERT(uResult); - g_VBoxWDbgVEHandler = NULL; + g_VBoxVehHandler = NULL; } } #endif @@ -1425,7 +1350,12 @@ BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) crInitMutex(&stub_init_mutex); #ifdef VDBG_VEHANDLER - vboxVDbgVEHandlerRegister(); + g_VBoxVehEnable = !!crGetenv("CR_DBG_VEH_ENABLE"); +# ifdef DEBUG_misha + g_VBoxVehEnable = 1; +# endif + if (g_VBoxVehEnable) + vboxVDbgVEHandlerRegister(); #endif crNetInit(NULL, NULL); @@ -1440,12 +1370,15 @@ BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) { crDebug("Failed to connect to host (is guest 3d acceleration enabled?), aborting ICD load."); #ifdef VDBG_VEHANDLER - vboxVDbgVEHandlerUnregister(); + if (g_VBoxVehEnable) + vboxVDbgVEHandlerUnregister(); #endif return FALSE; } else + { crNetFreeConnection(ns.conn); + } break; } @@ -1468,7 +1401,8 @@ BOOL WINAPI DllMain(HINSTANCE hDLLInst, DWORD fdwReason, LPVOID lpvReserved) #endif #ifdef VDBG_VEHANDLER - vboxVDbgVEHandlerUnregister(); + if (g_VBoxVehEnable) + vboxVDbgVEHandlerUnregister(); #endif break; } diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu.h b/src/VBox/Additions/common/crOpenGL/pack/packspu.h index 27f3526f..942761a8 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu.h +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu.h @@ -19,6 +19,9 @@ #include "cr_spu.h" #include "cr_threads.h" #include "state/cr_client.h" +#ifdef VBOX_WITH_CRPACKSPU_DUMPER +# include "cr_dump.h" +#endif typedef struct thread_info_t ThreadInfo; typedef struct context_info_t ContextInfo; @@ -70,6 +73,13 @@ typedef struct { bool bRunningUnderWDDM; #endif +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + SPUDispatchTable self; + + CR_RECORDER Recorder; + CR_DBGPRINT_DUMPER Dumper; +#endif + int numContexts; ContextInfo context[CR_MAX_CONTEXTS]; } PackSPU; @@ -82,7 +92,8 @@ extern PackSPU pack_spu; extern CRmutex _PackMutex; extern CRtsd _PackTSD; #define GET_THREAD_VAL() (crGetTSD(&_PackTSD)) -#define GET_THREAD_VAL_ID(_id) (&(pack_spu.thread[(_id) - THREAD_OFFSET_MAGIC])) +#define GET_THREAD_IDX(_id) ((_id) - THREAD_OFFSET_MAGIC) +#define GET_THREAD_VAL_ID(_id) (&(pack_spu.thread[GET_THREAD_IDX(_id)])) #else #define GET_THREAD_VAL() (&(pack_spu.thread[0])) #endif diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c index 8a741137..53cd3aff 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_bufferobject.c @@ -130,3 +130,31 @@ packspu_BindBufferARB( GLenum target, GLuint buffer ) crStateBindBufferARB(target, buffer); crPackBindBufferARB(target, buffer); } + +void PACKSPU_APIENTRY packspu_GenBuffersARB( GLsizei n, GLuint * buffer ) +{ + GET_THREAD(thread); + int writeback = 1; + if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network)) + { + crError( "packspu_GenBuffersARB doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" ); + } + if (pack_spu.swap) + { + crPackGenBuffersARBSWAP( n, buffer, &writeback ); + } + else + { + crPackGenBuffersARB( n, buffer, &writeback ); + } + packspuFlush( (void *) thread ); + CRPACKSPU_WRITEBACK_WAIT(thread, writeback); + + crStateRegBuffers(n, buffer); +} + +void PACKSPU_APIENTRY packspu_DeleteBuffersARB( GLsizei n, const GLuint * buffer ) +{ + crStateDeleteBuffersARB( n, buffer ); + crPackDeleteBuffersARB(n, buffer); +} diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c index 90fb5f90..08e41300 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_context.c @@ -130,6 +130,8 @@ packspu_VBoxConFlush(GLint con) CRASSERT(CRPACKSPU_IS_WDDM_CRHGSMI()); CRASSERT(thread->packer); packspuFlush((void *) thread); +#else + crError("VBoxConFlush not implemented!"); #endif } @@ -148,8 +150,17 @@ packspu_VBoxConDestroy(GLint con) crPackDeleteContext(thread->packer); + if (thread->buffer.pack) + { + crNetFree(thread->netServer.conn, thread->buffer.pack); + thread->buffer.pack = NULL; + } + crNetFreeConnection(thread->netServer.conn); + if (thread->netServer.name) + crFree(thread->netServer.name); + pack_spu.numThreads--; /*note can't shift the array here, because other threads have TLS references to array elements*/ crMemZero(thread, sizeof(ThreadInfo)); @@ -173,6 +184,63 @@ packspu_VBoxConDestroy(GLint con) #endif } +GLvoid PACKSPU_APIENTRY +packspu_VBoxConChromiumParameteriCR(GLint con, GLenum param, GLint value) +{ + GET_THREAD(thread); + CRPackContext * curPacker = crPackGetContext(); + ThreadInfo *curThread = thread; + int writeback = 1; + GLint serverCtx = (GLint) -1; + + CRASSERT(!curThread == !curPacker); + CRASSERT(!curThread || !curPacker || curThread->packer == curPacker); +#ifdef CHROMIUM_THREADSAFE + crLockMutex(&_PackMutex); +#endif + +#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) + CRASSERT(!con == !CRPACKSPU_IS_WDDM_CRHGSMI()); +#endif + + if (CRPACKSPU_IS_WDDM_CRHGSMI()) + { + if (!con) + { + crError("connection should be specified!"); + return; + } + thread = GET_THREAD_VAL_ID(con); + } + else + { + CRASSERT(!con); + if (!thread) + { + thread = packspuNewThread( +#if defined(VBOX_WITH_CRHGSMI) && defined(IN_GUEST) + NULL +#endif + ); + } + } + CRASSERT(thread); + CRASSERT(thread->packer); + + crPackSetContext( thread->packer ); + + packspu_ChromiumParameteriCR(param, value); + +#ifdef CHROMIUM_THREADSAFE + crUnlockMutex(&_PackMutex); +#endif + + if (CRPACKSPU_IS_WDDM_CRHGSMI()) + { + /* restore the packer context to the tls */ + crPackSetContext(curPacker); + } +} GLint PACKSPU_APIENTRY packspu_VBoxCreateContext( GLint con, const char *dpyName, GLint visual, GLint shareCtx ) @@ -412,7 +480,7 @@ void PACKSPU_APIENTRY packspu_MakeCurrent( GLint window, GLint nativeWindow, GLi } else { - if (!newCtx->fAutoFlush) + if (newCtx->fAutoFlush) { if (newCtx->currentThread && newCtx->currentThread != thread) { diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c index 555e60cd..ed555c48 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_framebuffer.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -59,8 +59,8 @@ packspu_DeleteFramebuffersEXT(GLsizei n, const GLuint * framebuffers) void PACKSPU_APIENTRY packspu_DeleteRenderbuffersEXT(GLsizei n, const GLuint * renderbuffers) { - crStateDeleteFramebuffersEXT(n, renderbuffers); - crPackDeleteFramebuffersEXT(n, renderbuffers); + crStateDeleteRenderbuffersEXT(n, renderbuffers); + crPackDeleteRenderbuffersEXT(n, renderbuffers); } void PACKSPU_APIENTRY @@ -97,3 +97,47 @@ packspu_CheckFramebufferStatusEXT(GLenum target) crStateSetFramebufferStatus(target, status); return status; } + +void PACKSPU_APIENTRY packspu_GenFramebuffersEXT( GLsizei n, GLuint * framebuffers ) +{ + GET_THREAD(thread); + int writeback = 1; + if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network)) + { + crError( "packspu_GenFramebuffersEXT doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" ); + } + if (pack_spu.swap) + { + crPackGenFramebuffersEXTSWAP( n, framebuffers, &writeback ); + } + else + { + crPackGenFramebuffersEXT( n, framebuffers, &writeback ); + } + packspuFlush( (void *) thread ); + CRPACKSPU_WRITEBACK_WAIT(thread, writeback); + + crStateRegFramebuffers(n, framebuffers); +} + +void PACKSPU_APIENTRY packspu_GenRenderbuffersEXT( GLsizei n, GLuint * renderbuffers ) +{ + GET_THREAD(thread); + int writeback = 1; + if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network)) + { + crError( "packspu_GenRenderbuffersEXT doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" ); + } + if (pack_spu.swap) + { + crPackGenRenderbuffersEXTSWAP( n, renderbuffers, &writeback ); + } + else + { + crPackGenRenderbuffersEXT( n, renderbuffers, &writeback ); + } + packspuFlush( (void *) thread ); + CRPACKSPU_WRITEBACK_WAIT(thread, writeback); + + crStateRegRenderbuffers(n, renderbuffers); +} diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py b/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py index 25aa4de2..82f00a85 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_get.py @@ -48,7 +48,6 @@ GLboolean crPackIsPixelStoreParm(GLenum pname) """ from get_sizes import * -from get_components import * easy_swaps = { 'GenTextures': '(unsigned int) n', @@ -117,6 +116,11 @@ for func_name in keys: #ifdef CR_ARB_vertex_program || pname == GL_MAX_VERTEX_ATTRIBS_ARB #endif +#ifndef GL_EXT_framebuffer_object + || pname == GL_FRAMEBUFFER_BINDING_EXT + || pname == GL_READ_FRAMEBUFFER_BINDING_EXT + || pname == GL_DRAW_FRAMEBUFFER_BINDING_EXT +#endif ) { #ifdef DEBUG @@ -200,7 +204,7 @@ for func_name in keys: if func_name in hard_funcs.keys(): print '\tif (pack_spu.swap)' print '\t{' - print '\t\tfor (i = 0 ; i < lookupComponents(pname) ; i++)' + print '\t\tfor (i = 0 ; i < crStateHlpComponentsCount(pname) ; i++)' print '\t\t{' if hard_funcs[func_name] == 'SWAPDOUBLE': print '\t\t\t%s[i] = %s(%s[i]);' % (lastParamName, hard_funcs[func_name], lastParamName) diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c index d0cc9040..6b3271d5 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_getshaders.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -98,7 +98,7 @@ void PACKSPU_APIENTRY packspu_GetAttachedShaders(GLuint program, GLsizei maxCoun crFree(pLocal); } -void PACKSPU_APIENTRY packspu_GetAttachedObjectsARB(GLhandleARB containerObj, GLsizei maxCount, GLsizei * count, GLhandleARB * obj) +void PACKSPU_APIENTRY packspu_GetAttachedObjectsARB(VBoxGLhandleARB containerObj, GLsizei maxCount, GLsizei * count, VBoxGLhandleARB * obj) { GET_THREAD(thread); int writeback = 1; @@ -106,7 +106,7 @@ void PACKSPU_APIENTRY packspu_GetAttachedObjectsARB(GLhandleARB containerObj, GL if (!obj) return; - pLocal = (GLsizei*) crAlloc(maxCount*sizeof(GLhandleARB)+sizeof(GLsizei)); + pLocal = (GLsizei*) crAlloc(maxCount*sizeof(VBoxGLhandleARB)+sizeof(GLsizei)); if (!pLocal) return; crPackGetAttachedObjectsARB(containerObj, maxCount, pLocal, NULL, &writeback); @@ -115,13 +115,13 @@ void PACKSPU_APIENTRY packspu_GetAttachedObjectsARB(GLhandleARB containerObj, GL CRPACKSPU_WRITEBACK_WAIT(thread, writeback); if (count) *count=*pLocal; - crMemcpy(obj, &pLocal[1], *pLocal*sizeof(GLhandleARB)); + crMemcpy(obj, &pLocal[1], *pLocal*sizeof(VBoxGLhandleARB)); crFree(pLocal); } AssertCompile(sizeof(GLsizei) == 4); -void PACKSPU_APIENTRY packspu_GetInfoLogARB(GLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog) +void PACKSPU_APIENTRY packspu_GetInfoLogARB(VBoxGLhandleARB obj, GLsizei maxLength, GLsizei * length, GLcharARB * infoLog) { GET_THREAD(thread); int writeback = 1; diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c index e19959a3..d58202ef 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_glsl.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -129,12 +129,14 @@ void PACKSPU_APIENTRY packspu_DeleteProgram(GLuint program) crPackDeleteProgram(program); } -void PACK_APIENTRY packspu_DeleteObjectARB(GLhandleARB obj) +void PACK_APIENTRY packspu_DeleteObjectARB(VBoxGLhandleARB obj) { GLuint hwid = crStateGetProgramHWID(obj); CRASSERT(obj); + /* we do not track shader creation inside guest since it is not needed currently. + * this is why we only care about programs here */ if (hwid) { crStateDeleteProgram(obj); @@ -143,8 +145,57 @@ void PACK_APIENTRY packspu_DeleteObjectARB(GLhandleARB obj) crPackDeleteObjectARB(obj); } +#ifdef VBOX_WITH_CRPACKSPU_DUMPER +static void packspu_RecCheckInitRec() +{ + if (pack_spu.Recorder.pDumper) + return; + + crDmpDbgPrintInit(&pack_spu.Dumper); + + crRecInit(&pack_spu.Recorder, NULL /*pBlitter: we do not support blitter operations here*/, &pack_spu.self, &pack_spu.Dumper.Base); +} +#endif + void PACKSPU_APIENTRY packspu_LinkProgram(GLuint program) { +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + GLint linkStatus = 0; +#endif + crStateLinkProgram(program); crPackLinkProgram(program); + +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + pack_spu.self.GetObjectParameterivARB(program, GL_OBJECT_LINK_STATUS_ARB, &linkStatus); + Assert(linkStatus); + if (!linkStatus) + { + CRContext *ctx = crStateGetCurrent(); + packspu_RecCheckInitRec(); + crRecDumpProgram(&pack_spu.Recorder, ctx, program, program); + } +#endif } + +void PACKSPU_APIENTRY packspu_CompileShader(GLuint shader) +{ +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + GLint compileStatus = 0; +#endif + +// crStateCompileShader(shader); + crPackCompileShader(shader); + +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + pack_spu.self.GetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus); + Assert(compileStatus); + if (!compileStatus) + { + CRContext *ctx = crStateGetCurrent(); + packspu_RecCheckInitRec(); + crRecDumpShader(&pack_spu.Recorder, ctx, shader, shader); + } +#endif +} + diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c index 6682058d..2781fa6f 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_init.c @@ -50,6 +50,10 @@ packSPUInit( int id, SPU *child, SPU *self, pack_spu.bRunningUnderWDDM = !!GetModuleHandle(VBOX_MODNAME_DISPD3D); #endif +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + memset(&pack_spu.Dumper, 0, sizeof (pack_spu.Dumper)); +#endif + if (!CRPACKSPU_IS_WDDM_CRHGSMI()) { /* This connects to the server, sets up the packer, etc. */ @@ -75,7 +79,12 @@ packSPUInit( int id, SPU *child, SPU *self, static void packSPUSelfDispatch(SPUDispatchTable *self) { +#ifdef VBOX_WITH_CRPACKSPU_DUMPER + crSPUInitDispatchTable( &(pack_spu.self) ); + crSPUCopyDispatchTable( &(pack_spu.self), self ); +#else (void)self; +#endif } static int diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c index ee4d4f88..3048bbc3 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_misc.c @@ -615,9 +615,18 @@ void PACKSPU_APIENTRY packspu_VBoxDetachThread() crPackSetContext(NULL); CR_UNLOCK_PACKER_CONTEXT(thread->packer); crPackDeleteContext(pack_spu.thread[i].packer); + + if (pack_spu.thread[i].buffer.pack) + { + crNetFree(pack_spu.thread[i].netServer.conn, pack_spu.thread[i].buffer.pack); + pack_spu.thread[i].buffer.pack = NULL; + } } crNetFreeConnection(pack_spu.thread[i].netServer.conn); + if (pack_spu.thread[i].netServer.name) + crFree(pack_spu.thread[i].netServer.name); + pack_spu.numThreads--; /*note can't shift the array here, because other threads have TLS references to array elements*/ crMemZero(&pack_spu.thread[i], sizeof(ThreadInfo)); @@ -712,3 +721,11 @@ void PACKSPU_APIENTRY packspu_VBoxPackDetachThread() { } #endif /*CHROMIUM_THREADSAFE*/ + +void PACKSPU_APIENTRY packspu_VBoxPresentComposition(GLint win, const struct VBOXVR_SCR_COMPOSITOR * pCompositor, const struct VBOXVR_SCR_COMPOSITOR_ENTRY *pChangedEntry) +{ +} + +void PACKSPU_APIENTRY packspu_StringMarkerGREMEDY(GLsizei len, const GLvoid *string) +{ +} diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_special b/src/VBox/Additions/common/crOpenGL/pack/packspu_special index 6b9edd95..d7a12124 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_special +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_special @@ -107,11 +107,13 @@ VBoxPackSetInjectID VBoxAttachThread VBoxDetachThread VBoxCreateContext +VBoxConChromiumParameteriCR VBoxWindowCreate VBoxWindowDestroy VBoxConCreate VBoxConDestroy VBoxConFlush +VBoxPresentComposition ChromiumParameteriCR CompressedTexImage1DARB CompressedTexImage2DARB @@ -119,3 +121,12 @@ CompressedTexImage3DARB CompressedTexSubImage1DARB CompressedTexSubImage2DARB CompressedTexSubImage3DARB +GenFramebuffersEXT +GenRenderbuffersEXT +DeleteFramebuffersEXT +DeleteRenderbuffersEXT +GenBuffersARB +DeleteBuffersARB +StringMarkerGREMEDY +GenTextures +CompileShader
\ No newline at end of file diff --git a/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c b/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c index b5c60aec..fca7fe66 100644 --- a/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c +++ b/src/VBox/Additions/common/crOpenGL/pack/packspu_texture.c @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -39,3 +39,32 @@ void PACKSPU_APIENTRY packspu_DeleteTextures(GLsizei n, const GLuint * textures) crPackDeleteTextures(n, textures); } +void PACKSPU_APIENTRY packspu_GenTextures( GLsizei n, GLuint * textures ) +{ + GET_THREAD(thread); + int writeback = 1; + unsigned int i; + if (!CRPACKSPU_IS_WDDM_CRHGSMI() && !(pack_spu.thread[pack_spu.idxThreadInUse].netServer.conn->actual_network)) + { + crError( "packspu_GenTextures doesn't work when there's no actual network involved!\nTry using the simplequery SPU in your chain!" ); + } + if (pack_spu.swap) + { + crPackGenTexturesSWAP( n, textures, &writeback ); + } + else + { + crPackGenTextures( n, textures, &writeback ); + } + packspuFlush( (void *) thread ); + CRPACKSPU_WRITEBACK_WAIT(thread, writeback); + if (pack_spu.swap) + { + for (i = 0 ; i < (unsigned int) n ; i++) + { + textures[i] = SWAP32(textures[i]); + } + } + + crStateRegTextures(n, textures); +} diff --git a/src/VBox/Additions/common/crOpenGL/stub.c b/src/VBox/Additions/common/crOpenGL/stub.c index 5131d4bf..5f0f3745 100644 --- a/src/VBox/Additions/common/crOpenGL/stub.c +++ b/src/VBox/Additions/common/crOpenGL/stub.c @@ -142,7 +142,7 @@ void APIENTRY crWindowPosition( GLint window, GLint x, GLint y ) } } -void APIENTRY crWindowVisibleRegion( GLint window, GLint cRects, void *pRects ) +void APIENTRY crWindowVisibleRegion( GLint window, GLint cRects, const void *pRects ) { const WindowInfo *winInfo = (const WindowInfo *) crHashtableSearch(stub.windowTable, (unsigned int) window); @@ -153,6 +153,11 @@ void APIENTRY crWindowVisibleRegion( GLint window, GLint cRects, void *pRects ) } } +void APIENTRY crVBoxTexPresent(GLuint texture, GLuint cfg, GLint xPos, GLint yPos, GLint cRects, const GLint *pRects) +{ + crError("not expected!"); +} + void APIENTRY crWindowShow( GLint window, GLint flag ) { WindowInfo *winInfo = (WindowInfo *) diff --git a/src/VBox/Additions/common/crOpenGL/stub.h b/src/VBox/Additions/common/crOpenGL/stub.h index 61ca2b92..f398e134 100644 --- a/src/VBox/Additions/common/crOpenGL/stub.h +++ b/src/VBox/Additions/common/crOpenGL/stub.h @@ -146,8 +146,7 @@ struct context_info_t Bool direct; GLXContext glxContext; CRHashTable *pGLXPixmapsHash; - Bool damageInitFailed; - Display *damageDpy; /* second display connection to read xdamage extension data */ + Bool damageQueryFailed; int damageEventsBase; #endif }; @@ -222,7 +221,6 @@ typedef struct { int trackWindowVisibleRgn; char *spu_dir; int force_pbuffers; - int viewportHack; /* thread safety stuff */ GLboolean threadSafe; @@ -343,6 +341,9 @@ extern ContextInfo *stubNewContext( const char *dpyName, GLint visBits, ContextT , struct VBOXUHGSMI *pHgsmi #endif ); +extern void stubConChromiumParameteriCR(GLint con, GLenum param, GLint value); +extern GLboolean stubCtxCreate(ContextInfo *context); +extern GLboolean stubCtxCheckCreate(ContextInfo *context); extern void stubDestroyContext( unsigned long contextId ); extern GLboolean stubMakeCurrent( WindowInfo *window, ContextInfo *context ); extern GLint stubNewWindow( const char *dpyName, GLint visBits ); @@ -354,6 +355,7 @@ extern GLboolean stubIsWindowVisible(WindowInfo *win); extern bool stubInit(void); extern void stubForcedFlush(GLint con); +extern void stubConFlush(GLint con); extern void APIENTRY stub_GetChromiumParametervCR( GLenum target, GLuint index, GLenum type, GLsizei count, GLvoid *values ); extern void APIENTRY glBoundsInfoCR(const CRrecti *, const GLbyte *, GLint, GLint); diff --git a/src/VBox/Additions/common/crOpenGL/wgl.c b/src/VBox/Additions/common/crOpenGL/wgl.c index 71166343..a2ccf751 100644 --- a/src/VBox/Additions/common/crOpenGL/wgl.c +++ b/src/VBox/Additions/common/crOpenGL/wgl.c @@ -16,6 +16,8 @@ #include <windows.h> #include <stdio.h> +#include <iprt/cdefs.h> + /* Currently host part will misbehave re-creating context with proper visual bits * if contexts with alternative visual bits is requested. * For now we just report a superset of all visual bits to avoid that. @@ -65,7 +67,7 @@ static GLuint ComputeVisBits( HDC hdc ) } #endif -int WINAPI wglChoosePixelFormat_prox( HDC hdc, CONST PIXELFORMATDESCRIPTOR *pfd ) +DECLEXPORT(int) WINAPI wglChoosePixelFormat_prox( HDC hdc, CONST PIXELFORMATDESCRIPTOR *pfd ) { DWORD okayFlags; @@ -162,7 +164,7 @@ int WINAPI wglChoosePixelFormat_prox( HDC hdc, CONST PIXELFORMATDESCRIPTOR *pfd return 1; } -BOOL WINAPI wglSetPixelFormat_prox( HDC hdc, int pixelFormat, +DECLEXPORT(BOOL) WINAPI wglSetPixelFormat_prox( HDC hdc, int pixelFormat, CONST PIXELFORMATDESCRIPTOR *pdf ) { CR_DDI_PROLOGUE(); @@ -174,14 +176,14 @@ BOOL WINAPI wglSetPixelFormat_prox( HDC hdc, int pixelFormat, return 1; } -BOOL WINAPI wglDeleteContext_prox( HGLRC hglrc ) +DECLEXPORT(BOOL) WINAPI wglDeleteContext_prox( HGLRC hglrc ) { CR_DDI_PROLOGUE(); stubDestroyContext( (unsigned long) hglrc ); return 1; } -BOOL WINAPI wglMakeCurrent_prox( HDC hdc, HGLRC hglrc ) +DECLEXPORT(BOOL) WINAPI wglMakeCurrent_prox( HDC hdc, HGLRC hglrc ) { ContextInfo *context; WindowInfo *window; @@ -208,14 +210,14 @@ BOOL WINAPI wglMakeCurrent_prox( HDC hdc, HGLRC hglrc ) return ret; } -HGLRC WINAPI wglGetCurrentContext_prox( void ) +DECLEXPORT(HGLRC) WINAPI wglGetCurrentContext_prox( void ) { ContextInfo *context = stubGetCurrentContext(); CR_DDI_PROLOGUE(); return (HGLRC) (context ? context->id : 0); } -HDC WINAPI wglGetCurrentDC_prox( void ) +DECLEXPORT(HDC) WINAPI wglGetCurrentDC_prox( void ) { ContextInfo *context = stubGetCurrentContext(); CR_DDI_PROLOGUE(); @@ -225,14 +227,14 @@ HDC WINAPI wglGetCurrentDC_prox( void ) return (HDC) NULL; } -int WINAPI wglGetPixelFormat_prox( HDC hdc ) +DECLEXPORT(int) WINAPI wglGetPixelFormat_prox( HDC hdc ) { CR_DDI_PROLOGUE(); /* this is what we call our generic pixelformat, regardless of the HDC */ return 1; } -int WINAPI wglDescribePixelFormat_prox( HDC hdc, int pixelFormat, UINT nBytes, +DECLEXPORT(int) WINAPI wglDescribePixelFormat_prox( HDC hdc, int pixelFormat, UINT nBytes, LPPIXELFORMATDESCRIPTOR pfd ) { CR_DDI_PROLOGUE(); @@ -286,15 +288,37 @@ int WINAPI wglDescribePixelFormat_prox( HDC hdc, int pixelFormat, UINT nBytes, return 1; } -BOOL WINAPI wglShareLists_prox( HGLRC hglrc1, HGLRC hglrc2 ) +DECLEXPORT(BOOL) WINAPI wglShareLists_prox( HGLRC hglrc1, HGLRC hglrc2 ) { CR_DDI_PROLOGUE(); crWarning( "wglShareLists: unsupported" ); return 0; } +DECLEXPORT(void) WINAPI VBoxCtxChromiumParameteriCR(HGLRC hglrc, GLenum param, GLint value) +{ + ContextInfo *context; + + CR_DDI_PROLOGUE(); + +// crHashtableLock(stub.windowTable); + crHashtableLock(stub.contextTable); + + context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc); + + if (context) + { + stubCtxCheckCreate(context); + stubConChromiumParameteriCR(CR_CTX_CON(context), param, value); + } + else + crWarning("invalid context %#x", hglrc); -HGLRC WINAPI VBoxCreateContext( HDC hdc, struct VBOXUHGSMI *pHgsmi ) + crHashtableUnlock(stub.contextTable); +// crHashtableUnlock(stub.windowTable); +} + +DECLEXPORT(HGLRC) WINAPI VBoxCreateContext( HDC hdc, struct VBOXUHGSMI *pHgsmi ) { char dpyName[MAX_DPY_NAME]; ContextInfo *context; @@ -324,12 +348,97 @@ HGLRC WINAPI VBoxCreateContext( HDC hdc, struct VBOXUHGSMI *pHgsmi ) return (HGLRC) context->id; } -HGLRC WINAPI wglCreateContext_prox( HDC hdc ) +DECLEXPORT(GLint) WINAPI VBoxGetWindowId( HDC hdc ) +{ + WindowInfo *window; + GLint winid = 0; + + CR_DDI_PROLOGUE(); + + crHashtableLock(stub.windowTable); + + window = stubGetWindowInfo(hdc); + if (!window) + { + crWarning("stubGetWindowInfo: window not found!"); + goto end; + } + if (!window->spuWindow) + { + crWarning("stubGetWindowInfo: window is null!"); + goto end; + } + + winid = window->spuWindow; + +end: + crHashtableUnlock(stub.windowTable); + return winid; +} + +DECLEXPORT(GLint) WINAPI VBoxGetContextId( HGLRC hglrc ) +{ + ContextInfo *context; + GLint ctxid = 0; + + CR_DDI_PROLOGUE(); + +// crHashtableLock(stub.windowTable); + crHashtableLock(stub.contextTable); + + context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc); + if (!context) + { + crWarning("crHashtableSearch: context not found!"); + goto end; + } + + if (context->type != CHROMIUM) + { + crWarning("unexpected context type %d", context->type); + goto end; + } + + if (context->spuContext <= 0) + { + crWarning("no spuSontext defined"); + goto end; + } + + ctxid = context->spuContext; + +end: + crHashtableUnlock(stub.contextTable); + return ctxid; +} + + +DECLEXPORT(HGLRC) WINAPI wglCreateContext_prox( HDC hdc ) { return VBoxCreateContext(hdc, NULL); } -BOOL WINAPI +DECLEXPORT(void) WINAPI VBoxFlushToHost ( HGLRC hglrc ) +{ + ContextInfo *context; + + CR_DDI_PROLOGUE(); + +// crHashtableLock(stub.windowTable); + crHashtableLock(stub.contextTable); + + context = (ContextInfo *) crHashtableSearch(stub.contextTable, (unsigned long) hglrc); + + if (context) + stubConFlush(CR_CTX_CON(context)); + else + crWarning("invalid context %#x", hglrc); + + crHashtableUnlock(stub.contextTable); +// crHashtableUnlock(stub.windowTable); +} + +DECLEXPORT(BOOL) WINAPI wglSwapBuffers_prox( HDC hdc ) { WindowInfo *window = stubGetWindowInfo(hdc); @@ -338,14 +447,14 @@ wglSwapBuffers_prox( HDC hdc ) return 1; } -BOOL WINAPI wglCopyContext_prox( HGLRC src, HGLRC dst, UINT mask ) +DECLEXPORT(BOOL) WINAPI wglCopyContext_prox( HGLRC src, HGLRC dst, UINT mask ) { CR_DDI_PROLOGUE(); crWarning( "wglCopyContext: unsupported" ); return 0; } -HGLRC WINAPI wglCreateLayerContext_prox( HDC hdc, int layerPlane ) +DECLEXPORT(HGLRC) WINAPI wglCreateLayerContext_prox( HDC hdc, int layerPlane ) { CR_DDI_PROLOGUE(); stubInit(); @@ -353,27 +462,27 @@ HGLRC WINAPI wglCreateLayerContext_prox( HDC hdc, int layerPlane ) return 0; } -PROC WINAPI wglGetProcAddress_prox( LPCSTR name ) +DECLEXPORT(PROC) WINAPI wglGetProcAddress_prox( LPCSTR name ) { CR_DDI_PROLOGUE(); return (PROC) crGetProcAddress( name ); } -BOOL WINAPI wglUseFontBitmapsA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase ) +DECLEXPORT(BOOL) WINAPI wglUseFontBitmapsA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase ) { CR_DDI_PROLOGUE(); crWarning( "wglUseFontBitmapsA: unsupported" ); return 0; } -BOOL WINAPI wglUseFontBitmapsW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase ) +DECLEXPORT(BOOL) WINAPI wglUseFontBitmapsW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase ) { CR_DDI_PROLOGUE(); crWarning( "wglUseFontBitmapsW: unsupported" ); return 0; } -BOOL WINAPI wglDescribeLayerPlane_prox( HDC hdc, int pixelFormat, int layerPlane, +DECLEXPORT(BOOL) WINAPI wglDescribeLayerPlane_prox( HDC hdc, int pixelFormat, int layerPlane, UINT nBytes, LPLAYERPLANEDESCRIPTOR lpd ) { CR_DDI_PROLOGUE(); @@ -381,7 +490,7 @@ BOOL WINAPI wglDescribeLayerPlane_prox( HDC hdc, int pixelFormat, int layerPlane return 0; } -int WINAPI wglSetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, +DECLEXPORT(int) WINAPI wglSetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, int entries, CONST COLORREF *cr ) { CR_DDI_PROLOGUE(); @@ -389,7 +498,7 @@ int WINAPI wglSetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, return 0; } -int WINAPI wglGetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, +DECLEXPORT(int) WINAPI wglGetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, int entries, COLORREF *cr ) { CR_DDI_PROLOGUE(); @@ -397,21 +506,21 @@ int WINAPI wglGetLayerPaletteEntries_prox( HDC hdc, int layerPlane, int start, return 0; } -BOOL WINAPI wglRealizeLayerPalette_prox( HDC hdc, int layerPlane, BOOL realize ) +DECLEXPORT(BOOL) WINAPI wglRealizeLayerPalette_prox( HDC hdc, int layerPlane, BOOL realize ) { CR_DDI_PROLOGUE(); crWarning( "wglRealizeLayerPalette: unsupported" ); return 0; } -DWORD WINAPI wglSwapMultipleBuffers_prox( UINT a, CONST void *b ) +DECLEXPORT(DWORD) WINAPI wglSwapMultipleBuffers_prox( UINT a, CONST void *b ) { CR_DDI_PROLOGUE(); crWarning( "wglSwapMultipleBuffer: unsupported" ); return 0; } -BOOL WINAPI wglUseFontOutlinesA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase, +DECLEXPORT(BOOL) WINAPI wglUseFontOutlinesA_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT gmf ) { @@ -420,7 +529,7 @@ BOOL WINAPI wglUseFontOutlinesA_prox( HDC hdc, DWORD first, DWORD count, DWORD l return 0; } -BOOL WINAPI wglUseFontOutlinesW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase, +DECLEXPORT(BOOL) WINAPI wglUseFontOutlinesW_prox( HDC hdc, DWORD first, DWORD count, DWORD listBase, FLOAT deviation, FLOAT extrusion, int format, LPGLYPHMETRICSFLOAT gmf ) { @@ -429,7 +538,7 @@ BOOL WINAPI wglUseFontOutlinesW_prox( HDC hdc, DWORD first, DWORD count, DWORD l return 0; } -BOOL WINAPI wglSwapLayerBuffers_prox( HDC hdc, UINT planes ) +DECLEXPORT(BOOL) WINAPI wglSwapLayerBuffers_prox( HDC hdc, UINT planes ) { CR_DDI_PROLOGUE(); if (planes == WGL_SWAP_MAIN_PLANE) @@ -443,7 +552,7 @@ BOOL WINAPI wglSwapLayerBuffers_prox( HDC hdc, UINT planes ) } } -BOOL WINAPI wglChoosePixelFormatEXT_prox +DECLEXPORT(BOOL) WINAPI wglChoosePixelFormatEXT_prox (HDC hdc, const int *piAttributes, const FLOAT *pfAttributes, UINT nMaxFormats, int *piFormats, UINT *nNumFormats) { int *pi; @@ -565,7 +674,7 @@ BOOL WINAPI wglChoosePixelFormatEXT_prox return 1; } -BOOL WINAPI wglGetPixelFormatAttribivEXT_prox +DECLEXPORT(BOOL) WINAPI wglGetPixelFormatAttribivEXT_prox (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, int *pValues) { UINT i; @@ -700,7 +809,7 @@ BOOL WINAPI wglGetPixelFormatAttribivEXT_prox return 1; } -BOOL WINAPI wglGetPixelFormatAttribfvEXT_prox +DECLEXPORT(BOOL) WINAPI wglGetPixelFormatAttribfvEXT_prox (HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, int *piAttributes, float *pValues) { UINT i; @@ -835,13 +944,13 @@ BOOL WINAPI wglGetPixelFormatAttribfvEXT_prox return 1; } -BOOL WINAPI wglSwapIntervalEXT_prox(int interval) +DECLEXPORT(BOOL) WINAPI wglSwapIntervalEXT_prox(int interval) { CR_DDI_PROLOGUE(); return TRUE; } -int WINAPI wglGetSwapIntervalEXT_prox() +DECLEXPORT(int) WINAPI wglGetSwapIntervalEXT_prox() { CR_DDI_PROLOGUE(); return 1; @@ -849,13 +958,13 @@ int WINAPI wglGetSwapIntervalEXT_prox() static GLubyte *gsz_wgl_extensions = "WGL_EXT_pixel_format WGL_ARB_pixel_format WGL_ARB_multisample"; -const GLubyte * WINAPI wglGetExtensionsStringEXT_prox() +DECLEXPORT(const GLubyte *) WINAPI wglGetExtensionsStringEXT_prox() { CR_DDI_PROLOGUE(); return gsz_wgl_extensions; } -const GLubyte * WINAPI wglGetExtensionsStringARB_prox(HDC hdc) +DECLEXPORT(const GLubyte *) WINAPI wglGetExtensionsStringARB_prox(HDC hdc) { CR_DDI_PROLOGUE(); (void) hdc; diff --git a/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py b/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py index 5de8cee9..fc167bed 100644 --- a/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py +++ b/src/VBox/Additions/common/crOpenGL/windows_getprocaddress.py @@ -37,7 +37,7 @@ static struct name_address functions[] = { """ -keys = apiutil.GetAllFunctions(sys.argv[1]+"/APIspec.txt") +keys = apiutil.GetAllFunctionsAndOmittedAliases(sys.argv[1]+"/APIspec.txt") for func_name in keys: if "Chromium" == apiutil.Category(func_name): continue @@ -48,9 +48,16 @@ for func_name in keys: if "GL_chromium" == apiutil.Category(func_name): pass #continue + # alias is the function we're aliasing + proc_name = func_name + if "omit" in apiutil.ChromiumProps(func_name): + alias = apiutil.Alias(func_name) + if alias: + proc_name = alias + wrap = apiutil.GetCategoryWrapper(func_name) name = "gl" + func_name - address = "cr_gl" + func_name + address = "cr_gl" + proc_name if wrap: print '#ifdef CR_%s' % wrap print '\t{ "%s", (CR_PROC) %s },' % (name, address) @@ -136,18 +143,7 @@ CR_PROC CR_APIENTRY crGetProcAddress( const char *name ) if (!crStrcmp( name, "wglSwapIntervalEXT" )) return (CR_PROC) wglSwapIntervalEXT; - /* this is needed for VSG Open Inventor stuff. - * @todo: make all these auto-generated!!! */ - if (!crStrcmp( name, "glBeginQuery" )) return (CR_PROC) cr_glBeginQueryARB; - if (!crStrcmp( name, "glDeleteQueries" )) return (CR_PROC) cr_glDeleteQueriesARB; - if (!crStrcmp( name, "glEndQuery" )) return (CR_PROC) cr_glEndQueryARB; - if (!crStrcmp( name, "glGenQueries" )) return (CR_PROC) cr_glGenQueriesARB; - if (!crStrcmp( name, "glGetQueryObjectiv" )) return (CR_PROC) cr_glGetQueryObjectivARB; - if (!crStrcmp( name, "glGetQueryObjectuiv" )) return (CR_PROC) cr_glGetQueryObjectuivARB; - if (!crStrcmp( name, "glGetQueryiv" )) return (CR_PROC) cr_glGetQueryivARB; - if (!crStrcmp( name, "glIsQuery" )) return (CR_PROC) cr_glIsQueryARB; - - crWarning("Returning GetProcAddress:NULL for %s", name); + crDebug("Returning GetProcAddress:NULL for %s", name); return NULL; } diff --git a/src/VBox/Additions/common/pam/pam_vbox.cpp b/src/VBox/Additions/common/pam/pam_vbox.cpp index b8d85fda..4852f033 100644 --- a/src/VBox/Additions/common/pam/pam_vbox.cpp +++ b/src/VBox/Additions/common/pam/pam_vbox.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2011 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Additions/common/testcase/tstPageFusion.cpp b/src/VBox/Additions/common/testcase/tstPageFusion.cpp index 321d3bff..74a010f2 100644 --- a/src/VBox/Additions/common/testcase/tstPageFusion.cpp +++ b/src/VBox/Additions/common/testcase/tstPageFusion.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; |