VirtualBox

source: vbox/trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp@ 33944

Last change on this file since 33944 was 33806, checked in by vboxsync, 14 years ago

ExtPack changes, related IPRT changes.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 24.4 KB
Line 
1/* $Id: VBoxExtPackHelperApp.cpp 33806 2010-11-05 17:20:15Z vboxsync $ */
2/** @file
3 * VirtualBox Main - Extension Pack Helper Application, usually set-uid-to-root.
4 */
5
6/*
7 * Copyright (C) 2010 Oracle Corporation
8 *
9 * Oracle Corporation confidential
10 * All rights reserved
11 */
12
13
14/*******************************************************************************
15* Header Files *
16*******************************************************************************/
17#include "include/ExtPackUtil.h"
18
19#include <iprt/buildconfig.h>
20//#include <iprt/ctype.h>
21#include <iprt/dir.h>
22//#include <iprt/env.h>
23#include <iprt/file.h>
24#include <iprt/fs.h>
25#include <iprt/getopt.h>
26#include <iprt/initterm.h>
27#include <iprt/message.h>
28#include <iprt/param.h>
29#include <iprt/path.h>
30//#include <iprt/pipe.h>
31#include <iprt/process.h>
32#include <iprt/string.h>
33#include <iprt/stream.h>
34
35#include <VBox/log.h>
36#include <VBox/err.h>
37#include <VBox/sup.h>
38#include <VBox/version.h>
39
40
41/* Override RTAssertShouldPanic to prevent gdb process creation. */
42RTDECL(bool) RTAssertShouldPanic(void)
43{
44 return true;
45}
46
47
48/**
49 * Handle the special standard options when these are specified after the
50 * command.
51 *
52 * @param ch The option character.
53 */
54static RTEXITCODE DoStandardOption(int ch)
55{
56 switch (ch)
57 {
58 case 'h':
59 {
60 RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n"
61 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n"
62 "All rights reserved.\n"
63 "\n"
64 "This NOT intended for general use, please use VBoxManage instead\n"
65 "or call the IExtPackManager API directly.\n"
66 "\n"
67 "Usage: %s <command> [options]\n"
68 "Commands:\n"
69 " install --base-dir <dir> --certificate-dir <dir> --name <name> \\\n"
70 " --tarball <tarball> --tarball-fd <fd>\n"
71 " uninstall --base-dir <dir> --name <name>\n"
72 , RTProcShortName());
73 return RTEXITCODE_SUCCESS;
74 }
75
76 case 'V':
77 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision());
78 return RTEXITCODE_SUCCESS;
79
80 default:
81 AssertFailedReturn(RTEXITCODE_FAILURE);
82 }
83}
84
85
86/**
87 * Checks if the cerficiate directory is valid.
88 *
89 * @returns true if it is valid, false if it isn't.
90 * @param pszCertDir The certificate directory to validate.
91 */
92static bool IsValidCertificateDir(const char *pszCertDir)
93{
94 /*
95 * Just be darn strict for now.
96 */
97 char szCorrect[RTPATH_MAX];
98 int rc = RTPathAppPrivateNoArch(szCorrect, sizeof(szCorrect));
99 if (RT_FAILURE(rc))
100 return false;
101 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_CERT_DIR);
102 if (RT_FAILURE(rc))
103 return false;
104
105 return RTPathCompare(szCorrect, pszCertDir) == 0;
106}
107
108
109/**
110 * Checks if the base directory is valid.
111 *
112 * @returns true if it is valid, false if it isn't.
113 * @param pszBaesDir The base directory to validate.
114 */
115static bool IsValidBaseDir(const char *pszBaseDir)
116{
117 /*
118 * Just be darn strict for now.
119 */
120 char szCorrect[RTPATH_MAX];
121 int rc = RTPathAppPrivateArch(szCorrect, sizeof(szCorrect));
122 if (RT_FAILURE(rc))
123 return false;
124 rc = RTPathAppend(szCorrect, sizeof(szCorrect), VBOX_EXTPACK_INSTALL_DIR);
125 if (RT_FAILURE(rc))
126 return false;
127
128 return RTPathCompare(szCorrect, pszBaseDir) == 0;
129}
130
131/**
132 * Cleans up a temporary extension pack directory.
133 *
134 * This is used by 'uninstall', 'cleanup' and in the failure path of 'install'.
135 *
136 * @returns The program exit code.
137 * @param pszDir The directory to clean up. The caller is
138 * responsible for making sure this is valid.
139 * @param fTemporary Whether this is a temporary install directory or
140 * not.
141 */
142static RTEXITCODE RemoveExtPackDir(const char *pszDir, bool fTemporary)
143{
144 /** @todo May have to undo 555 modes here later. */
145 int rc = RTDirRemoveRecursive(pszDir, RTDIRRMREC_F_CONTENT_AND_DIR);
146 if (RT_FAILURE(rc))
147 return RTMsgErrorExit(RTEXITCODE_FAILURE,
148 "Failed to delete the %sextension pack directory: %Rrc ('%s')",
149 fTemporary ? "temporary " : "", rc, pszDir);
150 return RTEXITCODE_SUCCESS;
151}
152
153
154/**
155 * Sets the permissions of the temporary extension pack directory just before
156 * renaming it.
157 *
158 * By default the temporary directory is only accessible by root, this function
159 * will make it world readable and browseable.
160 *
161 * @returns The program exit code.
162 * @param pszDir The temporary extension pack directory.
163 */
164static RTEXITCODE SetExtPackPermissions(const char *pszDir)
165{
166#if !defined(RT_OS_WINDOWS)
167 int rc = RTPathSetMode(pszDir, 0755);
168 if (RT_FAILURE(rc))
169 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to set directory permissions: %Rrc ('%s')", rc, pszDir);
170#else
171 /** @todo */
172#endif
173
174 return RTEXITCODE_SUCCESS;
175}
176
177/**
178 * Validates the extension pack.
179 *
180 * Operations performed:
181 * - Manifest seal check.
182 * - Manifest check.
183 * - Recursive hardening check.
184 * - XML validity check.
185 * - Name check (against XML).
186 *
187 * @returns The program exit code.
188 * @param pszDir The directory where the extension pack has been
189 * unpacked.
190 * @param pszName The expected extension pack name.
191 * @param pszTarball The name of the tarball in case we have to
192 * complain about something.
193 */
194static RTEXITCODE ValidateExtPack(const char *pszDir, const char *pszTarball, const char *pszName)
195{
196 /** @todo */
197 return RTEXITCODE_SUCCESS;
198}
199
200
201/**
202 * Unpacks the extension pack into the specified directory.
203 *
204 * This will apply ownership and permission changes to all the content, the
205 * exception is @a pszDirDst which will be handled by SetExtPackPermissions.
206 *
207 * @returns The program exit code.
208 * @param hTarballFile The tarball to unpack.
209 * @param pszDirDst Where to unpack it.
210 * @param pszTarball The name of the tarball in case we have to
211 * complain about something.
212 */
213static RTEXITCODE UnpackExtPack(RTFILE hTarballFile, const char *pszDirDst, const char *pszTarball)
214{
215 /** @todo */
216 return RTEXITCODE_SUCCESS;
217}
218
219
220/**
221 * The 2nd part of the installation process.
222 *
223 * @returns The program exit code.
224 * @param pszBaseDir The base directory.
225 * @param pszCertDir The certificat directory.
226 * @param pszTarball The tarball name.
227 * @param hTarballFile The handle to open the @a pszTarball file.
228 * @param hTarballFileOpt The tarball file handle (optional).
229 * @param pszName The extension pack name.
230 */
231static RTEXITCODE DoInstall2(const char *pszBaseDir, const char *pszCertDir, const char *pszTarball,
232 RTFILE hTarballFile, RTFILE hTarballFileOpt, const char *pszName)
233{
234 /*
235 * Do some basic validation of the tarball file.
236 */
237 RTFSOBJINFO ObjInfo;
238 int rc = RTFileQueryInfo(hTarballFile, &ObjInfo, RTFSOBJATTRADD_UNIX);
239 if (RT_FAILURE(rc))
240 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on '%s'", rc, pszTarball);
241 if (!RTFS_IS_FILE(ObjInfo.Attr.fMode))
242 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Not a regular file: %s", pszTarball);
243
244 if (hTarballFileOpt != NIL_RTFILE)
245 {
246 RTFSOBJINFO ObjInfo2;
247 rc = RTFileQueryInfo(hTarballFileOpt, &ObjInfo2, RTFSOBJATTRADD_UNIX);
248 if (RT_FAILURE(rc))
249 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTFileQueryInfo failed with %Rrc on --tarball-fd", rc);
250 if ( ObjInfo.Attr.u.Unix.INodeIdDevice != ObjInfo2.Attr.u.Unix.INodeIdDevice
251 || ObjInfo.Attr.u.Unix.INodeId != ObjInfo2.Attr.u.Unix.INodeId)
252 return RTMsgErrorExit(RTEXITCODE_FAILURE, "--tarball and --tarball-fd does not match");
253 }
254
255 /*
256 * Construct the paths to the two directories we'll be using.
257 */
258 char szFinalPath[RTPATH_MAX];
259 rc = RTPathJoin(szFinalPath, sizeof(szFinalPath), pszBaseDir, pszName);
260 if (RT_FAILURE(rc))
261 return RTMsgErrorExit(RTEXITCODE_FAILURE,
262 "Failed to construct the path to the final extension pack directory: %Rrc", rc);
263
264 char szTmpPath[RTPATH_MAX];
265 rc = RTPathJoin(szTmpPath, sizeof(szTmpPath) - 64, pszBaseDir, pszName);
266 if (RT_SUCCESS(rc))
267 {
268 size_t cchTmpPath = strlen(szTmpPath);
269 RTStrPrintf(&szTmpPath[cchTmpPath], sizeof(szTmpPath) - cchTmpPath, "-_-inst-%u", (uint32_t)RTProcSelf());
270 }
271 if (RT_FAILURE(rc))
272 return RTMsgErrorExit(RTEXITCODE_FAILURE,
273 "Failed to construct the path to the temporary extension pack directory: %Rrc", rc);
274
275 /*
276 * Check that they don't exist at this point in time.
277 */
278 rc = RTPathQueryInfoEx(szFinalPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
279 if (RT_SUCCESS(rc) && RTFS_IS_DIRECTORY(ObjInfo.Attr.fMode))
280 return RTMsgErrorExit(RTEXITCODE_FAILURE, "The extension pack is already installed. You must uninstall the old one first.");
281 if (RT_SUCCESS(rc))
282 return RTMsgErrorExit(RTEXITCODE_FAILURE,
283 "Found non-directory file system object where the extension pack would be installed ('%s')",
284 szFinalPath);
285 if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
286 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
287
288 rc = RTPathQueryInfoEx(szTmpPath, &ObjInfo, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
289 if (rc != VERR_FILE_NOT_FOUND && rc != VERR_PATH_NOT_FOUND)
290 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Unexpected RTPathQueryInfoEx status code %Rrc for '%s'", rc, szFinalPath);
291
292 /*
293 * Create the temporary directory and prepare the extension pack within it.
294 * If all checks out correctly, rename it to the final directory.
295 */
296 rc = RTDirCreate(szTmpPath, 0700);
297 if (RT_FAILURE(rc))
298 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create temporary directory: %Rrc ('%s')", rc, szTmpPath);
299
300 RTEXITCODE rcExit = UnpackExtPack(hTarballFile, szTmpPath, pszTarball);
301 if (rcExit == RTEXITCODE_SUCCESS)
302 rcExit = ValidateExtPack(szTmpPath, pszTarball, pszName);
303 if (rcExit == RTEXITCODE_SUCCESS)
304 rcExit = SetExtPackPermissions(szTmpPath);
305 if (rcExit == RTEXITCODE_SUCCESS)
306 {
307 rc = RTDirRename(szTmpPath, szFinalPath, RTPATHRENAME_FLAGS_NO_REPLACE);
308 if (RT_SUCCESS(rc))
309 RTMsgInfo("Successfully installed '%s' (%s)", pszName, pszTarball);
310 else
311 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,
312 "Failed to rename the temporary directory to the final one: %Rrc ('%s' -> '%s')",
313 rc, szTmpPath, szFinalPath);
314 }
315
316 /*
317 * Clean up the temporary directory on failure.
318 */
319 if (rcExit != RTEXITCODE_SUCCESS)
320 RemoveExtPackDir(szTmpPath, true /*fTemporary*/);
321
322 return rcExit;
323}
324
325
326/**
327 * Implements the 'install' command.
328 *
329 * @returns The program exit code.
330 * @param argc The number of program arguments.
331 * @param argv The program arguments.
332 */
333static RTEXITCODE DoInstall(int argc, char **argv)
334{
335 /*
336 * Parse the parameters.
337 *
338 * Note! The --base-dir and --cert-dir are only for checking that the
339 * caller and this help applications have the same idea of where
340 * things are. Likewise, the --name is for verifying assumptions
341 * the caller made about the name. The optional --tarball-fd option
342 * is just for easing the paranoia on the user side.
343 */
344 static const RTGETOPTDEF s_aOptions[] =
345 {
346 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
347 { "--cert-dir", 'c', RTGETOPT_REQ_STRING },
348 { "--name", 'n', RTGETOPT_REQ_STRING },
349 { "--tarball", 't', RTGETOPT_REQ_STRING },
350 { "--tarball-fd", 'f', RTGETOPT_REQ_UINT64 }
351 };
352 RTGETOPTSTATE GetState;
353 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
354 if (RT_FAILURE(rc))
355 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
356
357 const char *pszBaseDir = NULL;
358 const char *pszCertDir = NULL;
359 const char *pszName = NULL;
360 const char *pszTarball = NULL;
361 RTFILE hTarballFileOpt = NIL_RTFILE;
362 RTGETOPTUNION ValueUnion;
363 int ch;
364 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
365 {
366 switch (ch)
367 {
368 case 'b':
369 if (pszBaseDir)
370 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
371 pszBaseDir = ValueUnion.psz;
372 if (!IsValidBaseDir(pszBaseDir))
373 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
374 break;
375
376 case 'c':
377 if (pszCertDir)
378 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --cert-dir options");
379 pszCertDir = ValueUnion.psz;
380 if (!IsValidCertificateDir(pszCertDir))
381 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid certificate directory: '%s'", pszCertDir);
382 break;
383
384 case 'n':
385 if (pszName)
386 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
387 pszName = ValueUnion.psz;
388 if (!VBoxExtPackIsValidName(pszName))
389 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
390 break;
391
392 case 't':
393 if (pszTarball)
394 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball options");
395 pszTarball = ValueUnion.psz;
396 break;
397
398 case 'd':
399 {
400 if (hTarballFileOpt != NIL_RTFILE)
401 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --tarball-fd options");
402 RTHCUINTPTR hNative = (RTHCUINTPTR)ValueUnion.u64;
403 if (hNative != ValueUnion.u64)
404 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The --tarball-fd value is out of range: %#RX64", ValueUnion.u64);
405 rc = RTFileFromNative(&hTarballFileOpt, hNative);
406 if (RT_FAILURE(rc))
407 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "RTFileFromNative failed on --target-fd value: %Rrc", rc);
408 break;
409 }
410
411 case 'h':
412 case 'V':
413 return DoStandardOption(ch);
414
415 default:
416 return RTGetOptPrintError(ch, &ValueUnion);
417 }
418 break;
419 }
420 if (!pszName)
421 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
422 if (!pszBaseDir)
423 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
424 if (!pszCertDir)
425 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --cert-dir option");
426 if (!pszTarball)
427 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --tarball option");
428
429 /*
430 * Ok, down to business.
431 */
432 RTFILE hTarballFile;
433 rc = RTFileOpen(&hTarballFile, pszTarball, RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_WRITE);
434 if (RT_FAILURE(rc))
435 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to open the extension pack tarball: %Rrc ('%s')", rc, pszTarball);
436
437 RTEXITCODE rcExit = DoInstall2(pszBaseDir, pszCertDir, pszTarball, hTarballFile, hTarballFileOpt, pszName);
438 RTFileClose(hTarballFile);
439 return rcExit;
440}
441
442
443/**
444 * Implements the 'uninstall' command.
445 *
446 * @returns The program exit code.
447 * @param argc The number of program arguments.
448 * @param argv The program arguments.
449 */
450static RTEXITCODE DoUninstall(int argc, char **argv)
451{
452 /*
453 * Parse the parameters.
454 *
455 * Note! The --base-dir is only for checking that the caller and this help
456 * applications have the same idea of where things are.
457 */
458 static const RTGETOPTDEF s_aOptions[] =
459 {
460 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
461 { "--name", 'n', RTGETOPT_REQ_STRING }
462 };
463 RTGETOPTSTATE GetState;
464 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
465 if (RT_FAILURE(rc))
466 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
467
468 const char *pszBaseDir = NULL;
469 const char *pszName = NULL;
470 RTGETOPTUNION ValueUnion;
471 int ch;
472 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
473 {
474 switch (ch)
475 {
476 case 'b':
477 if (pszBaseDir)
478 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
479 pszBaseDir = ValueUnion.psz;
480 if (!IsValidBaseDir(pszBaseDir))
481 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
482 break;
483
484 case 'n':
485 if (pszName)
486 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --name options");
487 pszName = ValueUnion.psz;
488 if (!VBoxExtPackIsValidName(pszName))
489 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid extension pack name: '%s'", pszName);
490 break;
491
492 case 'h':
493 case 'V':
494 return DoStandardOption(ch);
495
496 default:
497 return RTGetOptPrintError(ch, &ValueUnion);
498 }
499 break;
500 }
501 if (!pszName)
502 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --name option");
503 if (!pszBaseDir)
504 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
505
506 /*
507 * Ok, down to business.
508 */
509 /* Check that it exists. */
510 char szExtPackDir[RTPATH_MAX];
511 rc = RTPathJoin(szExtPackDir, sizeof(szExtPackDir), pszBaseDir, pszName);
512 if (RT_FAILURE(rc))
513 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct extension pack path: %Rrc", rc);
514
515 if (!RTDirExists(szExtPackDir))
516 {
517 RTMsgInfo("Extension pack not installed. Nothing to do.");
518 return RTEXITCODE_SUCCESS;
519 }
520
521 /* Rename the extension pack directory before deleting it to prevent new
522 VM processes from picking it up. */
523 char szExtPackUnInstDir[RTPATH_MAX];
524 rc = RTPathJoin(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), pszBaseDir, pszName);
525 if (RT_SUCCESS(rc))
526 rc = RTStrCat(szExtPackUnInstDir, sizeof(szExtPackUnInstDir), "-_-uninst");
527 if (RT_FAILURE(rc))
528 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to construct temporary extension pack path: %Rrc", rc);
529
530 rc = RTDirRename(szExtPackDir, szExtPackUnInstDir, RTPATHRENAME_FLAGS_NO_REPLACE);
531 if (RT_FAILURE(rc))
532 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to rename the extension pack directory: %Rrc", rc);
533
534 /* Recursively delete the directory content. */
535 RTEXITCODE rcExit = RemoveExtPackDir(szExtPackUnInstDir, false /*fTemporary*/);
536 if (rcExit == RTEXITCODE_SUCCESS)
537 RTMsgInfo("Successfully removed extension pack '%s'\n", pszName);
538
539 return rcExit;
540}
541
542/**
543 * Implements the 'cleanup' command.
544 *
545 * @returns The program exit code.
546 * @param argc The number of program arguments.
547 * @param argv The program arguments.
548 */
549static RTEXITCODE DoCleanup(int argc, char **argv)
550{
551 /*
552 * Parse the parameters.
553 *
554 * Note! The --base-dir is only for checking that the caller and this help
555 * applications have the same idea of where things are.
556 */
557 static const RTGETOPTDEF s_aOptions[] =
558 {
559 { "--base-dir", 'b', RTGETOPT_REQ_STRING },
560 };
561 RTGETOPTSTATE GetState;
562 int rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
563 if (RT_FAILURE(rc))
564 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
565
566 const char *pszBaseDir = NULL;
567 RTGETOPTUNION ValueUnion;
568 int ch;
569 while ((ch = RTGetOpt(&GetState, &ValueUnion)))
570 {
571 switch (ch)
572 {
573 case 'b':
574 if (pszBaseDir)
575 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many --base-dir options");
576 pszBaseDir = ValueUnion.psz;
577 if (!IsValidBaseDir(pszBaseDir))
578 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Invalid base directory: '%s'", pszBaseDir);
579 break;
580
581 case 'h':
582 case 'V':
583 return DoStandardOption(ch);
584
585 default:
586 return RTGetOptPrintError(ch, &ValueUnion);
587 }
588 break;
589 }
590 if (!pszBaseDir)
591 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "Missing --base-dir option");
592
593 /*
594 * Ok, down to business.
595 */
596 PRTDIR pDir;
597 rc = RTDirOpen(&pDir, pszBaseDir);
598 if (RT_FAILURE(rc))
599 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed open the base directory: %Rrc ('%s')", rc, pszBaseDir);
600
601 uint32_t cCleaned = 0;
602 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
603 for (;;)
604 {
605 RTDIRENTRYEX Entry;
606 rc = RTDirReadEx(pDir, &Entry, NULL /*pcbDirEntry*/, RTFSOBJATTRADD_NOTHING, RTPATH_F_ON_LINK);
607 if (RT_FAILURE(rc))
608 {
609 if (rc != VERR_NO_MORE_FILES)
610 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTDirReadEx returns %Rrc", rc);
611 break;
612 }
613 if ( RTFS_IS_DIRECTORY(Entry.Info.Attr.fMode)
614 && strcmp(Entry.szName, ".") != 0
615 && strcmp(Entry.szName, "..") != 0
616 && !VBoxExtPackIsValidName(Entry.szName) )
617 {
618 char szPath[RTPATH_MAX];
619 rc = RTPathJoin(szPath, sizeof(szPath), pszBaseDir, Entry.szName);
620 if (RT_SUCCESS(rc))
621 {
622 RTEXITCODE rcExit2 = RemoveExtPackDir(szPath, true /*fTemporary*/);
623 if (rcExit2 == RTEXITCODE_SUCCESS)
624 RTMsgInfo("Successfully removed '%s'.", Entry.szName);
625 else if (rcExit == RTEXITCODE_SUCCESS)
626 rcExit = rcExit2;
627 }
628 else
629 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
630 cCleaned++;
631 }
632 }
633 RTDirClose(pDir);
634 if (!cCleaned)
635 RTMsgInfo("Nothing to clean.");
636 return rcExit;
637}
638
639
640
641int main(int argc, char **argv)
642{
643 /*
644 * Initialize IPRT and check that we're correctly installed.
645 */
646 int rc = RTR3Init();
647 if (RT_FAILURE(rc))
648 return RTMsgInitFailure(rc);
649
650 char szErr[2048];
651 rc = SUPR3HardenedVerifySelf(argv[0], true /*fInternal*/, szErr, sizeof(szErr));
652 if (RT_FAILURE(rc))
653 return RTMsgErrorExit(RTEXITCODE_FAILURE, "%s", szErr);
654
655 /*
656 * Parse the top level arguments until we find a command.
657 */
658 static const RTGETOPTDEF s_aOptions[] =
659 {
660#define CMD_INSTALL 1000
661 { "install", CMD_INSTALL, RTGETOPT_REQ_NOTHING },
662#define CMD_UNINSTALL 1001
663 { "uninstall", CMD_UNINSTALL, RTGETOPT_REQ_NOTHING },
664#define CMD_CLEANUP 1002
665 { "cleanup", CMD_CLEANUP, RTGETOPT_REQ_NOTHING },
666 };
667 RTGETOPTSTATE GetState;
668 rc = RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /*fFlags*/);
669 if (RT_FAILURE(rc))
670 return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTGetOptInit failed: %Rrc\n", rc);
671 for (;;)
672 {
673 RTGETOPTUNION ValueUnion;
674 int ch = RTGetOpt(&GetState, &ValueUnion);
675 switch (ch)
676 {
677 case 0:
678 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "No command specified");
679
680 case CMD_INSTALL:
681 return DoInstall(argc, argv);
682
683 case CMD_UNINSTALL:
684 return DoUninstall(argc, argv);
685
686 case CMD_CLEANUP:
687 return DoCleanup(argc, argv);
688
689 case 'h':
690 case 'V':
691 return DoStandardOption(ch);
692
693 default:
694 return RTGetOptPrintError(ch, &ValueUnion);
695 }
696 /* not currently reached */
697 }
698 /* not reached */
699}
700
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