VirtualBox

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

Last change on this file since 58029 was 57415, checked in by vboxsync, 10 years ago

More DECLCALLBACK fixes.

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

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