VirtualBox

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

最後變更 在這個檔案從107973是 107973,由 vboxsync 提交於 8 週 前

Windows driver installation: Fixed the ERROR_AUTHENTICODE_TRUST_NOT_ESTABLISHED error, added documentation and give a clue to users why this might have failed. See source for details. bugref:10762

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

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