VirtualBox

source: vbox/trunk/src/VBox/GuestHost/installation/VBoxDrvInst.cpp@ 108371

最後變更 在這個檔案從108371是 107988,由 vboxsync 提交於 7 週 前

Windows driver installation: Implemented APIs for controlling (driver SCM) services [renaming fix]. bugref:10762

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Author Date Id Revision
檔案大小: 39.4 KB
 
1/* $Id: VBoxDrvInst.cpp 107988 2025-01-30 12:14:47Z vboxsync $ */
2/** @file
3 * Driver installation utility for Windows hosts and guests.
4 */
5
6/*
7 * Copyright (C) 2024 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.alldomusa.eu.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28
29/*********************************************************************************************************************************
30* Header Files *
31*********************************************************************************************************************************/
32#include <iprt/buildconfig.h>
33#include <iprt/ctype.h>
34#include <iprt/cpp/ministring.h> /* For replacement fun. */
35#include <iprt/env.h>
36#include <iprt/getopt.h>
37#include <iprt/initterm.h>
38#include <iprt/message.h>
39#include <iprt/path.h>
40#include <iprt/types.h>
41#include <iprt/process.h> /* For RTProcShortName(). */
42#include <iprt/stream.h>
43#include <iprt/string.h>
44#include <iprt/system.h>
45#include <iprt/test.h>
46#include <iprt/utf16.h>
47#include <iprt/win/windows.h>
48
49#include <package-generated.h>
50#include "product-generated.h"
51
52#include <VBox/version.h>
53#include <VBox/log.h>
54
55#include <VBox/err.h>
56
57#include <VBox/GuestHost/VBoxWinDrvInst.h>
58#include <VBox/GuestHost/VBoxWinDrvStore.h>
59
60
61/*********************************************************************************************************************************
62* Prototypes *
63*********************************************************************************************************************************/
64static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdListMain(PRTGETOPTSTATE pGetState);
65static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdInstallMain(PRTGETOPTSTATE pGetState);
66static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdUninstallMain(PRTGETOPTSTATE pGetState);
67static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdServiceMain(PRTGETOPTSTATE pGetState);
68
69static DECLCALLBACK(const char *) vboxDrvInstCmdListHelp(PCRTGETOPTDEF pOpt);
70static DECLCALLBACK(const char *) vboxDrvInstCmdInstallHelp(PCRTGETOPTDEF pOpt);
71static DECLCALLBACK(const char *) vboxDrvInstCmdUninstallHelp(PCRTGETOPTDEF pOpt);
72static DECLCALLBACK(const char *) vboxDrvInstCmdServiceHelp(PCRTGETOPTDEF pOpt);
73
74struct VBOXDRVINSTCMD;
75static RTEXITCODE vboxDrvInstShowUsage(PRTSTREAM pStrm, VBOXDRVINSTCMD const *pOnlyCmd);
76
77
78/*********************************************************************************************************************************
79* Global Variables *
80*********************************************************************************************************************************/
81/** Verbosity level. */
82static unsigned g_uVerbosity = 0;
83static PRTLOGGER g_pLoggerRelease = NULL;
84static char g_szLogFile[RTPATH_MAX];
85static uint32_t g_cHistory = 10; /* Enable log rotation, 10 files. */
86static uint32_t g_uHistoryFileTime = RT_SEC_1DAY; /* Max 1 day per file. */
87static uint64_t g_uHistoryFileSize = 100 * _1M; /* Max 100MB per file. */
88
89
90/*********************************************************************************************************************************
91* Definitions *
92*********************************************************************************************************************************/
93typedef enum VBOXDRVINSTEXITCODE
94{
95 /** A reboot is needed in order to complete the (un)installation. */
96 VBOXDRVINSTEXITCODE_REBOOT_NEEDED = RTEXITCODE_END
97} VBOXDRVINSTEXITCODE;
98
99/**
100 * Driver installation command table entry.
101 */
102typedef struct VBOXDRVINSTCMD
103{
104 /** The command name. */
105 const char *pszCommand;
106 /** The command handler. */
107 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(PRTGETOPTSTATE pGetState));
108
109 /** Command description. */
110 const char *pszDesc;
111 /** Options array. */
112 PCRTGETOPTDEF paOptions;
113 /** Number of options in the option array. */
114 size_t cOptions;
115 /** Gets help for an option. */
116 DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt));
117} VBOXDRVINSTCMD;
118/** Pointer to a const VBOXDRVINSTCMD entry. */
119typedef VBOXDRVINSTCMD const *PCVBOXDRVINSTCMD;
120
121/**
122 * Command definition for the 'list' command.
123 */
124const VBOXDRVINSTCMD g_CmdList =
125{
126 "list",
127 vboxDrvInstCmdListMain,
128 "Lists installed drivers.",
129 NULL, /* paOptions */
130 0, /* cOptions */
131 vboxDrvInstCmdListHelp
132};
133
134/**
135 * Long option values for the 'install' command.
136 */
137enum
138{
139 VBOXDRVINST_INSTALL_OPT_INF_FILE = 900,
140 VBOXDRVINST_INSTALL_OPT_INF_SECTION,
141 VBOXDRVINST_INSTALL_OPT_MODEL,
142 VBOXDRVINST_INSTALL_OPT_PNPID,
143 VBOXDRVINST_INSTALL_OPT_NOT_FORCE,
144 VBOXDRVINST_INSTALL_OPT_NOT_SILENT,
145 VBOXDRVINST_INSTALL_OPT_IGNORE_REBOOT,
146 VBOXDRVINST_INSTALL_OPT_DEBUG_OS_VER
147};
148
149/**
150 * Command line parameters for the 'install' command.
151 */
152static const RTGETOPTDEF g_aCmdInstallOptions[] =
153{
154 { "--inf-file", VBOXDRVINST_INSTALL_OPT_INF_FILE, RTGETOPT_REQ_STRING },
155 { "--inf-section", VBOXDRVINST_INSTALL_OPT_INF_SECTION, RTGETOPT_REQ_STRING },
156 { "--model", VBOXDRVINST_INSTALL_OPT_MODEL, RTGETOPT_REQ_STRING },
157 { "--pnp", VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
158 { "--pnpid" , VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
159 { "--pnp-id", VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
160 { "--not-force", VBOXDRVINST_INSTALL_OPT_NOT_FORCE, RTGETOPT_REQ_NOTHING },
161 { "--not-silent", VBOXDRVINST_INSTALL_OPT_NOT_SILENT, RTGETOPT_REQ_NOTHING },
162 { "--ignore-reboot", VBOXDRVINST_INSTALL_OPT_IGNORE_REBOOT, RTGETOPT_REQ_NOTHING },
163 { "--debug-os-ver", VBOXDRVINST_INSTALL_OPT_DEBUG_OS_VER, RTGETOPT_REQ_UINT32_PAIR }
164};
165
166/**
167 * Command definition for the 'install' command.
168 */
169const VBOXDRVINSTCMD g_CmdInstall =
170{
171 "install",
172 vboxDrvInstCmdInstallMain,
173 "Installs a driver.",
174 g_aCmdInstallOptions,
175 RT_ELEMENTS(g_aCmdInstallOptions),
176 vboxDrvInstCmdInstallHelp
177};
178
179/**
180 * Long option values for the 'uninstall' command.
181 */
182enum
183{
184 VBOXDRVINST_UNINSTALL_OPT_INF_FILE = 900,
185 VBOXDRVINST_UNINSTALL_OPT_INF_SECTION,
186 VBOXDRVINST_UNINSTALL_OPT_MODEL,
187 VBOXDRVINST_UNINSTALL_OPT_PNPID,
188 VBOXDRVINST_UNINSTALL_OPT_NOT_SILENT,
189 VBOXDRVINST_UNINSTALL_OPT_IGNORE_REBOOT
190};
191
192/**
193 * Command line parameters for the 'uninstall' command.
194 */
195static const RTGETOPTDEF g_aCmdUninstallOptions[] =
196{
197 { "--inf-file", VBOXDRVINST_UNINSTALL_OPT_INF_FILE, RTGETOPT_REQ_STRING },
198 { "--inf-section", VBOXDRVINST_UNINSTALL_OPT_INF_SECTION, RTGETOPT_REQ_STRING },
199 { "--model", VBOXDRVINST_UNINSTALL_OPT_MODEL, RTGETOPT_REQ_STRING },
200 { "--pnp", VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
201 { "--pnpid" , VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
202 { "--pnp-id", VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
203 { "--not-silent", VBOXDRVINST_UNINSTALL_OPT_NOT_SILENT, RTGETOPT_REQ_NOTHING },
204 { "--ignore-reboot", VBOXDRVINST_UNINSTALL_OPT_IGNORE_REBOOT, RTGETOPT_REQ_NOTHING }
205};
206
207/**
208 * Command definition for the 'uninstall' command.
209 */
210const VBOXDRVINSTCMD g_CmdUninstall =
211{
212 "uninstall",
213 vboxDrvInstCmdUninstallMain,
214 "Uninstalls drivers.",
215 g_aCmdUninstallOptions,
216 RT_ELEMENTS(g_aCmdUninstallOptions),
217 vboxDrvInstCmdUninstallHelp
218};
219
220/**
221 * Long option values for the 'service' command.
222 */
223enum
224{
225 VBOXDRVINST_SERVICE_OPT_START = 900,
226 VBOXDRVINST_SERVICE_OPT_STOP,
227 VBOXDRVINST_SERVICE_OPT_RESTART,
228 VBOXDRVINST_SERVICE_OPT_WAIT,
229 VBOXDRVINST_SERVICE_OPT_NO_WAIT
230};
231
232/**
233 * Command line parameters for the 'service' command.
234 */
235static const RTGETOPTDEF g_aCmdServiceOptions[] =
236{
237 { "start", VBOXDRVINST_SERVICE_OPT_START, RTGETOPT_REQ_NOTHING },
238 { "stop", VBOXDRVINST_SERVICE_OPT_STOP, RTGETOPT_REQ_NOTHING },
239 { "restart", VBOXDRVINST_SERVICE_OPT_RESTART, RTGETOPT_REQ_NOTHING },
240 { "--wait", VBOXDRVINST_SERVICE_OPT_WAIT, RTGETOPT_REQ_INT32 },
241 { "--no-wait", VBOXDRVINST_SERVICE_OPT_NO_WAIT, RTGETOPT_REQ_NOTHING }
242};
243
244/**
245 * Command definition for the 'service' command.
246 */
247const VBOXDRVINSTCMD g_CmdService =
248{
249 "service",
250 vboxDrvInstCmdServiceMain,
251 "Controls services.",
252 g_aCmdServiceOptions,
253 RT_ELEMENTS(g_aCmdServiceOptions),
254 vboxDrvInstCmdServiceHelp
255};
256
257/**
258 * Commands.
259 */
260static const VBOXDRVINSTCMD * const g_apCommands[] =
261{
262 &g_CmdList,
263 &g_CmdInstall,
264 &g_CmdUninstall,
265 &g_CmdService
266};
267
268/**
269 * Common option definitions for all commands.
270 */
271static const RTGETOPTDEF g_aCmdCommonOptions[] =
272{
273 { "--logfile", 'l', RTGETOPT_REQ_STRING },
274 { "--help", 'h', RTGETOPT_REQ_NOTHING },
275 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
276 { "--version", 'V', RTGETOPT_REQ_NOTHING }
277};
278
279
280/*********************************************************************************************************************************
281* Implementation *
282*********************************************************************************************************************************/
283
284/**
285 * Logs message, va_list version.
286 *
287 * @returns VBox status code.
288 * @param pszPrefix Logging prefix to use. Can be NULL.
289 * @param pszFormat Format string to log.
290 * @param args va_list to use.
291 */
292DECLINLINE(void) vboxDrvInstLogExV(const char *pszPrefix, const char *pszFormat, va_list args)
293{
294 char *psz = NULL;
295 RTStrAPrintfV(&psz, pszFormat, args);
296 AssertPtrReturnVoid(psz);
297
298 if (pszPrefix)
299 LogRel(("%s: %s", pszPrefix, psz));
300 else
301 LogRel(("%s", psz));
302
303 RTStrFree(psz);
304}
305
306/**
307 * Logs a message.
308 *
309 * @returns VBox status code.
310 * @param pszFormat Format string to log.
311 */
312DECLINLINE(void) vboxDrvInstLogError(const char *pszFormat, ...)
313{
314 va_list args;
315 va_start(args, pszFormat);
316 vboxDrvInstLogExV("*** Error", pszFormat, args);
317 va_end(args);
318}
319
320/**
321 * Logs an error message.
322 *
323 * @returns VBox status code.
324 * @param pszFormat Format string to log.
325 */
326DECLINLINE(void) vboxDrvInstLog(const char *pszFormat, ...)
327{
328 va_list args;
329 va_start(args, pszFormat);
330 vboxDrvInstLogExV(NULL, pszFormat, args);
331 va_end(args);
332}
333
334/**
335 * Logging callback for the Windows driver (un)installation code.
336 */
337static DECLCALLBACK(void) vboxDrvInstLogCallback(VBOXWINDRIVERLOGTYPE enmType, const char *pszMsg, void *pvUser)
338{
339 RT_NOREF(pvUser);
340
341 /*
342 * Log to standard output:
343 */
344 switch (enmType)
345 {
346 case VBOXWINDRIVERLOGTYPE_ERROR:
347 vboxDrvInstLogError("%s\n", pszMsg);
348 break;
349
350 case VBOXWINDRIVERLOGTYPE_REBOOT_NEEDED:
351 vboxDrvInstLog("A reboot is needed in order to complete the (un)installation!\n");
352 break;
353
354 default:
355 vboxDrvInstLog("%s\n", pszMsg);
356 break;
357 }
358}
359
360/** Option help for the 'list' command. */
361static DECLCALLBACK(const char *) vboxDrvInstCmdListHelp(PCRTGETOPTDEF pOpt)
362{
363 switch (pOpt->iShort)
364 {
365 default:
366 break;
367 }
368 return NULL;
369}
370
371/**
372 * Main (entry) function for the 'list' command.
373 *
374 * @returns Program exit code.
375 * @param pGetState RTGetOpt state.
376 */
377static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdListMain(PRTGETOPTSTATE pGetState)
378{
379 const char *pszPattern = NULL;
380
381 int ch;
382 RTGETOPTUNION ValueUnion;
383 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
384 {
385 switch (ch)
386 {
387 case 'h':
388 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdList);
389
390 case VINF_GETOPT_NOT_OPTION:
391 {
392 /** @todo Use pattern to filter entries, e.g. "pnp:<PNP-ID>" or "model:VBoxSup*". */
393 pszPattern = ValueUnion.psz;
394 break;
395 }
396
397 default:
398 return RTGetOptPrintError(ch, &ValueUnion);
399 }
400 }
401
402 PVBOXWINDRVSTORE pStore;
403 int rc = VBoxWinDrvStoreCreate(&pStore);
404
405 PVBOXWINDRVSTORELIST pList = NULL;
406 if (pszPattern)
407 rc = VBoxWinDrvStoreQueryAny(pStore, pszPattern, &pList);
408 else
409 rc = VBoxWinDrvStoreQueryAll(pStore, &pList);
410 if (RT_SUCCESS(rc))
411 {
412 vboxDrvInstLog("Location: %s\n\n", VBoxWinDrvStoreBackendGetLocation(pStore));
413
414 vboxDrvInstLog("%-40s | %-40s\n", "OEM INF File", "Version");
415 vboxDrvInstLog("%-40s | %-40s\n", " Model (First)", "PnP ID (First)");
416 vboxDrvInstLog("--------------------------------------------------------------------------------\n");
417
418 size_t cEntries = 0;
419 PVBOXWINDRVSTOREENTRY pCur;
420 RTListForEach(&pList->List, pCur, VBOXWINDRVSTOREENTRY, Node)
421 {
422 vboxDrvInstLog("%-40ls | %-40ls\n",
423 pCur->wszInfFile, pCur->Ver.wszDriverVer);
424 vboxDrvInstLog(" %-36ls | %-40ls\n",
425 pCur->wszModel, pCur->wszPnpId);
426 cEntries++;
427 }
428
429 if (pszPattern)
430 vboxDrvInstLog("\nFound %zu entries (filtered).\n", cEntries);
431 else
432 vboxDrvInstLog("\nFound %zu entries.\n", cEntries);
433 }
434
435 VBoxWinDrvStoreListFree(pList);
436
437 VBoxWinDrvStoreDestroy(pStore);
438 pStore = NULL;
439
440 vboxDrvInstLog("\nUse DOS-style wildcards to adjust results.\n");
441 vboxDrvInstLog("Use \"--help\" to print syntax help.\n");
442
443 return RTEXITCODE_SUCCESS;
444}
445
446/** Option help for the 'install' command. */
447static DECLCALLBACK(const char *) vboxDrvInstCmdInstallHelp(PCRTGETOPTDEF pOpt)
448{
449 switch (pOpt->iShort)
450 {
451 case VBOXDRVINST_INSTALL_OPT_INF_FILE: return "Specifies the INF file to install";
452 case VBOXDRVINST_INSTALL_OPT_INF_SECTION: return "Specifies the INF section to install";
453 case VBOXDRVINST_INSTALL_OPT_MODEL: return "Specifies the driver model";
454 case VBOXDRVINST_INSTALL_OPT_PNPID: return "Specifies the PnP (device) ID";
455 case VBOXDRVINST_INSTALL_OPT_NOT_FORCE: return "Installation will not be forced";
456 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT: return "Installation will not run in silent mode";
457 case VBOXDRVINST_INSTALL_OPT_IGNORE_REBOOT: return "Ignores reboot requirements";
458 case VBOXDRVINST_INSTALL_OPT_DEBUG_OS_VER: return "Overwrites the detected OS version";
459 default:
460 break;
461 }
462 return NULL;
463}
464
465/**
466 * Main (entry) function for the 'install' command.
467 *
468 * @returns Program exit code.
469 * @param pGetState RTGetOpt state.
470 */
471static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdInstallMain(PRTGETOPTSTATE pGetState)
472{
473 char *pszInfFile = NULL;
474 char *pszModel = NULL;
475 char *pszPnpId = NULL;
476 char *pszInfSection = NULL;
477 uint64_t uOsVer = 0;
478
479 /* By default we want to force an installation.
480 *
481 * However, we do *not* want the installation to be silent by default,
482 * as this this will result in an ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED error
483 * if drivers get installed with our mixed SHA1 / SH256 certificates on older
484 * Windows guest (7, Vista, ++).
485 *
486 * So if the VBOX_WIN_DRIVERINSTALL_F_SILENT is missing, this will result in a
487 * (desired) Windows driver installation dialog to confirm (or reject) the installation
488 * by the user.
489 *
490 * On the other hand, for unattended installs we need VBOX_WIN_DRIVERINSTALL_F_SILENT
491 * being set, as our certificates will get installed into the Windows certificate
492 * store *before* we perform any driver installation.
493 */
494 uint32_t fInstall = VBOX_WIN_DRIVERINSTALL_F_FORCE;
495
496 /* Whether to ignore reboot messages or not. This will also affect the returned exit code. */
497 bool fIgnoreReboot = false;
498
499 int rc = VINF_SUCCESS;
500
501#define DUP_ARG_TO_STR(a_Str) \
502 a_Str = RTStrDup(ValueUnion.psz); \
503 if (!a_Str) \
504 { \
505 RTMsgError("Can't handle argument '%s': Out of memory\n", ValueUnion.psz); \
506 rc = VERR_NO_MEMORY; \
507 break; \
508 }
509
510 int ch;
511 RTGETOPTUNION ValueUnion;
512 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
513 {
514 switch (ch)
515 {
516 case 'h':
517 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdInstall);
518
519 case VBOXDRVINST_INSTALL_OPT_INF_FILE:
520 DUP_ARG_TO_STR(pszInfFile);
521 break;
522
523 case VBOXDRVINST_INSTALL_OPT_INF_SECTION:
524 DUP_ARG_TO_STR(pszInfSection);
525 break;
526
527 case VBOXDRVINST_INSTALL_OPT_MODEL:
528 DUP_ARG_TO_STR(pszModel);
529 break;
530
531 case VBOXDRVINST_INSTALL_OPT_PNPID:
532 DUP_ARG_TO_STR(pszPnpId);
533 break;
534
535 case VBOXDRVINST_INSTALL_OPT_NOT_FORCE:
536 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_FORCE;
537 break;
538
539 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT:
540 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_SILENT;
541 break;
542
543 case VBOXDRVINST_INSTALL_OPT_IGNORE_REBOOT:
544 fIgnoreReboot = true;
545 break;
546
547 case VBOXDRVINST_INSTALL_OPT_DEBUG_OS_VER:
548 uOsVer = RTSYSTEM_MAKE_NT_VERSION(ValueUnion.PairU32.uFirst, ValueUnion.PairU32.uSecond,
549 0 /* Build Version */);
550 break;
551
552 default:
553 return RTGetOptPrintError(ch, &ValueUnion);
554 }
555 }
556
557#undef DUP_ARG_TO_STR
558
559 if (RT_FAILURE(rc))
560 return RTEXITCODE_FAILURE;
561
562 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
563
564 VBOXWINDRVINST hWinDrvInst;
565 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
566 if (RT_SUCCESS(rc))
567 {
568 if (uOsVer)
569 VBoxWinDrvInstSetOsVersion(hWinDrvInst, uOsVer);
570
571 rc = VBoxWinDrvInstInstallEx(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fInstall);
572 if (RT_SUCCESS(rc))
573 {
574 if ( rc == VINF_REBOOT_NEEDED
575 && !fIgnoreReboot)
576 rcExit = (RTEXITCODE)VBOXDRVINSTEXITCODE_REBOOT_NEEDED;
577 }
578 else
579 rcExit = RTEXITCODE_FAILURE;
580
581 VBoxWinDrvInstDestroy(hWinDrvInst);
582 }
583
584 RTStrFree(pszInfFile);
585 RTStrFree(pszInfSection);
586 RTStrFree(pszModel);
587 RTStrFree(pszPnpId);
588
589 return rcExit;
590}
591
592/** Option help for the 'uninstall' command. */
593static DECLCALLBACK(const char *) vboxDrvInstCmdUninstallHelp(PCRTGETOPTDEF pOpt)
594{
595 switch (pOpt->iShort)
596 {
597 case VBOXDRVINST_UNINSTALL_OPT_INF_FILE: return "Specifies the INF File to uninstall";
598 case VBOXDRVINST_UNINSTALL_OPT_INF_SECTION: return "Specifies the INF section to uninstall";
599 case VBOXDRVINST_UNINSTALL_OPT_MODEL: return "Specifies the driver model to uninstall";
600 case VBOXDRVINST_UNINSTALL_OPT_PNPID: return "Specifies the PnP (device) ID to uninstall";
601 case VBOXDRVINST_UNINSTALL_OPT_IGNORE_REBOOT: return "Ignores reboot requirements";
602 default:
603 break;
604 }
605 return NULL;
606}
607
608/**
609 * Main (entry) function for the 'uninstall' command.
610 *
611 * @returns Program exit code.
612 * @param pGetState RTGetOpt state.
613 */
614static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdUninstallMain(PRTGETOPTSTATE pGetState)
615{
616 char *pszInfFile = NULL;
617 char *pszModel = NULL;
618 char *pszPnpId = NULL;
619 char *pszInfSection = NULL;
620
621 /* By default we want a silent uninstallation. */
622 uint32_t fInstall = VBOX_WIN_DRIVERINSTALL_F_SILENT;
623
624 /* Whether to ignore reboot messages or not. This will also affect the returned exit code. */
625 bool fIgnoreReboot = false;
626
627 int rc = VINF_SUCCESS;
628
629#define DUP_ARG_TO_STR(a_Str) \
630 a_Str = RTStrDup(ValueUnion.psz); \
631 if (!a_Str) \
632 { \
633 RTMsgError("Can't handle argument '%s': Out of memory\n", ValueUnion.psz); \
634 rc = VERR_NO_MEMORY; \
635 break; \
636 }
637
638 int ch;
639 RTGETOPTUNION ValueUnion;
640 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
641 {
642 switch (ch)
643 {
644 case 'h':
645 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdUninstall);
646
647 case VBOXDRVINST_UNINSTALL_OPT_INF_FILE:
648 DUP_ARG_TO_STR(pszInfFile);
649 break;
650
651 case VBOXDRVINST_UNINSTALL_OPT_INF_SECTION:
652 DUP_ARG_TO_STR(pszInfSection);
653 break;
654
655 case VBOXDRVINST_UNINSTALL_OPT_MODEL:
656 DUP_ARG_TO_STR(pszModel);
657 break;
658
659 case VBOXDRVINST_UNINSTALL_OPT_PNPID:
660 DUP_ARG_TO_STR(pszPnpId);
661 break;
662
663 case VBOXDRVINST_UNINSTALL_OPT_NOT_SILENT:
664 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_SILENT;
665 break;
666
667 case VBOXDRVINST_UNINSTALL_OPT_IGNORE_REBOOT:
668 fIgnoreReboot = true;
669 break;
670
671 default:
672 return RTGetOptPrintError(ch, &ValueUnion);
673 }
674 }
675
676#undef DUP_ARG_TO_STR
677
678 if (RT_FAILURE(rc))
679 return RTEXITCODE_FAILURE;
680
681 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
682
683 VBOXWINDRVINST hWinDrvInst;
684 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
685 if (RT_SUCCESS(rc))
686 {
687 rc = VBoxWinDrvInstUninstall(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fInstall);
688 if (RT_SUCCESS(rc))
689 {
690 if ( rc == VINF_REBOOT_NEEDED
691 && !fIgnoreReboot)
692 rcExit = (RTEXITCODE)VBOXDRVINSTEXITCODE_REBOOT_NEEDED;
693 }
694 else
695 rcExit = RTEXITCODE_FAILURE;
696
697 VBoxWinDrvInstDestroy(hWinDrvInst);
698 }
699
700 RTStrFree(pszInfFile);
701 RTStrFree(pszInfSection);
702 RTStrFree(pszModel);
703 RTStrFree(pszPnpId);
704
705 return rcExit;
706}
707
708/** Option help for the 'service' command. */
709static DECLCALLBACK(const char *) vboxDrvInstCmdServiceHelp(PCRTGETOPTDEF pOpt)
710{
711 switch (pOpt->iShort)
712 {
713 case VBOXDRVINST_SERVICE_OPT_START: return "Starts the service";
714 case VBOXDRVINST_SERVICE_OPT_STOP: return "Stops the service";
715 case VBOXDRVINST_SERVICE_OPT_RESTART: return "Restarts the service";
716 case VBOXDRVINST_SERVICE_OPT_WAIT: return "Waits for the service to reach the desired state";
717 case VBOXDRVINST_SERVICE_OPT_NO_WAIT: return "Skips waiting for the service to reach the desired state";
718
719 default:
720 break;
721 }
722 return NULL;
723}
724
725static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdServiceMain(PRTGETOPTSTATE pGetState)
726{
727 const char *pszService = NULL;
728 VBOXWINDRVSVCFN enmFn = VBOXWINDRVSVCFN_INVALID;
729 /* We wait 30s by default, unless specified otherwise below. */
730 uint32_t fFlags = VBOXWINDRVSVCFN_F_WAIT;
731 RTMSINTERVAL msTimeout = RT_MS_30SEC;
732
733 int ch;
734 RTGETOPTUNION ValueUnion;
735 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
736 {
737 switch (ch)
738 {
739 case 'h':
740 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdService);
741
742 case VBOXDRVINST_SERVICE_OPT_START:
743 {
744 if (enmFn != VBOXWINDRVSVCFN_INVALID)
745 return RTMsgErrorExitFailure("Service control function already specified\n");
746 enmFn = VBOXWINDRVSVCFN_START;
747 break;
748 }
749
750 case VBOXDRVINST_SERVICE_OPT_STOP:
751 {
752 if (enmFn != VBOXWINDRVSVCFN_INVALID)
753 return RTMsgErrorExitFailure("Service control function already specified\n");
754 enmFn = VBOXWINDRVSVCFN_STOP;
755 break;
756 }
757
758 case VBOXDRVINST_SERVICE_OPT_RESTART:
759 {
760 if (enmFn != VBOXWINDRVSVCFN_INVALID)
761 return RTMsgErrorExitFailure("Service control function already specified\n");
762 enmFn = VBOXWINDRVSVCFN_RESTART;
763 break;
764 }
765
766 case VBOXDRVINST_SERVICE_OPT_WAIT:
767 /* Note: fFlags already set above. */
768 msTimeout = ValueUnion.u32 * RT_MS_1SEC; /* Seconds -> Milliseconds. */
769 if (!msTimeout)
770 return RTMsgErrorExitFailure("Timeout value is invalid\n");
771 break;
772
773 case VBOXDRVINST_SERVICE_OPT_NO_WAIT:
774 fFlags &= ~VBOXWINDRVSVCFN_F_WAIT;
775 break;
776
777 case VINF_GETOPT_NOT_OPTION:
778 {
779 if (pszService)
780 return RTMsgErrorExitFailure("Service name already specified\n");
781
782 pszService = ValueUnion.psz;
783 break;
784 }
785
786 default:
787 return RTGetOptPrintError(ch, &ValueUnion);
788 }
789 }
790
791 if (!pszService)
792 return RTMsgErrorExitFailure("No service to control specified\n");
793 if (enmFn == VBOXWINDRVSVCFN_INVALID)
794 return RTMsgErrorExitFailure("No or invalid service control function specified\n");
795
796 VBOXWINDRVINST hWinDrvInst;
797 int rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
798 if (RT_SUCCESS(rc))
799 {
800 rc = VBoxWinDrvInstControlServiceEx(hWinDrvInst, pszService, enmFn, fFlags, msTimeout);
801 VBoxWinDrvInstDestroy(hWinDrvInst);
802 }
803
804 return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
805}
806
807/**
808 * Shows the commands and their descriptions.
809 *
810 * @returns RTEXITCODE
811 * @param pStrm Stream to use.
812 */
813static RTEXITCODE vboxDrvInstShowCommands(PRTSTREAM pStrm)
814{
815 RTStrmPrintf(pStrm, "Commands:\n");
816 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
817 RTStrmPrintf(pStrm, "%12s - %s\n", g_apCommands[iCmd]->pszCommand, g_apCommands[iCmd]->pszDesc);
818 return RTEXITCODE_SUCCESS;
819}
820
821/**
822 * Shows the general usage.
823 *
824 * @returns RTEXITCODE
825 * @param pStrm Stream to use.
826 */
827static RTEXITCODE vboxDrvInstShowUsage(PRTSTREAM pStrm, PCVBOXDRVINSTCMD pOnlyCmd)
828{
829 const char *pszProcName = RTProcShortName();
830
831 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n", pszProcName);
832 RTStrmPrintf(pStrm,
833 "\n"
834 "Global Options:\n"
835 " -h, -?, --help\n"
836 " Displays help\n"
837 " -l | --logfile <file>\n"
838 " Enables logging to a file\n"
839 " -v, --verbose\n"
840 " Increase verbosity\n"
841 " -V, --version\n"
842 " Displays version\n"
843 );
844
845 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
846 {
847 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
848 if (!pOnlyCmd || pCmd == pOnlyCmd)
849 {
850 RTStrmPrintf(pStrm,
851 "\n"
852 "Command '%s':\n"
853 " %s\n",
854 pCmd->pszCommand, pCmd->pszDesc);
855
856 if (!pCmd->paOptions)
857 continue;
858
859 RTStrmPrintf(pStrm, "Options for '%s':\n", pCmd->pszCommand);
860 PCRTGETOPTDEF const paOptions = pCmd->paOptions;
861 for (unsigned i = 0; i < pCmd->cOptions; i++)
862 {
863 if (RT_C_IS_PRINT(paOptions[i].iShort))
864 RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
865 else
866 RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);
867
868 const char *pszHelp = NULL;
869 if (pCmd->pfnOptionHelp)
870 pszHelp = pCmd->pfnOptionHelp(&paOptions[i]);
871 if (pszHelp)
872 RTStrmPrintf(pStrm, " %s\n", pszHelp);
873 }
874 }
875 }
876
877 RTStrmPrintf(pStrm, "\nExamples:\n");
878 RTStrmPrintf(pStrm, "\t%s install --inf-file C:\\Path\\To\\VBoxUSB.inf\n", pszProcName);
879 RTStrmPrintf(pStrm, "\t%s install --debug-os-ver 6:0 --inf-file C:\\Path\\To\\VBoxGuest.inf\n", pszProcName);
880 RTStrmPrintf(pStrm, "\t%s uninstall --inf -file C:\\Path\\To\\VBoxUSB.inf --pnp-id \"USB\\VID_80EE&PID_CAFE\"\n", pszProcName);
881 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB.AMD64\"\n", pszProcName);
882 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB*\"\n", pszProcName);
883 RTStrmPrintf(pStrm, "\t%s service VBoxSDS stop\n", pszProcName);
884 RTStrmPrintf(pStrm, "\t%s service VBoxSDS start --no-wait\n", pszProcName);
885 RTStrmPrintf(pStrm, "\t%s service VBoxSDS restart --wait 180\n", pszProcName);
886 RTStrmPrintf(pStrm, "\t%s list \"VBox*\"\n\n", pszProcName);
887 RTStrmPrintf(pStrm, "Exit codes:\n");
888 RTStrmPrintf(pStrm, "\t1 - The requested command failed.\n");
889 RTStrmPrintf(pStrm, "\t2 - Syntax error.\n");
890 RTStrmPrintf(pStrm, "\t5 - A reboot is needed in order to complete the (un)installation.\n\n");
891
892 return RTEXITCODE_SUCCESS;
893}
894
895/**
896 * Shows tool version.
897 *
898 * @returns RTEXITCODE
899 * @param pStrm Stream to use.
900 */
901static RTEXITCODE vboxDrvInstShowVersion(PRTSTREAM pStrm)
902{
903 RTStrmPrintf(pStrm, "%s\n", RTBldCfgRevisionStr());
904 return RTEXITCODE_SUCCESS;
905}
906
907/**
908 * Shows the logo.
909 *
910 * @param pStream Output stream to show logo on.
911 */
912static void vboxDrvInstShowLogo(PRTSTREAM pStream)
913{
914 RTStrmPrintf(pStream, VBOX_PRODUCT " VBoxDrvInst (Driver Installation Utility) Version " VBOX_VERSION_STRING " - r%s (%s)\n"
915 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n", RTBldCfgRevisionStr(), RTBldCfgTargetArch());
916}
917
918/**
919 * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
920 */
921static DECLCALLBACK(void) vboxDrvInstLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
922{
923 /* Some introductory information. */
924 static RTTIMESPEC s_TimeSpec;
925 char szTmp[256];
926 if (enmPhase == RTLOGPHASE_BEGIN)
927 RTTimeNow(&s_TimeSpec);
928 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
929
930 switch (enmPhase)
931 {
932 case RTLOGPHASE_BEGIN:
933 {
934 pfnLog(pLoggerRelease,
935 "VBoxDrvInst %s r%s (verbosity: %u) (%s %s) release log (%s)\n"
936 "Log opened %s\n",
937 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_uVerbosity,
938 __DATE__, __TIME__, RTBldCfgTargetArch(), szTmp);
939
940 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
941 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
942 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
943 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
944 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
945 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
946 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
947 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
948 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
949 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
950 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
951 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
952
953 /* the package type is interesting for Linux distributions */
954 char szExecName[RTPATH_MAX];
955 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
956 pfnLog(pLoggerRelease,
957 "Executable: %s\n"
958 "Process ID: %u\n"
959 "Package type: %s"
960#ifdef VBOX_OSE
961 " (OSE)"
962#endif
963 "\n",
964 pszExecName ? pszExecName : "unknown",
965 RTProcSelf(),
966 VBOX_PACKAGE_STRING);
967 break;
968 }
969
970 case RTLOGPHASE_PREROTATE:
971 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
972 break;
973
974 case RTLOGPHASE_POSTROTATE:
975 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
976 break;
977
978 case RTLOGPHASE_END:
979 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
980 break;
981
982 default:
983 /* nothing */
984 break;
985 }
986}
987
988
989/**
990 * Creates the default release logger outputting to the specified file.
991 *
992 * @return IPRT status code.
993 * @param pszLogFile Filename for log output.
994 */
995static int vboxDrvInstLogCreate(const char *pszLogFile)
996{
997 /* Create release logger (stdout + file). */
998 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
999 RTUINT fFlags = RTLOGFLAGS_USECRLF | RTLOGFLAGS_APPEND;
1000 int rc = RTLogCreateEx(&g_pLoggerRelease, "VBOXDRVINST_RELEASE_LOG", fFlags, "all",
1001 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
1002 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT | RTLOGDEST_USER,
1003 vboxDrvInstLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
1004 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
1005 NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
1006 if (RT_SUCCESS(rc))
1007 {
1008 /* register this logger as the release logger */
1009 RTLogRelSetDefaultInstance(g_pLoggerRelease);
1010
1011 /* Explicitly flush the log in case of VBOXDRVINST_RELEASE_LOG=buffered. */
1012 RTLogFlush(g_pLoggerRelease);
1013 }
1014
1015 return rc;
1016}
1017
1018/**
1019 * Destroys the currently active logging instance.
1020 */
1021static void vboxDrvInstLogDestroy(void)
1022{
1023 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
1024}
1025
1026/**
1027 * Performs initialization tasks before a specific command is being run.
1028 *
1029 * @returns VBox status code.
1030 */
1031static int vboxDrvInstInit(void)
1032{
1033 int rc = vboxDrvInstLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
1034 if (RT_FAILURE(rc))
1035 {
1036 RTMsgError("Failed to create release log '%s', rc=%Rrc\n", g_szLogFile[0] ? g_szLogFile : "<None>", rc);
1037 return rc;
1038 }
1039
1040 /* Refuse to run on too old Windows versions (<= NT4). */
1041 uint64_t const uNtVer = RTSystemGetNtVersion();
1042 if (RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer) <= 4)
1043 {
1044 vboxDrvInstLogError("Windows version (%d.%d.%d) too old and not supported\n", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer),
1045 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVer),
1046 RTSYSTEM_NT_VERSION_GET_BUILD(uNtVer));
1047 return VERR_NOT_SUPPORTED;
1048 }
1049
1050 return VINF_SUCCESS;
1051}
1052
1053/**
1054 * Performs destruction tasks after a specific command has been run.
1055 */
1056static void vboxDrvInstDestroy(void)
1057{
1058 vboxDrvInstLogDestroy();
1059}
1060
1061int main(int argc, char **argv)
1062{
1063 /*
1064 * Init IPRT.
1065 */
1066 int rc = RTR3InitExe(argc, &argv, 0);
1067 if (RT_FAILURE(rc))
1068 return RTMsgInitFailure(rc);
1069
1070 vboxDrvInstShowLogo(g_pStdOut);
1071
1072 /*
1073 * Process common options.
1074 */
1075 RTGETOPTSTATE GetState;
1076 RT_ZERO(GetState);
1077 rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions, RT_ELEMENTS(g_aCmdCommonOptions),
1078 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
1079 AssertRCReturn(rc, RTEXITCODE_INIT);
1080
1081 int ch;
1082 RTGETOPTUNION ValueUnion;
1083 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
1084 {
1085 switch (ch)
1086 {
1087 case 'h':
1088 return vboxDrvInstShowUsage(g_pStdOut, NULL);
1089
1090 case 'l':
1091 rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
1092 if (RT_FAILURE(rc))
1093 return RTMsgErrorExitFailure("Error setting logfile, rc=%Rrc\n", rc);
1094 break;
1095
1096 case 'v':
1097 g_uVerbosity++;
1098 break;
1099
1100 case 'V':
1101 return vboxDrvInstShowVersion(g_pStdOut);
1102
1103 case VERR_GETOPT_UNKNOWN_OPTION:
1104 return vboxDrvInstShowUsage(g_pStdOut, NULL);
1105
1106 case VINF_GETOPT_NOT_OPTION:
1107 {
1108 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
1109 {
1110 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
1111 if (strcmp(ValueUnion.psz, pCmd->pszCommand) == 0)
1112 {
1113 rc = vboxDrvInstInit();
1114 if (RT_FAILURE(rc))
1115 return RTEXITCODE_FAILURE;
1116
1117 /* Count the combined option definitions: */
1118 size_t cCombinedOptions = pCmd->cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
1119
1120 RTEXITCODE rcExit;
1121
1122 /* Combine the option definitions: */
1123 PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
1124 if (paCombinedOptions)
1125 {
1126 uint32_t idxOpts = 0;
1127 memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
1128 idxOpts += RT_ELEMENTS(g_aCmdCommonOptions);
1129
1130 memcpy(&paCombinedOptions[idxOpts], pCmd->paOptions, pCmd->cOptions * sizeof(RTGETOPTDEF));
1131 idxOpts += (uint32_t)pCmd->cOptions;
1132
1133 /* Re-initialize the option getter state and pass it to the command handler. */
1134 rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
1135 GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
1136
1137 if (RT_SUCCESS(rc))
1138 rcExit = pCmd->pfnHandler(&GetState);
1139 else
1140 rcExit = RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
1141 RTMemFree(paCombinedOptions);
1142 }
1143 else
1144 rcExit = RTMsgErrorExitFailure("Out of memory!");
1145
1146 vboxDrvInstDestroy();
1147 return rcExit;
1148 }
1149 }
1150 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
1151 vboxDrvInstShowCommands(g_pStdErr);
1152 return RTEXITCODE_SYNTAX;
1153 }
1154
1155 default:
1156 break;
1157 }
1158 }
1159
1160 /* List all Windows driver store entries if no command is given. */
1161 rc = vboxDrvInstInit();
1162 if (RT_FAILURE(rc))
1163 return RTEXITCODE_FAILURE;
1164 RTEXITCODE rcExit = vboxDrvInstCmdListMain(&GetState);
1165 vboxDrvInstDestroy();
1166 return rcExit;
1167}
1168
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

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