VirtualBox

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

Last change on this file since 86178 was 85121, checked in by vboxsync, 5 years ago

iprt/cdefs.h: Refactored the typedef use of DECLCALLBACK as well as DECLCALLBACKMEMBER to wrap the whole expression, similar to the DECLR?CALLBACKMEMBER macros. This allows adding a throw() at the end when compiling with the VC++ compiler to indicate that the callbacks won't throw anything, so we can stop supressing the C5039 warning about passing functions that can potential throw C++ exceptions to extern C code that can't necessarily cope with such (unwind,++). Introduced a few _EX variations that allows specifying different/no calling convention too, as that's handy when dynamically resolving host APIs. Fixed numerous places missing DECLCALLBACK and such. Left two angry @todos regarding use of CreateThread. bugref:9794

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