VBoxControl.cpp revision 4c953c3c459d80bc3e31a0a65a9dc0463f340e6b
/* $Id$ */
/** @file
* VBoxControl - Guest Additions Command Line Management Interface.
*/
/*
* Copyright (C) 2008-2010 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
#include <iprt/buildconfig.h>
#include <iprt/initterm.h>
#include <VBox/VBoxGuestLib.h>
#ifdef RT_OS_WINDOWS
# include <Windows.h>
#endif
#ifdef VBOX_WITH_GUEST_PROPS
#endif
/*******************************************************************************
* Global Variables *
*******************************************************************************/
/** The program name (derived from argv[0]). */
char const *g_pszProgName = "";
/** The current verbosity level. */
int g_cVerbosity = 0;
/**
* Displays the program usage message.
*
* @param u64Which
*
* @{
*/
/** Helper function */
{
/* Allow for up to 15 characters command name length (VBoxControl.exe) with
* perfect column alignment. Beyond that there's at least one space between
* the command if there are command line parameters. */
}
/** Enumerate the different parts of the usage we might want to print out */
enum g_eUsage
{
#ifdef RT_OS_LINUX
#endif
#ifdef VBOX_WITH_GUEST_PROPS
#endif
};
{
RTPrintf("Usage:\n\n");
RTPrintf("\n");
/* Exclude the Windows bits from the test version. Anyone who needs to test
* them can fix this. */
#if defined(RT_OS_LINUX) && !defined(VBOX_CONTROL_TEST)
#endif
#ifdef VBOX_WITH_GUEST_PROPS
{
doUsage("[-timestamp <last timestamp>]");
doUsage("[-timeout <timeout in ms>");
}
#endif
}
/** @} */
/**
* Displays an error message.
*
* @param pszFormat The message text.
* @param ... Format arguments.
*/
static void VBoxControlError(const char *pszFormat, ...)
{
// RTStrmPrintf(g_pStdErr, "%s: error: ", g_pszProgName);
}
#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
LONG (WINAPI * gpfnChangeDisplaySettingsEx)(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam);
{
unsigned i;
for (i = 0; i < nRects; i++)
{
{
return i;
}
}
return ~0;
}
{
unsigned i;
for (i = 0; i < nRects; i++)
{
{
return i;
}
}
return ~0;
}
{
unsigned i;
for (i = 0; i < nRects; i++)
{
{
return i;
}
}
return ~0;
}
{
unsigned i;
for (i = 0; i < nRects; i++)
{
{
return i;
}
}
return ~0;
}
void resizeRect(RECTL *paRects, unsigned nRects, unsigned iPrimary, unsigned iResized, int NewWidth, int NewHeight)
{
paNewRects[iResized].bottom += NewHeight - (paNewRects[iResized].bottom - paNewRects[iResized].top);
/* Verify all pairs of originally adjacent rectangles for all 4 directions.
* If the pair has a "good" delta (that is the first rectangle intersects the second)
* at a direction and the second rectangle is not primary one (which can not be moved),
* move the second rectangle to make it adjacent to the first one.
*/
/* X positive. */
unsigned iRect;
{
/* Find the next adjacent original rect in x positive direction. */
{
continue;
}
/* Check whether there is an X intesection between these adjacent rects in the new rectangles
* and fix the intersection if delta is "good".
*/
if (delta > 0)
{
Log(("XP intersection right %d left %d, diff %d\n",
delta));
}
}
/* X negative. */
{
/* Find the next adjacent original rect in x negative direction. */
{
continue;
}
/* Check whether there is an X intesection between these adjacent rects in the new rectangles
* and fix the intersection if delta is "good".
*/
if (delta < 0)
{
Log(("XN intersection left %d right %d, diff %d\n",
delta));
}
}
/* Y positive (in the computer sence, top->down). */
{
/* Find the next adjacent original rect in y positive direction. */
{
continue;
}
/* Check whether there is an Y intesection between these adjacent rects in the new rectangles
* and fix the intersection if delta is "good".
*/
if (delta > 0)
{
Log(("YP intersection bottom %d top %d, diff %d\n",
delta));
}
}
/* Y negative (in the computer sence, down->top). */
{
/* Find the next adjacent original rect in x negative direction. */
{
continue;
}
/* Check whether there is an Y intesection between these adjacent rects in the new rectangles
* and fix the intersection if delta is "good".
*/
if (delta < 0)
{
Log(("YN intersection top %d bottom %d, diff %d\n",
delta));
}
}
return;
}
/* Returns TRUE to try again. */
{
/* Find out how many display devices the system has */
DWORD NumDevices = 0;
DWORD i = 0;
{
{
NumDevices++;
}
{
NumDevices++;
}
i++;
}
{
return FALSE;
}
/* Fetch information about current devices and modes. */
DWORD DevPrimaryNum = 0;
i = 0;
{
{
bFetchDevice = TRUE;
}
{
bFetchDevice = TRUE;
}
if (bFetchDevice)
{
if (DevNum >= NumDevices)
{
return FALSE;
}
{
return FALSE;
}
Log(("%dx%d at %d,%d\n",
DevNum++;
}
i++;
}
if (Width == 0)
{
}
if (Height == 0)
{
}
/* Check whether a mode reset or a change is requested. */
if ( !fModeReset
{
Log(("VBoxDisplayThread : already at desired resolution.\n"));
return FALSE;
}
#ifdef Log
for (i = 0; i < NumDevices; i++)
{
Log(("[%d]: %d,%d %dx%d\n",
}
#endif /* Log */
/* Without this, Windows will not ask the miniport for its
* mode table but uses an internal cache instead.
*/
/* Assign the new rectangles to displays. */
for (i = 0; i < NumDevices; i++)
{
if ( i == Id
&& BitsPerPixel != 0)
{
}
}
/* A second call to ChangeDisplaySettings updates the monitor. */
{
/* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
return FALSE;
}
/* Retry the request. */
return TRUE;
}
{
{
return 1;
}
if (argc == 4)
{
}
if (hUser)
{
*(uintptr_t *)&gpfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
{
/* The screen index is 0 based in the ResizeDisplayDevice call. */
/* Horizontal resolution must be a multiple of 8, round down. */
xres &= ~0x7;
RTPrintf("done.\n");
}
else VBoxControlError("Error retrieving API for display change!");
}
else VBoxControlError("Error retrieving handle to user32.dll!");
return 0;
}
{
HKEY hkeyDeviceMap = 0;
status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap);
{
VBoxControlError("Error opening video device map registry key!\n");
return 0;
}
char szVideoLocation[256];
szVideoLocation[0] = 0;
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.
*/
if ( (status == ERROR_SUCCESS)
{
/* open that branch */
status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0, KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo);
}
else
{
}
return hkeyVideo;
}
{
if (hkeyVideo)
{
/* query the actual value */
status = RegQueryValueExA(hkeyVideo, "EnableVideoAccel", NULL, &dwKeyType, (LPBYTE)&fAcceleration, &len);
if (status != ERROR_SUCCESS)
RTPrintf("Video acceleration: default\n");
else
}
return 0;
}
{
/* must have exactly one argument: the new offset */
if ( (argc != 1)
{
return 1;
}
hkeyVideo = getVideoKey(true);
if (hkeyVideo)
{
int fAccel = 0;
fAccel = 1;
/* set a new value */
status = RegSetValueExA(hkeyVideo, "EnableVideoAccel", 0, REG_DWORD, (LPBYTE)&fAccel, sizeof(fAccel));
if (status != ERROR_SUCCESS)
{
}
}
return 0;
}
#define MAX_CUSTOM_MODES 128
/* the table of custom modes */
struct
{
} customModes[MAX_CUSTOM_MODES] = {0};
{
int curMode = 0;
/* null out the table */
do
{
char valueName[20];
if (status != ERROR_SUCCESS)
break;
if (status != ERROR_SUCCESS)
break;
if (status != ERROR_SUCCESS)
break;
/* check if the mode is OK */
&& ( (bpp != 16)
|| (bpp != 24)
|| (bpp != 32)))
break;
/* add mode to table */
++curMode;
if (curMode >= MAX_CUSTOM_MODES)
break;
} while(1);
}
{
int tableIndex = 0;
int modeIndex = 0;
/* first remove all values */
for (int i = 0; i < MAX_CUSTOM_MODES; i++)
{
char valueName[20];
}
do
{
if (tableIndex >= MAX_CUSTOM_MODES)
break;
/* is the table entry present? */
{
tableIndex++;
continue;
}
RTPrintf("writing mode %d (%dx%dx%d)\n", modeIndex, customModes[tableIndex].xres, customModes[tableIndex].yres, customModes[tableIndex].bpp);
char valueName[20];
modeIndex++;
tableIndex++;
} while(1);
}
{
if (argc != 0)
{
return 1;
}
if (hkeyVideo)
{
for (int i = 0; i < (sizeof(customModes) / sizeof(customModes[0])); i++)
{
if ( !customModes[i].xres
|| !customModes[i].yres
|| !customModes[i].bpp)
continue;
RTPrintf("Mode: %d x %d x %d\n",
}
}
return 0;
}
{
if (argc != 3)
{
return 1;
}
/** @todo better check including xres mod 8 = 0! */
&& ( (bpp != 16)
|| (bpp != 24)
|| (bpp != 32)))
{
VBoxControlError("Error: invalid mode specified!\n");
return 1;
}
if (hkeyVideo)
{
int i;
int fModeExists = 0;
for (i = 0; i < MAX_CUSTOM_MODES; i++)
{
/* mode exists? */
)
{
fModeExists = 1;
}
}
if (!fModeExists)
{
for (i = 0; i < MAX_CUSTOM_MODES; i++)
{
/* item free? */
if (!customModes[i].xres)
{
break;
}
}
}
}
return 0;
}
{
if (argc != 3)
{
return 1;
}
if (hkeyVideo)
{
for (int i = 0; i < MAX_CUSTOM_MODES; i++)
{
/* correct item? */
{
RTPrintf("found mode at index %d\n", i);
RT_ZERO(customModes[i]);
break;
}
}
}
return 0;
}
#endif /* RT_OS_WINDOWS */
#ifdef VBOX_WITH_GUEST_PROPS
/**
* Retrieves a value from the guest property store.
* This is accessed through the "VBoxGuestPropSvc" HGCM service.
*
* @returns 0 on success, 1 on failure
* @note see the command line API description for parameters
*/
{
using namespace guestProp;
bool verbose = false;
verbose = true;
else if (argc != 1)
{
return 1;
}
uint32_t u32ClientId = 0;
int rc = VINF_SUCCESS;
if (!RT_SUCCESS(rc))
/*
* Here we actually retrieve the value from the host.
*/
uint64_t u64Timestamp = 0;
/* The buffer for storing the data and its initial size. We leave a bit
* of space here in case the maximum values are raised. */
if (RT_SUCCESS(rc))
{
/* Because there is a race condition between our reading the size of a
* property and the guest updating it, we loop a few times here and
* hope. Actually this should never go wrong, as we are generous
* enough with buffer space. */
bool finish = false;
for (unsigned i = 0; (i < 10) && !finish; ++i)
{
{
rc = VERR_NO_MEMORY;
VBoxControlError("Out of memory\n");
}
else
{
&cbBuf);
}
if (VERR_BUFFER_OVERFLOW == rc)
/* Leave a bit of extra space to be safe */
cbBuf += 1024;
else
finish = true;
}
if (VERR_TOO_MUCH_DATA == rc)
VBoxControlError("Temporarily unable to retrieve the property\n");
}
/*
* And display it on the guest console.
*/
if (VERR_NOT_FOUND == rc)
RTPrintf("No value set!\n");
else if (RT_SUCCESS(rc))
{
if (verbose)
{
}
}
if (u32ClientId != 0)
}
/**
* Writes a value to the guest property store.
* This is accessed through the "VBoxGuestPropSvc" HGCM service.
*
* @returns 0 on success, 1 on failure
* @note see the command line API description for parameters
*/
{
/*
* Check the syntax. We can deduce the correct syntax from the number of
* arguments.
*/
bool usageOK = true;
if (2 == argc)
{
}
else if (3 == argc)
usageOK = false;
else if (4 == argc)
{
usageOK = false;
}
else if (argc != 1)
usageOK = false;
if (!usageOK)
{
return 1;
}
/* This is always needed. */
/*
* Do the actual setting.
*/
uint32_t u32ClientId = 0;
int rc = VINF_SUCCESS;
if (!RT_SUCCESS(rc))
if (RT_SUCCESS(rc))
{
else
if (!RT_SUCCESS(rc))
}
if (u32ClientId != 0)
}
/**
* Enumerates the properties in the guest property store.
* This is accessed through the "VBoxGuestPropSvc" HGCM service.
*
* @returns 0 on success, 1 on failure
* @note see the command line API description for parameters
*/
{
/*
* Check the syntax. We can deduce the correct syntax from the number of
* arguments.
*/
char const * const *papszPatterns = NULL;
if ( argc > 1
{
}
else if (argc != 0)
{
return 1;
}
/*
* Do the actual enumeration.
*/
uint32_t u32ClientId = 0;
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
{
RTPrintf("Name: %s, value: %s, timestamp: %lld, flags: %s\n",
if (RT_FAILURE(rc))
}
}
else if (VERR_NOT_FOUND == rc)
RTPrintf("No properties found.\n");
else
}
else
}
/**
* Waits for notifications of changes to guest properties.
* This is accessed through the "VBoxGuestPropSvc" HGCM service.
*
* @returns 0 on success, 1 on failure
* @note see the command line API description for parameters
*/
{
using namespace guestProp;
/*
* Handle arguments
*/
const char *pszPatterns = NULL;
uint64_t u64TimestampIn = 0;
bool usageOK = true;
if (argc < 1)
usageOK = false;
pszPatterns = argv[0];
{
{
if ( i + 1 >= argc
!= VINF_SUCCESS
)
usageOK = false;
else
++i;
}
{
if ( i + 1 >= argc
!= VINF_SUCCESS
)
usageOK = false;
else
++i;
}
else
usageOK = false;
}
if (!usageOK)
{
return 1;
}
/*
* Connect to the service
*/
uint32_t u32ClientId = 0;
int rc = VINF_SUCCESS;
if (!RT_SUCCESS(rc))
/*
* Retrieve the notification from the host
*/
uint64_t u64TimestampOut = 0;
/* The buffer for storing the data and its initial size. We leave a bit
* of space here in case the maximum values are raised. */
/* Because there is a race condition between our reading the size of a
* property and the guest updating it, we loop a few times here and
* hope. Actually this should never go wrong, as we are generous
* enough with buffer space. */
bool finish = false;
for (unsigned i = 0;
++i)
{
{
rc = VERR_NO_MEMORY;
VBoxControlError("Out of memory\n");
}
else
{
}
if (VERR_BUFFER_OVERFLOW == rc)
/* Leave a bit of extra space to be safe */
cbBuf += 1024;
else
finish = true;
if (rc == VERR_TOO_MUCH_DATA)
VBoxControlError("Temporarily unable to get a notification\n");
else if (rc == VERR_INTERRUPTED)
VBoxControlError("The request timed out or was interrupted\n");
#ifndef RT_OS_WINDOWS /* Windows guests do not do this right */
#endif
}
/*
* And display it on the guest console.
*/
if (VERR_NOT_FOUND == rc)
RTPrintf("No value set!\n");
else if (rc == VERR_BUFFER_OVERFLOW)
RTPrintf("Internal error: unable to determine the size of the data!\n");
else if (RT_SUCCESS(rc))
{
}
if (u32ClientId != 0)
}
/**
* Access the guest property store through the "VBoxGuestPropSvc" HGCM
* service.
*
* @returns 0 on success, 1 on failure
* @note see the command line API description for parameters
*/
{
if (0 == argc)
{
return 1;
}
/* else */
return 1;
}
#endif
/** command handler type */
typedef FNHANDLER *PFNHANDLER;
/** The table of all registered command handlers. */
struct COMMANDHANDLER
{
const char *command;
} g_commandHandlers[] =
{
#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
{ "getvideoacceleration", handleGetVideoAcceleration },
{ "setvideoacceleration", handleSetVideoAcceleration },
{ "listcustommodes", handleListCustomModes },
{ "addcustommode", handleAddCustomMode },
{ "removecustommode", handleRemoveCustomMode },
{ "setvideomode", handleSetVideoMode },
#endif
#ifdef VBOX_WITH_GUEST_PROPS
{ "guestproperty", handleGuestProperty },
#endif
};
/** Main function */
{
/** The application's global return code */
int rc = 0;
/** An IPRT return code for local use */
int rrc = VINF_SUCCESS;
/** The index of the command line argument we are currently processing */
int iArg = 1;
/** Should we show the logo text? */
bool showlogo = true;
/** Should we print the usage after the logo? For the -help switch. */
bool dohelp = false;
/** Will we be executing a command or just printing information? */
bool onlyinfo = false;
/*
* Start by handling command line switches
*/
/** Are we finished with handling switches? */
bool done = false;
{
)
{
/* Print version number, and do nothing else. */
onlyinfo = true;
showlogo = false;
done = true;
}
showlogo = false;
{
onlyinfo = true;
dohelp = true;
done = true;
}
else
/* We have found an argument which isn't a switch. Exit to the
* command processing bit. */
done = true;
if (!done)
++iArg;
}
/*
* Find the application name, show our logo if the user hasn't suppressed it,
* and show the usage if the user asked us to
*/
if (showlogo)
RTPrintf("VirtualBox Guest Additions Command Line Management Interface Version "
VBOX_VERSION_STRING "\n"
"(C) 2008-2010 Sun Microsystems, Inc.\n"
"All rights reserved.\n\n");
if (dohelp)
usage();
/*
* Do global initialisation for the programme if we will be handling a command
*/
if (!onlyinfo)
{
rrc = RTR3Init(); /** @todo r=bird: This ALWAYS goes first, the only exception is when you have to parse args to figure out which to call! */
if (!RT_SUCCESS(rrc))
{
rc = 1;
}
if (0 == rc)
{
if (!RT_SUCCESS(VbglR3Init()))
{
VBoxControlError("Could not contact the host system. Make sure that you are running this\n"
"application inside a VirtualBox guest system, and that you have sufficient\n"
"user permissions.\n");
rc = 1;
}
}
}
/*
* Now look for an actual command in the argument list and handle it.
*/
{
/*
* The input is in the guest OS'es codepage (NT guarantees ACP).
* For VBox we use UTF-8. For simplicity, just convert the argv[] array
* here.
*/
{
char *converted;
}
{
/** Is next parameter a known command? */
bool found = false;
/** And if so, what is its position in the table? */
unsigned index = 0;
&& !found
{
found = true;
else
++index;
}
if (found)
else
{
rc = 1;
usage();
}
}
else
{
/* The user didn't specify a command. */
rc = 1;
usage();
}
/*
* Free converted argument vector
*/
}
/*
* And exit, returning the status
*/
return rc;
}