VirtualBox

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

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

Windows driver installation: Added new VBoxDrvInst tool for generic Windows driver installation, listing and uninstallation, to have a common tool as a host and a guest version. bugref:10762

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 26.5 KB
Line 
1/* $Id: VBoxDrvInst.cpp 106936 2024-11-11 17:42:05Z 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 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include <iprt/buildconfig.h>
42#include <iprt/ctype.h>
43#include <iprt/cpp/ministring.h> /* For replacement fun. */
44#include <iprt/env.h>
45#include <iprt/getopt.h>
46#include <iprt/initterm.h>
47#include <iprt/message.h>
48#include <iprt/path.h>
49#include <iprt/types.h>
50#include <iprt/process.h> /* For RTProcShortName(). */
51#include <iprt/stream.h>
52#include <iprt/string.h>
53#include <iprt/test.h>
54#include <iprt/utf16.h>
55#include <iprt/win/windows.h>
56
57#include <package-generated.h>
58#include "product-generated.h"
59
60#include <VBox/version.h>
61#include <VBox/log.h>
62
63#include <VBox/err.h>
64
65#include <VBox/GuestHost/VBoxWinDrvInst.h>
66#include <VBox/GuestHost/VBoxWinDrvStore.h>
67
68
69/*********************************************************************************************************************************
70* Prototypes *
71*********************************************************************************************************************************/
72static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdListMain(PRTGETOPTSTATE pGetState);
73static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdInstallMain(PRTGETOPTSTATE pGetState);
74static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdUninstallMain(PRTGETOPTSTATE pGetState);
75
76static DECLCALLBACK(const char *) vboxDrvInstCmdListHelp(PCRTGETOPTDEF pOpt);
77static DECLCALLBACK(const char *) vboxDrvInstCmdInstallHelp(PCRTGETOPTDEF pOpt);
78static DECLCALLBACK(const char *) vboxDrvInstCmdUninstallHelp(PCRTGETOPTDEF pOpt);
79
80struct VBOXDRVINSTCMD;
81static RTEXITCODE vboxDrvInstShowUsage(PRTSTREAM pStrm, VBOXDRVINSTCMD const *pOnlyCmd);
82
83
84/*********************************************************************************************************************************
85* Global Variables *
86*********************************************************************************************************************************/
87/** Verbosity level. */
88static unsigned g_uVerbosity = 0;
89
90
91/*********************************************************************************************************************************
92* Definitions *
93*********************************************************************************************************************************/
94typedef enum VBOXDRVINSTEXITCODE
95{
96 /** A reboot is needed in order to complete the (un)installation. */
97 VBOXDRVINSTEXITCODE_REBOOT_NEEDED = RTEXITCODE_END
98} VBOXDRVINSTEXITCODE;
99
100/**
101 * Driver installation command table entry.
102 */
103typedef struct VBOXDRVINSTCMD
104{
105 /** The command name. */
106 const char *pszCommand;
107 /** The command handler. */
108 DECLCALLBACKMEMBER(RTEXITCODE, pfnHandler,(PRTGETOPTSTATE pGetState));
109
110 /** Command description. */
111 const char *pszDesc;
112 /** Options array. */
113 PCRTGETOPTDEF paOptions;
114 /** Number of options in the option array. */
115 size_t cOptions;
116 /** Gets help for an option. */
117 DECLCALLBACKMEMBER(const char *, pfnOptionHelp,(PCRTGETOPTDEF pOpt));
118} VBOXDRVINSTCMD;
119/** Pointer to a const VBOXDRVINSTCMD entry. */
120typedef VBOXDRVINSTCMD const *PCVBOXDRVINSTCMD;
121
122/**
123 * Long option values for the 'list' command.
124 */
125enum
126{
127 VBOXDRVINST_LIST_OPT_MODEL = 900,
128 VBOXDRVINST_LIST_OPT_PNPID
129};
130
131/**
132 * Command line parameters for the 'list' command.
133 */
134static const RTGETOPTDEF g_aCmdListOptions[] =
135{
136 { "--model", VBOXDRVINST_LIST_OPT_MODEL, RTGETOPT_REQ_STRING },
137 { "--pnp", VBOXDRVINST_LIST_OPT_PNPID, RTGETOPT_REQ_STRING },
138 { "--pnpid" , VBOXDRVINST_LIST_OPT_PNPID, RTGETOPT_REQ_STRING },
139 { "--pnp-id", VBOXDRVINST_LIST_OPT_PNPID, RTGETOPT_REQ_STRING }
140};
141
142/**
143 * Command definition for the 'list' command.
144 */
145const VBOXDRVINSTCMD g_CmdList =
146{
147 "list",
148 vboxDrvInstCmdListMain,
149 "Lists installed drivers.",
150 NULL, /* paOptions */
151 0, /* cOptions */
152 vboxDrvInstCmdListHelp
153};
154
155/**
156 * Long option values for the 'install' command.
157 */
158enum
159{
160 VBOXDRVINST_INSTALL_OPT_INF_FILE = 900,
161 VBOXDRVINST_INSTALL_OPT_INF_SECTION,
162 VBOXDRVINST_INSTALL_OPT_MODEL,
163 VBOXDRVINST_INSTALL_OPT_PNPID,
164 VBOXDRVINST_INSTALL_OPT_NOT_FORCE,
165 VBOXDRVINST_INSTALL_OPT_NOT_SILENT
166};
167
168/**
169 * Command line parameters for the 'install' command.
170 */
171static const RTGETOPTDEF g_aCmdInstallOptions[] =
172{
173 { "--inf-file", VBOXDRVINST_INSTALL_OPT_INF_FILE, RTGETOPT_REQ_STRING },
174 { "--inf-section", VBOXDRVINST_INSTALL_OPT_INF_SECTION, RTGETOPT_REQ_STRING },
175 { "--model", VBOXDRVINST_INSTALL_OPT_MODEL, RTGETOPT_REQ_STRING },
176 { "--pnp", VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
177 { "--pnpid" , VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
178 { "--pnp-id", VBOXDRVINST_INSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
179 { "--not-force", VBOXDRVINST_INSTALL_OPT_NOT_FORCE, RTGETOPT_REQ_NOTHING },
180 { "--not-silent", VBOXDRVINST_INSTALL_OPT_NOT_SILENT, RTGETOPT_REQ_NOTHING }
181};
182
183/**
184 * Command definition for the 'install' command.
185 */
186const VBOXDRVINSTCMD g_CmdInstall =
187{
188 "install",
189 vboxDrvInstCmdInstallMain,
190 "Installs a driver.",
191 g_aCmdInstallOptions,
192 RT_ELEMENTS(g_aCmdInstallOptions),
193 vboxDrvInstCmdInstallHelp
194};
195
196/**
197 * Long option values for the 'uninstall' command.
198 */
199enum
200{
201 VBOXDRVINST_UNINSTALL_OPT_INF_FILE = 900,
202 VBOXDRVINST_UNINSTALL_OPT_INF_SECTION,
203 VBOXDRVINST_UNINSTALL_OPT_MODEL,
204 VBOXDRVINST_UNINSTALL_OPT_PNPID
205};
206
207/**
208 * Command line parameters for the 'uninstall' command.
209 */
210static const RTGETOPTDEF g_aCmdUninstallOptions[] =
211{
212 { "--inf-file", VBOXDRVINST_UNINSTALL_OPT_INF_FILE, RTGETOPT_REQ_STRING },
213 { "--inf-section", VBOXDRVINST_UNINSTALL_OPT_INF_SECTION, RTGETOPT_REQ_STRING },
214 { "--model", VBOXDRVINST_UNINSTALL_OPT_MODEL, RTGETOPT_REQ_STRING },
215 { "--pnp", VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
216 { "--pnpid" , VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING },
217 { "--pnp-id", VBOXDRVINST_UNINSTALL_OPT_PNPID, RTGETOPT_REQ_STRING }
218};
219
220/**
221 * Command definition for the 'uninstall' command.
222 */
223const VBOXDRVINSTCMD g_CmdUninstall =
224{
225 "uninstall",
226 vboxDrvInstCmdUninstallMain,
227 "Uninstalls drivers.",
228 g_aCmdUninstallOptions,
229 RT_ELEMENTS(g_aCmdUninstallOptions),
230 vboxDrvInstCmdUninstallHelp
231};
232
233/**
234 * Commands.
235 */
236static const VBOXDRVINSTCMD * const g_apCommands[] =
237{
238 &g_CmdList,
239 &g_CmdInstall,
240 &g_CmdUninstall
241};
242
243/**
244 * Common option definitions for all commands.
245 */
246static const RTGETOPTDEF g_aCmdCommonOptions[] =
247{
248 { "--help", 'h', RTGETOPT_REQ_NOTHING },
249 { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
250 { "--version", 'V', RTGETOPT_REQ_NOTHING }
251};
252
253
254/*********************************************************************************************************************************
255* Implementation *
256*********************************************************************************************************************************/
257
258/**
259 * Logs message, va_list version.
260 *
261 * @returns VBox status code.
262 * @param
263 * @param pszFormat Format string to log.
264 * @param args va_list to use.
265 */
266DECLINLINE(void) vboxDrvInstLogExV(const char *pszFormat, va_list args)
267{
268 char *psz = NULL;
269 RTStrAPrintfV(&psz, pszFormat, args);
270 AssertPtrReturnVoid(psz);
271
272 LogRel(("%s", psz));
273 RTPrintf("%s", psz);
274
275 RTStrFree(psz);
276}
277
278/**
279 * Logs a message.
280 *
281 * @returns VBox status code.
282 * @param pszFormat Format string to log.
283 */
284DECLINLINE(void) vboxDrvInstLogError(const char *pszFormat, ...)
285{
286 va_list args;
287 va_start(args, pszFormat);
288 vboxDrvInstLogExV(pszFormat, args);
289 va_end(args);
290}
291
292/**
293 * Logs an error message.
294 *
295 * @returns VBox status code.
296 * @param pszFormat Format string to log.
297 */
298DECLINLINE(void) vboxDrvInstLog(const char *pszFormat, ...)
299{
300 va_list args;
301 va_start(args, pszFormat);
302 vboxDrvInstLogExV(pszFormat, args);
303 va_end(args);
304}
305
306/**
307 * Logging callback for the Windows driver (un)installation code.
308 */
309static DECLCALLBACK(void) vboxDrvInstLogCallback(VBOXWINDRIVERLOGTYPE enmType, const char *pszMsg, void *pvUser)
310{
311 RT_NOREF(pvUser);
312
313 /*
314 * Log to standard output:
315 */
316 switch (enmType)
317 {
318 case VBOXWINDRIVERLOGTYPE_ERROR:
319 vboxDrvInstLogError("*** Error: %s\n", pszMsg);
320 break;
321
322 case VBOXWINDRIVERLOGTYPE_REBOOT_NEEDED:
323 vboxDrvInstLog("A reboot is needed in order to complete the (un)installation!\n");
324 break;
325
326 default:
327 vboxDrvInstLog("%s\n", pszMsg);
328 break;
329 }
330}
331
332/** Option help for the 'list' command. */
333static DECLCALLBACK(const char *) vboxDrvInstCmdListHelp(PCRTGETOPTDEF pOpt)
334{
335 switch (pOpt->iShort)
336 {
337 case VBOXDRVINST_LIST_OPT_MODEL: return "Specifies the driver model";
338 case VBOXDRVINST_LIST_OPT_PNPID: return "Specifies the PnP (device) ID";
339 default:
340 break;
341 }
342 return NULL;
343}
344
345/**
346 * Main (entry) function for the 'list' command.
347 *
348 * @returns Program exit code.
349 * @param pGetState RTGetOpt state.
350 */
351static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdListMain(PRTGETOPTSTATE pGetState)
352{
353 const char *pszPattern = NULL;
354
355 int ch;
356 RTGETOPTUNION ValueUnion;
357 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
358 {
359 switch (ch)
360 {
361 case 'h':
362 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdList);
363
364 case VINF_GETOPT_NOT_OPTION:
365 {
366 /** @todo Use pattern to filter entries, e.g. "pnp:<PNP-ID>" or "model:VBoxSup*". */
367 pszPattern = ValueUnion.psz;
368 break;
369 }
370
371 default:
372 return RTGetOptPrintError(ch, &ValueUnion);
373 }
374 }
375
376 PVBOXWINDRVSTORE pStore;
377 int rc = VBoxWinDrvStoreCreate(&pStore);
378
379 PVBOXWINDRVSTORELIST pList = NULL;
380 if (pszPattern)
381 rc = VBoxWinDrvStoreQueryAny(pStore, pszPattern, &pList);
382 else
383 rc = VBoxWinDrvStoreQueryAll(pStore, &pList);
384 if (RT_SUCCESS(rc))
385 {
386 RTPrintf("Location: %s\n\n", VBoxWinDrvStoreBackendGetLocation(pStore));
387
388 RTPrintf("%-40s | %-40s\n", "OEM INF File", "Version");
389 RTPrintf("%-40s | %-40s\n", " Model (First)", "PnP ID (First)");
390 RTPrintf("--------------------------------------------------------------------------------\n");
391
392 size_t cEntries = 0;
393 PVBOXWINDRVSTOREENTRY pCur;
394 RTListForEach(&pList->List, pCur, VBOXWINDRVSTOREENTRY, Node)
395 {
396 RTPrintf("%-40ls | %-40ls\n",
397 pCur->wszInfFile, pCur->Ver.wszDriverVer);
398 RTPrintf(" %-36ls | %-40ls\n",
399 pCur->wszModel, pCur->wszPnpId);
400 cEntries++;
401 }
402
403 if (pszPattern)
404 RTPrintf("\nFound %zu entries (filtered).\n", cEntries);
405 else
406 RTPrintf("\nFound %zu entries.\n", cEntries);
407 }
408
409 VBoxWinDrvStoreListFree(pList);
410
411 VBoxWinDrvStoreDestroy(pStore);
412 pStore = NULL;
413
414 RTPrintf("\nUse DOS-style wildcards to adjust results.\n");
415 RTPrintf("Use \"--help\" to print syntax help.\n");
416
417 return RTEXITCODE_SUCCESS;
418}
419
420/** Option help for the 'install' command. */
421static DECLCALLBACK(const char *) vboxDrvInstCmdInstallHelp(PCRTGETOPTDEF pOpt)
422{
423 switch (pOpt->iShort)
424 {
425 case VBOXDRVINST_INSTALL_OPT_INF_FILE: return "Specifies the INF file to install";
426 case VBOXDRVINST_INSTALL_OPT_INF_SECTION: return "Specifies the INF section to install";
427 case VBOXDRVINST_INSTALL_OPT_MODEL: return "Specifies the driver model";
428 case VBOXDRVINST_INSTALL_OPT_PNPID: return "Specifies the PnP (device) ID";
429 case VBOXDRVINST_INSTALL_OPT_NOT_FORCE: return "Installation will not be forced";
430 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT: return "Installation will not run in silent mode";
431 default:
432 break;
433 }
434 return NULL;
435}
436
437/**
438 * Main (entry) function for the 'install' command.
439 *
440 * @returns Program exit code.
441 * @param pGetState RTGetOpt state.
442 */
443static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdInstallMain(PRTGETOPTSTATE pGetState)
444{
445 char *pszInfFile = NULL;
446 char *pszModel = NULL;
447 char *pszPnpId = NULL;
448 char *pszInfSection = NULL;
449
450 /* By default we want to force an installation and be silent. */
451 uint32_t fInstall = VBOX_WIN_DRIVERINSTALL_F_SILENT | VBOX_WIN_DRIVERINSTALL_F_FORCE;
452
453 int rc = VINF_SUCCESS;
454
455#define DUP_ARG_TO_STR(a_Str) \
456 a_Str = RTStrDup(ValueUnion.psz); \
457 if (!a_Str) \
458 { \
459 RTMsgError("Can't handle argument '%s': Out of memory\n", ValueUnion.psz); \
460 rc = VERR_NO_MEMORY; \
461 break; \
462 }
463
464 int ch;
465 RTGETOPTUNION ValueUnion;
466 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
467 {
468 switch (ch)
469 {
470 case 'h':
471 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdInstall);
472
473 case VBOXDRVINST_INSTALL_OPT_INF_FILE:
474 DUP_ARG_TO_STR(pszInfFile);
475 break;
476
477 case VBOXDRVINST_INSTALL_OPT_INF_SECTION:
478 DUP_ARG_TO_STR(pszInfSection);
479 break;
480
481 case VBOXDRVINST_INSTALL_OPT_MODEL:
482 DUP_ARG_TO_STR(pszModel);
483 break;
484
485 case VBOXDRVINST_INSTALL_OPT_PNPID:
486 DUP_ARG_TO_STR(pszPnpId);
487 break;
488
489 case VBOXDRVINST_INSTALL_OPT_NOT_FORCE:
490 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_FORCE;
491 break;
492
493 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT:
494 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_SILENT;
495 break;
496
497 default:
498 return RTGetOptPrintError(ch, &ValueUnion);
499 }
500 }
501
502#undef DUP_ARG_TO_STR
503
504 if (RT_FAILURE(rc))
505 return RTEXITCODE_FAILURE;
506
507 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
508
509 VBOXWINDRVINST hWinDrvInst;
510 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
511 if (RT_SUCCESS(rc))
512 {
513 rc = VBoxWinDrvInstInstallEx(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fInstall);
514 if (RT_SUCCESS(rc))
515 {
516 if (rc == VINF_REBOOT_NEEDED)
517 rcExit = (RTEXITCODE)VBOXDRVINSTEXITCODE_REBOOT_NEEDED;
518 }
519 else
520 rcExit = RTEXITCODE_FAILURE;
521 }
522
523 RTStrFree(pszInfFile);
524 RTStrFree(pszInfSection);
525 RTStrFree(pszModel);
526 RTStrFree(pszPnpId);
527
528 return rcExit;
529}
530
531/** Option help for the 'uninstall' command. */
532static DECLCALLBACK(const char *) vboxDrvInstCmdUninstallHelp(PCRTGETOPTDEF pOpt)
533{
534 switch (pOpt->iShort)
535 {
536 case VBOXDRVINST_UNINSTALL_OPT_INF_FILE: return "Specifies the INF File to install";
537 case VBOXDRVINST_UNINSTALL_OPT_INF_SECTION: return "Specifies the INF section to install";
538 case VBOXDRVINST_UNINSTALL_OPT_MODEL: return "Specifies the driver model to install";
539 case VBOXDRVINST_UNINSTALL_OPT_PNPID: return "Specifies the PnP (device) ID to install";
540 default:
541 break;
542 }
543 return NULL;
544}
545
546/**
547 * Main (entry) function for the 'uninstall' command.
548 *
549 * @returns Program exit code.
550 * @param pGetState RTGetOpt state.
551 */
552static DECLCALLBACK(RTEXITCODE) vboxDrvInstCmdUninstallMain(PRTGETOPTSTATE pGetState)
553{
554 char *pszInfFile = NULL;
555 char *pszModel = NULL;
556 char *pszPnpId = NULL;
557 char *pszInfSection = NULL;
558
559 /* By default we want a silent uninstallation. */
560 uint32_t fInstall = VBOX_WIN_DRIVERINSTALL_F_SILENT;
561
562 int rc = VINF_SUCCESS;
563
564#define DUP_ARG_TO_STR(a_Str) \
565 a_Str = RTStrDup(ValueUnion.psz); \
566 if (!a_Str) \
567 { \
568 RTMsgError("Can't handle argument '%s': Out of memory\n", ValueUnion.psz); \
569 rc = VERR_NO_MEMORY; \
570 break; \
571 }
572
573 int ch;
574 RTGETOPTUNION ValueUnion;
575 while ((ch = RTGetOpt(pGetState, &ValueUnion)))
576 {
577 switch (ch)
578 {
579 case 'h':
580 return vboxDrvInstShowUsage(g_pStdOut, &g_CmdUninstall);
581
582 case VBOXDRVINST_INSTALL_OPT_INF_FILE:
583 DUP_ARG_TO_STR(pszInfFile);
584 break;
585
586 case VBOXDRVINST_INSTALL_OPT_INF_SECTION:
587 DUP_ARG_TO_STR(pszInfSection);
588 break;
589
590 case VBOXDRVINST_INSTALL_OPT_MODEL:
591 DUP_ARG_TO_STR(pszModel);
592 break;
593
594 case VBOXDRVINST_INSTALL_OPT_PNPID:
595 DUP_ARG_TO_STR(pszPnpId);
596 break;
597
598 case VBOXDRVINST_INSTALL_OPT_NOT_SILENT:
599 fInstall &= ~VBOX_WIN_DRIVERINSTALL_F_SILENT;
600 break;
601
602 default:
603 return RTGetOptPrintError(ch, &ValueUnion);
604 }
605 }
606
607#undef DUP_ARG_TO_STR
608
609 if (RT_FAILURE(rc))
610 return RTEXITCODE_FAILURE;
611
612 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
613
614 VBOXWINDRVINST hWinDrvInst;
615 rc = VBoxWinDrvInstCreateEx(&hWinDrvInst, g_uVerbosity, &vboxDrvInstLogCallback, NULL /* pvUser */);
616 if (RT_SUCCESS(rc))
617 {
618 rc = VBoxWinDrvInstUninstall(hWinDrvInst, pszInfFile, pszModel, pszPnpId, fInstall);
619 if (RT_SUCCESS(rc))
620 {
621 if (rc == VINF_REBOOT_NEEDED)
622 rcExit = (RTEXITCODE)VBOXDRVINSTEXITCODE_REBOOT_NEEDED;
623 }
624 else
625 rcExit = RTEXITCODE_FAILURE;
626 }
627
628 RTStrFree(pszInfFile);
629 RTStrFree(pszInfSection);
630 RTStrFree(pszModel);
631 RTStrFree(pszPnpId);
632
633 return rcExit;
634}
635
636/**
637 * Shows the commands and their descriptions.
638 *
639 * @returns RTEXITCODE
640 * @param pStrm Stream to use.
641 */
642static RTEXITCODE vboxDrvInstShowCommands(PRTSTREAM pStrm)
643{
644 RTStrmPrintf(pStrm, "Commands:\n");
645 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
646 RTStrmPrintf(pStrm, "%12s - %s\n", g_apCommands[iCmd]->pszCommand, g_apCommands[iCmd]->pszDesc);
647 return RTEXITCODE_SUCCESS;
648}
649
650/**
651 * Shows the general usage.
652 *
653 * @returns RTEXITCODE
654 * @param pStrm Stream to use.
655 */
656static RTEXITCODE vboxDrvInstShowUsage(PRTSTREAM pStrm, PCVBOXDRVINSTCMD pOnlyCmd)
657{
658 const char *pszProcName = RTProcShortName();
659
660 RTStrmPrintf(pStrm, "usage: %s [global options] <command> [command-options]\n", pszProcName);
661 RTStrmPrintf(pStrm,
662 "\n"
663 "Global Options:\n"
664 " -v, --verbose\n"
665 " Increase verbosity\n"
666 " -V, --version\n"
667 " Displays version\n"
668 " -h, -?, --help\n"
669 " Displays help\n"
670 );
671
672 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
673 {
674 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
675 if (!pOnlyCmd || pCmd == pOnlyCmd)
676 {
677 RTStrmPrintf(pStrm,
678 "\n"
679 "Command '%s':\n"
680 " %s\n",
681 pCmd->pszCommand, pCmd->pszDesc);
682
683 if (!pCmd->paOptions)
684 continue;
685
686 RTStrmPrintf(pStrm, "Options for '%s':\n", pCmd->pszCommand);
687 PCRTGETOPTDEF const paOptions = pCmd->paOptions;
688 for (unsigned i = 0; i < pCmd->cOptions; i++)
689 {
690 if (RT_C_IS_PRINT(paOptions[i].iShort))
691 RTStrmPrintf(pStrm, " -%c, %s\n", paOptions[i].iShort, paOptions[i].pszLong);
692 else
693 RTStrmPrintf(pStrm, " %s\n", paOptions[i].pszLong);
694
695 const char *pszHelp = NULL;
696 if (pCmd->pfnOptionHelp)
697 pszHelp = pCmd->pfnOptionHelp(&paOptions[i]);
698 if (pszHelp)
699 RTStrmPrintf(pStrm, " %s\n", pszHelp);
700 }
701 }
702 }
703
704 RTStrmPrintf(pStrm, "\nExamples:\n");
705 RTStrmPrintf(pStrm, "\t%s install --inf-file C:\\Path\\To\\VBoxUSB.inf\n", pszProcName);
706 RTStrmPrintf(pStrm, "\t%s uninstall --inf -file C:\\Path\\To\\VBoxUSB.inf --pnp-id \"USB\\VID_80EE&PID_CAFE\"\n", pszProcName);
707 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB.AMD64\"\n", pszProcName);
708 RTStrmPrintf(pStrm, "\t%s uninstall --model \"VBoxUSB*\"\n", pszProcName);
709 RTStrmPrintf(pStrm, "\t%s list \"VBox*\"\n\n", pszProcName);
710 RTStrmPrintf(pStrm, "Exit codes:\n");
711 RTStrmPrintf(pStrm, "\t1 - The (un)installation failed.\n");
712 RTStrmPrintf(pStrm, "\t2 - Syntax error.\n");
713 RTStrmPrintf(pStrm, "\t5 - A reboot is needed in order to complete the (un)installation.\n\n");
714
715 return RTEXITCODE_SUCCESS;
716}
717
718/**
719 * Shows tool version.
720 *
721 * @returns RTEXITCODE
722 * @param pStrm Stream to use.
723 */
724static RTEXITCODE vboxDrvInstShowVersion(PRTSTREAM pStrm)
725{
726 RTStrmPrintf(pStrm, "%s\n", RTBldCfgRevisionStr());
727 return RTEXITCODE_SUCCESS;
728}
729
730/**
731 * Shows the logo.
732 *
733 * @param pStream Output stream to show logo on.
734 */
735static void vboxDrvInstShowLogo(PRTSTREAM pStream)
736{
737 RTStrmPrintf(pStream, VBOX_PRODUCT " VBoxDrvInst (Driver Installation Utility) Version " VBOX_VERSION_STRING " - r%s (%s)\n"
738 "Copyright (C) " VBOX_C_YEAR " " VBOX_VENDOR "\n\n", RTBldCfgRevisionStr(), RTBldCfgTargetArch());
739}
740
741int main(int argc, char **argv)
742{
743 /*
744 * Init IPRT.
745 */
746 int rc = RTR3InitExe(argc, &argv, 0);
747 if (RT_FAILURE(rc))
748 return RTMsgInitFailure(rc);
749
750 vboxDrvInstShowLogo(g_pStdOut);
751
752 /*
753 * Process common options.
754 */
755 RTGETOPTSTATE GetState;
756 RT_ZERO(GetState);
757 rc = RTGetOptInit(&GetState, argc, argv, g_aCmdCommonOptions, RT_ELEMENTS(g_aCmdCommonOptions),
758 1 /*idxFirst*/, 0 /*fFlags - must not sort! */);
759 AssertRCReturn(rc, RTEXITCODE_INIT);
760
761 int ch;
762 RTGETOPTUNION ValueUnion;
763 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
764 {
765 switch (ch)
766 {
767 case 'h':
768 return vboxDrvInstShowUsage(g_pStdOut, NULL);
769
770 case 'v':
771 g_uVerbosity++;
772 break;
773
774 case 'V':
775 return vboxDrvInstShowVersion(g_pStdOut);
776
777 case VERR_GETOPT_UNKNOWN_OPTION:
778 return vboxDrvInstShowUsage(g_pStdOut, NULL);
779
780 case VINF_GETOPT_NOT_OPTION:
781 {
782 for (uintptr_t iCmd = 0; iCmd < RT_ELEMENTS(g_apCommands); iCmd++)
783 {
784 PCVBOXDRVINSTCMD const pCmd = g_apCommands[iCmd];
785 if (strcmp(ValueUnion.psz, pCmd->pszCommand) == 0)
786 {
787 /* Count the combined option definitions: */
788 size_t cCombinedOptions = pCmd->cOptions + RT_ELEMENTS(g_aCmdCommonOptions);
789
790 /* Combine the option definitions: */
791 PRTGETOPTDEF paCombinedOptions = (PRTGETOPTDEF)RTMemAlloc(cCombinedOptions * sizeof(RTGETOPTDEF));
792 if (paCombinedOptions)
793 {
794 uint32_t idxOpts = 0;
795 memcpy(paCombinedOptions, g_aCmdCommonOptions, sizeof(g_aCmdCommonOptions));
796 idxOpts += RT_ELEMENTS(g_aCmdCommonOptions);
797
798 memcpy(&paCombinedOptions[idxOpts], pCmd->paOptions, pCmd->cOptions * sizeof(RTGETOPTDEF));
799 idxOpts += (uint32_t)pCmd->cOptions;
800
801 /* Re-initialize the option getter state and pass it to the command handler. */
802 rc = RTGetOptInit(&GetState, argc, argv, paCombinedOptions, cCombinedOptions,
803 GetState.iNext /*idxFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
804 if (RT_SUCCESS(rc))
805 {
806 RTEXITCODE rcExit = pCmd->pfnHandler(&GetState);
807 RTMemFree(paCombinedOptions);
808 return rcExit;
809 }
810 RTMemFree(paCombinedOptions);
811 return RTMsgErrorExitFailure("RTGetOptInit failed for '%s': %Rrc", ValueUnion.psz, rc);
812 }
813 return RTMsgErrorExitFailure("Out of memory!");
814 }
815 }
816 RTMsgError("Unknown command '%s'!\n", ValueUnion.psz);
817 vboxDrvInstShowCommands(g_pStdErr);
818 return RTEXITCODE_SYNTAX;
819 }
820
821 default:
822 break;
823 }
824 }
825
826 /* List all Windows driver store entries if no command is given. */
827 return vboxDrvInstCmdListMain(&GetState);
828}
829
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