VirtualBox

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

Last change on this file since 32205 was 32205, checked in by vboxsync, 15 years ago

VBoxControl: More cleanup.

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