VirtualBox

Changeset 84519 in vbox for trunk/src


Ignore:
Timestamp:
May 25, 2020 5:29:52 PM (5 years ago)
Author:
vboxsync
Message:

Guest Control/VBoxManage: Implemented dedicated "waitrunlevel" command to wait for specific GA run levels and also added more options like waiting for GA being ready to be upgraded by Guest Control.

Location:
trunk/src/VBox/Frontends/VBoxManage
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManage.h

    r84032 r84519  
    144144# define HELP_SCOPE_GSTCTRL_UPDATEGA        RT_BIT(13)
    145145# define HELP_SCOPE_GSTCTRL_WATCH           RT_BIT(14)
     146# define HELP_SCOPE_GSTCTRL_WAITRUNLEVEL    RT_BIT(15)
    146147#endif
    147148
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r83874 r84519  
    4444#include <iprt/path.h>
    4545#include <iprt/process.h> /* For RTProcSelf(). */
     46#include <iprt/semaphore.h>
    4647#include <iprt/thread.h>
    4748#include <iprt/vfs.h>
     
    104105 *  action shall be aborted. */
    105106static volatile bool g_fGuestCtrlCanceled = false;
     107/** Event semaphore used for wait notifications.
     108 *  Also being used for the listener implementations in VBoxManageGuestCtrlListener.cpp. */
     109       RTSEMEVENT    g_SemEventGuestCtrlCanceled = NIL_RTSEMEVENT;
    106110
    107111
     
    116120VBOX_LISTENER_DECLARE(GuestSessionEventListenerImpl)
    117121VBOX_LISTENER_DECLARE(GuestEventListenerImpl)
    118 
     122VBOX_LISTENER_DECLARE(GuestAdditionsRunlevelListener)
    119123
    120124/**
     
    237241                                | HELP_SCOPE_GSTCTRL_CLOSESESSION
    238242                                | HELP_SCOPE_GSTCTRL_UPDATEGA
    239                                 | HELP_SCOPE_GSTCTRL_WATCH;
     243                                | HELP_SCOPE_GSTCTRL_WATCH
     244                                | HELP_SCOPE_GSTCTRL_WAITRUNLEVEL;
    240245
    241246    /*                0         1         2         3         4         5         6         7         8XXXXXXXXXX */
     
    358363        if (fSubcommandScope & HELP_SCOPE_GSTCTRL_WATCH)
    359364            RTStrmPrintf(pStrm,
    360                      "                              watch [common-options]\n"
     365                     "                              watch [--timeout <msec>] [common-options]\n"
     366                     "\n");
     367        if (fSubcommandScope & HELP_SCOPE_GSTCTRL_WAITRUNLEVEL)
     368            RTStrmPrintf(pStrm,
     369                     "                              waitrunlevel [--timeout <msec>] [common-options]\n"
     370                     "                              <system|userland|desktop>\n"
    361371                     "\n");
    362372    }
     
    427437# endif
    428438#endif
     439
     440    if (RT_SUCCESS(rc))
     441        rc = RTSemEventCreate(&g_SemEventGuestCtrlCanceled);
     442
    429443    return rc;
    430444}
     
    450464# endif
    451465#endif
     466
     467    if (g_SemEventGuestCtrlCanceled != NIL_RTSEMEVENT)
     468    {
     469        RTSemEventDestroy(g_SemEventGuestCtrlCanceled);
     470        g_SemEventGuestCtrlCanceled = NIL_RTSEMEVENT;
     471    }
    452472    return rc;
    453473}
     
    26532673}
    26542674
     2675/**
     2676 * Waits for a Guest Additions run level being reached.
     2677 *
     2678 * @returns VBox status code.
     2679 *          Returns VERR_CANCELLED if waiting for cancelled due to signal handling, e.g. when CTRL+C or some sort was pressed.
     2680 * @param   pCtx                The guest control command context.
     2681 * @param   enmRunLevel         Run level to wait for.
     2682 * @param   cMsTimeout          Timeout (in ms) for waiting.
     2683 */
     2684static int gctlWaitForRunLevel(PGCTLCMDCTX pCtx, AdditionsRunLevelType_T enmRunLevel, RTMSINTERVAL cMsTimeout)
     2685{
     2686    int vrc;
     2687
     2688    try
     2689    {
     2690        HRESULT rc = S_OK;
     2691        /** Whether we need to actually wait for the run level or if we already reached it. */
     2692        bool fWait;
     2693
     2694        /* Install an event handler first to catch any runlevel changes. */
     2695        ComObjPtr<GuestAdditionsRunlevelListenerImpl> pGuestListener;
     2696        do
     2697        {
     2698            /* Listener creation. */
     2699            pGuestListener.createObject();
     2700            pGuestListener->init(new GuestAdditionsRunlevelListener(enmRunLevel));
     2701
     2702            /* Register for IGuest events. */
     2703            ComPtr<IEventSource> es;
     2704            CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(EventSource)(es.asOutParam()));
     2705            com::SafeArray<VBoxEventType_T> eventTypes;
     2706            eventTypes.push_back(VBoxEventType_OnGuestAdditionsStatusChanged);
     2707            CHECK_ERROR_BREAK(es, RegisterListener(pGuestListener, ComSafeArrayAsInParam(eventTypes),
     2708                                                   true /* Active listener */));
     2709
     2710            AdditionsRunLevelType_T enmRunLevelCur = AdditionsRunLevelType_None;
     2711            CHECK_ERROR_BREAK(pCtx->pGuest, COMGETTER(AdditionsRunLevel)(&enmRunLevelCur));
     2712            fWait = enmRunLevelCur != enmRunLevel;
     2713
     2714            if (pCtx->cVerbose)
     2715                RTPrintf("Current run level is %RU32\n", enmRunLevelCur);
     2716
     2717        } while (0);
     2718
     2719        if (fWait)
     2720        {
     2721            if (pCtx->cVerbose)
     2722                RTPrintf("Waiting for run level %RU32 ...\n", enmRunLevel);
     2723
     2724            RTMSINTERVAL tsStart = RTTimeMilliTS();
     2725            while (RTTimeMilliTS() - tsStart < cMsTimeout)
     2726            {
     2727                /* Wait for the global signal semaphore getting signalled. */
     2728                vrc = RTSemEventWait(g_SemEventGuestCtrlCanceled, 100 /* ms */);
     2729                if (RT_FAILURE(vrc))
     2730                {
     2731                    if (vrc == VERR_TIMEOUT)
     2732                        continue;
     2733                    else
     2734                    {
     2735                        RTPrintf("Waiting failed with %Rrc\n", vrc);
     2736                        break;
     2737                    }
     2738                }
     2739                else if (pCtx->cVerbose)
     2740                {
     2741                    RTPrintf("Run level %RU32 reached\n", enmRunLevel);
     2742                    break;
     2743                }
     2744
     2745                NativeEventQueue::getMainEventQueue()->processEventQueue(0);
     2746            }
     2747
     2748            if (   vrc == VERR_TIMEOUT
     2749                && pCtx->cVerbose)
     2750                RTPrintf("Run level %RU32 not reached within time\n", enmRunLevel);
     2751        }
     2752
     2753        if (!pGuestListener.isNull())
     2754        {
     2755            /* Guest callback unregistration. */
     2756            ComPtr<IEventSource> pES;
     2757            CHECK_ERROR(pCtx->pGuest, COMGETTER(EventSource)(pES.asOutParam()));
     2758            if (!pES.isNull())
     2759                CHECK_ERROR(pES, UnregisterListener(pGuestListener));
     2760            pGuestListener.setNull();
     2761        }
     2762
     2763        if (g_fGuestCtrlCanceled)
     2764            vrc = VERR_CANCELLED;
     2765    }
     2766    catch (std::bad_alloc &)
     2767    {
     2768        vrc = VERR_NO_MEMORY;
     2769    }
     2770
     2771    return vrc;
     2772}
     2773
    26552774static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx, int argc, char **argv)
    26562775{
    26572776    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
    26582777
     2778    /** Timeout to wait for the whole updating procedure to complete. */
     2779    uint32_t                cMsTimeout = RT_INDEFINITE_WAIT;
     2780    /** Source path to .ISO Guest Additions file to use. */
     2781    Utf8Str                 strSource;
     2782    com::SafeArray<IN_BSTR> aArgs;
     2783    /* Whether to reboot the guest automatically when the update process has finished successfully. */
     2784    bool fRebootOnFinish = false;
     2785    /* Whether to only wait for getting the update process started instead of waiting until it finishes. */
     2786    bool fWaitStartOnly  = false;
     2787    /* Whether to wait for the VM being ready to start the update. Needs Guest Additions facility reporting. */
     2788    bool fWaitReady      = false;
     2789    /* Whether to verify if the Guest Additions were successfully updated on the guest. */
     2790    bool fVerify         = false;
     2791
    26592792    /*
    2660      * Check the syntax.  We can deduce the correct syntax from the number of
    2661      * arguments.
     2793     * Parse arguments.
    26622794     */
    2663     Utf8Str strSource;
    2664     com::SafeArray<IN_BSTR> aArgs;
    2665     bool fWaitStartOnly = false;
     2795    enum KGSTCTRLUPDATEADDITIONSOPT
     2796    {
     2797        KGSTCTRLUPDATEADDITIONSOPT_REBOOT = 1000,
     2798        KGSTCTRLUPDATEADDITIONSOPT_SOURCE,
     2799        KGSTCTRLUPDATEADDITIONSOPT_TIMEOUT,
     2800        KGSTCTRLUPDATEADDITIONSOPT_VERIFY,
     2801        KGSTCTRLUPDATEADDITIONSOPT_WAITREADY,
     2802        KGSTCTRLUPDATEADDITIONSOPT_WAITSTART
     2803    };
    26662804
    26672805    static const RTGETOPTDEF s_aOptions[] =
    26682806    {
    26692807        GCTLCMD_COMMON_OPTION_DEFS()
    2670         { "--source",              's',         RTGETOPT_REQ_STRING  },
    2671         { "--wait-start",          'w',         RTGETOPT_REQ_NOTHING }
     2808        { "--reboot",              KGSTCTRLUPDATEADDITIONSOPT_REBOOT,           RTGETOPT_REQ_NOTHING },
     2809        { "--source",              KGSTCTRLUPDATEADDITIONSOPT_SOURCE,           RTGETOPT_REQ_STRING  },
     2810        { "--timeout",             KGSTCTRLUPDATEADDITIONSOPT_TIMEOUT,          RTGETOPT_REQ_UINT32 },
     2811        { "--verify",              KGSTCTRLUPDATEADDITIONSOPT_VERIFY,           RTGETOPT_REQ_NOTHING },
     2812        { "--wait-ready",          KGSTCTRLUPDATEADDITIONSOPT_WAITREADY,        RTGETOPT_REQ_NOTHING },
     2813        { "--wait-start",          KGSTCTRLUPDATEADDITIONSOPT_WAITSTART,        RTGETOPT_REQ_NOTHING }
    26722814    };
    26732815
     
    26852827            GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
    26862828
    2687             case 's':
     2829            case KGSTCTRLUPDATEADDITIONSOPT_REBOOT:
     2830                fRebootOnFinish = true;
     2831                break;
     2832
     2833            case KGSTCTRLUPDATEADDITIONSOPT_SOURCE:
    26882834                vrc = RTPathAbsCxx(strSource, ValueUnion.psz);
    26892835                if (RT_FAILURE(vrc))
     
    26912837                break;
    26922838
    2693             case 'w':
     2839            case KGSTCTRLUPDATEADDITIONSOPT_WAITSTART:
    26942840                fWaitStartOnly = true;
     2841                break;
     2842
     2843            case KGSTCTRLUPDATEADDITIONSOPT_WAITREADY:
     2844                fWaitReady = true;
     2845                break;
     2846
     2847            case KGSTCTRLUPDATEADDITIONSOPT_VERIFY:
     2848                fVerify = true;
    26952849                break;
    26962850
     
    27332887    }
    27342888
     2889
     2890
     2891#if 0
     2892        ComPtr<IGuest> guest;
     2893        rc = pConsole->COMGETTER(Guest)(guest.asOutParam());
     2894        if (SUCCEEDED(rc) && !guest.isNull())
     2895        {
     2896            SHOW_STRING_PROP_NOT_EMPTY(guest, OSTypeId, "GuestOSType", "OS type:");
     2897
     2898            AdditionsRunLevelType_T guestRunLevel; /** @todo Add a runlevel-to-string (e.g. 0 = "None") method? */
     2899            rc = guest->COMGETTER(AdditionsRunLevel)(&guestRunLevel);
     2900            if (SUCCEEDED(rc))
     2901                SHOW_ULONG_VALUE("GuestAdditionsRunLevel", "Additions run level:", (ULONG)guestRunLevel, "");
     2902
     2903            Bstr guestString;
     2904            rc = guest->COMGETTER(AdditionsVersion)(guestString.asOutParam());
     2905            if (   SUCCEEDED(rc)
     2906                && !guestString.isEmpty())
     2907            {
     2908                ULONG uRevision;
     2909                rc = guest->COMGETTER(AdditionsRevision)(&uRevision);
     2910                if (FAILED(rc))
     2911                    uRevision = 0;
     2912                RTStrPrintf(szValue, sizeof(szValue), "%ls r%u", guestString.raw(), uRevision);
     2913                SHOW_UTF8_STRING("GuestAdditionsVersion", "Additions version:", szValue);
     2914            }
     2915#endif
     2916
    27352917    if (RT_SUCCESS(vrc))
    27362918    {
    27372919        if (pCtx->cVerbose)
    27382920            RTPrintf("Using source: %s\n", strSource.c_str());
    2739 
    27402921
    27412922        RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
     
    27432924            return rcExit;
    27442925
    2745 
    2746         com::SafeArray<AdditionsUpdateFlag_T> aUpdateFlags;
    2747         if (fWaitStartOnly)
    2748         {
    2749             aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly);
     2926        if (fWaitReady)
     2927        {
    27502928            if (pCtx->cVerbose)
    2751                 RTPrintf("Preparing and waiting for Guest Additions installer to start ...\n");
    2752         }
    2753 
    2754         ComPtr<IProgress> pProgress;
    2755         CHECK_ERROR(pCtx->pGuest, UpdateGuestAdditions(Bstr(strSource).raw(),
    2756                                                        ComSafeArrayAsInParam(aArgs),
    2757                                                        /* Wait for whole update process to complete. */
    2758                                                        ComSafeArrayAsInParam(aUpdateFlags),
    2759                                                        pProgress.asOutParam()));
    2760         if (FAILED(rc))
    2761             vrc = gctlPrintError(pCtx->pGuest, COM_IIDOF(IGuest));
    2762         else
    2763         {
    2764             if (pCtx->cVerbose)
    2765                 rc = showProgress(pProgress);
     2929                RTPrintf("Waiting for current Guest Additions inside VM getting ready for updating ...\n");
     2930
     2931            const uint64_t uTsStart = RTTimeMilliTS();
     2932            vrc = gctlWaitForRunLevel(pCtx, AdditionsRunLevelType_Userland, cMsTimeout);
     2933            if (RT_SUCCESS(vrc))
     2934                cMsTimeout = cMsTimeout != RT_INDEFINITE_WAIT ? cMsTimeout - (RTTimeMilliTS() - uTsStart) : cMsTimeout;
     2935        }
     2936
     2937        if (RT_SUCCESS(vrc))
     2938        {
     2939            /* Get current Guest Additions version / revision. */
     2940            Bstr  strGstVerCur;
     2941            ULONG uGstRevCur   = 0;
     2942            rc = pCtx->pGuest->COMGETTER(AdditionsVersion)(strGstVerCur.asOutParam());
     2943            if (   SUCCEEDED(rc)
     2944                && !strGstVerCur.isEmpty())
     2945            {
     2946                rc = pCtx->pGuest->COMGETTER(AdditionsRevision)(&uGstRevCur);
     2947                if (SUCCEEDED(rc))
     2948                {
     2949                    if (pCtx->cVerbose)
     2950                        RTPrintf("Guest Additions %lsr%RU64 currently installed, waiting for Guest Additions installer to start ...\n",
     2951                                 strGstVerCur.raw(), uGstRevCur);
     2952                }
     2953            }
     2954
     2955            com::SafeArray<AdditionsUpdateFlag_T> aUpdateFlags;
     2956            if (fWaitStartOnly)
     2957                aUpdateFlags.push_back(AdditionsUpdateFlag_WaitForUpdateStartOnly);
     2958
     2959            ComPtr<IProgress> pProgress;
     2960            CHECK_ERROR(pCtx->pGuest, UpdateGuestAdditions(Bstr(strSource).raw(),
     2961                                                           ComSafeArrayAsInParam(aArgs),
     2962                                                           ComSafeArrayAsInParam(aUpdateFlags),
     2963                                                           pProgress.asOutParam()));
     2964            if (FAILED(rc))
     2965                vrc = gctlPrintError(pCtx->pGuest, COM_IIDOF(IGuest));
    27662966            else
    2767                 rc = pProgress->WaitForCompletion(-1 /* No timeout */);
    2768 
    2769             if (SUCCEEDED(rc))
    2770                 CHECK_PROGRESS_ERROR(pProgress, ("Guest additions update failed"));
    2771             vrc = gctlPrintProgressError(pProgress);
    2772             if (RT_SUCCESS(vrc))
    27732967            {
    2774                 RTPrintf("Guest Additions update successful.\n");
    2775                 RTPrintf("The guest needs to be restarted in order to make use of the updated Guest Additions.\n");
     2968                if (pCtx->cVerbose)
     2969                    rc = showProgress(pProgress);
     2970                else
     2971                    rc = pProgress->WaitForCompletion((int32_t)cMsTimeout);
     2972
     2973                if (SUCCEEDED(rc))
     2974                    CHECK_PROGRESS_ERROR(pProgress, ("Guest Additions update failed"));
     2975                vrc = gctlPrintProgressError(pProgress);
     2976                if (RT_SUCCESS(vrc))
     2977                {
     2978                    if (pCtx->cVerbose)
     2979                        RTPrintf("Guest Additions update successful.\n");
     2980
     2981                    if (fRebootOnFinish)
     2982                    {
     2983                        /** @todo Implement this. */
     2984                        vrc = VERR_NOT_IMPLEMENTED;
     2985
     2986                        if (RT_SUCCESS(vrc))
     2987                        {
     2988                            if (pCtx->cVerbose)
     2989                                RTPrintf("Rebooting guest ...\n");
     2990                        }
     2991
     2992                        if (RT_SUCCESS(vrc))
     2993                        {
     2994                            if (fWaitReady)
     2995                            {
     2996                                if (pCtx->cVerbose)
     2997                                    RTPrintf("Waiting for new Guest Additions inside VM getting ready ...\n");
     2998
     2999                                vrc = gctlWaitForRunLevel(pCtx, AdditionsRunLevelType_Userland, cMsTimeout);
     3000                                if (RT_SUCCESS(vrc))
     3001                                {
     3002                                    if (fVerify)
     3003                                    {
     3004                                        if (pCtx->cVerbose)
     3005                                            RTPrintf("Verifying Guest Additions update ...\n");
     3006
     3007                                        /* Get new Guest Additions version / revision. */
     3008                                        Bstr strGstVerNew;
     3009                                        ULONG uGstRevNew   = 0;
     3010                                        rc = pCtx->pGuest->COMGETTER(AdditionsVersion)(strGstVerNew.asOutParam());
     3011                                        if (   SUCCEEDED(rc)
     3012                                            && !strGstVerNew.isEmpty())
     3013                                        {
     3014                                            rc = pCtx->pGuest->COMGETTER(AdditionsRevision)(&uGstRevNew);
     3015                                            if (FAILED(rc))
     3016                                                uGstRevNew = 0;
     3017                                        }
     3018
     3019                                        /** @todo Do more verification here. */
     3020                                        vrc = uGstRevNew > uGstRevCur ? VINF_SUCCESS : VERR_NO_CHANGE;
     3021
     3022                                        if (pCtx->cVerbose)
     3023                                        {
     3024                                            RTPrintf("Old Guest Additions: %ls%RU64\n", strGstVerCur.raw(), uGstRevCur);
     3025                                            RTPrintf("New Guest Additions: %ls%RU64\n", strGstVerNew.raw(), uGstRevNew);
     3026
     3027                                            if (RT_FAILURE(vrc))
     3028                                            {
     3029                                                RTPrintf("\nError updating Guest Additions, please check guest installer log\n");
     3030                                            }
     3031                                            else
     3032                                            {
     3033                                                if (uGstRevNew < uGstRevCur)
     3034                                                    RTPrintf("\nWARNING: Guest Additions were downgraded\n");
     3035                                            }
     3036                                        }
     3037                                    }
     3038                                }
     3039                            }
     3040                            else if (pCtx->cVerbose)
     3041                                RTPrintf("The guest needs to be restarted in order to make use of the updated Guest Additions.\n");
     3042                        }
     3043                    }
     3044                }
    27763045            }
    27773046        }
    27783047    }
     3048
     3049    return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     3050}
     3051
     3052/**
     3053 * Returns a Guest Additions run level from a string.
     3054 *
     3055 * @returns Run level if found, or AdditionsRunLevelType_None if not found / invalid.
     3056 * @param   pcszStr             String to return run level for.
     3057 */
     3058static AdditionsRunLevelType_T gctlGetRunLevelFromStr(const char *pcszStr)
     3059{
     3060    AssertPtrReturn(pcszStr, AdditionsRunLevelType_None);
     3061
     3062    if      (RTStrICmp(pcszStr, "system")   == 0) return AdditionsRunLevelType_System;
     3063    else if (RTStrICmp(pcszStr, "userland") == 0) return AdditionsRunLevelType_Userland;
     3064    else if (RTStrICmp(pcszStr, "desktop") == 0)  return AdditionsRunLevelType_Desktop;
     3065
     3066    return AdditionsRunLevelType_None;
     3067}
     3068
     3069static DECLCALLBACK(RTEXITCODE) gctlHandleWaitRunLevel(PGCTLCMDCTX pCtx, int argc, char **argv)
     3070{
     3071    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     3072
     3073    /** Timeout to wait for run level being reached.
     3074     *  By default we wait until it's reached. */
     3075    uint32_t cMsTimeout = RT_INDEFINITE_WAIT;
     3076
     3077    /*
     3078     * Parse arguments.
     3079     */
     3080    enum KGSTCTRLWAITRUNLEVELOPT
     3081    {
     3082        KGSTCTRLWAITRUNLEVELOPT_TIMEOUT = 1000
     3083    };
     3084
     3085    static const RTGETOPTDEF s_aOptions[] =
     3086    {
     3087        GCTLCMD_COMMON_OPTION_DEFS()
     3088        { "--timeout",             KGSTCTRLWAITRUNLEVELOPT_TIMEOUT,          RTGETOPT_REQ_UINT32 }
     3089    };
     3090
     3091    int ch;
     3092    RTGETOPTUNION ValueUnion;
     3093    RTGETOPTSTATE GetState;
     3094    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3095
     3096    AdditionsRunLevelType_T enmRunLevel = AdditionsRunLevelType_None;
     3097
     3098    int vrc = VINF_SUCCESS;
     3099    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
     3100           && RT_SUCCESS(vrc))
     3101    {
     3102        switch (ch)
     3103        {
     3104            GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
     3105
     3106            case KGSTCTRLWAITRUNLEVELOPT_TIMEOUT:
     3107                cMsTimeout = ValueUnion.u32;
     3108                break;
     3109
     3110            case VINF_GETOPT_NOT_OPTION:
     3111            {
     3112                enmRunLevel = gctlGetRunLevelFromStr(ValueUnion.psz);
     3113                if (enmRunLevel == AdditionsRunLevelType_None)
     3114                    return errorSyntaxEx(USAGE_GUESTCONTROL, HELP_SCOPE_GSTCTRL_WAITRUNLEVEL,
     3115                                         "Invalid run level specified. Valid values are: system, userland, desktop");
     3116                break;
     3117            }
     3118
     3119            default:
     3120                return errorGetOptEx(USAGE_GUESTCONTROL, HELP_SCOPE_GSTCTRL_WAITRUNLEVEL, ch, &ValueUnion);
     3121        }
     3122    }
     3123
     3124    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
     3125    if (rcExit != RTEXITCODE_SUCCESS)
     3126        return rcExit;
     3127
     3128    if (enmRunLevel == AdditionsRunLevelType_None)
     3129        return errorSyntaxEx(USAGE_GUESTCONTROL, HELP_SCOPE_GSTCTRL_WAITRUNLEVEL, "Missing run level to wait for");
     3130
     3131    vrc = gctlWaitForRunLevel(pCtx, enmRunLevel, cMsTimeout);
    27793132
    27803133    return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     
    32573610    {
    32583611        GCTLCMD_COMMON_OPTION_DEFS()
     3612        { "--timeout",                      't',                                      RTGETOPT_REQ_UINT32  }
    32593613    };
     3614
     3615    uint32_t cMsTimeout = RT_INDEFINITE_WAIT;
    32603616
    32613617    int ch;
     
    32703626        {
    32713627            GCTLCMD_COMMON_OPTION_CASES(pCtx, ch, &ValueUnion);
     3628
     3629            case 't': /* Timeout */
     3630                cMsTimeout = ValueUnion.u32;
     3631                break;
    32723632
    32733633            case VINF_GETOPT_NOT_OPTION:
     
    33113671            RTPrintf("Waiting for events ...\n");
    33123672
    3313 /** @todo r=bird: This are-we-there-yet approach to things could easily be
    3314  *        replaced by a global event semaphore that gets signalled from the
    3315  *        signal handler and the callback event.  Please fix! */
    3316         while (!g_fGuestCtrlCanceled)
    3317         {
    3318             /** @todo Timeout handling (see above)? */
    3319             RTThreadSleep(10);
    3320         }
    3321 
    3322         if (pCtx->cVerbose)
    3323             RTPrintf("Signal caught, exiting ...\n");
     3673        /* Wait for the global signal semaphore getting signalled. */
     3674        int vrc = RTSemEventWait(g_SemEventGuestCtrlCanceled, cMsTimeout);
     3675        if (vrc == VERR_TIMEOUT)
     3676        {
     3677            if (pCtx->cVerbose)
     3678                RTPrintf("Waiting done\n");
     3679        }
     3680        else if (RT_FAILURE(vrc))
     3681            RTPrintf("Waiting failed with %Rrc\n", vrc);
    33243682
    33253683        if (!pGuestListener.isNull())
     
    33613719    static const GCTLCMDDEF s_aCmdDefs[] =
    33623720    {
    3363         { "run",                gctlHandleRun,              HELP_SCOPE_GSTCTRL_RUN,       0, },
    3364         { "start",              gctlHandleStart,            HELP_SCOPE_GSTCTRL_START,     0, },
    3365         { "copyfrom",           gctlHandleCopyFrom,         HELP_SCOPE_GSTCTRL_COPYFROM,  0, },
    3366         { "copyto",             gctlHandleCopyTo,           HELP_SCOPE_GSTCTRL_COPYTO,    0, },
    3367 
    3368         { "mkdir",              gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0, },
    3369         { "md",                 gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0, },
    3370         { "createdirectory",    gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0, },
    3371         { "createdir",          gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0, },
    3372 
    3373         { "rmdir",              gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0, },
    3374         { "removedir",          gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0, },
    3375         { "removedirectory",    gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0, },
    3376 
    3377         { "rm",                 gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0, },
    3378         { "removefile",         gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0, },
    3379         { "erase",              gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0, },
    3380         { "del",                gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0, },
    3381         { "delete",             gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0, },
    3382 
    3383         { "mv",                 gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0, },
    3384         { "move",               gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0, },
    3385         { "ren",                gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0, },
    3386         { "rename",             gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0, },
    3387 
    3388         { "mktemp",             gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0, },
    3389         { "createtemp",         gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0, },
    3390         { "createtemporary",    gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0, },
    3391 
    3392         { "stat",               gctlHandleStat,             HELP_SCOPE_GSTCTRL_STAT,      0, },
    3393 
    3394         { "closeprocess",       gctlHandleCloseProcess,     HELP_SCOPE_GSTCTRL_CLOSEPROCESS, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3395         { "closesession",       gctlHandleCloseSession,     HELP_SCOPE_GSTCTRL_CLOSESESSION, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3396         { "list",               gctlHandleList,             HELP_SCOPE_GSTCTRL_LIST,         GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3397         { "watch",              gctlHandleWatch,            HELP_SCOPE_GSTCTRL_WATCH,        GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3398 
    3399         {"updateguestadditions",gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3400         { "updateadditions",    gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
    3401         { "updatega",           gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER, },
     3721        { "run",                gctlHandleRun,              HELP_SCOPE_GSTCTRL_RUN,       0 },
     3722        { "start",              gctlHandleStart,            HELP_SCOPE_GSTCTRL_START,     0 },
     3723        { "copyfrom",           gctlHandleCopyFrom,         HELP_SCOPE_GSTCTRL_COPYFROM,  0 },
     3724        { "copyto",             gctlHandleCopyTo,           HELP_SCOPE_GSTCTRL_COPYTO,    0 },
     3725
     3726        { "mkdir",              gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0 },
     3727        { "md",                 gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0 },
     3728        { "createdirectory",    gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0 },
     3729        { "createdir",          gctrlHandleMkDir,           HELP_SCOPE_GSTCTRL_MKDIR,     0 },
     3730
     3731        { "rmdir",              gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0 },
     3732        { "removedir",          gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0 },
     3733        { "removedirectory",    gctlHandleRmDir,            HELP_SCOPE_GSTCTRL_RMDIR,     0 },
     3734
     3735        { "rm",                 gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0 },
     3736        { "removefile",         gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0 },
     3737        { "erase",              gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0 },
     3738        { "del",                gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0 },
     3739        { "delete",             gctlHandleRm,               HELP_SCOPE_GSTCTRL_RM,        0 },
     3740
     3741        { "mv",                 gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0 },
     3742        { "move",               gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0 },
     3743        { "ren",                gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0 },
     3744        { "rename",             gctlHandleMv,               HELP_SCOPE_GSTCTRL_MV,        0 },
     3745
     3746        { "mktemp",             gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0 },
     3747        { "createtemp",         gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0 },
     3748        { "createtemporary",    gctlHandleMkTemp,           HELP_SCOPE_GSTCTRL_MKTEMP,    0 },
     3749
     3750        { "stat",               gctlHandleStat,             HELP_SCOPE_GSTCTRL_STAT,      0 },
     3751
     3752        { "closeprocess",       gctlHandleCloseProcess,     HELP_SCOPE_GSTCTRL_CLOSEPROCESS, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER },
     3753        { "closesession",       gctlHandleCloseSession,     HELP_SCOPE_GSTCTRL_CLOSESESSION, GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER },
     3754        { "list",               gctlHandleList,             HELP_SCOPE_GSTCTRL_LIST,         GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER },
     3755        { "watch",              gctlHandleWatch,            HELP_SCOPE_GSTCTRL_WATCH,        GCTLCMDCTX_F_SESSION_ANONYMOUS | GCTLCMDCTX_F_NO_SIGNAL_HANDLER },
     3756
     3757        {"updateguestadditions",gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS },
     3758        { "updateadditions",    gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS },
     3759        { "updatega",           gctlHandleUpdateAdditions,  HELP_SCOPE_GSTCTRL_UPDATEGA,     GCTLCMDCTX_F_SESSION_ANONYMOUS },
     3760
     3761        { "waitrunlevel",       gctlHandleWaitRunLevel,     HELP_SCOPE_GSTCTRL_WAITRUNLEVEL, GCTLCMDCTX_F_SESSION_ANONYMOUS },
     3762        { "waitforrunlevel",    gctlHandleWaitRunLevel,     HELP_SCOPE_GSTCTRL_WAITRUNLEVEL, GCTLCMDCTX_F_SESSION_ANONYMOUS },
    34023763    };
    34033764
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.h

    r82968 r84519  
    2828#include <VBox/com/VirtualBox.h>
    2929
     30#include <iprt/semaphore.h>
    3031#include <iprt/time.h>
    3132
     
    4950class GuestEventListener;
    5051typedef ListenerImpl<GuestEventListener> GuestEventListenerImpl;
     52
     53class GuestAdditionsRunlevelListener;
     54typedef ListenerImpl<GuestAdditionsRunlevelListener> GuestAdditionsRunlevelListenerImpl;
    5155
    5256/** Simple statistics class for binding locally
     
    231235    GuestEventSessions mSessions;
    232236};
     237
     238/**
     239 *  Handler for Guest Additions runlevel change events.
     240 */
     241class GuestAdditionsRunlevelListener : public GuestListenerBase
     242{
     243
     244public:
     245
     246    GuestAdditionsRunlevelListener(AdditionsRunLevelType_T enmRunLevel);
     247
     248    virtual ~GuestAdditionsRunlevelListener(void);
     249
     250public:
     251
     252    void uninit(void);
     253
     254    STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent);
     255
     256protected:
     257
     258    /** The run level target we're waiting for. */
     259    AdditionsRunLevelType_T mRunLevelTarget;
     260};
    233261#endif /* !VBOX_ONLY_DOCS */
    234262
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrlListener.cpp

    r82968 r84519  
    2929#include <VBox/com/errorprint.h>
    3030
     31#include <iprt/semaphore.h>
    3132#include <iprt/time.h>
    3233
     
    3435#include <vector>
    3536
     37
     38/** Event semaphore we're using for notification. */
     39extern RTSEMEVENT g_SemEventGuestCtrlCanceled;
    3640
    3741
     
    515519}
    516520
     521/*
     522 * GuestAdditionsRunlevelListener
     523 * GuestAdditionsRunlevelListener
     524 * GuestAdditionsRunlevelListener
     525 */
     526
     527GuestAdditionsRunlevelListener::GuestAdditionsRunlevelListener(AdditionsRunLevelType_T enmRunLevel)
     528    : mRunLevelTarget(enmRunLevel)
     529{
     530}
     531
     532GuestAdditionsRunlevelListener::~GuestAdditionsRunlevelListener(void)
     533{
     534}
     535
     536void GuestAdditionsRunlevelListener::uninit(void)
     537{
     538}
     539
     540STDMETHODIMP GuestAdditionsRunlevelListener::HandleEvent(VBoxEventType_T aType, IEvent *aEvent)
     541{
     542    Assert(mRunLevelTarget != AdditionsRunLevelType_None);
     543
     544    HRESULT rc;
     545
     546    switch (aType)
     547    {
     548        case VBoxEventType_OnGuestAdditionsStatusChanged:
     549        {
     550            ComPtr<IGuestAdditionsStatusChangedEvent> pEvent = aEvent;
     551            Assert(!pEvent.isNull());
     552
     553            AdditionsRunLevelType_T RunLevelCur = AdditionsRunLevelType_None;
     554            CHECK_ERROR_BREAK(pEvent, COMGETTER(RunLevel)(&RunLevelCur));
     555
     556            if (mfVerbose)
     557                RTPrintf("Reached run level %RU32\n", RunLevelCur);
     558
     559            if (RunLevelCur == mRunLevelTarget)
     560            {
     561                int vrc = RTSemEventSignal(g_SemEventGuestCtrlCanceled);
     562                AssertRC(vrc);
     563            }
     564
     565            break;
     566        }
     567
     568        default:
     569            AssertFailed();
     570    }
     571
     572    return S_OK;
     573}
     574
    517575#endif /* !VBOX_ONLY_DOCS */
    518576
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