VirtualBox

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

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

Windows driver installation/VBoxDrvInst: Forgot to call VBoxWinDrvInstDestroy(). bugref:10762

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 31.9 KB
Line 
1/* $Id: VBoxDrvInst.cpp 107049 2024-11-20 09:19:15Z 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 VBoxWinDrvInstDestroy(hWinDrvInst);
501 }
502
503 RTStrFree(pszInfFile);
504 RTStrFree(pszInfSection);
505 RTStrFree(pszModel);
506 RTStrFree(pszPnpId);
507
508 return rcExit;
509}
510
511/** Option help for the 'uninstall' command. */
512static DECLCALLBACK(const char *) vboxDrvInstCmdUninstallHelp(PCRTGETOPTDEF pOpt)
513{
514 switch (pOpt->iShort)
515 {
516 case VBOXDRVINST_UNINSTALL_OPT_INF_FILE: return "Specifies the INF File to install";
517 case VBOXDRVINST_UNINSTALL_OPT_INF_SECTION: return "Specifies the INF section to install";
518 case VBOXDRVINST_UNINSTALL_OPT_MODEL: return "Specifies the driver model to install";
519 case VBOXDRVINST_UNINSTALL_OPT_PNPID: return "Specifies the PnP (device) ID to install";
520 default:
521 break;
522 }
523 return NULL;
524}
525
526/**
527 * Main (entry) function for the 'uninstall' command.
528 *
529 * @returns Program exit code.
530 * @param pGetState RTGetOpt state.
531 */
532static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdUninstallMain(PRTGETOPTSTATE pGetState)
533{
534 char *pszInfFile = NULL;
535 char *pszModel = NULL;
536 char *pszPnpId = NULL;
537 char *pszInfSection = NULL;
538
539 /* By default we want a silent uninstallation. */
540 uint32_t fInstall = VBOX_WIN_DRIVERINSTALL_F_SILENT;
541
542 int rc = VINF_SUCCESS;
543
544#define DUP_ARG_TO_STR(a_Str) \
545 a_Str = RTStrDup(ValueUnion.psz); \
546 if (!a_Str) \
547 { \
548 RTMsgError("Can't handle argument '%s': Out of memory\n", ValueUnion.psz); \
549 rc = VERR_NO_MEMORY; \
550 break; \
551 }
552
553 int ch;
554 RTGETOPTUNION ValueUnion;
555 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
556 {
557 switch (ch)
558 {
559 case 'h':
560 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdUninstall);
561
562 case VBOXDRVINST_INSTALL_OPT_INF_FILE:
563 DUP_ARG_TO_STR(pszInfFile);
564 break;
565
566 case VBOXDRVINST_INSTALL_OPT_INF_SECTION:
567 DUP_ARG_TO_STR(pszInfSection);
568 break;
569
570 case VBOXDRVINST_INSTALL_OPT_MODEL:
571 DUP_ARG_TO_STR(pszModel);
572 break;
573
574 case VBOXDRVINST_INSTALL_OPT_PNPID:
575 DUP_ARG_TO_STR(pszPnpId);
576 break;
577
578 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT:
579 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_SILENT;
580 break;
581
582 default:
583 return RTGetOptPrintError(ch, &ValueUnion);
584 }
585 }
586
587#undef DUP_ARG_TO_STR
588
589 if (RT_FAILURE(rc))
590 return RTEXITCODE_FAILURE;
591
592 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
593
594 VBOXWINDRVINST hWinDrvInst;
595 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
596 if (RT_SUCCESS(rc))
597 {
598 rc = VBoxWinDrvInstUninstall(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fInstall);
599 if (RT_SUCCESS(rc))
600 {
601 if (rc == VINF_REBOOT_NEEDED)
602 rcExit = (RTEXITCODE)VBOXDRVINSTEXITCODE_REBOOT_NEEDED;
603 }
604 else
605 rcExit = RTEXITCODE_FAILURE;
606
607 VBoxWinDrvInstDestroy(hWinDrvInst);
608 }
609
610 RTStrFree(pszInfFile);
611 RTStrFree(pszInfSection);
612 RTStrFree(pszModel);
613 RTStrFree(pszPnpId);
614
615 return rcExit;
616}
617
618/**
619 * Shows the commands and their descriptions.
620 *
621 * @returns RTEXITCODE
622 * @param pStrm Stream to use.
623 */
624static RTEXITCODE vboxDrvInstShowCommands(PRTSTREAM pStrm)
625{
626 RTStrmPrintf(pStrm, "Commands:\n");
627 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
628 RTStrmPrintf(pStrm, "%12s - %s\n", g_apCommands[iCmd]->pszCommand, g_apCommands[iCmd]->pszDesc);
629 return RTEXITCODE_SUCCESS;
630}
631
632/**
633 * Shows the general usage.
634 *
635 * @returns RTEXITCODE
636 * @param pStrm Stream to use.
637 */
638static RTEXITCODE vboxDrvInstShowUsage(PRTSTREAM pStrm, PCVBOXDRVINSTCMD pOnlyCmd)
639{
640 const char *pszProcName = RTProcShortName();
641
642 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n", pszProcName);
643 RTStrmPrintf(pStrm,
644 "\n"
645 "Global Options:\n"
646 " -h, -?, --help\n"
647 " Displays help\n"
648 " -l | --logfile <file>\n"
649 " Enables logging to a file\n"
650 " -v, --verbose\n"
651 " Increase verbosity\n"
652 " -V, --version\n"
653 " Displays version\n"
654 );
655
656 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
657 {
658 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
659 if (!pOnlyCmd || pCmd == pOnlyCmd)
660 {
661 RTStrmPrintf(pStrm,
662 "\n"
663 "Command '%s':\n"
664 " %s\n",
665 pCmd->pszCommand, pCmd->pszDesc);
666
667 if (!pCmd->paOptions)
668 continue;
669
670 RTStrmPrintf(pStrm, "Options for '%s':\n", pCmd->pszCommand);
671 PCRTGETOPTDEF const paOptions = pCmd->paOptions;
672 for (unsigned i = 0; i < pCmd->cOptions; i++)
673 {
674 if (RT_C_IS_PRINT(paOptions[i].iShort))
675 RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
676 else
677 RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);
678
679 const char *pszHelp = NULL;
680 if (pCmd->pfnOptionHelp)
681 pszHelp = pCmd->pfnOptionHelp(&paOptions[i]);
682 if (pszHelp)
683 RTStrmPrintf(pStrm, " %s\n", pszHelp);
684 }
685 }
686 }
687
688 RTStrmPrintf(pStrm, "\nExamples:\n");
689 RTStrmPrintf(pStrm, "\t%s install --inf-file C:\\Path\\To\\VBoxUSB.inf\n", pszProcName);
690 RTStrmPrintf(pStrm, "\t%s uninstall --inf -file C:\\Path\\To\\VBoxUSB.inf --pnp-id \"USB\\VID_80EE&PID_CAFE\"\n", pszProcName);
691 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB.AMD64\"\n", pszProcName);
692 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB*\"\n", pszProcName);
693 RTStrmPrintf(pStrm, "\t%s list \"VBox*\"\n\n", pszProcName);
694 RTStrmPrintf(pStrm, "Exit codes:\n");
695 RTStrmPrintf(pStrm, "\t1 - The (un)installation failed.\n");
696 RTStrmPrintf(pStrm, "\t2 - Syntax error.\n");
697 RTStrmPrintf(pStrm, "\t5 - A reboot is needed in order to complete the (un)installation.\n\n");
698
699 return RTEXITCODE_SUCCESS;
700}
701
702/**
703 * Shows tool version.
704 *
705 * @returns RTEXITCODE
706 * @param pStrm Stream to use.
707 */
708static RTEXITCODE vboxDrvInstShowVersion(PRTSTREAM pStrm)
709{
710 RTStrmPrintf(pStrm, "%s\n", RTBldCfgRevisionStr());
711 return RTEXITCODE_SUCCESS;
712}
713
714/**
715 * Shows the logo.
716 *
717 * @param pStream Output stream to show logo on.
718 */
719static void vboxDrvInstShowLogo(PRTSTREAM pStream)
720{
721 RTStrmPrintf(pStream, VBOX_PRODUCT " VBoxDrvInst (Driver Installation Utility) Version " VBOX_VERSION_STRING " - r%s (%s)\n"
722 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n", RTBldCfgRevisionStr(), RTBldCfgTargetArch());
723}
724
725/**
726 * @callback_method_impl{FNRTLOGPHASE, Release logger callback}
727 */
728static DECLCALLBACK(void) vboxDrvInstLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
729{
730 /* Some introductory information. */
731 static RTTIMESPEC s_TimeSpec;
732 char szTmp[256];
733 if (enmPhase == RTLOGPHASE_BEGIN)
734 RTTimeNow(&s_TimeSpec);
735 RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
736
737 switch (enmPhase)
738 {
739 case RTLOGPHASE_BEGIN:
740 {
741 pfnLog(pLoggerRelease,
742 "VBoxDrvInst %s r%s (verbosity: %u) (%s %s) release log (%s)\n"
743 "Log opened %s\n",
744 RTBldCfgVersion(), RTBldCfgRevisionStr(), g_uVerbosity,
745 __DATE__, __TIME__, RTBldCfgTargetArch(), szTmp);
746
747 int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
748 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
749 pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
750 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
751 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
752 pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
753 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
754 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
755 pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
756 vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp));
757 if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
758 pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
759
760 /* the package type is interesting for Linux distributions */
761 char szExecName[RTPATH_MAX];
762 char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
763 pfnLog(pLoggerRelease,
764 "Executable: %s\n"
765 "Process ID: %u\n"
766 "Package type: %s"
767#ifdef VBOX_OSE
768 " (OSE)"
769#endif
770 "\n",
771 pszExecName ? pszExecName : "unknown",
772 RTProcSelf(),
773 VBOX_PACKAGE_STRING);
774 break;
775 }
776
777 case RTLOGPHASE_PREROTATE:
778 pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
779 break;
780
781 case RTLOGPHASE_POSTROTATE:
782 pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
783 break;
784
785 case RTLOGPHASE_END:
786 pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
787 break;
788
789 default:
790 /* nothing */
791 break;
792 }
793}
794
795
796/**
797 * Creates the default release logger outputting to the specified file.
798 *
799 * @return IPRT status code.
800 * @param pszLogFile Filename for log output.
801 */
802static int vboxDrvInstLogCreate(const char *pszLogFile)
803{
804 /* Create release logger (stdout + file). */
805 static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
806 RTUINT fFlags = RTLOGFLAGS_USECRLF | RTLOGFLAGS_APPEND;
807 int rc = RTLogCreateEx(&g_pLoggerRelease, "VBOXDRVINST_RELEASE_LOG", fFlags, "all",
808 RT_ELEMENTS(s_apszGroups), s_apszGroups, UINT32_MAX /*cMaxEntriesPerGroup*/,
809 0 /*cBufDescs*/, NULL /*paBufDescs*/, RTLOGDEST_STDOUT | RTLOGDEST_USER,
810 vboxDrvInstLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
811 NULL /*pOutputIf*/, NULL /*pvOutputIfUser*/,
812 NULL /*pErrInfo*/, "%s", pszLogFile ? pszLogFile : "");
813 if (RT_SUCCESS(rc))
814 {
815 /* register this logger as the release logger */
816 RTLogRelSetDefaultInstance(g_pLoggerRelease);
817
818 /* Explicitly flush the log in case of VBOXDRVINST_RELEASE_LOG=buffered. */
819 RTLogFlush(g_pLoggerRelease);
820 }
821
822 return rc;
823}
824
825/**
826 * Destroys the currently active logging instance.
827 */
828static void vboxDrvInstLogDestroy(void)
829{
830 RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
831}
832
833/**
834 * Performs initialization tasks before a specific command is being run.
835 *
836 * @returns VBox status code.
837 */
838static int vboxDrvInstInit(void)
839{
840 int rc = vboxDrvInstLogCreate(g_szLogFile[0] ? g_szLogFile : NULL);
841 if (RT_FAILURE(rc))
842 {
843 RTMsgError("Failed to create release log '%s', rc=%Rrc\n", g_szLogFile[0] ? g_szLogFile : "<None>", rc);
844 return rc;
845 }
846
847 /* Refuse to run on too old Windows versions (<= NT4). */
848 uint64_t const uNtVer = RTSystemGetNtVersion();
849 if (RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer) <= 4)
850 {
851 vboxDrvInstLogError("Windows version (%d.%d.%d) too old and not supported\n", RTSYSTEM_NT_VERSION_GET_MAJOR(uNtVer),
852 RTSYSTEM_NT_VERSION_GET_MINOR(uNtVer),
853 RTSYSTEM_NT_VERSION_GET_BUILD(uNtVer));
854 return VERR_NOT_SUPPORTED;
855 }
856
857 return VINF_SUCCESS;
858}
859
860/**
861 * Performs destruction tasks after a specific command has been run.
862 */
863static void vboxDrvInstDestroy(void)
864{
865 vboxDrvInstLogDestroy();
866}
867
868int main(int argc, char **argv)
869{
870 /*
871 * Init IPRT.
872 */
873 int rc = RTR3InitExe(argc, &argv, 0);
874 if (RT_FAILURE(rc))
875 return RTMsgInitFailure(rc);
876
877 vboxDrvInstShowLogo(g_pStdOut);
878
879 /*
880 * Process common options.
881 */
882 RTGETOPTSTATE GetState;
883 RT_ZERO(GetState);
884 rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions, RT_ELEMENTS(g_aCmdCommonOptions),
885 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
886 AssertRCReturn(rc, RTEXITCODE_INIT);
887
888 int ch;
889 RTGETOPTUNION ValueUnion;
890 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
891 {
892 switch (ch)
893 {
894 case 'h':
895 return vboxDrvInstShowUsage(g_pStdOut, NULL);
896
897 case 'l':
898 rc = RTStrCopy(g_szLogFile, sizeof(g_szLogFile), ValueUnion.psz);
899 if (RT_FAILURE(rc))
900 return RTMsgErrorExitFailure("Error setting logfile, rc=%Rrc\n", rc);
901 break;
902
903 case 'v':
904 g_uVerbosity++;
905 break;
906
907 case 'V':
908 return vboxDrvInstShowVersion(g_pStdOut);
909
910 case VERR_GETOPT_UNKNOWN_OPTION:
911 return vboxDrvInstShowUsage(g_pStdOut, NULL);
912
913 case VINF_GETOPT_NOT_OPTION:
914 {
915 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
916 {
917 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
918 if (strcmp(ValueUnion.psz, pCmd->pszCommand) == 0)
919 {
920 rc = vboxDrvInstInit();
921 if (RT_FAILURE(rc))
922 return RTEXITCODE_FAILURE;
923
924 /* Count the combined option definitions: */
925 size_t cCombinedOptions = pCmd->cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
926
927 RTEXITCODE rcExit;
928
929 /* Combine the option definitions: */
930 PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
931 if (paCombinedOptions)
932 {
933 uint32_t idxOpts = 0;
934 memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
935 idxOpts += RT_ELEMENTS(g_aCmdCommonOptions);
936
937 memcpy(&paCombinedOptions[idxOpts], pCmd->paOptions, pCmd->cOptions * sizeof(RTGETOPTDEF));
938 idxOpts += (uint32_t)pCmd->cOptions;
939
940 /* Re-initialize the option getter state and pass it to the command handler. */
941 rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
942 GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
943
944 if (RT_SUCCESS(rc))
945 rcExit = pCmd->pfnHandler(&GetState);
946 else
947 rcExit = RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
948 RTMemFree(paCombinedOptions);
949 }
950 else
951 rcExit = RTMsgErrorExitFailure("Out of memory!");
952
953 vboxDrvInstDestroy();
954 return rcExit;
955 }
956 }
957 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
958 vboxDrvInstShowCommands(g_pStdErr);
959 return RTEXITCODE_SYNTAX;
960 }
961
962 default:
963 break;
964 }
965 }
966
967 /* List all Windows driver store entries if no command is given. */
968 rc = vboxDrvInstInit();
969 if (RT_FAILURE(rc))
970 return RTEXITCODE_FAILURE;
971 RTEXITCODE rcExit = vboxDrvInstCmdListMain(&GetState);
972 vboxDrvInstDestroy();
973 return rcExit;
974}
975
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