VirtualBox

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

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

Additions/VBoxGuestLib+Additions/VBoxControl: Guest core dumps (VbglR3WriteCoreDump), untested.

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