VirtualBox

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

Last change on this file since 55628 was 54611, checked in by vboxsync, 10 years ago

VBoxControl writelog: Append newline to each argument if it hasn't got any.

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