VirtualBox

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

Last change on this file since 75467 was 75467, checked in by vboxsync, 7 years ago

VBoxControl,VBoxService: Some OS/2 bits.

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

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