VirtualBox

source: vbox/trunk/src/VBox/Additions/common/VBoxControl/VBoxControl.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 70.6 KB
RevLine 
[22562]1/* $Id: VBoxControl.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
[10099]2/** @file
[21218]3 * VBoxControl - Guest Additions Command Line Management Interface.
[10099]4 */
5
6/*
[98103]7 * Copyright (C) 2008-2023 Oracle and/or its affiliates.
[10099]8 *
[96407]9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
[10099]26 */
27
[57358]28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
[21218]32#include <iprt/alloca.h>
[25347]33#include <iprt/cpp/autores.h>
[22562]34#include <iprt/buildconfig.h>
[75469]35#include <iprt/ctype.h>
[76474]36#include <iprt/errcore.h>
[54580]37#include <iprt/getopt.h>
[21218]38#include <iprt/initterm.h>
[10797]39#include <iprt/mem.h>
[32201]40#include <iprt/message.h>
[21218]41#include <iprt/path.h>
[10099]42#include <iprt/string.h>
43#include <iprt/stream.h>
[70431]44#include <iprt/zip.h>
[10797]45#include <VBox/log.h>
[10099]46#include <VBox/version.h>
[21218]47#include <VBox/VBoxGuestLib.h>
[10797]48#ifdef RT_OS_WINDOWS
[62679]49# include <iprt/win/windows.h>
[10099]50#endif
[10797]51#ifdef VBOX_WITH_GUEST_PROPS
52# include <VBox/HostServices/GuestPropertySvc.h>
53#endif
[75467]54#ifdef VBOX_WITH_SHARED_FOLDERS
55# include <VBox/shflsvc.h>
56# ifdef RT_OS_OS2
[75469]57# define OS2EMX_PLAIN_CHAR
[75467]58# define INCL_ERRORS
59# define INCL_DOSFILEMGR
60# include <os2emx.h>
61# endif
62#endif
[44992]63#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
64# include <VBox/VBoxGuest.h>
[68651]65# include "../VBoxGuest/lib/VBoxGuestR3LibInternal.h" /* HACK ALERT! Using vbglR3DoIOCtl directly!! */
[44992]66#endif
[10099]67
[44992]68
[57358]69/*********************************************************************************************************************************
70* Global Variables *
71*********************************************************************************************************************************/
[10099]72/** The program name (derived from argv[0]). */
73char const *g_pszProgName = "";
74/** The current verbosity level. */
75int g_cVerbosity = 0;
76
77
[58144]78/** @name Displays the program usage message.
79 * @{
80 */
81
[10099]82/**
[58144]83 * Helper function that does indentation.
[10099]84 *
[58144]85 * @param pszLine Text.
86 * @param pszName Program name.
87 * @param pszCommand Command/option syntax.
[10859]88 */
[58144]89static void doUsage(char const *pszLine, char const *pszName = "", char const *pszCommand = "")
[10099]90{
[25873]91 /* Allow for up to 15 characters command name length (VBoxControl.exe) with
92 * perfect column alignment. Beyond that there's at least one space between
93 * the command if there are command line parameters. */
[58144]94 RTPrintf("%s %-*s%s%s\n",
95 pszName,
[58146]96 *pszLine ? 35 - strlen(pszName) : 1, pszCommand,
[58144]97 *pszLine ? " " : "", pszLine);
[10099]98}
99
100/** Enumerate the different parts of the usage we might want to print out */
[32205]101enum VBoxControlUsage
[10099]102{
[25875]103#ifdef RT_OS_WINDOWS
[10797]104 GET_VIDEO_ACCEL,
105 SET_VIDEO_ACCEL,
[46896]106 VIDEO_FLAGS,
[10797]107 LIST_CUST_MODES,
108 ADD_CUST_MODE,
109 REMOVE_CUST_MODE,
110 SET_VIDEO_MODE,
111#endif
112#ifdef VBOX_WITH_GUEST_PROPS
[10825]113 GUEST_PROP,
[10099]114#endif
[31002]115#ifdef VBOX_WITH_SHARED_FOLDERS
116 GUEST_SHAREDFOLDERS,
117#endif
[32575]118#if !defined(VBOX_CONTROL_TEST)
[32574]119 WRITE_CORE_DUMP,
[32575]120#endif
[54580]121 WRITE_LOG,
[32207]122 TAKE_SNAPSHOT,
123 SAVE_STATE,
124 SUSPEND,
125 POWER_OFF,
126 VERSION,
127 HELP,
[10099]128 USAGE_ALL = UINT32_MAX
129};
130
[54580]131static RTEXITCODE usage(enum VBoxControlUsage eWhich = USAGE_ALL)
[10099]132{
133 RTPrintf("Usage:\n\n");
[54520]134 doUsage("print version number and exit", g_pszProgName, "[-V|--version]");
[38750]135 doUsage("suppress the logo", g_pszProgName, "--nologo ...");
[25873]136 RTPrintf("\n");
[10099]137
[44992]138 /* Exclude the Windows bits from the test version. Anyone who needs to
139 test them can fix this. */
[25875]140#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
[44328]141 if (eWhich == GET_VIDEO_ACCEL || eWhich == USAGE_ALL)
[25873]142 doUsage("", g_pszProgName, "getvideoacceleration");
[44328]143 if (eWhich == SET_VIDEO_ACCEL || eWhich == USAGE_ALL)
[25873]144 doUsage("<on|off>", g_pszProgName, "setvideoacceleration");
[46896]145 if (eWhich == VIDEO_FLAGS || eWhich == USAGE_ALL)
146 doUsage("<get|set|clear|delete> [hex mask]", g_pszProgName, "videoflags");
[44328]147 if (eWhich == LIST_CUST_MODES || eWhich == USAGE_ALL)
[25873]148 doUsage("", g_pszProgName, "listcustommodes");
[44328]149 if (eWhich == ADD_CUST_MODE || eWhich == USAGE_ALL)
[25873]150 doUsage("<width> <height> <bpp>", g_pszProgName, "addcustommode");
[44328]151 if (eWhich == REMOVE_CUST_MODE || eWhich == USAGE_ALL)
[25873]152 doUsage("<width> <height> <bpp>", g_pszProgName, "removecustommode");
[44328]153 if (eWhich == SET_VIDEO_MODE || eWhich == USAGE_ALL)
[25873]154 doUsage("<width> <height> <bpp> <screen>", g_pszProgName, "setvideomode");
[10797]155#endif
156#ifdef VBOX_WITH_GUEST_PROPS
[44328]157 if (eWhich == GUEST_PROP || eWhich == USAGE_ALL)
[10825]158 {
[38750]159 doUsage("get <property> [--verbose]", g_pszProgName, "guestproperty");
160 doUsage("set <property> [<value> [--flags <flags>]]", g_pszProgName, "guestproperty");
[44328]161 doUsage("delete|unset <property>", g_pszProgName, "guestproperty");
[38750]162 doUsage("enumerate [--patterns <patterns>]", g_pszProgName, "guestproperty");
[25873]163 doUsage("wait <patterns>", g_pszProgName, "guestproperty");
[38750]164 doUsage("[--timestamp <last timestamp>]");
165 doUsage("[--timeout <timeout in ms>");
[10825]166 }
[10236]167#endif
[31002]168#ifdef VBOX_WITH_SHARED_FOLDERS
[75467]169 if (eWhich == GUEST_SHAREDFOLDERS || eWhich == USAGE_ALL)
[31002]170 {
[75467]171 doUsage("list [--automount]", g_pszProgName, "sharedfolder");
172# ifdef RT_OS_OS2
173 doUsage("use <drive> <folder>", g_pszProgName, "sharedfolder");
174 doUsage("unuse <drive>", g_pszProgName, "sharedfolder");
175# endif
[31002]176 }
177#endif
[32207]178
[32575]179#if !defined(VBOX_CONTROL_TEST)
[32574]180 if (eWhich == WRITE_CORE_DUMP || eWhich == USAGE_ALL)
181 doUsage("", g_pszProgName, "writecoredump");
[32575]182#endif
[54580]183 if (eWhich == WRITE_LOG || eWhich == USAGE_ALL)
[54611]184 doUsage("", g_pszProgName, "writelog [-n|--no-newline] [--] <msg>");
[32207]185 if (eWhich == TAKE_SNAPSHOT || eWhich == USAGE_ALL)
186 doUsage("", g_pszProgName, "takesnapshot");
187 if (eWhich == SAVE_STATE || eWhich == USAGE_ALL)
188 doUsage("", g_pszProgName, "savestate");
189 if (eWhich == SUSPEND || eWhich == USAGE_ALL)
190 doUsage("", g_pszProgName, "suspend");
191 if (eWhich == POWER_OFF || eWhich == USAGE_ALL)
192 doUsage("", g_pszProgName, "poweroff");
193 if (eWhich == HELP || eWhich == USAGE_ALL)
194 doUsage("[command]", g_pszProgName, "help");
195 if (eWhich == VERSION || eWhich == USAGE_ALL)
196 doUsage("", g_pszProgName, "version");
[54580]197
198 return RTEXITCODE_SUCCESS;
[10099]199}
[32207]200
[10099]201/** @} */
202
[54580]203
[10099]204/**
[54580]205 * Implementation of the '--version' option.
206 *
207 * @returns RTEXITCODE_SUCCESS
208 */
209static RTEXITCODE printVersion(void)
210{
211 RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
212 return RTEXITCODE_SUCCESS;
213}
214
215
216/**
[10099]217 * Displays an error message.
218 *
[32205]219 * @returns RTEXITCODE_FAILURE.
220 * @param pszFormat The message text. No newline.
[10099]221 * @param ... Format arguments.
222 */
[32205]223static RTEXITCODE VBoxControlError(const char *pszFormat, ...)
[10099]224{
[54580]225 /** @todo prefix with current command. */
[32205]226 va_list va;
227 va_start(va, pszFormat);
228 RTMsgErrorV(pszFormat, va);
229 va_end(va);
230 return RTEXITCODE_FAILURE;
231}
[10099]232
[32205]233
234/**
[54580]235 * Displays a getopt error.
236 *
237 * @returns RTEXITCODE_FAILURE.
238 * @param ch The RTGetOpt return value.
239 * @param pValueUnion The RTGetOpt return data.
240 */
241static RTEXITCODE VBoxCtrlGetOptError(int ch, PCRTGETOPTUNION pValueUnion)
242{
243 /** @todo prefix with current command. */
244 return RTGetOptPrintError(ch, pValueUnion);
245}
246
247
248/**
[32205]249 * Displays an syntax error message.
250 *
251 * @returns RTEXITCODE_FAILURE.
252 * @param pszFormat The message text. No newline.
253 * @param ... Format arguments.
254 */
255static RTEXITCODE VBoxControlSyntaxError(const char *pszFormat, ...)
256{
[54580]257 /** @todo prefix with current command. */
[10099]258 va_list va;
259 va_start(va, pszFormat);
[32205]260 RTMsgErrorV(pszFormat, va);
[10099]261 va_end(va);
[75467]262 return RTEXITCODE_SYNTAX;
[10099]263}
264
[14220]265#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
[10797]266
[70211]267decltype(ChangeDisplaySettingsExA) *g_pfnChangeDisplaySettingsExA;
268decltype(ChangeDisplaySettings) *g_pfnChangeDisplaySettingsA;
269decltype(EnumDisplaySettingsA) *g_pfnEnumDisplaySettingsA;
[10797]270
[62849]271static unsigned nextAdjacentRectXP(RECTL const *paRects, unsigned cRects, unsigned iRect)
[10797]272{
[62849]273 for (unsigned i = 0; i < cRects; i++)
[10797]274 if (paRects[iRect].right == paRects[i].left)
275 return i;
[62849]276 return ~0U;
[10797]277}
278
[62849]279static unsigned nextAdjacentRectXN(RECTL const *paRects, unsigned cRects, unsigned iRect)
[10797]280{
[62849]281 for (unsigned i = 0; i < cRects; i++)
[10797]282 if (paRects[iRect].left == paRects[i].right)
283 return i;
[62849]284 return ~0U;
[10797]285}
286
[62849]287static unsigned nextAdjacentRectYP(RECTL const *paRects, unsigned cRects, unsigned iRect)
[10797]288{
[62849]289 for (unsigned i = 0; i < cRects; i++)
[10797]290 if (paRects[iRect].bottom == paRects[i].top)
291 return i;
[62849]292 return ~0U;
[10797]293}
294
[62849]295unsigned nextAdjacentRectYN(RECTL const *paRects, unsigned cRects, unsigned iRect)
[10797]296{
[62849]297 for (unsigned i = 0; i < cRects; i++)
[10797]298 if (paRects[iRect].top == paRects[i].bottom)
299 return i;
[62849]300 return ~0U;
[10797]301}
302
[62849]303void resizeRect(RECTL *paRects, unsigned cRects, unsigned iPrimary, unsigned iResized, int NewWidth, int NewHeight)
[10797]304{
[62849]305 RECTL *paNewRects = (RECTL *)alloca(sizeof (RECTL) * cRects);
306 memcpy (paNewRects, paRects, sizeof(RECTL) * cRects);
307 paNewRects[iResized].right += NewWidth - (paNewRects[iResized].right - paNewRects[iResized].left);
[10797]308 paNewRects[iResized].bottom += NewHeight - (paNewRects[iResized].bottom - paNewRects[iResized].top);
[10859]309
310 /* Verify all pairs of originally adjacent rectangles for all 4 directions.
[10797]311 * If the pair has a "good" delta (that is the first rectangle intersects the second)
312 * at a direction and the second rectangle is not primary one (which can not be moved),
313 * move the second rectangle to make it adjacent to the first one.
314 */
[10859]315
[10797]316 /* X positive. */
317 unsigned iRect;
[62849]318 for (iRect = 0; iRect < cRects; iRect++)
[10797]319 {
320 /* Find the next adjacent original rect in x positive direction. */
[62849]321 unsigned iNextRect = nextAdjacentRectXP (paRects, cRects, iRect);
[10797]322 Log(("next %d -> %d\n", iRect, iNextRect));
[10859]323
[10797]324 if (iNextRect == ~0 || iNextRect == iPrimary)
325 {
326 continue;
327 }
[10859]328
[33540]329 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
[10797]330 * and fix the intersection if delta is "good".
331 */
332 int delta = paNewRects[iRect].right - paNewRects[iNextRect].left;
[10859]333
[10797]334 if (delta > 0)
335 {
336 Log(("XP intersection right %d left %d, diff %d\n",
337 paNewRects[iRect].right, paNewRects[iNextRect].left,
338 delta));
[10859]339
[10797]340 paNewRects[iNextRect].left += delta;
341 paNewRects[iNextRect].right += delta;
342 }
343 }
[10859]344
[10797]345 /* X negative. */
[62849]346 for (iRect = 0; iRect < cRects; iRect++)
[10797]347 {
348 /* Find the next adjacent original rect in x negative direction. */
[62849]349 unsigned iNextRect = nextAdjacentRectXN (paRects, cRects, iRect);
[10797]350 Log(("next %d -> %d\n", iRect, iNextRect));
[10859]351
[10797]352 if (iNextRect == ~0 || iNextRect == iPrimary)
353 {
354 continue;
355 }
[10859]356
[33540]357 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
[10797]358 * and fix the intersection if delta is "good".
359 */
360 int delta = paNewRects[iRect].left - paNewRects[iNextRect].right;
[10859]361
[10797]362 if (delta < 0)
363 {
364 Log(("XN intersection left %d right %d, diff %d\n",
365 paNewRects[iRect].left, paNewRects[iNextRect].right,
366 delta));
[10859]367
[10797]368 paNewRects[iNextRect].left += delta;
369 paNewRects[iNextRect].right += delta;
370 }
371 }
[10859]372
[33540]373 /* Y positive (in the computer sense, top->down). */
[62849]374 for (iRect = 0; iRect < cRects; iRect++)
[10797]375 {
376 /* Find the next adjacent original rect in y positive direction. */
[62849]377 unsigned iNextRect = nextAdjacentRectYP (paRects, cRects, iRect);
[10797]378 Log(("next %d -> %d\n", iRect, iNextRect));
[10859]379
[10797]380 if (iNextRect == ~0 || iNextRect == iPrimary)
381 {
382 continue;
383 }
[10859]384
[33540]385 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
[10797]386 * and fix the intersection if delta is "good".
387 */
388 int delta = paNewRects[iRect].bottom - paNewRects[iNextRect].top;
[10859]389
[10797]390 if (delta > 0)
391 {
392 Log(("YP intersection bottom %d top %d, diff %d\n",
393 paNewRects[iRect].bottom, paNewRects[iNextRect].top,
394 delta));
[10859]395
[10797]396 paNewRects[iNextRect].top += delta;
397 paNewRects[iNextRect].bottom += delta;
398 }
399 }
[10859]400
[33540]401 /* Y negative (in the computer sense, down->top). */
[62849]402 for (iRect = 0; iRect < cRects; iRect++)
[10797]403 {
404 /* Find the next adjacent original rect in x negative direction. */
[62849]405 unsigned iNextRect = nextAdjacentRectYN (paRects, cRects, iRect);
[10797]406 Log(("next %d -> %d\n", iRect, iNextRect));
[10859]407
[10797]408 if (iNextRect == ~0 || iNextRect == iPrimary)
409 {
410 continue;
411 }
[10859]412
[33540]413 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
[10797]414 * and fix the intersection if delta is "good".
415 */
416 int delta = paNewRects[iRect].top - paNewRects[iNextRect].bottom;
[10859]417
[10797]418 if (delta < 0)
419 {
420 Log(("YN intersection top %d bottom %d, diff %d\n",
421 paNewRects[iRect].top, paNewRects[iNextRect].bottom,
422 delta));
[10859]423
[10797]424 paNewRects[iNextRect].top += delta;
425 paNewRects[iNextRect].bottom += delta;
426 }
427 }
[10859]428
[62849]429 memcpy (paRects, paNewRects, sizeof (RECTL) * cRects);
[10797]430 return;
431}
432
433/* Returns TRUE to try again. */
434static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
435{
436 BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
[10859]437
[10797]438 DISPLAY_DEVICE DisplayDevice;
[62849]439 RT_ZERO(DisplayDevice);
[10797]440 DisplayDevice.cb = sizeof(DisplayDevice);
[10859]441
[10797]442 /* Find out how many display devices the system has */
443 DWORD NumDevices = 0;
444 DWORD i = 0;
[62849]445 while (EnumDisplayDevices(NULL, i, &DisplayDevice, 0))
[10859]446 {
[10797]447 Log(("[%d] %s\n", i, DisplayDevice.DeviceName));
448
449 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
450 {
[62849]451 Log(("Found primary device. err %d\n", GetLastError()));
[10797]452 NumDevices++;
453 }
454 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
455 {
[10859]456
[62849]457 Log(("Found secondary device. err %d\n", GetLastError()));
[10797]458 NumDevices++;
459 }
[10859]460
[62849]461 RT_ZERO(DisplayDevice);
[10797]462 DisplayDevice.cb = sizeof(DisplayDevice);
463 i++;
464 }
[10859]465
[62849]466 Log(("Found total %d devices. err %d\n", NumDevices, GetLastError()));
[10859]467
[10797]468 if (NumDevices == 0 || Id >= NumDevices)
469 {
[62849]470 Log(("Requested identifier %d is invalid. err %d\n", Id, GetLastError()));
[10797]471 return FALSE;
472 }
[10859]473
[62849]474 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca(sizeof (DISPLAY_DEVICE) * NumDevices);
475 DEVMODE *paDeviceModes = (DEVMODE *)alloca(sizeof (DEVMODE) * NumDevices);
476 RECTL *paRects = (RECTL *)alloca(sizeof (RECTL) * NumDevices);
[10859]477
[10797]478 /* Fetch information about current devices and modes. */
479 DWORD DevNum = 0;
480 DWORD DevPrimaryNum = 0;
[10859]481
[62849]482 RT_ZERO(DisplayDevice);
[10797]483 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
[10859]484
[10797]485 i = 0;
486 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
[10859]487 {
[10797]488 Log(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
[10859]489
[62849]490 BOOL fFetchDevice = FALSE;
[10797]491
492 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
493 {
[62849]494 Log(("Found primary device. err %d\n", GetLastError()));
[10797]495 DevPrimaryNum = DevNum;
[62849]496 fFetchDevice = TRUE;
[10797]497 }
498 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
499 {
[10859]500
[62849]501 Log(("Found secondary device. err %d\n", GetLastError()));
502 fFetchDevice = TRUE;
[10797]503 }
[10859]504
[62849]505 if (fFetchDevice)
[10797]506 {
507 if (DevNum >= NumDevices)
508 {
509 Log(("%d >= %d\n", NumDevices, DevNum));
510 return FALSE;
511 }
[10859]512
[10797]513 paDisplayDevices[DevNum] = DisplayDevice;
[10859]514
[62849]515 RT_BZERO(&paDeviceModes[DevNum], sizeof(DEVMODE));
[10797]516 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
[70211]517 if (!g_pfnEnumDisplaySettingsA((LPSTR)DisplayDevice.DeviceName, ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
[10797]518 {
[62849]519 Log(("EnumDisplaySettings err %d\n", GetLastError()));
[10797]520 return FALSE;
521 }
[10859]522
[10797]523 Log(("%dx%d at %d,%d\n",
524 paDeviceModes[DevNum].dmPelsWidth,
525 paDeviceModes[DevNum].dmPelsHeight,
526 paDeviceModes[DevNum].dmPosition.x,
527 paDeviceModes[DevNum].dmPosition.y));
[10859]528
[10797]529 paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
530 paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
531 paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
532 paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
533 DevNum++;
534 }
[10859]535
[62849]536 RT_ZERO(DisplayDevice);
[10797]537 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
538 i++;
539 }
[10859]540
[10797]541 if (Width == 0)
542 Width = paRects[Id].right - paRects[Id].left;
543
544 if (Height == 0)
545 Height = paRects[Id].bottom - paRects[Id].top;
546
547 /* Check whether a mode reset or a change is requested. */
548 if ( !fModeReset
[62849]549 && paRects[Id].right - paRects[Id].left == (LONG)Width
550 && paRects[Id].bottom - paRects[Id].top == (LONG)Height
[10797]551 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
552 {
553 Log(("VBoxDisplayThread : already at desired resolution.\n"));
554 return FALSE;
555 }
556
557 resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
[62849]558#ifdef LOG_ENABLED
[10797]559 for (i = 0; i < NumDevices; i++)
560 Log(("[%d]: %d,%d %dx%d\n",
[62849]561 i, paRects[i].left, paRects[i].top,
562 paRects[i].right - paRects[i].left,
563 paRects[i].bottom - paRects[i].top));
[10797]564#endif /* Log */
[10859]565
[10797]566 /* Without this, Windows will not ask the miniport for its
567 * mode table but uses an internal cache instead.
568 */
569 DEVMODE tempDevMode;
[62849]570 RT_ZERO(tempDevMode);
[10797]571 tempDevMode.dmSize = sizeof(DEVMODE);
[70211]572 g_pfnEnumDisplaySettingsA(NULL, 0xffffff, &tempDevMode);
[10797]573
574 /* Assign the new rectangles to displays. */
575 for (i = 0; i < NumDevices; i++)
576 {
577 paDeviceModes[i].dmPosition.x = paRects[i].left;
578 paDeviceModes[i].dmPosition.y = paRects[i].top;
579 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
580 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
[10859]581
[10797]582 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
[10859]583
[10797]584 if ( i == Id
585 && BitsPerPixel != 0)
586 {
587 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
588 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
589 }
[85121]590 Log(("calling pfnChangeDisplaySettingsEx %p\n", RT_CB_LOG_CAST(g_pfnChangeDisplaySettingsExA)));
[70211]591 g_pfnChangeDisplaySettingsExA((LPSTR)paDisplayDevices[i].DeviceName,
592 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
593 Log(("ChangeDisplaySettingsEx position err %d\n", GetLastError()));
[10797]594 }
[10859]595
[10797]596 /* A second call to ChangeDisplaySettings updates the monitor. */
[70211]597 LONG status = g_pfnChangeDisplaySettingsA(NULL, 0);
[10797]598 Log(("ChangeDisplaySettings update status %d\n", status));
599 if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
600 {
601 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
602 return FALSE;
603 }
604
605 /* Retry the request. */
606 return TRUE;
607}
608
[57415]609static DECLCALLBACK(RTEXITCODE) handleSetVideoMode(int argc, char *argv[])
[10797]610{
611 if (argc != 3 && argc != 4)
612 {
613 usage(SET_VIDEO_MODE);
[32201]614 return RTEXITCODE_FAILURE;
[10797]615 }
616
[95763]617 DWORD xres = RTStrToUInt32(argv[0]);
618 DWORD yres = RTStrToUInt32(argv[1]);
619 DWORD bpp = RTStrToUInt32(argv[2]);
[10797]620 DWORD scr = 0;
621 if (argc == 4)
[95763]622 scr = RTStrToUInt32(argv[3]);
[10797]623
[70211]624 HMODULE hmodUser = GetModuleHandle("user32.dll");
625 if (hmodUser)
[10797]626 {
[70211]627 /* ChangeDisplaySettingsExA was probably added in W2K, whereas ChangeDisplaySettingsA
628 and EnumDisplaySettingsA was added in NT 3.51. */
629 g_pfnChangeDisplaySettingsExA = (decltype(g_pfnChangeDisplaySettingsExA))GetProcAddress(hmodUser, "ChangeDisplaySettingsExA");
630 g_pfnChangeDisplaySettingsA = (decltype(g_pfnChangeDisplaySettingsA)) GetProcAddress(hmodUser, "ChangeDisplaySettingsA");
631 g_pfnEnumDisplaySettingsA = (decltype(g_pfnEnumDisplaySettingsA)) GetProcAddress(hmodUser, "EnumDisplaySettingsA");
[10859]632
[70211]633 Log(("VBoxService: g_pfnChangeDisplaySettingsExA=%p g_pfnChangeDisplaySettingsA=%p g_pfnEnumDisplaySettingsA=%p\n",
[85121]634 RT_CB_LOG_CAST(g_pfnChangeDisplaySettingsExA), RT_CB_LOG_CAST(g_pfnChangeDisplaySettingsA),
635 RT_CB_LOG_CAST(g_pfnEnumDisplaySettingsA)));
[70211]636
637 if ( g_pfnChangeDisplaySettingsExA
638 && g_pfnChangeDisplaySettingsA
639 && g_pfnEnumDisplaySettingsA)
[10797]640 {
641 /* The screen index is 0 based in the ResizeDisplayDevice call. */
[62849]642 scr = scr > 0 ? scr - 1 : 0;
[10859]643
[10797]644 /* Horizontal resolution must be a multiple of 8, round down. */
645 xres &= ~0x7;
646
[11563]647 RTPrintf("Setting resolution of display %d to %dx%dx%d ...", scr, xres, yres, bpp);
[10797]648 ResizeDisplayDevice(scr, xres, yres, bpp);
[11563]649 RTPrintf("done.\n");
[10797]650 }
[32201]651 else
652 VBoxControlError("Error retrieving API for display change!");
[10797]653 }
[32201]654 else
655 VBoxControlError("Error retrieving handle to user32.dll!");
[11822]656
[32201]657 return RTEXITCODE_SUCCESS;
[10797]658}
659
[46895]660static int checkVBoxVideoKey(HKEY hkeyVideo)
[10797]661{
[62849]662 RTUTF16 wszValue[128];
663 DWORD cbValue = sizeof(wszValue);
664 DWORD dwKeyType;
665 LONG status = RegQueryValueExW(hkeyVideo, L"Device Description", NULL, &dwKeyType, (LPBYTE)wszValue, &cbValue);
[46895]666 if (status == ERROR_SUCCESS)
[10797]667 {
[46895]668 /* WDDM has additional chars after "Adapter" */
[62849]669 static char s_szDeviceDescription[] = "VirtualBox Graphics Adapter";
670 wszValue[sizeof(s_szDeviceDescription) - 1] = '\0';
671 if (RTUtf16ICmpAscii(wszValue, s_szDeviceDescription) == 0)
[46895]672 return VINF_SUCCESS;
673 }
674
675 return VERR_NOT_FOUND;
676}
677
678static HKEY getVideoKey(bool writable)
679{
680 HKEY hkeyDeviceMap = 0;
681 LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap);
682 if (status != ERROR_SUCCESS || !hkeyDeviceMap)
683 {
[10797]684 VBoxControlError("Error opening video device map registry key!\n");
685 return 0;
686 }
[46895]687
688 HKEY hkeyVideo = 0;
689 ULONG iDevice;
[10797]690 DWORD dwKeyType;
[46895]691
[10797]692 /*
[46895]693 * Scan all '\Device\VideoX' REG_SZ keys to find VBox video driver entry.
694 * 'ObjectNumberList' REG_BINARY is an array of 32 bit device indexes (X).
[10797]695 */
[46895]696
697 /* Get the 'ObjectNumberList' */
[62849]698 ULONG cDevices = 0;
[46895]699 DWORD adwObjectNumberList[256];
[62849]700 DWORD cbValue = sizeof(adwObjectNumberList);
701 status = RegQueryValueExA(hkeyDeviceMap, "ObjectNumberList", NULL, &dwKeyType, (LPBYTE)&adwObjectNumberList[0], &cbValue);
[46895]702
703 if ( status == ERROR_SUCCESS
704 && dwKeyType == REG_BINARY)
[62849]705 cDevices = cbValue / sizeof(DWORD);
[10797]706 else
707 {
[46895]708 /* The list might not exists. Use 'MaxObjectNumber' REG_DWORD and build a list. */
709 DWORD dwMaxObjectNumber = 0;
[62849]710 cbValue = sizeof(dwMaxObjectNumber);
711 status = RegQueryValueExA(hkeyDeviceMap, "MaxObjectNumber", NULL, &dwKeyType, (LPBYTE)&dwMaxObjectNumber, &cbValue);
[46895]712 if ( status == ERROR_SUCCESS
713 && dwKeyType == REG_DWORD)
714 {
715 /* 'MaxObjectNumber' is inclusive. */
[62849]716 cDevices = RT_MIN(dwMaxObjectNumber + 1, RT_ELEMENTS(adwObjectNumberList));
717 for (iDevice = 0; iDevice < cDevices; iDevice++)
[46895]718 adwObjectNumberList[iDevice] = iDevice;
719 }
[10797]720 }
[48938]721
[62849]722 if (cDevices == 0)
[46895]723 {
724 /* Always try '\Device\Video0' as the old code did. Enum can be used in this case in principle. */
725 adwObjectNumberList[0] = 0;
[62849]726 cDevices = 1;
[46895]727 }
728
729 /* Scan device entries */
[62849]730 for (iDevice = 0; iDevice < cDevices; iDevice++)
[46895]731 {
[95763]732 RTUTF16 wszValueName[64];
733 RTUtf16Printf(wszValueName, RT_ELEMENTS(wszValueName), "\\Device\\Video%u", adwObjectNumberList[iDevice]);
[46895]734
[95763]735 RTUTF16 wszVideoLocation[256];
736 cbValue = sizeof(wszVideoLocation);
737 status = RegQueryValueExW(hkeyDeviceMap, wszValueName, NULL, &dwKeyType, (LPBYTE)&wszVideoLocation[0], &cbValue);
[46895]738
739 /* This value starts with '\REGISTRY\Machine' */
740 if ( status == ERROR_SUCCESS
741 && dwKeyType == REG_SZ
[95763]742 && RTUtf16NICmpAscii(wszVideoLocation, RT_STR_TUPLE("\\REGISTRY\\Machine")) == 0)
[46895]743 {
[95763]744 status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, &wszVideoLocation[18], 0,
[46895]745 KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo);
746 if (status == ERROR_SUCCESS)
747 {
748 int rc = checkVBoxVideoKey(hkeyVideo);
749 if (RT_SUCCESS(rc))
750 {
751 /* Found, return hkeyVideo to the caller. */
752 break;
753 }
754
755 RegCloseKey(hkeyVideo);
756 hkeyVideo = 0;
757 }
758 }
759 }
760
761 if (hkeyVideo == 0)
762 {
763 VBoxControlError("Error opening video registry key!\n");
764 }
765
[10797]766 RegCloseKey(hkeyDeviceMap);
767 return hkeyVideo;
768}
769
[57415]770static DECLCALLBACK(RTEXITCODE) handleGetVideoAcceleration(int argc, char *argv[])
[10797]771{
[62849]772 RT_NOREF2(argc, argv);
[10797]773 ULONG status;
774 HKEY hkeyVideo = getVideoKey(false);
775
776 if (hkeyVideo)
777 {
778 /* query the actual value */
779 DWORD fAcceleration = 1;
[62849]780 DWORD cbValue = sizeof(fAcceleration);
[10797]781 DWORD dwKeyType;
[62849]782 status = RegQueryValueExA(hkeyVideo, "EnableVideoAccel", NULL, &dwKeyType, (LPBYTE)&fAcceleration, &cbValue);
[10797]783 if (status != ERROR_SUCCESS)
784 RTPrintf("Video acceleration: default\n");
785 else
786 RTPrintf("Video acceleration: %s\n", fAcceleration ? "on" : "off");
787 RegCloseKey(hkeyVideo);
788 }
[32201]789 return RTEXITCODE_SUCCESS;
[10797]790}
791
[57415]792static DECLCALLBACK(RTEXITCODE) handleSetVideoAcceleration(int argc, char *argv[])
[10797]793{
794 ULONG status;
795 HKEY hkeyVideo;
796
797 /* must have exactly one argument: the new offset */
798 if ( (argc != 1)
[32023]799 || ( RTStrICmp(argv[0], "on")
800 && RTStrICmp(argv[0], "off")))
[10797]801 {
802 usage(SET_VIDEO_ACCEL);
[32201]803 return RTEXITCODE_FAILURE;
[10797]804 }
805
806 hkeyVideo = getVideoKey(true);
807
808 if (hkeyVideo)
809 {
810 int fAccel = 0;
[32023]811 if (RTStrICmp(argv[0], "on") == 0)
[10797]812 fAccel = 1;
813 /* set a new value */
814 status = RegSetValueExA(hkeyVideo, "EnableVideoAccel", 0, REG_DWORD, (LPBYTE)&fAccel, sizeof(fAccel));
815 if (status != ERROR_SUCCESS)
816 {
817 VBoxControlError("Error %d writing video acceleration status!\n", status);
818 }
819 RegCloseKey(hkeyVideo);
820 }
[32201]821 return RTEXITCODE_SUCCESS;
[10797]822}
823
[57415]824static DECLCALLBACK(RTEXITCODE) videoFlagsGet(void)
[46896]825{
826 HKEY hkeyVideo = getVideoKey(false);
827
828 if (hkeyVideo)
829 {
830 DWORD dwFlags = 0;
[62849]831 DWORD cbValue = sizeof(dwFlags);
[46896]832 DWORD dwKeyType;
[62849]833 ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &cbValue);
[46896]834 if (status != ERROR_SUCCESS)
835 RTPrintf("Video flags: default\n");
836 else
837 RTPrintf("Video flags: 0x%08X\n", dwFlags);
838 RegCloseKey(hkeyVideo);
839 return RTEXITCODE_SUCCESS;
840 }
841
842 return RTEXITCODE_FAILURE;
843}
844
[57415]845static DECLCALLBACK(RTEXITCODE) videoFlagsDelete(void)
[46896]846{
847 HKEY hkeyVideo = getVideoKey(true);
848
849 if (hkeyVideo)
850 {
851 ULONG status = RegDeleteValueA(hkeyVideo, "VBoxVideoFlags");
852 if (status != ERROR_SUCCESS)
853 VBoxControlError("Error %d deleting video flags.\n", status);
854 RegCloseKey(hkeyVideo);
855 return RTEXITCODE_SUCCESS;
856 }
857
858 return RTEXITCODE_FAILURE;
859}
860
[57415]861static DECLCALLBACK(RTEXITCODE) videoFlagsModify(bool fSet, int argc, char *argv[])
[46896]862{
863 if (argc != 1)
864 {
865 VBoxControlError("Mask required.\n");
866 return RTEXITCODE_FAILURE;
867 }
868
869 uint32_t u32Mask = 0;
870 int rc = RTStrToUInt32Full(argv[0], 16, &u32Mask);
871 if (RT_FAILURE(rc))
872 {
873 VBoxControlError("Invalid video flags mask.\n");
874 return RTEXITCODE_FAILURE;
875 }
876
877 RTEXITCODE exitCode = RTEXITCODE_SUCCESS;
878
879 HKEY hkeyVideo = getVideoKey(true);
880 if (hkeyVideo)
881 {
882 DWORD dwFlags = 0;
[62849]883 DWORD cbValue = sizeof(dwFlags);
[46896]884 DWORD dwKeyType;
[62849]885 ULONG status = RegQueryValueExA(hkeyVideo, "VBoxVideoFlags", NULL, &dwKeyType, (LPBYTE)&dwFlags, &cbValue);
[46896]886 if (status != ERROR_SUCCESS)
887 dwFlags = 0;
888
[62849]889 dwFlags = fSet ? dwFlags | u32Mask : dwFlags & ~u32Mask;
[46896]890
891 status = RegSetValueExA(hkeyVideo, "VBoxVideoFlags", 0, REG_DWORD, (LPBYTE)&dwFlags, sizeof(dwFlags));
892 if (status != ERROR_SUCCESS)
893 {
894 VBoxControlError("Error %d writing video flags.\n", status);
895 exitCode = RTEXITCODE_FAILURE;
896 }
897
898 RegCloseKey(hkeyVideo);
899 }
900 else
901 {
902 exitCode = RTEXITCODE_FAILURE;
903 }
904
905 return exitCode;
906}
907
[57415]908static DECLCALLBACK(RTEXITCODE) handleVideoFlags(int argc, char *argv[])
[46896]909{
910 /* Must have a keyword and optional value (32 bit hex string). */
911 if (argc != 1 && argc != 2)
912 {
913 VBoxControlError("Invalid number of arguments.\n");
914 usage(VIDEO_FLAGS);
915 return RTEXITCODE_FAILURE;
916 }
917
918 RTEXITCODE exitCode = RTEXITCODE_SUCCESS;
919
920 if (RTStrICmp(argv[0], "get") == 0)
921 {
922 exitCode = videoFlagsGet();
923 }
924 else if (RTStrICmp(argv[0], "delete") == 0)
925 {
926 exitCode = videoFlagsDelete();
927 }
928 else if (RTStrICmp(argv[0], "set") == 0)
929 {
930 exitCode = videoFlagsModify(true, argc - 1, &argv[1]);
931 }
932 else if (RTStrICmp(argv[0], "clear") == 0)
933 {
934 exitCode = videoFlagsModify(false, argc - 1, &argv[1]);
935 }
936 else
937 {
938 VBoxControlError("Invalid command.\n");
939 exitCode = RTEXITCODE_FAILURE;
940 }
941
942 if (exitCode != RTEXITCODE_SUCCESS)
943 {
944 usage(VIDEO_FLAGS);
945 }
946
947 return exitCode;
948}
949
[10797]950#define MAX_CUSTOM_MODES 128
951
952/* the table of custom modes */
953struct
954{
955 DWORD xres;
956 DWORD yres;
957 DWORD bpp;
[93300]958} customModes[MAX_CUSTOM_MODES] = {{0}};
[10797]959
960void getCustomModes(HKEY hkeyVideo)
961{
962 ULONG status;
963 int curMode = 0;
964
965 /* null out the table */
[24320]966 RT_ZERO(customModes);
[10797]967
968 do
969 {
970 char valueName[20];
971 DWORD xres, yres, bpp = 0;
972 DWORD dwType;
973 DWORD dwLen = sizeof(DWORD);
974
975 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", curMode);
976 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&xres, &dwLen);
977 if (status != ERROR_SUCCESS)
978 break;
979 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", curMode);
980 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&yres, &dwLen);
981 if (status != ERROR_SUCCESS)
982 break;
983 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", curMode);
984 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&bpp, &dwLen);
985 if (status != ERROR_SUCCESS)
986 break;
987
988 /* check if the mode is OK */
989 if ( (xres > (1 << 16))
[55804]990 || (yres > (1 << 16))
991 || ( (bpp != 16)
992 && (bpp != 24)
993 && (bpp != 32)))
[10797]994 break;
995
996 /* add mode to table */
997 customModes[curMode].xres = xres;
998 customModes[curMode].yres = yres;
999 customModes[curMode].bpp = bpp;
1000
1001 ++curMode;
1002
1003 if (curMode >= MAX_CUSTOM_MODES)
1004 break;
1005 } while(1);
1006}
1007
1008void writeCustomModes(HKEY hkeyVideo)
1009{
1010 ULONG status;
1011 int tableIndex = 0;
1012 int modeIndex = 0;
1013
1014 /* first remove all values */
1015 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
1016 {
1017 char valueName[20];
1018 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", i);
1019 RegDeleteValueA(hkeyVideo, valueName);
1020 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", i);
1021 RegDeleteValueA(hkeyVideo, valueName);
1022 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", i);
1023 RegDeleteValueA(hkeyVideo, valueName);
1024 }
1025
1026 do
1027 {
1028 if (tableIndex >= MAX_CUSTOM_MODES)
1029 break;
1030
1031 /* is the table entry present? */
1032 if ( (!customModes[tableIndex].xres)
1033 || (!customModes[tableIndex].yres)
1034 || (!customModes[tableIndex].bpp))
1035 {
1036 tableIndex++;
1037 continue;
1038 }
1039
1040 RTPrintf("writing mode %d (%dx%dx%d)\n", modeIndex, customModes[tableIndex].xres, customModes[tableIndex].yres, customModes[tableIndex].bpp);
1041 char valueName[20];
1042 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", modeIndex);
1043 status = RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].xres,
1044 sizeof(customModes[tableIndex].xres));
1045 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", modeIndex);
1046 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].yres,
1047 sizeof(customModes[tableIndex].yres));
1048 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", modeIndex);
1049 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].bpp,
1050 sizeof(customModes[tableIndex].bpp));
1051
1052 modeIndex++;
1053 tableIndex++;
1054
1055 } while(1);
1056
1057}
1058
[57415]1059static DECLCALLBACK(RTEXITCODE) handleListCustomModes(int argc, char *argv[])
[10797]1060{
[62849]1061 RT_NOREF1(argv);
[10797]1062 if (argc != 0)
1063 {
1064 usage(LIST_CUST_MODES);
[32201]1065 return RTEXITCODE_FAILURE;
[10797]1066 }
1067
1068 HKEY hkeyVideo = getVideoKey(false);
1069
1070 if (hkeyVideo)
1071 {
1072 getCustomModes(hkeyVideo);
1073 for (int i = 0; i < (sizeof(customModes) / sizeof(customModes[0])); i++)
1074 {
1075 if ( !customModes[i].xres
1076 || !customModes[i].yres
1077 || !customModes[i].bpp)
1078 continue;
1079
1080 RTPrintf("Mode: %d x %d x %d\n",
1081 customModes[i].xres, customModes[i].yres, customModes[i].bpp);
1082 }
1083 RegCloseKey(hkeyVideo);
1084 }
[32201]1085 return RTEXITCODE_SUCCESS;
[10797]1086}
1087
[57415]1088static DECLCALLBACK(RTEXITCODE) handleAddCustomMode(int argc, char *argv[])
[10797]1089{
1090 if (argc != 3)
1091 {
1092 usage(ADD_CUST_MODE);
[32201]1093 return RTEXITCODE_FAILURE;
[10797]1094 }
1095
[95763]1096 DWORD xres = RTStrToUInt32(argv[0]);
1097 DWORD yres = RTStrToUInt32(argv[1]);
1098 DWORD bpp = RTStrToUInt32(argv[2]);
[10797]1099
1100 /** @todo better check including xres mod 8 = 0! */
1101 if ( (xres > (1 << 16))
[55804]1102 || (yres > (1 << 16))
1103 || ( (bpp != 16)
1104 && (bpp != 24)
1105 && (bpp != 32)))
[10797]1106 {
[32205]1107 VBoxControlError("invalid mode specified!\n");
[32201]1108 return RTEXITCODE_FAILURE;
[10797]1109 }
1110
1111 HKEY hkeyVideo = getVideoKey(true);
1112
1113 if (hkeyVideo)
1114 {
1115 int i;
1116 int fModeExists = 0;
1117 getCustomModes(hkeyVideo);
1118 for (i = 0; i < MAX_CUSTOM_MODES; i++)
1119 {
1120 /* mode exists? */
1121 if ( customModes[i].xres == xres
1122 && customModes[i].yres == yres
1123 && customModes[i].bpp == bpp
1124 )
1125 {
1126 fModeExists = 1;
1127 }
1128 }
1129 if (!fModeExists)
1130 {
1131 for (i = 0; i < MAX_CUSTOM_MODES; i++)
1132 {
1133 /* item free? */
1134 if (!customModes[i].xres)
1135 {
1136 customModes[i].xres = xres;
1137 customModes[i].yres = yres;
1138 customModes[i].bpp = bpp;
1139 break;
1140 }
1141 }
1142 writeCustomModes(hkeyVideo);
1143 }
1144 RegCloseKey(hkeyVideo);
1145 }
[32201]1146 return RTEXITCODE_SUCCESS;
[10797]1147}
1148
[57415]1149static DECLCALLBACK(RTEXITCODE) handleRemoveCustomMode(int argc, char *argv[])
[10797]1150{
1151 if (argc != 3)
1152 {
1153 usage(REMOVE_CUST_MODE);
[32201]1154 return RTEXITCODE_FAILURE;
[10797]1155 }
1156
[95763]1157 DWORD xres = RTStrToUInt32(argv[0]);
1158 DWORD yres = RTStrToUInt32(argv[1]);
1159 DWORD bpp = RTStrToUInt32(argv[2]);
[10797]1160
1161 HKEY hkeyVideo = getVideoKey(true);
1162
1163 if (hkeyVideo)
1164 {
1165 getCustomModes(hkeyVideo);
1166 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
1167 {
1168 /* correct item? */
1169 if ( (customModes[i].xres == xres)
1170 && (customModes[i].yres == yres)
1171 && (customModes[i].bpp == bpp))
1172 {
1173 RTPrintf("found mode at index %d\n", i);
[24322]1174 RT_ZERO(customModes[i]);
[10797]1175 break;
1176 }
1177 }
1178 writeCustomModes(hkeyVideo);
1179 RegCloseKey(hkeyVideo);
1180 }
[11822]1181
[32201]1182 return RTEXITCODE_SUCCESS;
[10797]1183}
1184
1185#endif /* RT_OS_WINDOWS */
1186
1187#ifdef VBOX_WITH_GUEST_PROPS
[10099]1188/**
[10797]1189 * Retrieves a value from the guest property store.
1190 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
[10099]1191 *
[32201]1192 * @returns Command exit code.
[10825]1193 * @note see the command line API description for parameters
[10099]1194 */
[32201]1195static RTEXITCODE getGuestProperty(int argc, char **argv)
[10099]1196{
[32023]1197 bool fVerbose = false;
[62849]1198 if ( argc == 2
[32198]1199 && ( strcmp(argv[1], "-verbose") == 0
1200 || strcmp(argv[1], "--verbose") == 0)
1201 )
[32023]1202 fVerbose = true;
[10825]1203 else if (argc != 1)
[10099]1204 {
[10825]1205 usage(GUEST_PROP);
[32201]1206 return RTEXITCODE_FAILURE;
[10099]1207 }
[10797]1208
[10825]1209 uint32_t u32ClientId = 0;
[10797]1210 int rc = VINF_SUCCESS;
1211
[10829]1212 rc = VbglR3GuestPropConnect(&u32ClientId);
[49950]1213 if (RT_FAILURE(rc))
[10829]1214 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
[10825]1215
[32023]1216 /*
1217 * Here we actually retrieve the value from the host.
1218 */
[10825]1219 const char *pszName = argv[0];
1220 char *pszValue = NULL;
1221 uint64_t u64Timestamp = 0;
1222 char *pszFlags = NULL;
[10931]1223 /* The buffer for storing the data and its initial size. We leave a bit
1224 * of space here in case the maximum values are raised. */
1225 void *pvBuf = NULL;
[70058]1226 uint32_t cbBuf = GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + 1024;
[10825]1227 if (RT_SUCCESS(rc))
1228 {
1229 /* Because there is a race condition between our reading the size of a
1230 * property and the guest updating it, we loop a few times here and
1231 * hope. Actually this should never go wrong, as we are generous
1232 * enough with buffer space. */
[62849]1233 bool fFinished = false;
1234 for (unsigned i = 0; i < 10 && !fFinished; ++i)
[10825]1235 {
[10834]1236 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
1237 if (NULL == pvTmpBuf)
[10825]1238 {
[10829]1239 rc = VERR_NO_MEMORY;
1240 VBoxControlError("Out of memory\n");
[10825]1241 }
[10829]1242 else
[10834]1243 {
1244 pvBuf = pvTmpBuf;
[10829]1245 rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cbBuf,
1246 &pszValue, &u64Timestamp, &pszFlags,
1247 &cbBuf);
[10834]1248 }
[10829]1249 if (VERR_BUFFER_OVERFLOW == rc)
1250 /* Leave a bit of extra space to be safe */
1251 cbBuf += 1024;
1252 else
[62849]1253 fFinished = true;
[10825]1254 }
1255 if (VERR_TOO_MUCH_DATA == rc)
1256 VBoxControlError("Temporarily unable to retrieve the property\n");
[49950]1257 else if (RT_FAILURE(rc) && rc != VERR_NOT_FOUND)
[10099]1258 VBoxControlError("Failed to retrieve the property value, error %Rrc\n", rc);
1259 }
[32023]1260
1261 /*
1262 * And display it on the guest console.
1263 */
[10143]1264 if (VERR_NOT_FOUND == rc)
1265 RTPrintf("No value set!\n");
[10859]1266 else if (RT_SUCCESS(rc))
1267 {
[38733]1268 RTPrintf("Value: %s\n", pszValue);
[32023]1269 if (fVerbose)
[10859]1270 {
1271 RTPrintf("Timestamp: %lld ns\n", u64Timestamp);
[38733]1272 RTPrintf("Flags: %s\n", pszFlags);
[10859]1273 }
[10825]1274 }
1275
1276 if (u32ClientId != 0)
1277 VbglR3GuestPropDisconnect(u32ClientId);
[10859]1278 RTMemFree(pvBuf);
[32201]1279 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[10099]1280}
1281
1282
1283/**
[10797]1284 * Writes a value to the guest property store.
1285 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
[10099]1286 *
[32201]1287 * @returns Command exit code.
[10825]1288 * @note see the command line API description for parameters
[10099]1289 */
[32201]1290static RTEXITCODE setGuestProperty(int argc, char *argv[])
[10099]1291{
[32023]1292 /*
1293 * Check the syntax. We can deduce the correct syntax from the number of
1294 * arguments.
1295 */
[62849]1296 bool fUsageOK = true;
[10825]1297 const char *pszName = NULL;
1298 const char *pszValue = NULL;
1299 const char *pszFlags = NULL;
1300 if (2 == argc)
[10797]1301 {
[10825]1302 pszValue = argv[1];
1303 }
1304 else if (3 == argc)
[62849]1305 fUsageOK = false;
[10825]1306 else if (4 == argc)
1307 {
1308 pszValue = argv[1];
[32198]1309 if ( strcmp(argv[2], "-flags") != 0
1310 && strcmp(argv[2], "--flags") != 0)
[62849]1311 fUsageOK = false;
[10825]1312 pszFlags = argv[3];
1313 }
1314 else if (argc != 1)
[62849]1315 fUsageOK = false;
1316 if (!fUsageOK)
[10825]1317 {
1318 usage(GUEST_PROP);
[32201]1319 return RTEXITCODE_FAILURE;
[10797]1320 }
[11104]1321 /* This is always needed. */
1322 pszName = argv[0];
[10797]1323
[32023]1324 /*
1325 * Do the actual setting.
1326 */
[10825]1327 uint32_t u32ClientId = 0;
[10143]1328 int rc = VINF_SUCCESS;
[10825]1329 rc = VbglR3GuestPropConnect(&u32ClientId);
[49950]1330 if (RT_FAILURE(rc))
[10797]1331 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
[49950]1332 else
[10099]1333 {
[10825]1334 if (pszFlags != NULL)
1335 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, pszFlags);
1336 else
1337 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
[49950]1338 if (RT_FAILURE(rc))
[10099]1339 VBoxControlError("Failed to store the property value, error %Rrc\n", rc);
1340 }
[10825]1341
1342 if (u32ClientId != 0)
1343 VbglR3GuestPropDisconnect(u32ClientId);
[32201]1344 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[10099]1345}
[10825]1346
1347
1348/**
[44324]1349 * Deletes a guest property from the guest property store.
1350 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1351 *
1352 * @returns Command exit code.
1353 * @note see the command line API description for parameters
1354 */
1355static RTEXITCODE deleteGuestProperty(int argc, char *argv[])
1356{
1357 /*
1358 * Check the syntax. We can deduce the correct syntax from the number of
1359 * arguments.
1360 */
[62849]1361 bool fUsageOK = true;
[44324]1362 const char *pszName = NULL;
1363 if (argc < 1)
[62849]1364 fUsageOK = false;
1365 if (!fUsageOK)
[44324]1366 {
1367 usage(GUEST_PROP);
1368 return RTEXITCODE_FAILURE;
1369 }
1370 /* This is always needed. */
1371 pszName = argv[0];
1372
1373 /*
1374 * Do the actual setting.
1375 */
1376 uint32_t u32ClientId = 0;
1377 int rc = VbglR3GuestPropConnect(&u32ClientId);
[49950]1378 if (RT_FAILURE(rc))
[44324]1379 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
[49950]1380 else
[44324]1381 {
1382 rc = VbglR3GuestPropDelete(u32ClientId, pszName);
[49950]1383 if (RT_FAILURE(rc))
[44324]1384 VBoxControlError("Failed to delete the property value, error %Rrc\n", rc);
1385 }
1386
1387 if (u32ClientId != 0)
1388 VbglR3GuestPropDisconnect(u32ClientId);
1389 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1390}
1391
1392
1393/**
[10931]1394 * Enumerates the properties in the guest property store.
1395 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1396 *
[32201]1397 * @returns Command exit code.
[10931]1398 * @note see the command line API description for parameters
1399 */
[32201]1400static RTEXITCODE enumGuestProperty(int argc, char *argv[])
[10931]1401{
[12812]1402 /*
1403 * Check the syntax. We can deduce the correct syntax from the number of
1404 * arguments.
1405 */
1406 char const * const *papszPatterns = NULL;
[18509]1407 uint32_t cPatterns = 0;
[12812]1408 if ( argc > 1
[32198]1409 && ( strcmp(argv[0], "-patterns") == 0
1410 || strcmp(argv[0], "--patterns") == 0))
[12812]1411 {
1412 papszPatterns = (char const * const *)&argv[1];
1413 cPatterns = argc - 1;
1414 }
[10931]1415 else if (argc != 0)
1416 {
1417 usage(GUEST_PROP);
[32201]1418 return RTEXITCODE_FAILURE;
[10931]1419 }
1420
[12812]1421 /*
1422 * Do the actual enumeration.
1423 */
[10931]1424 uint32_t u32ClientId = 0;
[12812]1425 int rc = VbglR3GuestPropConnect(&u32ClientId);
[10931]1426 if (RT_SUCCESS(rc))
1427 {
[12812]1428 PVBGLR3GUESTPROPENUM pHandle;
1429 const char *pszName, *pszValue, *pszFlags;
1430 uint64_t u64Timestamp;
1431
1432 rc = VbglR3GuestPropEnum(u32ClientId, papszPatterns, cPatterns, &pHandle,
[10931]1433 &pszName, &pszValue, &u64Timestamp, &pszFlags);
1434 if (RT_SUCCESS(rc))
1435 {
[13282]1436 while (RT_SUCCESS(rc) && pszName)
[12812]1437 {
1438 RTPrintf("Name: %s, value: %s, timestamp: %lld, flags: %s\n",
[75702]1439 pszName, pszValue ? pszValue : "", u64Timestamp, pszFlags);
[12812]1440
1441 rc = VbglR3GuestPropEnumNext(pHandle, &pszName, &pszValue, &u64Timestamp, &pszFlags);
1442 if (RT_FAILURE(rc))
1443 VBoxControlError("Error while enumerating guest properties: %Rrc\n", rc);
1444 }
1445
1446 VbglR3GuestPropEnumFree(pHandle);
[10931]1447 }
1448 else if (VERR_NOT_FOUND == rc)
1449 RTPrintf("No properties found.\n");
1450 else
[12719]1451 VBoxControlError("Failed to enumerate the guest properties! Error: %Rrc\n", rc);
[12812]1452 VbglR3GuestPropDisconnect(u32ClientId);
[10931]1453 }
[12812]1454 else
1455 VBoxControlError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
[32201]1456 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[10931]1457}
1458
1459
1460/**
[14220]1461 * Waits for notifications of changes to guest properties.
1462 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1463 *
[32201]1464 * @returns Command exit code.
[14220]1465 * @note see the command line API description for parameters
1466 */
[32201]1467static RTEXITCODE waitGuestProperty(int argc, char **argv)
[14220]1468{
1469 /*
1470 * Handle arguments
1471 */
1472 const char *pszPatterns = NULL;
1473 uint64_t u64TimestampIn = 0;
1474 uint32_t u32Timeout = RT_INDEFINITE_WAIT;
[62849]1475 bool fUsageOK = true;
[14220]1476 if (argc < 1)
[62849]1477 fUsageOK = false;
[14220]1478 pszPatterns = argv[0];
[62849]1479 for (int i = 1; fUsageOK && i < argc; ++i)
[14220]1480 {
[32198]1481 if ( strcmp(argv[i], "-timeout") == 0
1482 || strcmp(argv[i], "--timeout") == 0)
[14220]1483 {
1484 if ( i + 1 >= argc
1485 || RTStrToUInt32Full(argv[i + 1], 10, &u32Timeout)
1486 != VINF_SUCCESS
1487 )
[62849]1488 fUsageOK = false;
[14220]1489 else
1490 ++i;
1491 }
[32198]1492 else if ( strcmp(argv[i], "-timestamp") == 0
1493 || strcmp(argv[i], "--timestamp") == 0)
[14220]1494 {
1495 if ( i + 1 >= argc
1496 || RTStrToUInt64Full(argv[i + 1], 10, &u64TimestampIn)
1497 != VINF_SUCCESS
1498 )
[62849]1499 fUsageOK = false;
[14220]1500 else
1501 ++i;
1502 }
1503 else
[62849]1504 fUsageOK = false;
[14220]1505 }
[62849]1506 if (!fUsageOK)
[14220]1507 {
1508 usage(GUEST_PROP);
[32201]1509 return RTEXITCODE_FAILURE;
[14220]1510 }
1511
1512 /*
1513 * Connect to the service
1514 */
1515 uint32_t u32ClientId = 0;
[75954]1516 int rc = VbglR3GuestPropConnect(&u32ClientId);
[49950]1517 if (RT_FAILURE(rc))
[14220]1518 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1519
1520 /*
1521 * Retrieve the notification from the host
1522 */
1523 char *pszName = NULL;
1524 char *pszValue = NULL;
1525 uint64_t u64TimestampOut = 0;
1526 char *pszFlags = NULL;
[94184]1527 bool fWasDeleted = false;
[14220]1528 /* The buffer for storing the data and its initial size. We leave a bit
1529 * of space here in case the maximum values are raised. */
1530 void *pvBuf = NULL;
[95326]1531 uint32_t cbBuf = GUEST_PROP_MAX_NAME_LEN + GUEST_PROP_MAX_VALUE_LEN + GUEST_PROP_MAX_FLAGS_LEN + _1K;
[14220]1532 /* Because there is a race condition between our reading the size of a
1533 * property and the guest updating it, we loop a few times here and
1534 * hope. Actually this should never go wrong, as we are generous
1535 * enough with buffer space. */
[95326]1536 for (unsigned iTry = 0; ; iTry++)
[14220]1537 {
[95326]1538 pvBuf = RTMemRealloc(pvBuf, cbBuf);
1539 if (pvBuf != NULL)
[14220]1540 {
1541 rc = VbglR3GuestPropWait(u32ClientId, pszPatterns, pvBuf, cbBuf,
1542 u64TimestampIn, u32Timeout,
1543 &pszName, &pszValue, &u64TimestampOut,
[94184]1544 &pszFlags, &cbBuf, &fWasDeleted);
[95326]1545 if (rc == VERR_BUFFER_OVERFLOW && iTry < 10)
1546 {
1547 cbBuf += _1K; /* Add a bit of extra space to be on the safe side. */
1548 continue;
1549 }
1550 if (rc == VERR_TOO_MUCH_DATA)
1551 VBoxControlError("Temporarily unable to get a notification\n");
1552 else if (rc == VERR_INTERRUPTED)
1553 VBoxControlError("The request timed out or was interrupted\n");
1554 else if (RT_FAILURE(rc) && rc != VERR_NOT_FOUND)
1555 VBoxControlError("Failed to get a notification, error %Rrc\n", rc);
[14220]1556 }
1557 else
[95326]1558 {
1559 VBoxControlError("Out of memory\n");
1560 rc = VERR_NO_MEMORY;
1561 }
1562 break;
[14220]1563 }
[32023]1564
1565 /*
1566 * And display it on the guest console.
1567 */
[14220]1568 if (VERR_NOT_FOUND == rc)
1569 RTPrintf("No value set!\n");
1570 else if (rc == VERR_BUFFER_OVERFLOW)
1571 RTPrintf("Internal error: unable to determine the size of the data!\n");
1572 else if (RT_SUCCESS(rc))
1573 {
[94184]1574 if (fWasDeleted)
1575 {
1576 RTPrintf("Property %s was deleted\n", pszName);
1577 }
1578 else
1579 {
1580 RTPrintf("Name: %s\n", pszName);
1581 RTPrintf("Value: %s\n", pszValue);
1582 RTPrintf("Timestamp: %lld ns\n", u64TimestampOut);
1583 RTPrintf("Flags: %s\n", pszFlags);
1584 }
[14220]1585 }
1586
1587 if (u32ClientId != 0)
1588 VbglR3GuestPropDisconnect(u32ClientId);
1589 RTMemFree(pvBuf);
[32201]1590 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[14220]1591}
1592
1593
1594/**
[10825]1595 * Access the guest property store through the "VBoxGuestPropSvc" HGCM
1596 * service.
1597 *
1598 * @returns 0 on success, 1 on failure
1599 * @note see the command line API description for parameters
1600 */
[57415]1601static DECLCALLBACK(RTEXITCODE) handleGuestProperty(int argc, char *argv[])
[10825]1602{
[62849]1603 if (argc == 0)
[10825]1604 {
1605 usage(GUEST_PROP);
[32201]1606 return RTEXITCODE_FAILURE;
[10825]1607 }
[32201]1608 if (!strcmp(argv[0], "get"))
[10825]1609 return getGuestProperty(argc - 1, argv + 1);
[62849]1610 if (!strcmp(argv[0], "set"))
[10825]1611 return setGuestProperty(argc - 1, argv + 1);
[62849]1612 if (!strcmp(argv[0], "delete") || !strcmp(argv[0], "unset"))
[44324]1613 return deleteGuestProperty(argc - 1, argv + 1);
[62849]1614 if (!strcmp(argv[0], "enumerate"))
[10931]1615 return enumGuestProperty(argc - 1, argv + 1);
[62849]1616 if (!strcmp(argv[0], "wait"))
[14220]1617 return waitGuestProperty(argc - 1, argv + 1);
[62849]1618 /* unknown cmd */
[10825]1619 usage(GUEST_PROP);
[32201]1620 return RTEXITCODE_FAILURE;
[10825]1621}
[75467]1622
[31002]1623#endif
[75467]1624#ifdef VBOX_WITH_SHARED_FOLDERS
[10825]1625
[31002]1626/**
1627 * Lists the Shared Folders provided by the host.
1628 */
[75470]1629static RTEXITCODE sharedFolder_list(int argc, char **argv)
[31002]1630{
[62849]1631 bool fUsageOK = true;
[31002]1632 bool fOnlyShowAutoMount = false;
[31052]1633 if (argc == 1)
1634 {
[75467]1635 if (!strcmp(argv[0], "--automount"))
[31052]1636 fOnlyShowAutoMount = true;
1637 else
[62849]1638 fUsageOK = false;
[31002]1639 }
1640 else if (argc > 1)
[62849]1641 fUsageOK = false;
1642 if (!fUsageOK)
[31002]1643 {
1644 usage(GUEST_SHAREDFOLDERS);
[75467]1645 return RTEXITCODE_SYNTAX;
[31002]1646 }
1647
1648 uint32_t u32ClientId;
1649 int rc = VbglR3SharedFolderConnect(&u32ClientId);
[49950]1650 if (RT_FAILURE(rc))
[31002]1651 VBoxControlError("Failed to connect to the shared folder service, error %Rrc\n", rc);
1652 else
1653 {
[31052]1654 PVBGLR3SHAREDFOLDERMAPPING paMappings;
1655 uint32_t cMappings;
[62849]1656 rc = VbglR3SharedFolderGetMappings(u32ClientId, fOnlyShowAutoMount, &paMappings, &cMappings);
[31052]1657 if (RT_SUCCESS(rc))
1658 {
1659 if (fOnlyShowAutoMount)
1660 RTPrintf("Auto-mounted Shared Folder mappings (%u):\n\n", cMappings);
1661 else
1662 RTPrintf("Shared Folder mappings (%u):\n\n", cMappings);
[31002]1663
[31052]1664 for (uint32_t i = 0; i < cMappings; i++)
[31002]1665 {
[31052]1666 char *pszName;
[75467]1667 char *pszMntPt;
1668 uint64_t fFlags;
1669 uint32_t uRootIdVer;
1670 rc = VbglR3SharedFolderQueryFolderInfo(u32ClientId, paMappings[i].u32Root, 0,
1671 &pszName, &pszMntPt, &fFlags, &uRootIdVer);
[31052]1672 if (RT_SUCCESS(rc))
[31002]1673 {
[75467]1674 RTPrintf("%02u - %s [idRoot=%u", i + 1, pszName, paMappings[i].u32Root);
1675 if (fFlags & SHFL_MIF_WRITABLE)
1676 RTPrintf(" writable");
1677 else
1678 RTPrintf(" readonly");
1679 if (fFlags & SHFL_MIF_AUTO_MOUNT)
1680 RTPrintf(" auto-mount");
1681 if (fFlags & SHFL_MIF_SYMLINK_CREATION)
1682 RTPrintf(" create-symlink");
1683 if (fFlags & SHFL_MIF_HOST_ICASE)
1684 RTPrintf(" host-icase");
1685 if (fFlags & SHFL_MIF_GUEST_ICASE)
1686 RTPrintf(" guest-icase");
1687 if (*pszMntPt)
1688 RTPrintf(" mnt-pt=%s", pszMntPt);
[75470]1689 RTPrintf("]");
1690# ifdef RT_OS_OS2
1691 /* Show drive letters: */
1692 const char *pszOn = " on";
1693 for (char chDrive = 'A'; chDrive <= 'Z'; chDrive++)
1694 {
1695 char szDrive[4] = { chDrive, ':', '\0', '\0' };
1696 union
1697 {
1698 FSQBUFFER2 FsQueryBuf;
1699 char achPadding[512];
1700 } uBuf;
1701 RT_ZERO(uBuf);
1702 ULONG cbBuf = sizeof(uBuf) - 2;
1703 APIRET rcOs2 = DosQueryFSAttach(szDrive, 0, FSAIL_QUERYNAME, &uBuf.FsQueryBuf, &cbBuf);
1704 if (rcOs2 == NO_ERROR)
1705 {
1706 const char *pszFsdName = (const char *)&uBuf.FsQueryBuf.szName[uBuf.FsQueryBuf.cbName + 1];
1707 if ( uBuf.FsQueryBuf.iType == FSAT_REMOTEDRV
1708 && RTStrICmpAscii(pszFsdName, "VBOXSF") == 0)
1709 {
1710 const char *pszMountedName = (const char *)&pszFsdName[uBuf.FsQueryBuf.cbFSDName + 1];
1711 if (RTStrICmp(pszMountedName, pszName) == 0)
1712 {
1713 const char *pszTag = pszMountedName + strlen(pszMountedName) + 1; /* safe */
[75471]1714 if (*pszTag != '\0')
1715 RTPrintf("%s %s (%s)", pszOn, szDrive, pszTag);
[75470]1716 else
1717 RTPrintf("%s %s", pszOn, szDrive);
1718 pszOn = ",";
1719 }
1720 }
1721 }
1722 }
1723# endif
1724 RTPrintf("\n");
1725
[31052]1726 RTStrFree(pszName);
[75467]1727 RTStrFree(pszMntPt);
[31002]1728 }
[31052]1729 else
1730 VBoxControlError("Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
1731 paMappings[i].u32Root, rc);
[31002]1732 }
[41443]1733 if (!cMappings)
[31052]1734 RTPrintf("No Shared Folders available.\n");
1735 VbglR3SharedFolderFreeMappings(paMappings);
[31002]1736 }
1737 else
[31052]1738 VBoxControlError("Error while getting the shared folder mappings, rc = %Rrc\n", rc);
[31002]1739 VbglR3SharedFolderDisconnect(u32ClientId);
1740 }
[32201]1741 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
[31002]1742}
1743
[75467]1744# ifdef RT_OS_OS2
[31002]1745/**
[75467]1746 * Attaches a shared folder to a drive letter.
1747 */
[75470]1748static RTEXITCODE sharedFolder_use(int argc, char **argv)
[75467]1749{
1750 /*
1751 * Takes a drive letter and a share name as arguments.
1752 */
1753 if (argc != 2)
[75470]1754 return VBoxControlSyntaxError("sharedfolder use: expected a drive letter and a shared folder name\n");
[75467]1755
1756 const char *pszDrive = argv[0];
1757 if (!RT_C_IS_ALPHA(pszDrive[0]) || pszDrive[1] != ':' || pszDrive[2] != '\0')
[75470]1758 return VBoxControlSyntaxError("sharedfolder use: not a drive letter: %s\n", pszDrive);
[75467]1759
[75469]1760 static const char s_szTag[] = "VBoxControl";
1761 char szzNameAndTag[256];
[75467]1762 const char *pszName = argv[1];
1763 size_t cchName = strlen(pszName);
1764 if (cchName < 1)
[75470]1765 return VBoxControlSyntaxError("sharedfolder use: shared folder name cannot be empty!\n");
[75469]1766 if (cchName + 1 + sizeof(s_szTag) >= sizeof(szzNameAndTag))
[75470]1767 return VBoxControlSyntaxError("sharedfolder use: shared folder name is too long! (%s)\n", pszName);
[75467]1768
1769 /*
1770 * Do the attaching.
1771 */
[75469]1772 memcpy(szzNameAndTag, pszName, cchName);
[75467]1773 szzNameAndTag[cchName] = '\0';
1774 memcpy(&szzNameAndTag[cchName + 1], s_szTag, sizeof(s_szTag));
1775
[75469]1776 APIRET rcOs2 = DosFSAttach(pszDrive, "VBOXSF", szzNameAndTag, cchName + 1 + sizeof(s_szTag), FS_ATTACH);
1777 if (rcOs2 == NO_ERROR)
[75467]1778 return RTEXITCODE_SUCCESS;
[75470]1779 if (rcOs2 == ERROR_INVALID_FSD_NAME)
1780 return VBoxControlError("Shared folders IFS not installed?\n");
[75467]1781 return VBoxControlError("DosFSAttach/FS_ATTACH failed to attach '%s' to '%s': %u\n", pszName, pszDrive, rcOs2);
1782}
1783
1784/**
1785 * Detaches a shared folder from a drive letter.
1786 */
[75470]1787static RTEXITCODE sharedFolder_unuse(int argc, char **argv)
[75467]1788{
1789 /*
1790 * Only takes a drive letter as argument.
1791 */
1792 if (argc != 1)
[75470]1793 return VBoxControlSyntaxError("sharedfolder unuse: expected drive letter\n");
[75467]1794 const char *pszDrive = argv[0];
1795 if (!RT_C_IS_ALPHA(pszDrive[0]) || pszDrive[1] != ':' || pszDrive[2] != '\0')
[75470]1796 return VBoxControlSyntaxError("sharedfolder unuse: not a drive letter: %s\n", pszDrive);
[75467]1797
1798 /*
1799 * Do the detaching.
1800 */
1801 APIRET rcOs2 = DosFSAttach(pszDrive, "VBOXSF", NULL, 0, FS_DETACH);
1802 if (rcOs2 == NO_ERROR)
1803 return RTEXITCODE_SUCCESS;
1804 return VBoxControlError("DosFSAttach/FS_DETACH failed on '%s': %u\n", pszDrive, rcOs2);
1805}
1806
1807# endif /* RT_OS_OS2 */
1808
1809
1810/**
[31002]1811 * Handles Shared Folders control.
1812 *
1813 * @returns 0 on success, 1 on failure
1814 * @note see the command line API description for parameters
[32205]1815 * (r=bird: yeah, right. The API description contains nil about params)
[31002]1816 */
[57415]1817static DECLCALLBACK(RTEXITCODE) handleSharedFolder(int argc, char *argv[])
[31002]1818{
[62849]1819 if (argc == 0)
[31002]1820 {
1821 usage(GUEST_SHAREDFOLDERS);
[32201]1822 return RTEXITCODE_FAILURE;
[31002]1823 }
[32201]1824 if (!strcmp(argv[0], "list"))
[75470]1825 return sharedFolder_list(argc - 1, argv + 1);
[75467]1826# ifdef RT_OS_OS2
1827 if (!strcmp(argv[0], "use"))
[75470]1828 return sharedFolder_use(argc - 1, argv + 1);
[75467]1829 if (!strcmp(argv[0], "unuse"))
[75470]1830 return sharedFolder_unuse(argc - 1, argv + 1);
[75467]1831# endif
1832
[31002]1833 usage(GUEST_SHAREDFOLDERS);
[32201]1834 return RTEXITCODE_FAILURE;
[31002]1835}
[75467]1836
[10099]1837#endif
[75467]1838#if !defined(VBOX_CONTROL_TEST)
[10099]1839
[32207]1840/**
[32576]1841 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: writecoredump}
[32207]1842 */
[57415]1843static DECLCALLBACK(RTEXITCODE) handleWriteCoreDump(int argc, char *argv[])
[32574]1844{
[62849]1845 RT_NOREF2(argc, argv);
[32574]1846 int rc = VbglR3WriteCoreDump();
1847 if (RT_SUCCESS(rc))
1848 {
[32586]1849 RTPrintf("Guest core dump successful.\n");
[32574]1850 return RTEXITCODE_SUCCESS;
1851 }
1852 else
1853 {
[32586]1854 VBoxControlError("Error while taking guest core dump. rc=%Rrc\n", rc);
[32574]1855 return RTEXITCODE_FAILURE;
1856 }
1857}
[75467]1858
[32575]1859#endif
[75467]1860#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
[32574]1861
1862/**
[44992]1863 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
1864 */
[57415]1865static DECLCALLBACK(RTEXITCODE) handleDpc(int argc, char *argv[])
[44992]1866{
[68550]1867 RT_NOREF(argc, argv);
1868 int rc = VERR_NOT_IMPLEMENTED;
[44992]1869# ifndef VBOX_CONTROL_TEST
1870 for (int i = 0; i < 30; i++)
1871 {
[68550]1872 VBGLREQHDR Req;
1873 VBGLREQHDR_INIT(&Req, DPC_LATENCY_CHECKER);
1874 rc = vbglR3DoIOCtl(VBGL_IOCTL_DPC_LATENCY_CHECKER, &Req, sizeof(Req));
1875 if (RT_SUCCESS(rc))
1876 RTPrintf("%d\n", i);
1877 else
[44992]1878 break;
1879 }
1880# endif
1881 if (RT_FAILURE(rc))
1882 return VBoxControlError("Error. rc=%Rrc\n", rc);
1883 RTPrintf("Samples collection completed.\n");
1884 return RTEXITCODE_SUCCESS;
1885}
1886#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1887
1888
1889/**
[54580]1890 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: writelog}
1891 */
[57415]1892static DECLCALLBACK(RTEXITCODE) handleWriteLog(int argc, char *argv[])
[54580]1893{
1894 static const RTGETOPTDEF s_aOptions[] =
1895 {
[54611]1896 { "--no-newline", 'n', RTGETOPT_REQ_NOTHING },
[54580]1897 };
[54611]1898 bool fNoNewline = false;
[54580]1899
1900 RTGETOPTSTATE GetOptState;
1901 int rc = RTGetOptInit(&GetOptState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions),
1902 0 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1903 if (RT_SUCCESS(rc))
1904 {
1905 RTGETOPTUNION ValueUnion;
1906 int ch;
[54607]1907 while ((ch = RTGetOpt(&GetOptState, &ValueUnion)) != 0)
[54580]1908 {
1909 switch (ch)
1910 {
1911 case VINF_GETOPT_NOT_OPTION:
[54611]1912 {
1913 size_t cch = strlen(ValueUnion.psz);
1914 if ( fNoNewline
1915 || (cch > 0 && ValueUnion.psz[cch - 1] == '\n') )
1916 rc = VbglR3WriteLog(ValueUnion.psz, cch);
1917 else
1918 {
1919 char *pszDup = (char *)RTMemDupEx(ValueUnion.psz, cch, 2);
1920 if (RT_SUCCESS(rc))
1921 {
1922 pszDup[cch++] = '\n';
1923 pszDup[cch] = '\0';
1924 rc = VbglR3WriteLog(pszDup, cch);
1925 RTMemFree(pszDup);
1926 }
1927 else
1928 rc = VERR_NO_MEMORY;
1929 }
[54580]1930 if (RT_FAILURE(rc))
1931 return VBoxControlError("VbglR3WriteLog: %Rrc", rc);
1932 break;
[54611]1933 }
[54580]1934
[54611]1935 case 'n':
1936 fNoNewline = true;
1937 break;
1938
[54580]1939 case 'h': return usage(WRITE_LOG);
1940 case 'V': return printVersion();
1941 default:
1942 return VBoxCtrlGetOptError(ch, &ValueUnion);
1943 }
1944 }
1945 }
1946 else
1947 return VBoxControlError("RTGetOptInit: %Rrc", rc);
1948 return RTEXITCODE_SUCCESS;
1949}
1950
1951
1952/**
[32574]1953 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: takesnapshot}
1954 */
[57415]1955static DECLCALLBACK(RTEXITCODE) handleTakeSnapshot(int argc, char *argv[])
[32207]1956{
[62849]1957 RT_NOREF2(argc, argv); //VbglR3VmTakeSnapshot(argv[0], argv[1]);
[32207]1958 return VBoxControlError("not implemented");
1959}
1960
1961/**
1962 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: savestate}
1963 */
[57415]1964static DECLCALLBACK(RTEXITCODE) handleSaveState(int argc, char *argv[])
[32207]1965{
[62849]1966 RT_NOREF2(argc, argv); //VbglR3VmSaveState();
[32207]1967 return VBoxControlError("not implemented");
1968}
1969
1970/**
1971 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: suspend|pause}
1972 */
[57415]1973static DECLCALLBACK(RTEXITCODE) handleSuspend(int argc, char *argv[])
[32207]1974{
[62849]1975 RT_NOREF2(argc, argv); //VbglR3VmSuspend();
[32207]1976 return VBoxControlError("not implemented");
1977}
1978
1979/**
1980 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: poweroff|powerdown}
1981 */
[57415]1982static DECLCALLBACK(RTEXITCODE) handlePowerOff(int argc, char *argv[])
[32207]1983{
[62849]1984 RT_NOREF2(argc, argv); //VbglR3VmPowerOff();
[32207]1985 return VBoxControlError("not implemented");
1986}
1987
1988/**
1989 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: version}
1990 */
[57415]1991static DECLCALLBACK(RTEXITCODE) handleVersion(int argc, char *argv[])
[32205]1992{
[62849]1993 RT_NOREF1(argv);
[32205]1994 if (argc)
1995 return VBoxControlSyntaxError("getversion does not take any arguments");
[54580]1996 return printVersion();
[32205]1997}
1998
[32207]1999/**
2000 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
2001 */
[57415]2002static DECLCALLBACK(RTEXITCODE) handleHelp(int argc, char *argv[])
[32205]2003{
[62849]2004 RT_NOREF2(argc, argv); /* ignore arguments for now. */
[32205]2005 usage();
2006 return RTEXITCODE_SUCCESS;
2007}
2008
[70431]2009/**
2010 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: ls}
2011 */
2012static DECLCALLBACK(RTEXITCODE) handleLs(int argc, char *argv[])
2013{
2014 return RTFsCmdLs(argc + 1, argv - 1);
2015}
[41972]2016
[70431]2017/**
2018 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: tar}
2019 */
2020static DECLCALLBACK(RTEXITCODE) handleTar(int argc, char *argv[])
2021{
2022 return RTZipTarCmd(argc + 1, argv - 1);
2023}
2024
2025/**
2026 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: tar}
2027 */
2028static DECLCALLBACK(RTEXITCODE) handleGzip(int argc, char *argv[])
2029{
2030 return RTZipGzipCmd(argc + 1, argv - 1);
2031}
2032
2033/**
2034 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: unzip}
2035 */
2036static DECLCALLBACK(RTEXITCODE) handleUnzip(int argc, char *argv[])
2037{
2038 return RTZipUnzipCmd(argc + 1, argv - 1);
2039}
2040
2041
[10099]2042/** command handler type */
[85121]2043typedef DECLCALLBACKTYPE(RTEXITCODE, FNVBOXCTRLCMDHANDLER,(int argc, char *argv[]));
[32207]2044typedef FNVBOXCTRLCMDHANDLER *PFNVBOXCTRLCMDHANDLER;
[10099]2045
2046/** The table of all registered command handlers. */
[10111]2047struct COMMANDHANDLER
[10099]2048{
[70431]2049 const char *pszCommand;
2050 PFNVBOXCTRLCMDHANDLER pfnHandler;
2051 bool fNeedDevice;
[32201]2052} g_aCommandHandlers[] =
[10099]2053{
[14220]2054#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
[70431]2055 { "getvideoacceleration", handleGetVideoAcceleration, true },
2056 { "setvideoacceleration", handleSetVideoAcceleration, true },
2057 { "videoflags", handleVideoFlags, true },
2058 { "listcustommodes", handleListCustomModes, true },
2059 { "addcustommode", handleAddCustomMode, true },
2060 { "removecustommode", handleRemoveCustomMode, true },
2061 { "setvideomode", handleSetVideoMode, true },
[10797]2062#endif
2063#ifdef VBOX_WITH_GUEST_PROPS
[70431]2064 { "guestproperty", handleGuestProperty, true },
[10099]2065#endif
[31002]2066#ifdef VBOX_WITH_SHARED_FOLDERS
[70431]2067 { "sharedfolder", handleSharedFolder, true },
[31002]2068#endif
[32574]2069#if !defined(VBOX_CONTROL_TEST)
[70431]2070 { "writecoredump", handleWriteCoreDump, true },
[32574]2071#endif
[44992]2072#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
[70431]2073 { "dpc", handleDpc, true },
[44992]2074#endif
[70431]2075 { "writelog", handleWriteLog, true },
2076 { "takesnapshot", handleTakeSnapshot, true },
2077 { "savestate", handleSaveState, true },
2078 { "suspend", handleSuspend, true },
2079 { "pause", handleSuspend, true },
2080 { "poweroff", handlePowerOff, true },
2081 { "powerdown", handlePowerOff, true },
2082 { "getversion", handleVersion, false },
2083 { "version", handleVersion, false },
2084 { "help", handleHelp, false },
2085 /* Hany tricks that doesn't cost much space: */
2086 { "gzip", handleGzip, false },
2087 { "ls", handleLs, false },
2088 { "tar", handleTar, false },
2089 { "unzip", handleUnzip, false },
[10099]2090};
2091
2092/** Main function */
2093int main(int argc, char **argv)
2094{
2095 /** The application's global return code */
[32201]2096 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
[10099]2097 /** An IPRT return code for local use */
2098 int rrc = VINF_SUCCESS;
2099 /** The index of the command line argument we are currently processing */
2100 int iArg = 1;
2101 /** Should we show the logo text? */
[32023]2102 bool fShowLogo = true;
[10825]2103 /** Should we print the usage after the logo? For the -help switch. */
[32023]2104 bool fDoHelp = false;
[10099]2105 /** Will we be executing a command or just printing information? */
[32023]2106 bool fOnlyInfo = false;
[10099]2107
[38636]2108 rrc = RTR3InitExe(argc, &argv, 0);
[32201]2109 if (RT_FAILURE(rrc))
2110 return RTMsgInitFailure(rrc);
2111
[32023]2112 /*
2113 * Start by handling command line switches
2114 */
[32201]2115 /** @todo RTGetOpt conversion of the whole file. */
2116 bool done = false; /**< Are we finished with handling switches? */
[10099]2117 while (!done && (iArg < argc))
2118 {
[32201]2119 if ( !strcmp(argv[iArg], "-V")
2120 || !strcmp(argv[iArg], "-v")
2121 || !strcmp(argv[iArg], "--version")
2122 || !strcmp(argv[iArg], "-version")
[10099]2123 )
[32208]2124 {
2125 /* Print version number, and do nothing else. */
[54580]2126 printVersion();
[32208]2127 fOnlyInfo = true;
2128 fShowLogo = false;
2129 done = true;
2130 }
[32201]2131 else if ( !strcmp(argv[iArg], "-nologo")
2132 || !strcmp(argv[iArg], "--nologo"))
[32023]2133 fShowLogo = false;
[32201]2134 else if ( !strcmp(argv[iArg], "-help")
2135 || !strcmp(argv[iArg], "--help"))
[10099]2136 {
[32023]2137 fOnlyInfo = true;
2138 fDoHelp = true;
[10859]2139 done = true;
[10099]2140 }
2141 else
2142 /* We have found an argument which isn't a switch. Exit to the
2143 * command processing bit. */
2144 done = true;
2145 if (!done)
2146 ++iArg;
2147 }
2148
[32023]2149 /*
2150 * Find the application name, show our logo if the user hasn't suppressed it,
2151 * and show the usage if the user asked us to
2152 */
[10099]2153 g_pszProgName = RTPathFilename(argv[0]);
[32023]2154 if (fShowLogo)
[26197]2155 RTPrintf(VBOX_PRODUCT " Guest Additions Command Line Management Interface Version "
[10099]2156 VBOX_VERSION_STRING "\n"
[96399]2157 "Copyright (C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n\n");
[32023]2158 if (fDoHelp)
[10099]2159 usage();
2160
[32023]2161 /*
2162 * Now look for an actual command in the argument list and handle it.
2163 */
[32201]2164 if (!fOnlyInfo && rcExit == RTEXITCODE_SUCCESS)
[10099]2165 {
2166 if (argc > iArg)
2167 {
[32205]2168 /*
2169 * Try locate the command and execute it, complain if not found.
2170 */
2171 unsigned i;
2172 for (i = 0; i < RT_ELEMENTS(g_aCommandHandlers); i++)
2173 if (!strcmp(argv[iArg], g_aCommandHandlers[i].pszCommand))
2174 {
[70431]2175 if (g_aCommandHandlers[i].fNeedDevice)
2176 {
2177 rrc = VbglR3Init();
2178 if (RT_FAILURE(rrc))
2179 {
2180 VBoxControlError("Could not contact the host system. Make sure that you are running this\n"
2181 "application inside a VirtualBox guest system, and that you have sufficient\n"
2182 "user permissions.\n");
2183 rcExit = RTEXITCODE_FAILURE;
2184 }
2185 }
2186 if (rcExit == RTEXITCODE_SUCCESS)
2187 rcExit = g_aCommandHandlers[i].pfnHandler(argc - iArg - 1, argv + iArg + 1);
[32205]2188 break;
2189 }
2190 if (i >= RT_ELEMENTS(g_aCommandHandlers))
[10099]2191 {
2192 usage();
[70431]2193 rcExit = RTEXITCODE_SYNTAX;
[10099]2194 }
2195 }
2196 else
2197 {
2198 /* The user didn't specify a command. */
2199 usage();
[70431]2200 rcExit = RTEXITCODE_SYNTAX;
[10099]2201 }
2202 }
2203
[32023]2204 /*
2205 * And exit, returning the status
2206 */
[32201]2207 return rcExit;
[10099]2208}
[21218]2209
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use