VirtualBox

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

Last change on this file since 107457 was 107393, checked in by vboxsync, 5 months ago

Windows driver installation/VBoxDrvInst: Fixed option definitions for the 'uninstall' sub command. White space fixes.

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

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