VirtualBox

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

Last change on this file since 106963 was 106963, checked in by vboxsync, 3 months ago

Windows driver installation/VBoxDrvInst: Refuse to run on <= NT4. Logging adjustments. bugref:10762

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

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