Changeset 33806 in vbox for trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp
- Timestamp:
- Nov 5, 2010 5:20:15 PM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp
r33784 r33806 19 19 #include <iprt/buildconfig.h> 20 20 //#include <iprt/ctype.h> 21 //#include <iprt/dir.h>21 #include <iprt/dir.h> 22 22 //#include <iprt/env.h> 23 //#include <iprt/file.h> 23 #include <iprt/file.h> 24 #include <iprt/fs.h> 24 25 #include <iprt/getopt.h> 25 26 #include <iprt/initterm.h> 26 27 #include <iprt/message.h> 27 //#include <iprt/param.h>28 #include <iprt/param.h> 28 29 #include <iprt/path.h> 29 30 //#include <iprt/pipe.h> 31 #include <iprt/process.h> 30 32 #include <iprt/string.h> 31 33 #include <iprt/stream.h> … … 33 35 #include <VBox/log.h> 34 36 #include <VBox/err.h> 37 #include <VBox/sup.h> 35 38 #include <VBox/version.h> 36 39 … … 40 43 { 41 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 */ 54 static 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 */ 92 static 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 */ 115 static 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 */ 142 static 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 */ 164 static 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 */ 194 static 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 */ 213 static 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 */ 231 static 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; 42 323 } 43 324 … … 52 333 static RTEXITCODE DoInstall(int argc, char **argv) 53 334 { 54 return RTEXITCODE_FAILURE; 55 } 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 56 442 57 443 /** … … 64 450 static RTEXITCODE DoUninstall(int argc, char **argv) 65 451 { 66 return RTEXITCODE_FAILURE; 67 } 68 69 70 int main(int argc, char **argv) 71 { 72 int rc = RTR3Init(); 73 if (RT_FAILURE(rc)) 74 return RTMsgInitFailure(rc); 75 76 RTEXITCODE rcExit = RTEXITCODE_FAILURE; 77 if (argc > 1) 78 { 79 /* 80 * Command string switch. 81 */ 82 if (!strcmp(argv[1], "install")) 83 rcExit = DoInstall(argc, argv); 84 else if (!strcmp(argv[1], "uninstall")) 85 rcExit = DoUninstall(argc, argv); 86 else 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) 87 475 { 88 /* 89 * Didn't match a command, check for standard options. 90 */ 91 RTGETOPTSTATE State; 92 rc = RTGetOptInit(&State, argc, argv, NULL, 0, 1, 0 /*fFlags*/); 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 */ 549 static 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); 93 620 if (RT_SUCCESS(rc)) 94 621 { 95 for (;;) 96 { 97 RTGETOPTUNION ValueUnion; 98 int ch = RTGetOpt(&State, &ValueUnion); 99 switch (ch) 100 { 101 case 'h': 102 RTMsgInfo(VBOX_PRODUCT " Extension Pack Helper App\n" 103 "(C) " VBOX_C_YEAR " " VBOX_VENDOR "\n" 104 "All rights reserved.\n" 105 "\n" 106 "This NOT intended for general use, please use VBoxManage instead\n" 107 "or call the IExtPackManager API directly.\n" 108 "\n" 109 "Usage: %s <command> [options]\n" 110 "Commands:\n" 111 " install --base-dir <dir> --name <name> --tarball <tarball> --tarball-fd <fd>\n" 112 " uninstall --base-dir <dir> --name <name>\n" 113 , RTPathFilename(argv[0])); 114 rcExit = RTEXITCODE_SUCCESS; 115 break; 116 117 case 'V': 118 RTPrintf("%sr%d\n", VBOX_VERSION_STRING, RTBldCfgRevision()); 119 rcExit = RTEXITCODE_SUCCESS; 120 break; 121 122 default: 123 rcExit = RTGetOptPrintError(ch, &ValueUnion); 124 break; 125 } 126 } 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; 127 627 } 128 628 else 129 RTMsgError("RTGetOptInit failed: %Rrc\n", rc); 629 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName); 630 cCleaned++; 130 631 } 131 632 } 132 else 133 RTMsgError("No command was specified\n"); 633 RTDirClose(pDir); 634 if (!cCleaned) 635 RTMsgInfo("Nothing to clean."); 134 636 return rcExit; 135 637 } 136 638 639 640 641 int 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 TracChangeset
for help on using the changeset viewer.