VirtualBox

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

Last change on this file since 46895 was 46895, checked in by vboxsync, 12 years ago

VBoxControl: Windows: search for the VirtualBox video driver registry key.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id Revision
File size: 55.3 KB
Line 
1/* $Id: VBoxControl.cpp 46895 2013-07-02 08:03:03Z vboxsync $ */
2/** @file
3 * VBoxControl - Guest Additions Command Line Management Interface.
4 */
5
6/*
7 * Copyright (C) 2008-2013 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.215389.xyz. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#include <iprt/alloca.h>
22#include <iprt/cpp/autores.h>
23#include <iprt/buildconfig.h>
24#include <iprt/initterm.h>
25#include <iprt/mem.h>
26#include <iprt/message.h>
27#include <iprt/path.h>
28#include <iprt/string.h>
29#include <iprt/stream.h>
30#include <VBox/log.h>
31#include <VBox/version.h>
32#include <VBox/VBoxGuestLib.h>
33#ifdef RT_OS_WINDOWS
34# include <Windows.h>
35#endif
36#ifdef VBOX_WITH_GUEST_PROPS
37# include <VBox/HostServices/GuestPropertySvc.h>
38#endif
39#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
40# include <VBox/VBoxGuest.h>
41# include "../VBoxGuestLib/VBGLR3Internal.h" /* HACK ALERT! Using vbglR3DoIOCtl directly!! */
42#endif
43
44
45/*******************************************************************************
46* Global Variables *
47*******************************************************************************/
48/** The program name (derived from argv[0]). */
49char const *g_pszProgName = "";
50/** The current verbosity level. */
51int g_cVerbosity = 0;
52
53
54/**
55 * Displays the program usage message.
56 *
57 * @param u64Which
58 *
59 * @{
60 */
61
62/** Helper function */
63static void doUsage(char const *line, char const *name = "", char const *command = "")
64{
65 /* Allow for up to 15 characters command name length (VBoxControl.exe) with
66 * perfect column alignment. Beyond that there's at least one space between
67 * the command if there are command line parameters. */
68 RTPrintf("%s %-*s%s%s\n", name, strlen(line) ? 35 - strlen(name) : 1,
69 command, strlen(line) ? " " : "", line);
70}
71
72/** Enumerate the different parts of the usage we might want to print out */
73enum VBoxControlUsage
74{
75#ifdef RT_OS_WINDOWS
76 GET_VIDEO_ACCEL,
77 SET_VIDEO_ACCEL,
78 LIST_CUST_MODES,
79 ADD_CUST_MODE,
80 REMOVE_CUST_MODE,
81 SET_VIDEO_MODE,
82#endif
83#ifdef VBOX_WITH_GUEST_PROPS
84 GUEST_PROP,
85#endif
86#ifdef VBOX_WITH_SHARED_FOLDERS
87 GUEST_SHAREDFOLDERS,
88#endif
89#if !defined(VBOX_CONTROL_TEST)
90 WRITE_CORE_DUMP,
91#endif
92 TAKE_SNAPSHOT,
93 SAVE_STATE,
94 SUSPEND,
95 POWER_OFF,
96 VERSION,
97 HELP,
98 USAGE_ALL = UINT32_MAX
99};
100
101static void usage(enum VBoxControlUsage eWhich = USAGE_ALL)
102{
103 RTPrintf("Usage:\n\n");
104 doUsage("print version number and exit", g_pszProgName, "[-v|--version]");
105 doUsage("suppress the logo", g_pszProgName, "--nologo ...");
106 RTPrintf("\n");
107
108 /* Exclude the Windows bits from the test version. Anyone who needs to
109 test them can fix this. */
110#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
111 if (eWhich == GET_VIDEO_ACCEL || eWhich == USAGE_ALL)
112 doUsage("", g_pszProgName, "getvideoacceleration");
113 if (eWhich == SET_VIDEO_ACCEL || eWhich == USAGE_ALL)
114 doUsage("<on|off>", g_pszProgName, "setvideoacceleration");
115 if (eWhich == LIST_CUST_MODES || eWhich == USAGE_ALL)
116 doUsage("", g_pszProgName, "listcustommodes");
117 if (eWhich == ADD_CUST_MODE || eWhich == USAGE_ALL)
118 doUsage("<width> <height> <bpp>", g_pszProgName, "addcustommode");
119 if (eWhich == REMOVE_CUST_MODE || eWhich == USAGE_ALL)
120 doUsage("<width> <height> <bpp>", g_pszProgName, "removecustommode");
121 if (eWhich == SET_VIDEO_MODE || eWhich == USAGE_ALL)
122 doUsage("<width> <height> <bpp> <screen>", g_pszProgName, "setvideomode");
123#endif
124#ifdef VBOX_WITH_GUEST_PROPS
125 if (eWhich == GUEST_PROP || eWhich == USAGE_ALL)
126 {
127 doUsage("get <property> [--verbose]", g_pszProgName, "guestproperty");
128 doUsage("set <property> [<value> [--flags <flags>]]", g_pszProgName, "guestproperty");
129 doUsage("delete|unset <property>", g_pszProgName, "guestproperty");
130 doUsage("enumerate [--patterns <patterns>]", g_pszProgName, "guestproperty");
131 doUsage("wait <patterns>", g_pszProgName, "guestproperty");
132 doUsage("[--timestamp <last timestamp>]");
133 doUsage("[--timeout <timeout in ms>");
134 }
135#endif
136#ifdef VBOX_WITH_SHARED_FOLDERS
137 if (eWhich == GUEST_SHAREDFOLDERS || eWhich == USAGE_ALL)
138 {
139 doUsage("list [-automount]", g_pszProgName, "sharedfolder");
140 }
141#endif
142
143#if !defined(VBOX_CONTROL_TEST)
144 if (eWhich == WRITE_CORE_DUMP || eWhich == USAGE_ALL)
145 doUsage("", g_pszProgName, "writecoredump");
146#endif
147 if (eWhich == TAKE_SNAPSHOT || eWhich == USAGE_ALL)
148 doUsage("", g_pszProgName, "takesnapshot");
149 if (eWhich == SAVE_STATE || eWhich == USAGE_ALL)
150 doUsage("", g_pszProgName, "savestate");
151 if (eWhich == SUSPEND || eWhich == USAGE_ALL)
152 doUsage("", g_pszProgName, "suspend");
153 if (eWhich == POWER_OFF || eWhich == USAGE_ALL)
154 doUsage("", g_pszProgName, "poweroff");
155 if (eWhich == HELP || eWhich == USAGE_ALL)
156 doUsage("[command]", g_pszProgName, "help");
157 if (eWhich == VERSION || eWhich == USAGE_ALL)
158 doUsage("", g_pszProgName, "version");
159}
160
161/** @} */
162
163/**
164 * Displays an error message.
165 *
166 * @returns RTEXITCODE_FAILURE.
167 * @param pszFormat The message text. No newline.
168 * @param ... Format arguments.
169 */
170static RTEXITCODE VBoxControlError(const char *pszFormat, ...)
171{
172 va_list va;
173 va_start(va, pszFormat);
174 RTMsgErrorV(pszFormat, va);
175 va_end(va);
176 return RTEXITCODE_FAILURE;
177}
178
179
180/**
181 * Displays an syntax error message.
182 *
183 * @returns RTEXITCODE_FAILURE.
184 * @param pszFormat The message text. No newline.
185 * @param ... Format arguments.
186 */
187static RTEXITCODE VBoxControlSyntaxError(const char *pszFormat, ...)
188{
189 va_list va;
190 va_start(va, pszFormat);
191 RTMsgErrorV(pszFormat, va);
192 va_end(va);
193 return RTEXITCODE_FAILURE;
194}
195
196#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
197
198LONG (WINAPI * gpfnChangeDisplaySettingsEx)(LPCTSTR lpszDeviceName, LPDEVMODE lpDevMode, HWND hwnd, DWORD dwflags, LPVOID lParam);
199
200static unsigned nextAdjacentRectXP (RECTL *paRects, unsigned nRects, unsigned iRect)
201{
202 unsigned i;
203 for (i = 0; i < nRects; i++)
204 {
205 if (paRects[iRect].right == paRects[i].left)
206 {
207 return i;
208 }
209 }
210 return ~0;
211}
212
213static unsigned nextAdjacentRectXN (RECTL *paRects, unsigned nRects, unsigned iRect)
214{
215 unsigned i;
216 for (i = 0; i < nRects; i++)
217 {
218 if (paRects[iRect].left == paRects[i].right)
219 {
220 return i;
221 }
222 }
223 return ~0;
224}
225
226static unsigned nextAdjacentRectYP (RECTL *paRects, unsigned nRects, unsigned iRect)
227{
228 unsigned i;
229 for (i = 0; i < nRects; i++)
230 {
231 if (paRects[iRect].bottom == paRects[i].top)
232 {
233 return i;
234 }
235 }
236 return ~0;
237}
238
239unsigned nextAdjacentRectYN (RECTL *paRects, unsigned nRects, unsigned iRect)
240{
241 unsigned i;
242 for (i = 0; i < nRects; i++)
243 {
244 if (paRects[iRect].top == paRects[i].bottom)
245 {
246 return i;
247 }
248 }
249 return ~0;
250}
251
252void resizeRect(RECTL *paRects, unsigned nRects, unsigned iPrimary, unsigned iResized, int NewWidth, int NewHeight)
253{
254 RECTL *paNewRects = (RECTL *)alloca (sizeof (RECTL) * nRects);
255 memcpy (paNewRects, paRects, sizeof (RECTL) * nRects);
256 paNewRects[iResized].right += NewWidth - (paNewRects[iResized].right - paNewRects[iResized].left);
257 paNewRects[iResized].bottom += NewHeight - (paNewRects[iResized].bottom - paNewRects[iResized].top);
258
259 /* Verify all pairs of originally adjacent rectangles for all 4 directions.
260 * If the pair has a "good" delta (that is the first rectangle intersects the second)
261 * at a direction and the second rectangle is not primary one (which can not be moved),
262 * move the second rectangle to make it adjacent to the first one.
263 */
264
265 /* X positive. */
266 unsigned iRect;
267 for (iRect = 0; iRect < nRects; iRect++)
268 {
269 /* Find the next adjacent original rect in x positive direction. */
270 unsigned iNextRect = nextAdjacentRectXP (paRects, nRects, iRect);
271 Log(("next %d -> %d\n", iRect, iNextRect));
272
273 if (iNextRect == ~0 || iNextRect == iPrimary)
274 {
275 continue;
276 }
277
278 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
279 * and fix the intersection if delta is "good".
280 */
281 int delta = paNewRects[iRect].right - paNewRects[iNextRect].left;
282
283 if (delta > 0)
284 {
285 Log(("XP intersection right %d left %d, diff %d\n",
286 paNewRects[iRect].right, paNewRects[iNextRect].left,
287 delta));
288
289 paNewRects[iNextRect].left += delta;
290 paNewRects[iNextRect].right += delta;
291 }
292 }
293
294 /* X negative. */
295 for (iRect = 0; iRect < nRects; iRect++)
296 {
297 /* Find the next adjacent original rect in x negative direction. */
298 unsigned iNextRect = nextAdjacentRectXN (paRects, nRects, iRect);
299 Log(("next %d -> %d\n", iRect, iNextRect));
300
301 if (iNextRect == ~0 || iNextRect == iPrimary)
302 {
303 continue;
304 }
305
306 /* Check whether there is an X intersection between these adjacent rects in the new rectangles
307 * and fix the intersection if delta is "good".
308 */
309 int delta = paNewRects[iRect].left - paNewRects[iNextRect].right;
310
311 if (delta < 0)
312 {
313 Log(("XN intersection left %d right %d, diff %d\n",
314 paNewRects[iRect].left, paNewRects[iNextRect].right,
315 delta));
316
317 paNewRects[iNextRect].left += delta;
318 paNewRects[iNextRect].right += delta;
319 }
320 }
321
322 /* Y positive (in the computer sense, top->down). */
323 for (iRect = 0; iRect < nRects; iRect++)
324 {
325 /* Find the next adjacent original rect in y positive direction. */
326 unsigned iNextRect = nextAdjacentRectYP (paRects, nRects, iRect);
327 Log(("next %d -> %d\n", iRect, iNextRect));
328
329 if (iNextRect == ~0 || iNextRect == iPrimary)
330 {
331 continue;
332 }
333
334 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
335 * and fix the intersection if delta is "good".
336 */
337 int delta = paNewRects[iRect].bottom - paNewRects[iNextRect].top;
338
339 if (delta > 0)
340 {
341 Log(("YP intersection bottom %d top %d, diff %d\n",
342 paNewRects[iRect].bottom, paNewRects[iNextRect].top,
343 delta));
344
345 paNewRects[iNextRect].top += delta;
346 paNewRects[iNextRect].bottom += delta;
347 }
348 }
349
350 /* Y negative (in the computer sense, down->top). */
351 for (iRect = 0; iRect < nRects; iRect++)
352 {
353 /* Find the next adjacent original rect in x negative direction. */
354 unsigned iNextRect = nextAdjacentRectYN (paRects, nRects, iRect);
355 Log(("next %d -> %d\n", iRect, iNextRect));
356
357 if (iNextRect == ~0 || iNextRect == iPrimary)
358 {
359 continue;
360 }
361
362 /* Check whether there is an Y intersection between these adjacent rects in the new rectangles
363 * and fix the intersection if delta is "good".
364 */
365 int delta = paNewRects[iRect].top - paNewRects[iNextRect].bottom;
366
367 if (delta < 0)
368 {
369 Log(("YN intersection top %d bottom %d, diff %d\n",
370 paNewRects[iRect].top, paNewRects[iNextRect].bottom,
371 delta));
372
373 paNewRects[iNextRect].top += delta;
374 paNewRects[iNextRect].bottom += delta;
375 }
376 }
377
378 memcpy (paRects, paNewRects, sizeof (RECTL) * nRects);
379 return;
380}
381
382/* Returns TRUE to try again. */
383static BOOL ResizeDisplayDevice(ULONG Id, DWORD Width, DWORD Height, DWORD BitsPerPixel)
384{
385 BOOL fModeReset = (Width == 0 && Height == 0 && BitsPerPixel == 0);
386
387 DISPLAY_DEVICE DisplayDevice;
388
389 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
390 DisplayDevice.cb = sizeof(DisplayDevice);
391
392 /* Find out how many display devices the system has */
393 DWORD NumDevices = 0;
394 DWORD i = 0;
395 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
396 {
397 Log(("[%d] %s\n", i, DisplayDevice.DeviceName));
398
399 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
400 {
401 Log(("Found primary device. err %d\n", GetLastError ()));
402 NumDevices++;
403 }
404 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
405 {
406
407 Log(("Found secondary device. err %d\n", GetLastError ()));
408 NumDevices++;
409 }
410
411 ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
412 DisplayDevice.cb = sizeof(DisplayDevice);
413 i++;
414 }
415
416 Log(("Found total %d devices. err %d\n", NumDevices, GetLastError ()));
417
418 if (NumDevices == 0 || Id >= NumDevices)
419 {
420 Log(("Requested identifier %d is invalid. err %d\n", Id, GetLastError ()));
421 return FALSE;
422 }
423
424 DISPLAY_DEVICE *paDisplayDevices = (DISPLAY_DEVICE *)alloca (sizeof (DISPLAY_DEVICE) * NumDevices);
425 DEVMODE *paDeviceModes = (DEVMODE *)alloca (sizeof (DEVMODE) * NumDevices);
426 RECTL *paRects = (RECTL *)alloca (sizeof (RECTL) * NumDevices);
427
428 /* Fetch information about current devices and modes. */
429 DWORD DevNum = 0;
430 DWORD DevPrimaryNum = 0;
431
432 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
433 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
434
435 i = 0;
436 while (EnumDisplayDevices (NULL, i, &DisplayDevice, 0))
437 {
438 Log(("[%d(%d)] %s\n", i, DevNum, DisplayDevice.DeviceName));
439
440 BOOL bFetchDevice = FALSE;
441
442 if (DisplayDevice.StateFlags & DISPLAY_DEVICE_PRIMARY_DEVICE)
443 {
444 Log(("Found primary device. err %d\n", GetLastError ()));
445 DevPrimaryNum = DevNum;
446 bFetchDevice = TRUE;
447 }
448 else if (!(DisplayDevice.StateFlags & DISPLAY_DEVICE_MIRRORING_DRIVER))
449 {
450
451 Log(("Found secondary device. err %d\n", GetLastError ()));
452 bFetchDevice = TRUE;
453 }
454
455 if (bFetchDevice)
456 {
457 if (DevNum >= NumDevices)
458 {
459 Log(("%d >= %d\n", NumDevices, DevNum));
460 return FALSE;
461 }
462
463 paDisplayDevices[DevNum] = DisplayDevice;
464
465 ZeroMemory(&paDeviceModes[DevNum], sizeof(DEVMODE));
466 paDeviceModes[DevNum].dmSize = sizeof(DEVMODE);
467 if (!EnumDisplaySettings((LPSTR)DisplayDevice.DeviceName,
468 ENUM_REGISTRY_SETTINGS, &paDeviceModes[DevNum]))
469 {
470 Log(("EnumDisplaySettings err %d\n", GetLastError ()));
471 return FALSE;
472 }
473
474 Log(("%dx%d at %d,%d\n",
475 paDeviceModes[DevNum].dmPelsWidth,
476 paDeviceModes[DevNum].dmPelsHeight,
477 paDeviceModes[DevNum].dmPosition.x,
478 paDeviceModes[DevNum].dmPosition.y));
479
480 paRects[DevNum].left = paDeviceModes[DevNum].dmPosition.x;
481 paRects[DevNum].top = paDeviceModes[DevNum].dmPosition.y;
482 paRects[DevNum].right = paDeviceModes[DevNum].dmPosition.x + paDeviceModes[DevNum].dmPelsWidth;
483 paRects[DevNum].bottom = paDeviceModes[DevNum].dmPosition.y + paDeviceModes[DevNum].dmPelsHeight;
484 DevNum++;
485 }
486
487 ZeroMemory(&DisplayDevice, sizeof(DISPLAY_DEVICE));
488 DisplayDevice.cb = sizeof(DISPLAY_DEVICE);
489 i++;
490 }
491
492 if (Width == 0)
493 {
494 Width = paRects[Id].right - paRects[Id].left;
495 }
496
497 if (Height == 0)
498 {
499 Height = paRects[Id].bottom - paRects[Id].top;
500 }
501
502 /* Check whether a mode reset or a change is requested. */
503 if ( !fModeReset
504 && paRects[Id].right - paRects[Id].left == Width
505 && paRects[Id].bottom - paRects[Id].top == Height
506 && paDeviceModes[Id].dmBitsPerPel == BitsPerPixel)
507 {
508 Log(("VBoxDisplayThread : already at desired resolution.\n"));
509 return FALSE;
510 }
511
512 resizeRect(paRects, NumDevices, DevPrimaryNum, Id, Width, Height);
513#ifdef Log
514 for (i = 0; i < NumDevices; i++)
515 {
516 Log(("[%d]: %d,%d %dx%d\n",
517 i, paRects[i].left, paRects[i].top,
518 paRects[i].right - paRects[i].left,
519 paRects[i].bottom - paRects[i].top));
520 }
521#endif /* Log */
522
523 /* Without this, Windows will not ask the miniport for its
524 * mode table but uses an internal cache instead.
525 */
526 DEVMODE tempDevMode;
527 ZeroMemory (&tempDevMode, sizeof (tempDevMode));
528 tempDevMode.dmSize = sizeof(DEVMODE);
529 EnumDisplaySettings(NULL, 0xffffff, &tempDevMode);
530
531 /* Assign the new rectangles to displays. */
532 for (i = 0; i < NumDevices; i++)
533 {
534 paDeviceModes[i].dmPosition.x = paRects[i].left;
535 paDeviceModes[i].dmPosition.y = paRects[i].top;
536 paDeviceModes[i].dmPelsWidth = paRects[i].right - paRects[i].left;
537 paDeviceModes[i].dmPelsHeight = paRects[i].bottom - paRects[i].top;
538
539 paDeviceModes[i].dmFields = DM_POSITION | DM_PELSHEIGHT | DM_PELSWIDTH;
540
541 if ( i == Id
542 && BitsPerPixel != 0)
543 {
544 paDeviceModes[i].dmFields |= DM_BITSPERPEL;
545 paDeviceModes[i].dmBitsPerPel = BitsPerPixel;
546 }
547 Log(("calling pfnChangeDisplaySettingsEx %x\n", gpfnChangeDisplaySettingsEx));
548 gpfnChangeDisplaySettingsEx((LPSTR)paDisplayDevices[i].DeviceName,
549 &paDeviceModes[i], NULL, CDS_NORESET | CDS_UPDATEREGISTRY, NULL);
550 Log(("ChangeDisplaySettings position err %d\n", GetLastError ()));
551 }
552
553 /* A second call to ChangeDisplaySettings updates the monitor. */
554 LONG status = ChangeDisplaySettings(NULL, 0);
555 Log(("ChangeDisplaySettings update status %d\n", status));
556 if (status == DISP_CHANGE_SUCCESSFUL || status == DISP_CHANGE_BADMODE)
557 {
558 /* Successfully set new video mode or our driver can not set the requested mode. Stop trying. */
559 return FALSE;
560 }
561
562 /* Retry the request. */
563 return TRUE;
564}
565
566static RTEXITCODE handleSetVideoMode(int argc, char *argv[])
567{
568 if (argc != 3 && argc != 4)
569 {
570 usage(SET_VIDEO_MODE);
571 return RTEXITCODE_FAILURE;
572 }
573
574 DWORD xres = atoi(argv[0]);
575 DWORD yres = atoi(argv[1]);
576 DWORD bpp = atoi(argv[2]);
577 DWORD scr = 0;
578
579 if (argc == 4)
580 {
581 scr = atoi(argv[3]);
582 }
583
584 HMODULE hUser = GetModuleHandle("user32.dll");
585
586 if (hUser)
587 {
588 *(uintptr_t *)&gpfnChangeDisplaySettingsEx = (uintptr_t)GetProcAddress(hUser, "ChangeDisplaySettingsExA");
589 Log(("VBoxService: pChangeDisplaySettingsEx = %p\n", gpfnChangeDisplaySettingsEx));
590
591 if (gpfnChangeDisplaySettingsEx)
592 {
593 /* The screen index is 0 based in the ResizeDisplayDevice call. */
594 scr = scr > 0? scr - 1: 0;
595
596 /* Horizontal resolution must be a multiple of 8, round down. */
597 xres &= ~0x7;
598
599 RTPrintf("Setting resolution of display %d to %dx%dx%d ...", scr, xres, yres, bpp);
600 ResizeDisplayDevice(scr, xres, yres, bpp);
601 RTPrintf("done.\n");
602 }
603 else
604 VBoxControlError("Error retrieving API for display change!");
605 }
606 else
607 VBoxControlError("Error retrieving handle to user32.dll!");
608
609 return RTEXITCODE_SUCCESS;
610}
611
612static int checkVBoxVideoKey(HKEY hkeyVideo)
613{
614 char szValue[128];
615 DWORD len = sizeof(szValue);
616 DWORD dwKeyType;
617 LONG status = RegQueryValueExA(hkeyVideo, "Device Description", NULL, &dwKeyType,
618 (LPBYTE)szValue, &len);
619
620 if (status == ERROR_SUCCESS)
621 {
622 /* WDDM has additional chars after "Adapter" */
623 static char sszDeviceDescription[] = "VirtualBox Graphics Adapter";
624 if (_strnicmp(szValue, sszDeviceDescription, sizeof(sszDeviceDescription) - sizeof(char)) == 0)
625 {
626 return VINF_SUCCESS;
627 }
628 }
629
630 return VERR_NOT_FOUND;
631}
632
633static HKEY getVideoKey(bool writable)
634{
635 HKEY hkeyDeviceMap = 0;
636 LONG status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, "HARDWARE\\DEVICEMAP\\VIDEO", 0, KEY_READ, &hkeyDeviceMap);
637 if (status != ERROR_SUCCESS || !hkeyDeviceMap)
638 {
639 VBoxControlError("Error opening video device map registry key!\n");
640 return 0;
641 }
642
643 HKEY hkeyVideo = 0;
644 ULONG iDevice;
645 DWORD dwKeyType;
646
647 /*
648 * Scan all '\Device\VideoX' REG_SZ keys to find VBox video driver entry.
649 * 'ObjectNumberList' REG_BINARY is an array of 32 bit device indexes (X).
650 */
651
652 /* Get the 'ObjectNumberList' */
653 ULONG numDevices = 0;
654 DWORD adwObjectNumberList[256];
655 DWORD len = sizeof(adwObjectNumberList);
656 status = RegQueryValueExA(hkeyDeviceMap, "ObjectNumberList", NULL, &dwKeyType, (LPBYTE)&adwObjectNumberList[0], &len);
657
658 if ( status == ERROR_SUCCESS
659 && dwKeyType == REG_BINARY)
660 {
661 numDevices = len / sizeof(DWORD);
662 }
663 else
664 {
665 /* The list might not exists. Use 'MaxObjectNumber' REG_DWORD and build a list. */
666 DWORD dwMaxObjectNumber = 0;
667 len = sizeof(dwMaxObjectNumber);
668 status = RegQueryValueExA(hkeyDeviceMap, "MaxObjectNumber", NULL, &dwKeyType, (LPBYTE)&dwMaxObjectNumber, &len);
669
670 if ( status == ERROR_SUCCESS
671 && dwKeyType == REG_DWORD)
672 {
673 /* 'MaxObjectNumber' is inclusive. */
674 numDevices = RT_MIN(dwMaxObjectNumber + 1, RT_ELEMENTS(adwObjectNumberList));
675 for (iDevice = 0; iDevice < numDevices; iDevice++)
676 {
677 adwObjectNumberList[iDevice] = iDevice;
678 }
679 }
680 }
681
682 if (numDevices == 0)
683 {
684 /* Always try '\Device\Video0' as the old code did. Enum can be used in this case in principle. */
685 adwObjectNumberList[0] = 0;
686 numDevices = 1;
687 }
688
689 /* Scan device entries */
690 for (iDevice = 0; iDevice < numDevices; iDevice++)
691 {
692 char szValueName[64];
693 RTStrPrintf(szValueName, sizeof(szValueName), "\\Device\\Video%u", adwObjectNumberList[iDevice]);
694
695 char szVideoLocation[256];
696 len = sizeof(szVideoLocation);
697 status = RegQueryValueExA(hkeyDeviceMap, szValueName, NULL, &dwKeyType, (LPBYTE)&szVideoLocation[0], &len);
698
699 /* This value starts with '\REGISTRY\Machine' */
700 if ( status == ERROR_SUCCESS
701 && dwKeyType == REG_SZ
702 && _strnicmp(szVideoLocation, "\\REGISTRY\\Machine", 17) == 0)
703 {
704 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, &szVideoLocation[18], 0,
705 KEY_READ | (writable ? KEY_WRITE : 0), &hkeyVideo);
706 if (status == ERROR_SUCCESS)
707 {
708 int rc = checkVBoxVideoKey(hkeyVideo);
709 if (RT_SUCCESS(rc))
710 {
711 /* Found, return hkeyVideo to the caller. */
712 break;
713 }
714
715 RegCloseKey(hkeyVideo);
716 hkeyVideo = 0;
717 }
718 }
719 }
720
721 if (hkeyVideo == 0)
722 {
723 VBoxControlError("Error opening video registry key!\n");
724 }
725
726 RegCloseKey(hkeyDeviceMap);
727 return hkeyVideo;
728}
729
730static RTEXITCODE handleGetVideoAcceleration(int argc, char *argv[])
731{
732 ULONG status;
733 HKEY hkeyVideo = getVideoKey(false);
734
735 if (hkeyVideo)
736 {
737 /* query the actual value */
738 DWORD fAcceleration = 1;
739 DWORD len = sizeof(fAcceleration);
740 DWORD dwKeyType;
741 status = RegQueryValueExA(hkeyVideo, "EnableVideoAccel", NULL, &dwKeyType, (LPBYTE)&fAcceleration, &len);
742 if (status != ERROR_SUCCESS)
743 RTPrintf("Video acceleration: default\n");
744 else
745 RTPrintf("Video acceleration: %s\n", fAcceleration ? "on" : "off");
746 RegCloseKey(hkeyVideo);
747 }
748 return RTEXITCODE_SUCCESS;
749}
750
751static RTEXITCODE handleSetVideoAcceleration(int argc, char *argv[])
752{
753 ULONG status;
754 HKEY hkeyVideo;
755
756 /* must have exactly one argument: the new offset */
757 if ( (argc != 1)
758 || ( RTStrICmp(argv[0], "on")
759 && RTStrICmp(argv[0], "off")))
760 {
761 usage(SET_VIDEO_ACCEL);
762 return RTEXITCODE_FAILURE;
763 }
764
765 hkeyVideo = getVideoKey(true);
766
767 if (hkeyVideo)
768 {
769 int fAccel = 0;
770 if (RTStrICmp(argv[0], "on") == 0)
771 fAccel = 1;
772 /* set a new value */
773 status = RegSetValueExA(hkeyVideo, "EnableVideoAccel", 0, REG_DWORD, (LPBYTE)&fAccel, sizeof(fAccel));
774 if (status != ERROR_SUCCESS)
775 {
776 VBoxControlError("Error %d writing video acceleration status!\n", status);
777 }
778 RegCloseKey(hkeyVideo);
779 }
780 return RTEXITCODE_SUCCESS;
781}
782
783#define MAX_CUSTOM_MODES 128
784
785/* the table of custom modes */
786struct
787{
788 DWORD xres;
789 DWORD yres;
790 DWORD bpp;
791} customModes[MAX_CUSTOM_MODES] = {0};
792
793void getCustomModes(HKEY hkeyVideo)
794{
795 ULONG status;
796 int curMode = 0;
797
798 /* null out the table */
799 RT_ZERO(customModes);
800
801 do
802 {
803 char valueName[20];
804 DWORD xres, yres, bpp = 0;
805 DWORD dwType;
806 DWORD dwLen = sizeof(DWORD);
807
808 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", curMode);
809 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&xres, &dwLen);
810 if (status != ERROR_SUCCESS)
811 break;
812 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", curMode);
813 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&yres, &dwLen);
814 if (status != ERROR_SUCCESS)
815 break;
816 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", curMode);
817 status = RegQueryValueExA(hkeyVideo, valueName, NULL, &dwType, (LPBYTE)&bpp, &dwLen);
818 if (status != ERROR_SUCCESS)
819 break;
820
821 /* check if the mode is OK */
822 if ( (xres > (1 << 16))
823 && (yres > (1 << 16))
824 && ( (bpp != 16)
825 || (bpp != 24)
826 || (bpp != 32)))
827 break;
828
829 /* add mode to table */
830 customModes[curMode].xres = xres;
831 customModes[curMode].yres = yres;
832 customModes[curMode].bpp = bpp;
833
834 ++curMode;
835
836 if (curMode >= MAX_CUSTOM_MODES)
837 break;
838 } while(1);
839}
840
841void writeCustomModes(HKEY hkeyVideo)
842{
843 ULONG status;
844 int tableIndex = 0;
845 int modeIndex = 0;
846
847 /* first remove all values */
848 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
849 {
850 char valueName[20];
851 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", i);
852 RegDeleteValueA(hkeyVideo, valueName);
853 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", i);
854 RegDeleteValueA(hkeyVideo, valueName);
855 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", i);
856 RegDeleteValueA(hkeyVideo, valueName);
857 }
858
859 do
860 {
861 if (tableIndex >= MAX_CUSTOM_MODES)
862 break;
863
864 /* is the table entry present? */
865 if ( (!customModes[tableIndex].xres)
866 || (!customModes[tableIndex].yres)
867 || (!customModes[tableIndex].bpp))
868 {
869 tableIndex++;
870 continue;
871 }
872
873 RTPrintf("writing mode %d (%dx%dx%d)\n", modeIndex, customModes[tableIndex].xres, customModes[tableIndex].yres, customModes[tableIndex].bpp);
874 char valueName[20];
875 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dWidth", modeIndex);
876 status = RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].xres,
877 sizeof(customModes[tableIndex].xres));
878 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dHeight", modeIndex);
879 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].yres,
880 sizeof(customModes[tableIndex].yres));
881 RTStrPrintf(valueName, sizeof(valueName), "CustomMode%dBPP", modeIndex);
882 RegSetValueExA(hkeyVideo, valueName, 0, REG_DWORD, (LPBYTE)&customModes[tableIndex].bpp,
883 sizeof(customModes[tableIndex].bpp));
884
885 modeIndex++;
886 tableIndex++;
887
888 } while(1);
889
890}
891
892static RTEXITCODE handleListCustomModes(int argc, char *argv[])
893{
894 if (argc != 0)
895 {
896 usage(LIST_CUST_MODES);
897 return RTEXITCODE_FAILURE;
898 }
899
900 HKEY hkeyVideo = getVideoKey(false);
901
902 if (hkeyVideo)
903 {
904 getCustomModes(hkeyVideo);
905 for (int i = 0; i < (sizeof(customModes) / sizeof(customModes[0])); i++)
906 {
907 if ( !customModes[i].xres
908 || !customModes[i].yres
909 || !customModes[i].bpp)
910 continue;
911
912 RTPrintf("Mode: %d x %d x %d\n",
913 customModes[i].xres, customModes[i].yres, customModes[i].bpp);
914 }
915 RegCloseKey(hkeyVideo);
916 }
917 return RTEXITCODE_SUCCESS;
918}
919
920static RTEXITCODE handleAddCustomMode(int argc, char *argv[])
921{
922 if (argc != 3)
923 {
924 usage(ADD_CUST_MODE);
925 return RTEXITCODE_FAILURE;
926 }
927
928 DWORD xres = atoi(argv[0]);
929 DWORD yres = atoi(argv[1]);
930 DWORD bpp = atoi(argv[2]);
931
932 /** @todo better check including xres mod 8 = 0! */
933 if ( (xres > (1 << 16))
934 && (yres > (1 << 16))
935 && ( (bpp != 16)
936 || (bpp != 24)
937 || (bpp != 32)))
938 {
939 VBoxControlError("invalid mode specified!\n");
940 return RTEXITCODE_FAILURE;
941 }
942
943 HKEY hkeyVideo = getVideoKey(true);
944
945 if (hkeyVideo)
946 {
947 int i;
948 int fModeExists = 0;
949 getCustomModes(hkeyVideo);
950 for (i = 0; i < MAX_CUSTOM_MODES; i++)
951 {
952 /* mode exists? */
953 if ( customModes[i].xres == xres
954 && customModes[i].yres == yres
955 && customModes[i].bpp == bpp
956 )
957 {
958 fModeExists = 1;
959 }
960 }
961 if (!fModeExists)
962 {
963 for (i = 0; i < MAX_CUSTOM_MODES; i++)
964 {
965 /* item free? */
966 if (!customModes[i].xres)
967 {
968 customModes[i].xres = xres;
969 customModes[i].yres = yres;
970 customModes[i].bpp = bpp;
971 break;
972 }
973 }
974 writeCustomModes(hkeyVideo);
975 }
976 RegCloseKey(hkeyVideo);
977 }
978 return RTEXITCODE_SUCCESS;
979}
980
981static RTEXITCODE handleRemoveCustomMode(int argc, char *argv[])
982{
983 if (argc != 3)
984 {
985 usage(REMOVE_CUST_MODE);
986 return RTEXITCODE_FAILURE;
987 }
988
989 DWORD xres = atoi(argv[0]);
990 DWORD yres = atoi(argv[1]);
991 DWORD bpp = atoi(argv[2]);
992
993 HKEY hkeyVideo = getVideoKey(true);
994
995 if (hkeyVideo)
996 {
997 getCustomModes(hkeyVideo);
998 for (int i = 0; i < MAX_CUSTOM_MODES; i++)
999 {
1000 /* correct item? */
1001 if ( (customModes[i].xres == xres)
1002 && (customModes[i].yres == yres)
1003 && (customModes[i].bpp == bpp))
1004 {
1005 RTPrintf("found mode at index %d\n", i);
1006 RT_ZERO(customModes[i]);
1007 break;
1008 }
1009 }
1010 writeCustomModes(hkeyVideo);
1011 RegCloseKey(hkeyVideo);
1012 }
1013
1014 return RTEXITCODE_SUCCESS;
1015}
1016
1017#endif /* RT_OS_WINDOWS */
1018
1019#ifdef VBOX_WITH_GUEST_PROPS
1020/**
1021 * Retrieves a value from the guest property store.
1022 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1023 *
1024 * @returns Command exit code.
1025 * @note see the command line API description for parameters
1026 */
1027static RTEXITCODE getGuestProperty(int argc, char **argv)
1028{
1029 using namespace guestProp;
1030
1031 bool fVerbose = false;
1032 if ( 2 == argc
1033 && ( strcmp(argv[1], "-verbose") == 0
1034 || strcmp(argv[1], "--verbose") == 0)
1035 )
1036 fVerbose = true;
1037 else if (argc != 1)
1038 {
1039 usage(GUEST_PROP);
1040 return RTEXITCODE_FAILURE;
1041 }
1042
1043 uint32_t u32ClientId = 0;
1044 int rc = VINF_SUCCESS;
1045
1046 rc = VbglR3GuestPropConnect(&u32ClientId);
1047 if (!RT_SUCCESS(rc))
1048 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1049
1050 /*
1051 * Here we actually retrieve the value from the host.
1052 */
1053 const char *pszName = argv[0];
1054 char *pszValue = NULL;
1055 uint64_t u64Timestamp = 0;
1056 char *pszFlags = NULL;
1057 /* The buffer for storing the data and its initial size. We leave a bit
1058 * of space here in case the maximum values are raised. */
1059 void *pvBuf = NULL;
1060 uint32_t cbBuf = MAX_VALUE_LEN + MAX_FLAGS_LEN + 1024;
1061 if (RT_SUCCESS(rc))
1062 {
1063 /* Because there is a race condition between our reading the size of a
1064 * property and the guest updating it, we loop a few times here and
1065 * hope. Actually this should never go wrong, as we are generous
1066 * enough with buffer space. */
1067 bool finish = false;
1068 for (unsigned i = 0; (i < 10) && !finish; ++i)
1069 {
1070 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
1071 if (NULL == pvTmpBuf)
1072 {
1073 rc = VERR_NO_MEMORY;
1074 VBoxControlError("Out of memory\n");
1075 }
1076 else
1077 {
1078 pvBuf = pvTmpBuf;
1079 rc = VbglR3GuestPropRead(u32ClientId, pszName, pvBuf, cbBuf,
1080 &pszValue, &u64Timestamp, &pszFlags,
1081 &cbBuf);
1082 }
1083 if (VERR_BUFFER_OVERFLOW == rc)
1084 /* Leave a bit of extra space to be safe */
1085 cbBuf += 1024;
1086 else
1087 finish = true;
1088 }
1089 if (VERR_TOO_MUCH_DATA == rc)
1090 VBoxControlError("Temporarily unable to retrieve the property\n");
1091 else if (!RT_SUCCESS(rc) && (rc != VERR_NOT_FOUND))
1092 VBoxControlError("Failed to retrieve the property value, error %Rrc\n", rc);
1093 }
1094
1095 /*
1096 * And display it on the guest console.
1097 */
1098 if (VERR_NOT_FOUND == rc)
1099 RTPrintf("No value set!\n");
1100 else if (RT_SUCCESS(rc))
1101 {
1102 RTPrintf("Value: %s\n", pszValue);
1103 if (fVerbose)
1104 {
1105 RTPrintf("Timestamp: %lld ns\n", u64Timestamp);
1106 RTPrintf("Flags: %s\n", pszFlags);
1107 }
1108 }
1109
1110 if (u32ClientId != 0)
1111 VbglR3GuestPropDisconnect(u32ClientId);
1112 RTMemFree(pvBuf);
1113 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1114}
1115
1116
1117/**
1118 * Writes a value to the guest property store.
1119 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1120 *
1121 * @returns Command exit code.
1122 * @note see the command line API description for parameters
1123 */
1124static RTEXITCODE setGuestProperty(int argc, char *argv[])
1125{
1126 /*
1127 * Check the syntax. We can deduce the correct syntax from the number of
1128 * arguments.
1129 */
1130 bool usageOK = true;
1131 const char *pszName = NULL;
1132 const char *pszValue = NULL;
1133 const char *pszFlags = NULL;
1134 if (2 == argc)
1135 {
1136 pszValue = argv[1];
1137 }
1138 else if (3 == argc)
1139 usageOK = false;
1140 else if (4 == argc)
1141 {
1142 pszValue = argv[1];
1143 if ( strcmp(argv[2], "-flags") != 0
1144 && strcmp(argv[2], "--flags") != 0)
1145 usageOK = false;
1146 pszFlags = argv[3];
1147 }
1148 else if (argc != 1)
1149 usageOK = false;
1150 if (!usageOK)
1151 {
1152 usage(GUEST_PROP);
1153 return RTEXITCODE_FAILURE;
1154 }
1155 /* This is always needed. */
1156 pszName = argv[0];
1157
1158 /*
1159 * Do the actual setting.
1160 */
1161 uint32_t u32ClientId = 0;
1162 int rc = VINF_SUCCESS;
1163 rc = VbglR3GuestPropConnect(&u32ClientId);
1164 if (!RT_SUCCESS(rc))
1165 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1166 if (RT_SUCCESS(rc))
1167 {
1168 if (pszFlags != NULL)
1169 rc = VbglR3GuestPropWrite(u32ClientId, pszName, pszValue, pszFlags);
1170 else
1171 rc = VbglR3GuestPropWriteValue(u32ClientId, pszName, pszValue);
1172 if (!RT_SUCCESS(rc))
1173 VBoxControlError("Failed to store the property value, error %Rrc\n", rc);
1174 }
1175
1176 if (u32ClientId != 0)
1177 VbglR3GuestPropDisconnect(u32ClientId);
1178 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1179}
1180
1181
1182/**
1183 * Deletes a guest property from the guest property store.
1184 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1185 *
1186 * @returns Command exit code.
1187 * @note see the command line API description for parameters
1188 */
1189static RTEXITCODE deleteGuestProperty(int argc, char *argv[])
1190{
1191 /*
1192 * Check the syntax. We can deduce the correct syntax from the number of
1193 * arguments.
1194 */
1195 bool usageOK = true;
1196 const char *pszName = NULL;
1197 if (argc < 1)
1198 usageOK = false;
1199 if (!usageOK)
1200 {
1201 usage(GUEST_PROP);
1202 return RTEXITCODE_FAILURE;
1203 }
1204 /* This is always needed. */
1205 pszName = argv[0];
1206
1207 /*
1208 * Do the actual setting.
1209 */
1210 uint32_t u32ClientId = 0;
1211 int rc = VbglR3GuestPropConnect(&u32ClientId);
1212 if (!RT_SUCCESS(rc))
1213 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1214 if (RT_SUCCESS(rc))
1215 {
1216 rc = VbglR3GuestPropDelete(u32ClientId, pszName);
1217 if (!RT_SUCCESS(rc))
1218 VBoxControlError("Failed to delete the property value, error %Rrc\n", rc);
1219 }
1220
1221 if (u32ClientId != 0)
1222 VbglR3GuestPropDisconnect(u32ClientId);
1223 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1224}
1225
1226
1227/**
1228 * Enumerates the properties in the guest property store.
1229 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1230 *
1231 * @returns Command exit code.
1232 * @note see the command line API description for parameters
1233 */
1234static RTEXITCODE enumGuestProperty(int argc, char *argv[])
1235{
1236 /*
1237 * Check the syntax. We can deduce the correct syntax from the number of
1238 * arguments.
1239 */
1240 char const * const *papszPatterns = NULL;
1241 uint32_t cPatterns = 0;
1242 if ( argc > 1
1243 && ( strcmp(argv[0], "-patterns") == 0
1244 || strcmp(argv[0], "--patterns") == 0))
1245 {
1246 papszPatterns = (char const * const *)&argv[1];
1247 cPatterns = argc - 1;
1248 }
1249 else if (argc != 0)
1250 {
1251 usage(GUEST_PROP);
1252 return RTEXITCODE_FAILURE;
1253 }
1254
1255 /*
1256 * Do the actual enumeration.
1257 */
1258 uint32_t u32ClientId = 0;
1259 int rc = VbglR3GuestPropConnect(&u32ClientId);
1260 if (RT_SUCCESS(rc))
1261 {
1262 PVBGLR3GUESTPROPENUM pHandle;
1263 const char *pszName, *pszValue, *pszFlags;
1264 uint64_t u64Timestamp;
1265
1266 rc = VbglR3GuestPropEnum(u32ClientId, papszPatterns, cPatterns, &pHandle,
1267 &pszName, &pszValue, &u64Timestamp, &pszFlags);
1268 if (RT_SUCCESS(rc))
1269 {
1270 while (RT_SUCCESS(rc) && pszName)
1271 {
1272 RTPrintf("Name: %s, value: %s, timestamp: %lld, flags: %s\n",
1273 pszName, pszValue, u64Timestamp, pszFlags);
1274
1275 rc = VbglR3GuestPropEnumNext(pHandle, &pszName, &pszValue, &u64Timestamp, &pszFlags);
1276 if (RT_FAILURE(rc))
1277 VBoxControlError("Error while enumerating guest properties: %Rrc\n", rc);
1278 }
1279
1280 VbglR3GuestPropEnumFree(pHandle);
1281 }
1282 else if (VERR_NOT_FOUND == rc)
1283 RTPrintf("No properties found.\n");
1284 else
1285 VBoxControlError("Failed to enumerate the guest properties! Error: %Rrc\n", rc);
1286 VbglR3GuestPropDisconnect(u32ClientId);
1287 }
1288 else
1289 VBoxControlError("Failed to connect to the guest property service! Error: %Rrc\n", rc);
1290 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1291}
1292
1293
1294/**
1295 * Waits for notifications of changes to guest properties.
1296 * This is accessed through the "VBoxGuestPropSvc" HGCM service.
1297 *
1298 * @returns Command exit code.
1299 * @note see the command line API description for parameters
1300 */
1301static RTEXITCODE waitGuestProperty(int argc, char **argv)
1302{
1303 using namespace guestProp;
1304
1305 /*
1306 * Handle arguments
1307 */
1308 const char *pszPatterns = NULL;
1309 uint64_t u64TimestampIn = 0;
1310 uint32_t u32Timeout = RT_INDEFINITE_WAIT;
1311 bool usageOK = true;
1312 if (argc < 1)
1313 usageOK = false;
1314 pszPatterns = argv[0];
1315 for (int i = 1; usageOK && i < argc; ++i)
1316 {
1317 if ( strcmp(argv[i], "-timeout") == 0
1318 || strcmp(argv[i], "--timeout") == 0)
1319 {
1320 if ( i + 1 >= argc
1321 || RTStrToUInt32Full(argv[i + 1], 10, &u32Timeout)
1322 != VINF_SUCCESS
1323 )
1324 usageOK = false;
1325 else
1326 ++i;
1327 }
1328 else if ( strcmp(argv[i], "-timestamp") == 0
1329 || strcmp(argv[i], "--timestamp") == 0)
1330 {
1331 if ( i + 1 >= argc
1332 || RTStrToUInt64Full(argv[i + 1], 10, &u64TimestampIn)
1333 != VINF_SUCCESS
1334 )
1335 usageOK = false;
1336 else
1337 ++i;
1338 }
1339 else
1340 usageOK = false;
1341 }
1342 if (!usageOK)
1343 {
1344 usage(GUEST_PROP);
1345 return RTEXITCODE_FAILURE;
1346 }
1347
1348 /*
1349 * Connect to the service
1350 */
1351 uint32_t u32ClientId = 0;
1352 int rc = VINF_SUCCESS;
1353
1354 rc = VbglR3GuestPropConnect(&u32ClientId);
1355 if (!RT_SUCCESS(rc))
1356 VBoxControlError("Failed to connect to the guest property service, error %Rrc\n", rc);
1357
1358 /*
1359 * Retrieve the notification from the host
1360 */
1361 char *pszName = NULL;
1362 char *pszValue = NULL;
1363 uint64_t u64TimestampOut = 0;
1364 char *pszFlags = NULL;
1365 /* The buffer for storing the data and its initial size. We leave a bit
1366 * of space here in case the maximum values are raised. */
1367 void *pvBuf = NULL;
1368 uint32_t cbBuf = MAX_NAME_LEN + MAX_VALUE_LEN + MAX_FLAGS_LEN + 1024;
1369 /* Because there is a race condition between our reading the size of a
1370 * property and the guest updating it, we loop a few times here and
1371 * hope. Actually this should never go wrong, as we are generous
1372 * enough with buffer space. */
1373 bool finish = false;
1374 for (unsigned i = 0;
1375 (RT_SUCCESS(rc) || rc == VERR_BUFFER_OVERFLOW) && !finish && (i < 10);
1376 ++i)
1377 {
1378 void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
1379 if (NULL == pvTmpBuf)
1380 {
1381 rc = VERR_NO_MEMORY;
1382 VBoxControlError("Out of memory\n");
1383 }
1384 else
1385 {
1386 pvBuf = pvTmpBuf;
1387 rc = VbglR3GuestPropWait(u32ClientId, pszPatterns, pvBuf, cbBuf,
1388 u64TimestampIn, u32Timeout,
1389 &pszName, &pszValue, &u64TimestampOut,
1390 &pszFlags, &cbBuf);
1391 }
1392 if (VERR_BUFFER_OVERFLOW == rc)
1393 /* Leave a bit of extra space to be safe */
1394 cbBuf += 1024;
1395 else
1396 finish = true;
1397 if (rc == VERR_TOO_MUCH_DATA)
1398 VBoxControlError("Temporarily unable to get a notification\n");
1399 else if (rc == VERR_INTERRUPTED)
1400 VBoxControlError("The request timed out or was interrupted\n");
1401#ifndef RT_OS_WINDOWS /* Windows guests do not do this right */
1402 else if (!RT_SUCCESS(rc) && (rc != VERR_NOT_FOUND))
1403 VBoxControlError("Failed to get a notification, error %Rrc\n", rc);
1404#endif
1405 }
1406
1407 /*
1408 * And display it on the guest console.
1409 */
1410 if (VERR_NOT_FOUND == rc)
1411 RTPrintf("No value set!\n");
1412 else if (rc == VERR_BUFFER_OVERFLOW)
1413 RTPrintf("Internal error: unable to determine the size of the data!\n");
1414 else if (RT_SUCCESS(rc))
1415 {
1416 RTPrintf("Name: %s\n", pszName);
1417 RTPrintf("Value: %s\n", pszValue);
1418 RTPrintf("Timestamp: %lld ns\n", u64TimestampOut);
1419 RTPrintf("Flags: %s\n", pszFlags);
1420 }
1421
1422 if (u32ClientId != 0)
1423 VbglR3GuestPropDisconnect(u32ClientId);
1424 RTMemFree(pvBuf);
1425 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1426}
1427
1428
1429/**
1430 * Access the guest property store through the "VBoxGuestPropSvc" HGCM
1431 * service.
1432 *
1433 * @returns 0 on success, 1 on failure
1434 * @note see the command line API description for parameters
1435 */
1436static RTEXITCODE handleGuestProperty(int argc, char *argv[])
1437{
1438 if (0 == argc)
1439 {
1440 usage(GUEST_PROP);
1441 return RTEXITCODE_FAILURE;
1442 }
1443 if (!strcmp(argv[0], "get"))
1444 return getGuestProperty(argc - 1, argv + 1);
1445 else if (!strcmp(argv[0], "set"))
1446 return setGuestProperty(argc - 1, argv + 1);
1447 else if (!strcmp(argv[0], "delete") || !strcmp(argv[0], "unset"))
1448 return deleteGuestProperty(argc - 1, argv + 1);
1449 else if (!strcmp(argv[0], "enumerate"))
1450 return enumGuestProperty(argc - 1, argv + 1);
1451 else if (!strcmp(argv[0], "wait"))
1452 return waitGuestProperty(argc - 1, argv + 1);
1453 /* else */
1454 usage(GUEST_PROP);
1455 return RTEXITCODE_FAILURE;
1456}
1457#endif
1458
1459#ifdef VBOX_WITH_SHARED_FOLDERS
1460/**
1461 * Lists the Shared Folders provided by the host.
1462 */
1463static RTEXITCODE listSharedFolders(int argc, char **argv)
1464{
1465 bool usageOK = true;
1466 bool fOnlyShowAutoMount = false;
1467 if (argc == 1)
1468 {
1469 if ( !strcmp(argv[0], "-automount")
1470 || !strcmp(argv[0], "--automount"))
1471 fOnlyShowAutoMount = true;
1472 else
1473 usageOK = false;
1474 }
1475 else if (argc > 1)
1476 usageOK = false;
1477
1478 if (!usageOK)
1479 {
1480 usage(GUEST_SHAREDFOLDERS);
1481 return RTEXITCODE_FAILURE;
1482 }
1483
1484 uint32_t u32ClientId;
1485 int rc = VbglR3SharedFolderConnect(&u32ClientId);
1486 if (!RT_SUCCESS(rc))
1487 VBoxControlError("Failed to connect to the shared folder service, error %Rrc\n", rc);
1488 else
1489 {
1490 PVBGLR3SHAREDFOLDERMAPPING paMappings;
1491 uint32_t cMappings;
1492 rc = VbglR3SharedFolderGetMappings(u32ClientId, fOnlyShowAutoMount,
1493 &paMappings, &cMappings);
1494 if (RT_SUCCESS(rc))
1495 {
1496 if (fOnlyShowAutoMount)
1497 RTPrintf("Auto-mounted Shared Folder mappings (%u):\n\n", cMappings);
1498 else
1499 RTPrintf("Shared Folder mappings (%u):\n\n", cMappings);
1500
1501 for (uint32_t i = 0; i < cMappings; i++)
1502 {
1503 char *pszName;
1504 rc = VbglR3SharedFolderGetName(u32ClientId, paMappings[i].u32Root, &pszName);
1505 if (RT_SUCCESS(rc))
1506 {
1507 RTPrintf("%02u - %s\n", i + 1, pszName);
1508 RTStrFree(pszName);
1509 }
1510 else
1511 VBoxControlError("Error while getting the shared folder name for root node = %u, rc = %Rrc\n",
1512 paMappings[i].u32Root, rc);
1513 }
1514 if (!cMappings)
1515 RTPrintf("No Shared Folders available.\n");
1516 VbglR3SharedFolderFreeMappings(paMappings);
1517 }
1518 else
1519 VBoxControlError("Error while getting the shared folder mappings, rc = %Rrc\n", rc);
1520 VbglR3SharedFolderDisconnect(u32ClientId);
1521 }
1522 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
1523}
1524
1525/**
1526 * Handles Shared Folders control.
1527 *
1528 * @returns 0 on success, 1 on failure
1529 * @note see the command line API description for parameters
1530 * (r=bird: yeah, right. The API description contains nil about params)
1531 */
1532static RTEXITCODE handleSharedFolder(int argc, char *argv[])
1533{
1534 if (0 == argc)
1535 {
1536 usage(GUEST_SHAREDFOLDERS);
1537 return RTEXITCODE_FAILURE;
1538 }
1539 if (!strcmp(argv[0], "list"))
1540 return listSharedFolders(argc - 1, argv + 1);
1541 /* else */
1542 usage(GUEST_SHAREDFOLDERS);
1543 return RTEXITCODE_FAILURE;
1544}
1545#endif
1546
1547#if !defined(VBOX_CONTROL_TEST)
1548/**
1549 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: writecoredump}
1550 */
1551static RTEXITCODE handleWriteCoreDump(int argc, char *argv[])
1552{
1553 int rc = VbglR3WriteCoreDump();
1554 if (RT_SUCCESS(rc))
1555 {
1556 RTPrintf("Guest core dump successful.\n");
1557 return RTEXITCODE_SUCCESS;
1558 }
1559 else
1560 {
1561 VBoxControlError("Error while taking guest core dump. rc=%Rrc\n", rc);
1562 return RTEXITCODE_FAILURE;
1563 }
1564}
1565#endif
1566
1567#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1568/**
1569 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
1570 */
1571static RTEXITCODE handleDpc(int argc, char *argv[])
1572{
1573# ifndef VBOX_CONTROL_TEST
1574 int rc;
1575 for (int i = 0; i < 30; i++)
1576 {
1577 rc = vbglR3DoIOCtl(VBOXGUEST_IOCTL_DPC_LATENCY_CHECKER, NULL, 0);
1578 if (RT_FAILURE(rc))
1579 break;
1580 RTPrintf("%d\n", i);
1581 }
1582# else
1583 int rc = VERR_NOT_IMPLEMENTED;
1584# endif
1585 if (RT_FAILURE(rc))
1586 return VBoxControlError("Error. rc=%Rrc\n", rc);
1587 RTPrintf("Samples collection completed.\n");
1588 return RTEXITCODE_SUCCESS;
1589}
1590#endif /* VBOX_WITH_DPC_LATENCY_CHECKER */
1591
1592
1593/**
1594 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: takesnapshot}
1595 */
1596static RTEXITCODE handleTakeSnapshot(int argc, char *argv[])
1597{
1598 //VbglR3VmTakeSnapshot(argv[0], argv[1]);
1599 return VBoxControlError("not implemented");
1600}
1601
1602/**
1603 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: savestate}
1604 */
1605static RTEXITCODE handleSaveState(int argc, char *argv[])
1606{
1607 //VbglR3VmSaveState();
1608 return VBoxControlError("not implemented");
1609}
1610
1611/**
1612 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: suspend|pause}
1613 */
1614static RTEXITCODE handleSuspend(int argc, char *argv[])
1615{
1616 //VbglR3VmSuspend();
1617 return VBoxControlError("not implemented");
1618}
1619
1620/**
1621 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: poweroff|powerdown}
1622 */
1623static RTEXITCODE handlePowerOff(int argc, char *argv[])
1624{
1625 //VbglR3VmPowerOff();
1626 return VBoxControlError("not implemented");
1627}
1628
1629/**
1630 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: version}
1631 */
1632static RTEXITCODE handleVersion(int argc, char *argv[])
1633{
1634 if (argc)
1635 return VBoxControlSyntaxError("getversion does not take any arguments");
1636 RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1637 return RTEXITCODE_SUCCESS;
1638}
1639
1640/**
1641 * @callback_method_impl{FNVBOXCTRLCMDHANDLER, Command: help}
1642 */
1643static RTEXITCODE handleHelp(int argc, char *argv[])
1644{
1645 /* ignore arguments for now. */
1646 usage();
1647 return RTEXITCODE_SUCCESS;
1648}
1649
1650
1651/** command handler type */
1652typedef DECLCALLBACK(RTEXITCODE) FNVBOXCTRLCMDHANDLER(int argc, char *argv[]);
1653typedef FNVBOXCTRLCMDHANDLER *PFNVBOXCTRLCMDHANDLER;
1654
1655/** The table of all registered command handlers. */
1656struct COMMANDHANDLER
1657{
1658 const char *pszCommand;
1659 PFNVBOXCTRLCMDHANDLER pfnHandler;
1660} g_aCommandHandlers[] =
1661{
1662#if defined(RT_OS_WINDOWS) && !defined(VBOX_CONTROL_TEST)
1663 { "getvideoacceleration", handleGetVideoAcceleration },
1664 { "setvideoacceleration", handleSetVideoAcceleration },
1665 { "listcustommodes", handleListCustomModes },
1666 { "addcustommode", handleAddCustomMode },
1667 { "removecustommode", handleRemoveCustomMode },
1668 { "setvideomode", handleSetVideoMode },
1669#endif
1670#ifdef VBOX_WITH_GUEST_PROPS
1671 { "guestproperty", handleGuestProperty },
1672#endif
1673#ifdef VBOX_WITH_SHARED_FOLDERS
1674 { "sharedfolder", handleSharedFolder },
1675#endif
1676#if !defined(VBOX_CONTROL_TEST)
1677 { "writecoredump", handleWriteCoreDump },
1678#endif
1679#ifdef VBOX_WITH_DPC_LATENCY_CHECKER
1680 { "dpc", handleDpc },
1681#endif
1682 { "takesnapshot", handleTakeSnapshot },
1683 { "savestate", handleSaveState },
1684 { "suspend", handleSuspend },
1685 { "pause", handleSuspend },
1686 { "poweroff", handlePowerOff },
1687 { "powerdown", handlePowerOff },
1688 { "getversion", handleVersion },
1689 { "version", handleVersion },
1690 { "help", handleHelp }
1691};
1692
1693/** Main function */
1694int main(int argc, char **argv)
1695{
1696 /** The application's global return code */
1697 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
1698 /** An IPRT return code for local use */
1699 int rrc = VINF_SUCCESS;
1700 /** The index of the command line argument we are currently processing */
1701 int iArg = 1;
1702 /** Should we show the logo text? */
1703 bool fShowLogo = true;
1704 /** Should we print the usage after the logo? For the -help switch. */
1705 bool fDoHelp = false;
1706 /** Will we be executing a command or just printing information? */
1707 bool fOnlyInfo = false;
1708
1709 rrc = RTR3InitExe(argc, &argv, 0);
1710 if (RT_FAILURE(rrc))
1711 return RTMsgInitFailure(rrc);
1712
1713 /*
1714 * Start by handling command line switches
1715 */
1716 /** @todo RTGetOpt conversion of the whole file. */
1717 bool done = false; /**< Are we finished with handling switches? */
1718 while (!done && (iArg < argc))
1719 {
1720 if ( !strcmp(argv[iArg], "-V")
1721 || !strcmp(argv[iArg], "-v")
1722 || !strcmp(argv[iArg], "--version")
1723 || !strcmp(argv[iArg], "-version")
1724 )
1725 {
1726 /* Print version number, and do nothing else. */
1727 RTPrintf("%sr%u\n", VBOX_VERSION_STRING, RTBldCfgRevision());
1728 fOnlyInfo = true;
1729 fShowLogo = false;
1730 done = true;
1731 }
1732 else if ( !strcmp(argv[iArg], "-nologo")
1733 || !strcmp(argv[iArg], "--nologo"))
1734 fShowLogo = false;
1735 else if ( !strcmp(argv[iArg], "-help")
1736 || !strcmp(argv[iArg], "--help"))
1737 {
1738 fOnlyInfo = true;
1739 fDoHelp = true;
1740 done = true;
1741 }
1742 else
1743 /* We have found an argument which isn't a switch. Exit to the
1744 * command processing bit. */
1745 done = true;
1746 if (!done)
1747 ++iArg;
1748 }
1749
1750 /*
1751 * Find the application name, show our logo if the user hasn't suppressed it,
1752 * and show the usage if the user asked us to
1753 */
1754 g_pszProgName = RTPathFilename(argv[0]);
1755 if (fShowLogo)
1756 RTPrintf(VBOX_PRODUCT " Guest Additions Command Line Management Interface Version "
1757 VBOX_VERSION_STRING "\n"
1758 "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
1759 "All rights reserved.\n\n");
1760 if (fDoHelp)
1761 usage();
1762
1763 /*
1764 * Do global initialisation for the programme if we will be handling a command
1765 */
1766 if (!fOnlyInfo)
1767 {
1768 rrc = VbglR3Init();
1769 if (RT_FAILURE(rrc))
1770 {
1771 VBoxControlError("Could not contact the host system. Make sure that you are running this\n"
1772 "application inside a VirtualBox guest system, and that you have sufficient\n"
1773 "user permissions.\n");
1774 rcExit = RTEXITCODE_FAILURE;
1775 }
1776 }
1777
1778 /*
1779 * Now look for an actual command in the argument list and handle it.
1780 */
1781
1782 if (!fOnlyInfo && rcExit == RTEXITCODE_SUCCESS)
1783 {
1784 if (argc > iArg)
1785 {
1786 /*
1787 * Try locate the command and execute it, complain if not found.
1788 */
1789 unsigned i;
1790 for (i = 0; i < RT_ELEMENTS(g_aCommandHandlers); i++)
1791 if (!strcmp(argv[iArg], g_aCommandHandlers[i].pszCommand))
1792 {
1793 rcExit = g_aCommandHandlers[i].pfnHandler(argc - iArg - 1, argv + iArg + 1);
1794 break;
1795 }
1796 if (i >= RT_ELEMENTS(g_aCommandHandlers))
1797 {
1798 rcExit = RTEXITCODE_FAILURE;
1799 usage();
1800 }
1801 }
1802 else
1803 {
1804 /* The user didn't specify a command. */
1805 rcExit = RTEXITCODE_FAILURE;
1806 usage();
1807 }
1808 }
1809
1810 /*
1811 * And exit, returning the status
1812 */
1813 return rcExit;
1814}
1815
Note: See TracBrowser for help on using the repository browser.

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