VirtualBox

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

Last change on this file since 37826 was 33540, checked in by vboxsync, 14 years ago

*: spelling fixes, thanks Timeless!

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

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