VirtualBox

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

Last change on this file since 47631 was 46896, checked in by vboxsync, 12 years ago

VBoxDisp,VBoxVideo,VBoxControl: registry flags to tweak the Windows guest graphics driver.

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

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