VirtualBox

Ignore:
Timestamp:
Nov 5, 2010 5:20:15 PM (14 years ago)
Author:
vboxsync
Message:

ExtPack changes, related IPRT changes.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Main/VBoxExtPackHelperApp.cpp

    r33784 r33806  
    1919#include <iprt/buildconfig.h>
    2020//#include <iprt/ctype.h>
    21 //#include <iprt/dir.h>
     21#include <iprt/dir.h>
    2222//#include <iprt/env.h>
    23 //#include <iprt/file.h>
     23#include <iprt/file.h>
     24#include <iprt/fs.h>
    2425#include <iprt/getopt.h>
    2526#include <iprt/initterm.h>
    2627#include <iprt/message.h>
    27 //#include <iprt/param.h>
     28#include <iprt/param.h>
    2829#include <iprt/path.h>
    2930//#include <iprt/pipe.h>
     31#include <iprt/process.h>
    3032#include <iprt/string.h>
    3133#include <iprt/stream.h>
     
    3335#include <VBox/log.h>
    3436#include <VBox/err.h>
     37#include <VBox/sup.h>
    3538#include <VBox/version.h>
    3639
     
    4043{
    4144    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;
    42323}
    43324
     
    52333static RTEXITCODE DoInstall(int argc, char **argv)
    53334{
    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
    56442
    57443/**
     
    64450static RTEXITCODE DoUninstall(int argc, char **argv)
    65451{
    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)
    87475        {
    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 */
     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);
    93620            if (RT_SUCCESS(rc))
    94621            {
    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;
    127627            }
    128628            else
    129                 RTMsgError("RTGetOptInit failed: %Rrc\n", rc);
     629                rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "RTPathJoin failed with %Rrc for '%s'", rc, Entry.szName);
     630            cCleaned++;
    130631        }
    131632    }
    132     else
    133         RTMsgError("No command was specified\n");
     633    RTDirClose(pDir);
     634    if (!cCleaned)
     635        RTMsgInfo("Nothing to clean.");
    134636    return rcExit;
    135637}
    136638
     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 TracChangeset for help on using the changeset viewer.

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