VirtualBox

忽略:
時間撮記:
2015-4-13 下午03:53:01 (10 年 以前)
作者:
vboxsync
訊息:

Main/Console+Machine+Session+Snapshot: move the save state and snapshot related methods from IConsole to IMachine, with lots of unavoidable code restructuring and cleanup. Also define two new machine states (so that the "Saving" one is specifically for saving state now) which requires more changes everywhere
Frontends: necessary adjustments
doc/SDK: document the changes

檔案:
修改 1 筆資料

圖例:

未更動
新增
刪除
  • trunk/src/VBox/Main/src-client/ConsoleImpl.cpp

    r55185 r55214  
    164164          mProgress(aProgress),
    165165          mServerProgress(aServerProgress),
    166           mpUVM(NULL),
    167166          mRC(E_FAIL),
    168167          mpSafeVMPtr(NULL)
     
    175174        {
    176175            mpSafeVMPtr = new Console::SafeVMPtr(aConsole);
    177             if (mpSafeVMPtr->isOk())
    178                 mpUVM = mpSafeVMPtr->rawUVM();
    179             else
     176            if (!mpSafeVMPtr->isOk())
    180177                mRC = mpSafeVMPtr->rc();
    181178        }
     
    205202    Utf8Str                     mErrorMsg;
    206203    const ComPtr<IProgress>     mServerProgress;
    207     PUVM                        mpUVM;
    208204
    209205private:
     
    212208};
    213209
    214 struct VMTakeSnapshotTask : public VMTask
    215 {
    216     VMTakeSnapshotTask(Console *aConsole,
    217                        Progress *aProgress,
    218                        IN_BSTR aName,
    219                        IN_BSTR aDescription)
    220         : VMTask(aConsole, aProgress, NULL /* aServerProgress */,
    221                  false /* aUsesVMPtr */),
    222           bstrName(aName),
    223           bstrDescription(aDescription),
    224           lastMachineState(MachineState_Null)
    225     {}
    226 
    227     Bstr                    bstrName,
    228                             bstrDescription;
    229     Bstr                    bstrSavedStateFile;         // received from BeginTakeSnapshot()
    230     MachineState_T          lastMachineState;
    231     bool                    fTakingSnapshotOnline;
    232     ULONG                   ulMemSize;
    233 };
    234210
    235211struct VMPowerUpTask : public VMTask
     
    264240                 true /* aUsesVMPtr */)
    265241    {}
    266 };
    267 
    268 struct VMSaveTask : public VMTask
    269 {
    270     VMSaveTask(Console *aConsole,
    271                const ComPtr<IProgress> &aServerProgress,
    272                const Utf8Str &aSavedStateFile,
    273                MachineState_T aMachineStateBefore,
    274                Reason_T aReason)
    275         : VMTask(aConsole, NULL /* aProgress */, aServerProgress,
    276                  true /* aUsesVMPtr */),
    277           mSavedStateFile(aSavedStateFile),
    278           mMachineStateBefore(aMachineStateBefore),
    279           mReason(aReason)
    280     {}
    281 
    282     Utf8Str mSavedStateFile;
    283     /* The local machine state we had before. Required if something fails */
    284     MachineState_T mMachineStateBefore;
    285     /* The reason for saving state */
    286     Reason_T mReason;
    287242};
    288243
     
    597552            meAttachmentType[slot] = NetworkAttachmentType_Null;
    598553
    599         // VirtualBox 4.0: We no longer initialize the VMMDev instance here,
    600         // which starts the HGCM thread. Instead, this is now done in the
    601         // power-up thread when a VM is actually being powered up to avoid
    602         // having HGCM threads all over the place every time a session is
    603         // opened, even if that session will not run a VM.
    604         //     unconst(m_pVMMDev) = new VMMDev(this);
    605         //     AssertReturn(mVMMDev, E_FAIL);
    606 
    607554#ifdef VBOX_WITH_PDM_AUDIO_DRIVER
    608555        unconst(mAudioVRDE) = new AudioVRDE(this);
     
    21062053HRESULT Console::powerUp(ComPtr<IProgress> &aProgress)
    21072054{
    2108     ComObjPtr<IProgress> pProgress;
    2109     i_powerUp(pProgress.asOutParam(), false /* aPaused */);
    2110     pProgress.queryInterfaceTo(aProgress.asOutParam());
     2055    i_powerUp(aProgress.asOutParam(), false /* aPaused */);
    21112056    return S_OK;
    21122057}
     
    21142059HRESULT Console::powerUpPaused(ComPtr<IProgress> &aProgress)
    21152060{
    2116     ComObjPtr<IProgress> pProgress;
    2117     i_powerUp(pProgress.asOutParam(), true /* aPaused */);
    2118     pProgress.queryInterfaceTo(aProgress.asOutParam());
     2061    i_powerUp(aProgress.asOutParam(), true /* aPaused */);
    21192062    return S_OK;
    21202063}
     
    21332076        case MachineState_Stuck:
    21342077            break;
     2078
     2079        /* Try cancel the save state. */
     2080        case MachineState_Saving:
     2081            if (!mptrCancelableProgress.isNull())
     2082            {
     2083                HRESULT hrc = mptrCancelableProgress->Cancel();
     2084                if (SUCCEEDED(hrc))
     2085                    break;
     2086            }
     2087            return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point during a save state"));
    21352088
    21362089        /* Try cancel the teleportation. */
     
    21452098            return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in a teleportation"));
    21462099
     2100        /* Try cancel the online snapshot. */
     2101        case MachineState_OnlineSnapshotting:
     2102            if (!mptrCancelableProgress.isNull())
     2103            {
     2104                HRESULT hrc = mptrCancelableProgress->Cancel();
     2105                if (SUCCEEDED(hrc))
     2106                    break;
     2107            }
     2108            return setError(VBOX_E_INVALID_VM_STATE, tr("Cannot power down at this point in an online snapshot"));
     2109
    21472110        /* Try cancel the live snapshot. */
    21482111        case MachineState_LiveSnapshotting:
     
    25392502    LogFlowThisFuncEnter();
    25402503
    2541     HRESULT rc = i_resume(Reason_Unspecified);
     2504    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     2505
     2506    if (mMachineState != MachineState_Paused)
     2507        return setError(VBOX_E_INVALID_VM_STATE,
     2508                        tr("Cannot resume the machine as it is not paused (machine state: %s)"),
     2509                        Global::stringifyMachineState(mMachineState));
     2510
     2511    HRESULT rc = i_resume(Reason_Unspecified, alock);
    25422512
    25432513    LogFlowThisFunc(("rc=%Rhrc\n", rc));
     
    27202690    LogFlowThisFunc(("rc=%Rhrc\n", rc));
    27212691    LogFlowThisFuncLeave();
    2722     return rc;
    2723 }
    2724 
    2725 HRESULT Console::saveState(ComPtr<IProgress> &aProgress)
    2726 {
    2727     LogFlowThisFuncEnter();
    2728     ComObjPtr<IProgress> pProgress;
    2729 
    2730     HRESULT rc = i_saveState(Reason_Unspecified, pProgress.asOutParam());
    2731     pProgress.queryInterfaceTo(aProgress.asOutParam());
    2732 
    2733     LogFlowThisFunc(("rc=%Rhrc\n", rc));
    2734     LogFlowThisFuncLeave();
    2735     return rc;
    2736 }
    2737 
    2738 HRESULT Console::adoptSavedState(const com::Utf8Str &aSavedStateFile)
    2739 {
    2740     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2741 
    2742     if (   mMachineState != MachineState_PoweredOff
    2743         && mMachineState != MachineState_Teleported
    2744         && mMachineState != MachineState_Aborted
    2745        )
    2746         return setError(VBOX_E_INVALID_VM_STATE,
    2747             tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
    2748             Global::stringifyMachineState(mMachineState));
    2749 
    2750     return mControl->AdoptSavedState(Bstr(aSavedStateFile.c_str()).raw());
    2751 }
    2752 
    2753 HRESULT Console::discardSavedState(BOOL aFRemoveFile)
    2754 {
    2755     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    2756 
    2757     if (mMachineState != MachineState_Saved)
    2758         return setError(VBOX_E_INVALID_VM_STATE,
    2759             tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
    2760             Global::stringifyMachineState(mMachineState));
    2761 
    2762     HRESULT rc = mControl->SetRemoveSavedStateFile(aFRemoveFile);
    2763     if (FAILED(rc)) return rc;
    2764 
    2765     /*
    2766      * Saved -> PoweredOff transition will be detected in the SessionMachine
    2767      * and properly handled.
    2768      */
    2769     rc = i_setMachineState(MachineState_PoweredOff);
    2770 
    27712692    return rc;
    27722693}
     
    31553076
    31563077    return rc;
    3157 }
    3158 
    3159 HRESULT Console::takeSnapshot(const com::Utf8Str &aName,
    3160                               const com::Utf8Str &aDescription,
    3161                               ComPtr<IProgress> &aProgress)
    3162 {
    3163     LogFlowThisFuncEnter();
    3164 
    3165     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3166     LogFlowThisFunc(("aName='%s' mMachineState=%d\n", aName.c_str(), mMachineState));
    3167 
    3168     if (Global::IsTransient(mMachineState))
    3169         return setError(VBOX_E_INVALID_VM_STATE,
    3170                         tr("Cannot take a snapshot of the machine while it is changing the state (machine state: %s)"),
    3171                         Global::stringifyMachineState(mMachineState));
    3172 
    3173     HRESULT rc = S_OK;
    3174 
    3175     /* prepare the progress object:
    3176        a) count the no. of hard disk attachments to get a matching no. of progress sub-operations */
    3177     ULONG cOperations = 2;              // always at least setting up + finishing up
    3178     ULONG ulTotalOperationsWeight = 2;  // one each for setting up + finishing up
    3179     SafeIfaceArray<IMediumAttachment> aMediumAttachments;
    3180     rc = mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(aMediumAttachments));
    3181     if (FAILED(rc))
    3182         return setError(rc, tr("Cannot get medium attachments of the machine"));
    3183 
    3184     ULONG ulMemSize;
    3185     rc = mMachine->COMGETTER(MemorySize)(&ulMemSize);
    3186     if (FAILED(rc))
    3187         return rc;
    3188 
    3189     for (size_t i = 0;
    3190          i < aMediumAttachments.size();
    3191          ++i)
    3192     {
    3193         DeviceType_T type;
    3194         rc = aMediumAttachments[i]->COMGETTER(Type)(&type);
    3195         if (FAILED(rc))
    3196             return rc;
    3197 
    3198         if (type == DeviceType_HardDisk)
    3199         {
    3200             ++cOperations;
    3201 
    3202             // assume that creating a diff image takes as long as saving a 1MB state
    3203             // (note, the same value must be used in SessionMachine::BeginTakingSnapshot() on the server!)
    3204             ulTotalOperationsWeight += 1;
    3205         }
    3206     }
    3207 
    3208     // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied)
    3209     bool const fTakingSnapshotOnline = Global::IsOnline(mMachineState);
    3210 
    3211     LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState));
    3212 
    3213     if (fTakingSnapshotOnline)
    3214     {
    3215         ++cOperations;
    3216         ulTotalOperationsWeight += ulMemSize;
    3217     }
    3218 
    3219     // finally, create the progress object
    3220     ComObjPtr<Progress> pProgress;
    3221     pProgress.createObject();
    3222     rc = pProgress->init(static_cast<IConsole *>(this),
    3223                          Bstr(tr("Taking a snapshot of the virtual machine")).raw(),
    3224                             (mMachineState >= MachineState_FirstOnline)
    3225                          && (mMachineState <= MachineState_LastOnline) /* aCancelable */,
    3226                          cOperations,
    3227                          ulTotalOperationsWeight,
    3228                          Bstr(tr("Setting up snapshot operation")).raw(),      // first sub-op description
    3229                          1);        // ulFirstOperationWeight
    3230 
    3231     if (FAILED(rc))
    3232         return rc;
    3233 
    3234     VMTakeSnapshotTask *pTask;
    3235     if (!(pTask = new VMTakeSnapshotTask(this, pProgress, Bstr(aName).raw(), Bstr(aDescription).raw())))
    3236         return E_OUTOFMEMORY;
    3237 
    3238     Assert(pTask->mProgress);
    3239 
    3240     try
    3241     {
    3242         mptrCancelableProgress = pProgress;
    3243 
    3244         /*
    3245          * If we fail here it means a PowerDown() call happened on another
    3246          * thread while we were doing Pause() (which releases the Console lock).
    3247          * We assign PowerDown() a higher precedence than TakeSnapshot(),
    3248          * therefore just return the error to the caller.
    3249          */
    3250         rc = pTask->rc();
    3251         if (FAILED(rc)) throw rc;
    3252 
    3253         pTask->ulMemSize = ulMemSize;
    3254 
    3255         /* memorize the current machine state */
    3256         pTask->lastMachineState = mMachineState;
    3257         pTask->fTakingSnapshotOnline = fTakingSnapshotOnline;
    3258 
    3259         int vrc = RTThreadCreate(NULL,
    3260                                  Console::i_fntTakeSnapshotWorker,
    3261                                  (void *)pTask,
    3262                                  0,
    3263                                  RTTHREADTYPE_MAIN_WORKER,
    3264                                  0,
    3265                                  "TakeSnap");
    3266         if (FAILED(vrc))
    3267             throw setError(E_FAIL,
    3268                            tr("Could not create VMTakeSnap thread (%Rrc)"),
    3269                            vrc);
    3270 
    3271         pTask->mProgress.queryInterfaceTo(aProgress.asOutParam());
    3272     }
    3273     catch (HRESULT erc)
    3274     {
    3275         delete pTask;
    3276         rc = erc;
    3277         mptrCancelableProgress.setNull();
    3278     }
    3279 
    3280     LogFlowThisFunc(("rc=%Rhrc\n", rc));
    3281     LogFlowThisFuncLeave();
    3282     return rc;
    3283 }
    3284 
    3285 HRESULT Console::deleteSnapshot(const com::Guid &aId, ComPtr<IProgress> &aProgress)
    3286 {
    3287     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3288 
    3289     if (Global::IsTransient(mMachineState))
    3290         return setError(VBOX_E_INVALID_VM_STATE,
    3291                         tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
    3292                         Global::stringifyMachineState(mMachineState));
    3293     ComObjPtr<IProgress> iProgress;
    3294     MachineState_T machineState = MachineState_Null;
    3295     HRESULT rc = mControl->DeleteSnapshot((IConsole *)this, Bstr(aId.toString()).raw(), Bstr(aId.toString()).raw(),
    3296                                           FALSE /* fDeleteAllChildren */, &machineState, iProgress.asOutParam());
    3297     if (FAILED(rc)) return rc;
    3298     iProgress.queryInterfaceTo(aProgress.asOutParam());
    3299 
    3300     i_setMachineStateLocally(machineState);
    3301     return S_OK;
    3302 }
    3303 
    3304 HRESULT Console::deleteSnapshotAndAllChildren(const com::Guid &aId, ComPtr<IProgress> &aProgress)
    3305 
    3306 {
    3307     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3308 
    3309     if (Global::IsTransient(mMachineState))
    3310         return setError(VBOX_E_INVALID_VM_STATE,
    3311                         tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
    3312                         Global::stringifyMachineState(mMachineState));
    3313 
    3314     ComObjPtr<IProgress> iProgress;
    3315     MachineState_T machineState = MachineState_Null;
    3316     HRESULT rc = mControl->DeleteSnapshot((IConsole *)this, Bstr(aId.toString()).raw(), Bstr(aId.toString()).raw(),
    3317                                           TRUE /* fDeleteAllChildren */, &machineState, iProgress.asOutParam());
    3318     if (FAILED(rc)) return rc;
    3319     iProgress.queryInterfaceTo(aProgress.asOutParam());
    3320 
    3321     i_setMachineStateLocally(machineState);
    3322     return S_OK;
    3323 }
    3324 
    3325 HRESULT Console::deleteSnapshotRange(const com::Guid &aStartId, const com::Guid &aEndId, ComPtr<IProgress> &aProgress)
    3326 {
    3327     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3328 
    3329     if (Global::IsTransient(mMachineState))
    3330         return setError(VBOX_E_INVALID_VM_STATE,
    3331                         tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"),
    3332                         Global::stringifyMachineState(mMachineState));
    3333 
    3334     ComObjPtr<IProgress> iProgress;
    3335     MachineState_T machineState = MachineState_Null;
    3336     HRESULT rc = mControl->DeleteSnapshot((IConsole *)this, Bstr(aStartId.toString()).raw(), Bstr(aEndId.toString()).raw(),
    3337                                           FALSE /* fDeleteAllChildren */, &machineState, iProgress.asOutParam());
    3338     if (FAILED(rc)) return rc;
    3339     iProgress.queryInterfaceTo(aProgress.asOutParam());
    3340 
    3341     i_setMachineStateLocally(machineState);
    3342     return S_OK;
    3343 }
    3344 
    3345 HRESULT Console::restoreSnapshot(const ComPtr<ISnapshot> &aSnapshot, ComPtr<IProgress> &aProgress)
    3346 {
    3347     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    3348 
    3349     if (Global::IsOnlineOrTransient(mMachineState))
    3350         return setError(VBOX_E_INVALID_VM_STATE,
    3351                         tr("Cannot delete the current state of the running machine (machine state: %s)"),
    3352                         Global::stringifyMachineState(mMachineState));
    3353 
    3354     ISnapshot* iSnapshot = aSnapshot;
    3355     ComObjPtr<IProgress> iProgress;
    3356     MachineState_T machineState = MachineState_Null;
    3357     HRESULT rc = mControl->RestoreSnapshot((IConsole*)this, iSnapshot, &machineState, iProgress.asOutParam());
    3358     if (FAILED(rc)) return rc;
    3359     iProgress.queryInterfaceTo(aProgress.asOutParam());
    3360 
    3361     i_setMachineStateLocally(machineState);
    3362     return S_OK;
    33633078}
    33643079
     
    36453360                                    getStaticComponentName(),
    36463361                                    Utf8StrFmt("Invalid state '%s' for changing medium",
    3647                                         VMR3GetStateName(enmVMState)),
     3362                                               VMR3GetStateName(enmVMState)),
    36483363                                    false /*aWarning*/,
    36493364                                    true /*aLogIt*/);
     
    62195934
    62205935    /* We will need to release the lock before doing the actual merge */
    6221     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     5936    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    62225937
    62235938    /* paranoia - we don't want merges to happen while teleporting etc. */
     
    62956010    AssertComRCReturnRC(rc);
    62966011
     6012    Assert(mMachineState == MachineState_DeletingSnapshotOnline);
     6013
     6014    /* Pause the VM, as it might have pending IO on this drive */
     6015    bool fResume = false;
     6016    rc = i_suspendBeforeConfigChange(ptrVM.rawUVM(), &alock, &fResume);
     6017    if (FAILED(rc))
     6018        return rc;
     6019
    62976020    alock.release();
    6298 
    6299     /* Pause the VM, as it might have pending IO on this drive */
    6300     VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
    6301     if (mMachineState == MachineState_DeletingSnapshotOnline)
    6302     {
    6303         LogFlowFunc(("Suspending the VM...\n"));
    6304         /* disable the callback to prevent Console-level state change */
    6305         mVMStateChangeCallbackDisabled = true;
    6306         int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
    6307         mVMStateChangeCallbackDisabled = false;
    6308         AssertRCReturn(vrc2, E_FAIL);
    6309     }
    6310 
    63116021    vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
    63126022                           (PFNRT)i_reconfigureMediumAttachment, 13,
     
    63166026    /* error handling is after resuming the VM */
    63176027
    6318     if (mMachineState == MachineState_DeletingSnapshotOnline)
    6319     {
    6320         LogFlowFunc(("Resuming the VM...\n"));
    6321         /* disable the callback to prevent Console-level state change */
    6322         mVMStateChangeCallbackDisabled = true;
    6323         int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
    6324         mVMStateChangeCallbackDisabled = false;
    6325         if (RT_FAILURE(vrc2))
    6326         {
    6327             /* too bad, we failed. try to sync the console state with the VMM state */
    6328             AssertLogRelRC(vrc2);
    6329             i_vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
    6330         }
    6331     }
     6028    if (fResume)
     6029        i_resumeAfterConfigChange(ptrVM.rawUVM());
    63326030
    63336031    if (RT_FAILURE(vrc))
     
    63566054        return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc);
    63576055
     6056    alock.acquire();
    63586057    /* Pause the VM, as it might have pending IO on this drive */
    6359     enmVMState = VMR3GetStateU(ptrVM.rawUVM());
    6360     if (mMachineState == MachineState_DeletingSnapshotOnline)
    6361     {
    6362         LogFlowFunc(("Suspending the VM...\n"));
    6363         /* disable the callback to prevent Console-level state change */
    6364         mVMStateChangeCallbackDisabled = true;
    6365         int vrc2 = VMR3Suspend(ptrVM.rawUVM(), VMSUSPENDREASON_RECONFIG);
    6366         mVMStateChangeCallbackDisabled = false;
    6367         AssertRCReturn(vrc2, E_FAIL);
    6368     }
     6058    rc = i_suspendBeforeConfigChange(ptrVM.rawUVM(), &alock, &fResume);
     6059    if (FAILED(rc))
     6060        return rc;
     6061    alock.release();
    63696062
    63706063    /* Update medium chain and state now, so that the VM can continue. */
     
    63786071    /* error handling is after resuming the VM */
    63796072
    6380     if (mMachineState == MachineState_DeletingSnapshotOnline)
    6381     {
    6382         LogFlowFunc(("Resuming the VM...\n"));
    6383         /* disable the callback to prevent Console-level state change */
    6384         mVMStateChangeCallbackDisabled = true;
    6385         int vrc2 = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_RECONFIG);
    6386         mVMStateChangeCallbackDisabled = false;
    6387         AssertRC(vrc2);
    6388         if (RT_FAILURE(vrc2))
    6389         {
    6390             /* too bad, we failed. try to sync the console state with the VMM state */
    6391             i_vmstateChangeCallback(ptrVM.rawUVM(), VMSTATE_SUSPENDED, enmVMState, this);
    6392         }
    6393     }
     6073    if (fResume)
     6074        i_resumeAfterConfigChange(ptrVM.rawUVM());
    63946075
    63956076    if (RT_FAILURE(vrc))
     
    63976078    if (FAILED(rc))
    63986079        return rc;
     6080
     6081    return rc;
     6082}
     6083
     6084HRESULT Console::i_reconfigureMediumAttachments(const std::vector<ComPtr<IMediumAttachment> > &aAttachments)
     6085{
     6086    HRESULT rc = S_OK;
     6087
     6088    AutoCaller autoCaller(this);
     6089    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     6090
     6091    /* get the VM handle. */
     6092    SafeVMPtr ptrVM(this);
     6093    if (!ptrVM.isOk())
     6094        return ptrVM.rc();
     6095
     6096    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     6097
     6098    for (size_t i = 0; i < aAttachments.size(); ++i)
     6099    {
     6100        ComPtr<IStorageController> pStorageController;
     6101        Bstr controllerName;
     6102        ULONG lInstance;
     6103        StorageControllerType_T enmController;
     6104        StorageBus_T enmBus;
     6105        BOOL fUseHostIOCache;
     6106
     6107        /*
     6108         * We could pass the objects, but then EMT would have to do lots of
     6109         * IPC (to VBoxSVC) which takes a significant amount of time.
     6110         * Better query needed values here and pass them.
     6111         */
     6112        rc = aAttachments[i]->COMGETTER(Controller)(controllerName.asOutParam());
     6113        if (FAILED(rc))
     6114            throw rc;
     6115
     6116        rc = mMachine->GetStorageControllerByName(controllerName.raw(),
     6117                                                  pStorageController.asOutParam());
     6118        if (FAILED(rc))
     6119            throw rc;
     6120
     6121        rc = pStorageController->COMGETTER(ControllerType)(&enmController);
     6122        if (FAILED(rc))
     6123            throw rc;
     6124        rc = pStorageController->COMGETTER(Instance)(&lInstance);
     6125        if (FAILED(rc))
     6126            throw rc;
     6127        rc = pStorageController->COMGETTER(Bus)(&enmBus);
     6128        if (FAILED(rc))
     6129            throw rc;
     6130        rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
     6131        if (FAILED(rc))
     6132            throw rc;
     6133
     6134        const char *pcszDevice = i_convertControllerTypeToDev(enmController);
     6135
     6136        BOOL fBuiltinIOCache;
     6137        rc = mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
     6138        if (FAILED(rc))
     6139            throw rc;
     6140
     6141        alock.release();
     6142
     6143        int vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
     6144                                   (PFNRT)i_reconfigureMediumAttachment, 13,
     6145                                   this, ptrVM.rawUVM(), pcszDevice, lInstance, enmBus, fUseHostIOCache,
     6146                                   fBuiltinIOCache, false /* fSetupMerge */, 0 /* uMergeSource */,
     6147                                   0 /* uMergeTarget */, aAttachments[i], mMachineState, &rc);
     6148        if (RT_FAILURE(vrc))
     6149            throw setError(E_FAIL, tr("%Rrc"), vrc);
     6150        if (FAILED(rc))
     6151            throw rc;
     6152
     6153        alock.acquire();
     6154    }
    63996155
    64006156    return rc;
     
    64506206        case MachineState_Paused:
    64516207        case MachineState_TeleportingPausedVM:
    6452         case MachineState_Saving:
     6208        case MachineState_OnlineSnapshotting:
    64536209
    64546210        /* Remove any keys which are supposed to be removed on a suspend. */
     
    65026258 * a specific reason.
    65036259 */
    6504 HRESULT Console::i_resume(Reason_T aReason)
     6260HRESULT Console::i_resume(Reason_T aReason, AutoWriteLock &alock)
    65056261{
    65066262    LogFlowThisFuncEnter();
     
    65086264    AutoCaller autoCaller(this);
    65096265    if (FAILED(autoCaller.rc())) return autoCaller.rc();
    6510 
    6511     AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    6512 
    6513     if (mMachineState != MachineState_Paused)
    6514         return setError(VBOX_E_INVALID_VM_STATE,
    6515                         tr("Cannot resume the machine as it is not paused (machine state: %s)"),
    6516                         Global::stringifyMachineState(mMachineState));
    65176266
    65186267    /* get the VM handle. */
     
    65446293        if (aReason == Reason_HostResume)
    65456294            enmReason = VMRESUMEREASON_HOST_RESUME;
     6295        else if (aReason == Reason_Snapshot)
     6296            enmReason = VMRESUMEREASON_STATE_SAVED;
     6297
     6298        // for snapshots: no state change callback, VBoxSVC does everything
     6299        if (aReason == Reason_Snapshot)
     6300            mVMStateChangeCallbackDisabled = true;
    65466301        vrc = VMR3Resume(ptrVM.rawUVM(), enmReason);
     6302        if (aReason == Reason_Snapshot)
     6303            mVMStateChangeCallbackDisabled = false;
    65476304    }
    65486305
     
    65586315
    65596316/**
    6560  * Worker for Console::SaveState and internal entry point for saving state of
    6561  * a VM for a specific reason.
    6562  */
    6563 HRESULT Console::i_saveState(Reason_T aReason, IProgress **aProgress)
     6317 * Internal entry point for saving state of a VM for a specific reason. This
     6318 * method is completely synchronous.
     6319 *
     6320 * The machine state is already set appropriately. It is only changed when
     6321 * saving state actually paused the VM (happens with live snapshots and
     6322 * teleportation), and in this case reflects the now paused variant.
     6323 *
     6324 * @note Locks this object for writing.
     6325 */
     6326HRESULT Console::i_saveState(Reason_T aReason, const ComPtr<IProgress> &aProgress, const Utf8Str &aStateFilePath, bool aPauseVM, bool &aLeftPaused)
    65646327{
    65656328    LogFlowThisFuncEnter();
    6566 
    6567     CheckComArgOutPointerValid(aProgress);
     6329    aLeftPaused = false;
     6330
     6331    AssertReturn(!aProgress.isNull(), E_INVALIDARG);
     6332    AssertReturn(!aStateFilePath.isEmpty(), E_INVALIDARG);
    65686333
    65696334    AutoCaller autoCaller(this);
     
    65736338
    65746339    LogFlowThisFunc(("mMachineState=%d\n", mMachineState));
    6575     if (   mMachineState != MachineState_Running
    6576         && mMachineState != MachineState_Paused)
     6340    if (   mMachineState != MachineState_Saving
     6341        && mMachineState != MachineState_LiveSnapshotting
     6342        && mMachineState != MachineState_OnlineSnapshotting
     6343        && mMachineState != MachineState_Teleporting
     6344        && mMachineState != MachineState_TeleportingPausedVM)
    65776345    {
    65786346        return setError(VBOX_E_INVALID_VM_STATE,
     
    65806348            Global::stringifyMachineState(mMachineState));
    65816349    }
     6350    bool fContinueAfterwards = mMachineState != MachineState_Saving;
    65826351
    65836352    Bstr strDisableSaveState;
     
    65906359        LogRel(("Saving state of VM, reason \"%s\"\n", Global::stringifyReason(aReason)));
    65916360
    6592     /* memorize the current machine state */
    6593     MachineState_T lastMachineState = mMachineState;
    6594 
    6595     if (mMachineState == MachineState_Running)
    6596     {
    6597         /* get the VM handle. */
    6598         SafeVMPtr ptrVM(this);
    6599         if (!ptrVM.isOk())
    6600             return ptrVM.rc();
    6601 
     6361    /* ensure the directory for the saved state file exists */
     6362    {
     6363        Utf8Str dir = aStateFilePath;
     6364        dir.stripFilename();
     6365        if (!RTDirExists(dir.c_str()))
     6366        {
     6367            int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
     6368            if (RT_FAILURE(vrc))
     6369                return setError(VBOX_E_FILE_ERROR,
     6370                                tr("Could not create a directory '%s' to save the state to (%Rrc)"),
     6371                                dir.c_str(), vrc);
     6372        }
     6373    }
     6374
     6375    /* Get the VM handle early, we need it in several places. */
     6376    SafeVMPtr ptrVM(this);
     6377    if (!ptrVM.isOk())
     6378        return ptrVM.rc();
     6379
     6380    bool fPaused = false;
     6381    if (aPauseVM)
     6382    {
    66026383        /* release the lock before a VMR3* call (EMT might wait for it, @bugref{7648})! */
    66036384        alock.release();
     
    66106391        alock.acquire();
    66116392
    6612         HRESULT hrc = S_OK;
    66136393        if (RT_FAILURE(vrc))
    6614             hrc = setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
    6615         if (FAILED(hrc))
    6616             return hrc;
    6617     }
    6618 
    6619     HRESULT rc = S_OK;
    6620     bool fBeganSavingState = false;
    6621     bool fTaskCreationFailed = false;
    6622 
    6623     do
    6624     {
    6625         ComPtr<IProgress> pProgress;
    6626         Bstr stateFilePath;
    6627 
     6394            return setError(VBOX_E_VM_ERROR, tr("Could not suspend the machine execution (%Rrc)"), vrc);
     6395        fPaused = true;
     6396    }
     6397
     6398    LogFlowFunc(("Saving the state to '%s'...\n", aStateFilePath.c_str()));
     6399
     6400    mptrCancelableProgress = aProgress;
     6401    alock.release();
     6402    int vrc = VMR3Save(ptrVM.rawUVM(),
     6403                       aStateFilePath.c_str(),
     6404                       fContinueAfterwards,
     6405                       Console::i_stateProgressCallback,
     6406                       static_cast<IProgress *>(aProgress),
     6407                       &aLeftPaused);
     6408    alock.acquire();
     6409    mptrCancelableProgress.setNull();
     6410    if (RT_FAILURE(vrc))
     6411    {
     6412        if (fPaused)
     6413        {
     6414            alock.release();
     6415            VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_RESTORED);
     6416            alock.acquire();
     6417        }
     6418        return setError(E_FAIL, tr("Failed to save the machine state to '%s' (%Rrc)"),
     6419                        aStateFilePath.c_str(), vrc);
     6420    }
     6421    Assert(fContinueAfterwards || !aLeftPaused);
     6422
     6423    if (!fContinueAfterwards)
     6424    {
    66286425        /*
    6629          * request a saved state file path from the server
    6630          * (this will set the machine state to Saving on the server to block
    6631          * others from accessing this machine)
     6426         * The machine has been successfully saved, so power it down
     6427         * (vmstateChangeCallback() will set state to Saved on success).
     6428         * Note: we release the VM caller, otherwise it will deadlock.
    66326429         */
    6633         rc = mControl->BeginSavingState(pProgress.asOutParam(),
    6634                                         stateFilePath.asOutParam());
    6635         if (FAILED(rc))
    6636             break;
    6637 
    6638         fBeganSavingState = true;
    6639 
    6640         /* sync the state with the server */
    6641         i_setMachineStateLocally(MachineState_Saving);
    6642 
    6643         /* ensure the directory for the saved state file exists */
    6644         {
    6645             Utf8Str dir = stateFilePath;
    6646             dir.stripFilename();
    6647             if (!RTDirExists(dir.c_str()))
    6648             {
    6649                 int vrc = RTDirCreateFullPath(dir.c_str(), 0700);
    6650                 if (RT_FAILURE(vrc))
    6651                 {
    6652                     rc = setError(VBOX_E_FILE_ERROR,
    6653                         tr("Could not create a directory '%s' to save the state to (%Rrc)"),
    6654                         dir.c_str(), vrc);
    6655                     break;
    6656                 }
    6657             }
    6658         }
    6659 
    6660         /* Create a task object early to ensure mpUVM protection is successful. */
    6661         std::auto_ptr<VMSaveTask> task(new VMSaveTask(this, pProgress,
    6662                                                       stateFilePath,
    6663                                                       lastMachineState,
    6664                                                       aReason));
    6665         rc = task->rc();
    6666         /*
    6667          * If we fail here it means a PowerDown() call happened on another
    6668          * thread while we were doing Pause() (which releases the Console lock).
    6669          * We assign PowerDown() a higher precedence than SaveState(),
    6670          * therefore just return the error to the caller.
    6671          */
    6672         if (FAILED(rc))
    6673         {
    6674             fTaskCreationFailed = true;
    6675             break;
    6676         }
    6677 
    6678         /* create a thread to wait until the VM state is saved */
    6679         int vrc = RTThreadCreate(NULL, Console::i_saveStateThread, (void *)task.get(),
    6680                                  0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave");
    6681         if (RT_FAILURE(vrc))
    6682         {
    6683             rc = setError(E_FAIL, "Could not create VMSave thread (%Rrc)", vrc);
    6684             break;
    6685         }
    6686 
    6687         /* task is now owned by saveStateThread(), so release it */
    6688         task.release();
    6689 
    6690         /* return the progress to the caller */
    6691         pProgress.queryInterfaceTo(aProgress);
    6692     } while (0);
    6693 
    6694     if (FAILED(rc) && !fTaskCreationFailed)
    6695     {
    6696         /* preserve existing error info */
    6697         ErrorInfoKeeper eik;
    6698 
    6699         if (fBeganSavingState)
    6700         {
    6701             /*
    6702              * cancel the requested save state procedure.
    6703              * This will reset the machine state to the state it had right
    6704              * before calling mControl->BeginSavingState().
    6705              */
    6706             mControl->EndSavingState(eik.getResultCode(), eik.getText().raw());
    6707         }
    6708 
    6709         if (lastMachineState == MachineState_Running)
    6710         {
    6711             /* restore the paused state if appropriate */
    6712             i_setMachineStateLocally(MachineState_Paused);
    6713             /* restore the running state if appropriate */
    6714             SafeVMPtr ptrVM(this);
    6715             if (ptrVM.isOk())
    6716             {
    6717                 alock.release();
    6718                 VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_RESTORED);
    6719                 alock.acquire();
    6720             }
    6721         }
    6722         else
    6723             i_setMachineStateLocally(lastMachineState);
    6724     }
    6725 
    6726     LogFlowThisFunc(("rc=%Rhrc\n", rc));
    6727     LogFlowThisFuncLeave();
    6728     return rc;
     6430        ptrVM.release();
     6431        alock.release();
     6432        autoCaller.release();
     6433        HRESULT rc = i_powerDown();
     6434        AssertComRC(rc);
     6435        autoCaller.add();
     6436        alock.acquire();
     6437    }
     6438    else
     6439    {
     6440        if (fPaused)
     6441            aLeftPaused = true;
     6442    }
     6443
     6444    LogFlowFuncLeave();
     6445    return S_OK;
     6446}
     6447
     6448/**
     6449 * Internal entry point for cancelling a VM save state.
     6450 *
     6451 * @note Locks this object for writing.
     6452 */
     6453HRESULT Console::i_cancelSaveState()
     6454{
     6455    LogFlowThisFuncEnter();
     6456
     6457    AutoCaller autoCaller(this);
     6458    if (FAILED(autoCaller.rc())) return autoCaller.rc();
     6459
     6460    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
     6461
     6462    /* Get the VM handle. */
     6463    SafeVMPtr ptrVM(this);
     6464    if (!ptrVM.isOk())
     6465        return ptrVM.rc();
     6466
     6467    SSMR3Cancel(ptrVM.rawUVM());
     6468
     6469    LogFlowFuncLeave();
     6470    return S_OK;
    67296471}
    67306472
     
    67456487
    67466488    AssertReturn(   mMachineState == MachineState_Saving
     6489                 || mMachineState == MachineState_OnlineSnapshotting
    67476490                 || mMachineState == MachineState_LiveSnapshotting
    6748                  || mMachineState == MachineState_RestoringSnapshot
    6749                  || mMachineState == MachineState_DeletingSnapshot
    67506491                 || mMachineState == MachineState_DeletingSnapshotOnline
    67516492                 || mMachineState == MachineState_DeletingSnapshotPaused
     6493                 || aMachineState == MachineState_Saving
     6494                 || aMachineState == MachineState_OnlineSnapshotting
     6495                 || aMachineState == MachineState_LiveSnapshotting
     6496                 || aMachineState == MachineState_DeletingSnapshotOnline
     6497                 || aMachineState == MachineState_DeletingSnapshotPaused
    67526498                 , E_FAIL);
    67536499
    67546500    return i_setMachineStateLocally(aMachineState);
     6501}
     6502
     6503/**
     6504 * Gets called by Session::COMGETTER(NominalState)()
     6505 * (IInternalSessionControl::getNominalState()).
     6506 *
     6507 * @note Locks this object for reading.
     6508 */
     6509HRESULT Console::i_getNominalState(MachineState_T &aNominalState)
     6510{
     6511    LogFlowThisFuncEnter();
     6512
     6513    AutoCaller autoCaller(this);
     6514    AssertComRCReturnRC(autoCaller.rc());
     6515
     6516    /* Get the VM handle. */
     6517    SafeVMPtr ptrVM(this);
     6518    if (!ptrVM.isOk())
     6519        return ptrVM.rc();
     6520
     6521    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     6522
     6523    MachineState_T enmMachineState = MachineState_Null;
     6524    VMSTATE enmVMState = VMR3GetStateU(ptrVM.rawUVM());
     6525    switch (enmVMState)
     6526    {
     6527        case VMSTATE_CREATING:
     6528        case VMSTATE_CREATED:
     6529        case VMSTATE_POWERING_ON:
     6530            enmMachineState = MachineState_Starting;
     6531            break;
     6532        case VMSTATE_LOADING:
     6533            enmMachineState = MachineState_Restoring;
     6534            break;
     6535        case VMSTATE_RESUMING:
     6536        case VMSTATE_SUSPENDING:
     6537        case VMSTATE_SUSPENDING_LS:
     6538        case VMSTATE_SUSPENDING_EXT_LS:
     6539        case VMSTATE_SUSPENDED:
     6540        case VMSTATE_SUSPENDED_LS:
     6541        case VMSTATE_SUSPENDED_EXT_LS:
     6542            enmMachineState = MachineState_Paused;
     6543            break;
     6544        case VMSTATE_RUNNING:
     6545        case VMSTATE_RUNNING_LS:
     6546        case VMSTATE_RUNNING_FT:
     6547        case VMSTATE_RESETTING:
     6548        case VMSTATE_RESETTING_LS:
     6549        case VMSTATE_DEBUGGING:
     6550        case VMSTATE_DEBUGGING_LS:
     6551            enmMachineState = MachineState_Running;
     6552            break;
     6553        case VMSTATE_SAVING:
     6554            enmMachineState = MachineState_Saving;
     6555            break;
     6556        case VMSTATE_POWERING_OFF:
     6557        case VMSTATE_POWERING_OFF_LS:
     6558        case VMSTATE_DESTROYING:
     6559            enmMachineState = MachineState_Stopping;
     6560            break;
     6561        case VMSTATE_OFF:
     6562        case VMSTATE_OFF_LS:
     6563        case VMSTATE_FATAL_ERROR:
     6564        case VMSTATE_FATAL_ERROR_LS:
     6565        case VMSTATE_LOAD_FAILURE:
     6566        case VMSTATE_TERMINATED:
     6567            enmMachineState = MachineState_PoweredOff;
     6568            break;
     6569        case VMSTATE_GURU_MEDITATION:
     6570        case VMSTATE_GURU_MEDITATION_LS:
     6571            enmMachineState = MachineState_Stuck;
     6572            break;
     6573        default:
     6574            AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
     6575            enmMachineState = MachineState_PoweredOff;
     6576    }
     6577    aNominalState = enmMachineState;
     6578
     6579    LogFlowFuncLeave();
     6580    return S_OK;
    67556581}
    67566582
     
    71606986    try
    71616987    {
    7162 
    71636988        if (Global::IsOnlineOrTransient(mMachineState))
    71646989            throw setError(VBOX_E_INVALID_VM_STATE,
     
    72737098        }
    72747099
    7275         /* Setup task object and thread to carry out the operaton
    7276          * Asycnhronously */
     7100        /* Setup task object and thread to carry out the operation
     7101         * asynchronously */
    72777102        std::auto_ptr<VMPowerUpTask> task(new VMPowerUpTask(this, pPowerupProgress));
    72787103        ComAssertComRCRetRC(task->rc());
     
    78927717 */
    78937718HRESULT Console::i_setMachineState(MachineState_T aMachineState,
    7894                                  bool aUpdateServer /* = true */)
     7719                                   bool aUpdateServer /* = true */)
    78957720{
    78967721    AutoCaller autoCaller(this);
     
    79057730        LogThisFunc(("machineState=%s -> %s aUpdateServer=%RTbool\n",
    79067731                     Global::stringifyMachineState(mMachineState), Global::stringifyMachineState(aMachineState), aUpdateServer));
     7732        LogRel(("Console: machine state changed to %s\n", Global::stringifyMachineState(aMachineState)));
    79077733        mMachineState = aMachineState;
    79087734
     
    85158341
    85168342                case MachineState_LiveSnapshotting:
    8517                     that->i_setMachineState(MachineState_Saving);
     8343                    that->i_setMachineState(MachineState_OnlineSnapshotting);
    85188344                    break;
    85198345
     
    85248350                case MachineState_TeleportingIn:
    85258351                case MachineState_FaultTolerantSyncing:
     8352                case MachineState_OnlineSnapshotting:
    85268353                    /* The worker thread handles the transition. */
    85278354                    break;
    85288355
    8529                 default:
    8530                     AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
    85318356                case MachineState_Running:
    85328357                    that->i_setMachineState(MachineState_Paused);
     
    85368361                    /* Nothing to do. */
    85378362                    break;
     8363
     8364                default:
     8365                    AssertMsgFailed(("%s\n", Global::stringifyMachineState(that->mMachineState)));
    85388366            }
    85398367            break;
     
    85538381
    85548382                case MachineState_LiveSnapshotting:
    8555                     that->i_setMachineState(MachineState_Saving);
     8383                    that->i_setMachineState(MachineState_OnlineSnapshotting);
    85568384                    break;
    85578385
     
    96139441        // Create the VMM device object, which starts the HGCM thread; do this only
    96149442        // once for the console, for the pathological case that the same console
    9615         // object is used to power up a VM twice. VirtualBox 4.0: we now do that
    9616         // here instead of the Console constructor (see Console::init())
     9443        // object is used to power up a VM twice.
    96179444        if (!pConsole->m_pVMMDev)
    96189445        {
     
    101029929
    101039930/**
    10104  * Progress cancelation callback employed by Console::fntTakeSnapshotWorker.
    10105  */
    10106 static void takesnapshotProgressCancelCallback(void *pvUser)
    10107 {
    10108     PUVM pUVM = (PUVM)pvUser;
    10109     SSMR3Cancel(pUVM);
    10110 }
    10111 
    10112 /**
    10113  * Worker thread created by Console::TakeSnapshot.
    10114  * @param Thread The current thread (ignored).
    10115  * @param pvUser The task.
    10116  * @return VINF_SUCCESS (ignored).
    10117  */
    10118 /*static*/
    10119 DECLCALLBACK(int) Console::i_fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser)
    10120 {
    10121     VMTakeSnapshotTask *pTask = (VMTakeSnapshotTask*)pvUser;
    10122 
    10123     // taking a snapshot consists of the following:
    10124 
    10125     // 1) creating a diff image for each virtual hard disk, into which write operations go after
    10126     //    the snapshot has been created (done in VBoxSVC, in SessionMachine::BeginTakingSnapshot)
    10127     // 2) creating a Snapshot object with the state of the machine (hardware + storage,
    10128     //    done in VBoxSVC, also in SessionMachine::BeginTakingSnapshot)
    10129     // 3) saving the state of the virtual machine (here, in the VM process, if the machine is online)
    10130 
    10131     Console    *that                 = pTask->mConsole;
    10132     bool        fBeganTakingSnapshot = false;
    10133     bool        fSuspenededBySave    = false;
    10134 
    10135     AutoCaller autoCaller(that);
    10136     if (FAILED(autoCaller.rc()))
    10137     {
    10138         that->mptrCancelableProgress.setNull();
    10139         return autoCaller.rc();
    10140     }
    10141 
    10142     AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);
    10143 
    10144     HRESULT rc = S_OK;
    10145 
    10146     try
    10147     {
    10148         /* STEP 1 + 2:
    10149          * request creating the diff images on the server and create the snapshot object
    10150          * (this will set the machine state to Saving on the server to block
    10151          * others from accessing this machine)
    10152          */
    10153         rc = that->mControl->BeginTakingSnapshot(that,
    10154                                                  pTask->bstrName.raw(),
    10155                                                  pTask->bstrDescription.raw(),
    10156                                                  pTask->mProgress,
    10157                                                  pTask->fTakingSnapshotOnline,
    10158                                                  pTask->bstrSavedStateFile.asOutParam());
    10159         if (FAILED(rc))
    10160             throw rc;
    10161 
    10162         fBeganTakingSnapshot = true;
    10163 
    10164         /* Check sanity: for offline snapshots there must not be a saved state
    10165          * file name. All other combinations are valid (even though online
    10166          * snapshots without saved state file seems inconsistent - there are
    10167          * some exotic use cases, which need to be explicitly enabled, see the
    10168          * code of SessionMachine::BeginTakingSnapshot. */
    10169         if (   !pTask->fTakingSnapshotOnline
    10170             && !pTask->bstrSavedStateFile.isEmpty())
    10171             throw i_setErrorStatic(E_FAIL, "Invalid state of saved state file");
    10172 
    10173         /* sync the state with the server */
    10174         if (pTask->lastMachineState == MachineState_Running)
    10175             that->i_setMachineStateLocally(MachineState_LiveSnapshotting);
    10176         else
    10177             that->i_setMachineStateLocally(MachineState_Saving);
    10178 
    10179         // STEP 3: save the VM state (if online)
    10180         if (pTask->fTakingSnapshotOnline)
    10181         {
    10182             int vrc;
    10183             SafeVMPtr ptrVM(that);
    10184             if (!ptrVM.isOk())
    10185                 throw ptrVM.rc();
    10186 
    10187             pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(),
    10188                                                pTask->ulMemSize);       // operation weight, same as computed
    10189                                                                         // when setting up progress object
    10190             if (!pTask->bstrSavedStateFile.isEmpty())
    10191             {
    10192                 Utf8Str strSavedStateFile(pTask->bstrSavedStateFile);
    10193 
    10194                 pTask->mProgress->i_setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM());
    10195 
    10196                 alock.release();
    10197                 LogFlowFunc(("VMR3Save...\n"));
    10198                 vrc = VMR3Save(ptrVM.rawUVM(),
    10199                                strSavedStateFile.c_str(),
    10200                                true /*fContinueAfterwards*/,
    10201                                Console::i_stateProgressCallback,
    10202                                static_cast<IProgress *>(pTask->mProgress),
    10203                                &fSuspenededBySave);
    10204                 alock.acquire();
    10205                 if (RT_FAILURE(vrc))
    10206                     throw i_setErrorStatic(E_FAIL,
    10207                                          tr("Failed to save the machine state to '%s' (%Rrc)"),
    10208                                          strSavedStateFile.c_str(), vrc);
    10209 
    10210                 pTask->mProgress->i_setCancelCallback(NULL, NULL);
    10211             }
    10212             else
    10213                 LogRel(("Console: skipped saving state as part of online snapshot\n"));
    10214 
    10215             if (!pTask->mProgress->i_notifyPointOfNoReturn())
    10216                 throw i_setErrorStatic(E_FAIL, tr("Canceled"));
    10217             that->mptrCancelableProgress.setNull();
    10218 
    10219             // STEP 4: reattach hard disks
    10220             LogFlowFunc(("Reattaching new differencing hard disks...\n"));
    10221 
    10222             pTask->mProgress->SetNextOperation(Bstr(tr("Reconfiguring medium attachments")).raw(),
    10223                                                1);       // operation weight, same as computed when setting up progress object
    10224 
    10225             com::SafeIfaceArray<IMediumAttachment> atts;
    10226             rc = that->mMachine->COMGETTER(MediumAttachments)(ComSafeArrayAsOutParam(atts));
    10227             if (FAILED(rc))
    10228                 throw rc;
    10229 
    10230             for (size_t i = 0;
    10231                 i < atts.size();
    10232                 ++i)
    10233             {
    10234                 ComPtr<IStorageController> pStorageController;
    10235                 Bstr controllerName;
    10236                 ULONG lInstance;
    10237                 StorageControllerType_T enmController;
    10238                 StorageBus_T enmBus;
    10239                 BOOL fUseHostIOCache;
    10240 
    10241                 /*
    10242                 * We can't pass a storage controller object directly
    10243                 * (g++ complains about not being able to pass non POD types through '...')
    10244                 * so we have to query needed values here and pass them.
    10245                 */
    10246                 rc = atts[i]->COMGETTER(Controller)(controllerName.asOutParam());
    10247                 if (FAILED(rc))
    10248                     throw rc;
    10249 
    10250                 rc = that->mMachine->GetStorageControllerByName(controllerName.raw(),
    10251                                                                 pStorageController.asOutParam());
    10252                 if (FAILED(rc))
    10253                     throw rc;
    10254 
    10255                 rc = pStorageController->COMGETTER(ControllerType)(&enmController);
    10256                 if (FAILED(rc))
    10257                     throw rc;
    10258                 rc = pStorageController->COMGETTER(Instance)(&lInstance);
    10259                 if (FAILED(rc))
    10260                     throw rc;
    10261                 rc = pStorageController->COMGETTER(Bus)(&enmBus);
    10262                 if (FAILED(rc))
    10263                     throw rc;
    10264                 rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
    10265                 if (FAILED(rc))
    10266                     throw rc;
    10267 
    10268                 const char *pcszDevice = Console::i_convertControllerTypeToDev(enmController);
    10269 
    10270                 BOOL fBuiltinIOCache;
    10271                 rc = that->mMachine->COMGETTER(IOCacheEnabled)(&fBuiltinIOCache);
    10272                 if (FAILED(rc))
    10273                     throw rc;
    10274 
    10275                 alock.release();
    10276                 vrc = VMR3ReqCallWaitU(ptrVM.rawUVM(), VMCPUID_ANY,
    10277                                        (PFNRT)i_reconfigureMediumAttachment, 13,
    10278                                        that, ptrVM.rawUVM(), pcszDevice, lInstance, enmBus, fUseHostIOCache,
    10279                                        fBuiltinIOCache, false /* fSetupMerge */, 0 /* uMergeSource */,
    10280                                        0 /* uMergeTarget */, atts[i], that->mMachineState, &rc);
    10281                 alock.acquire();
    10282                 if (RT_FAILURE(vrc))
    10283                     throw i_setErrorStatic(E_FAIL, Console::tr("%Rrc"), vrc);
    10284                 if (FAILED(rc))
    10285                     throw rc;
    10286             }
    10287         }
    10288 
    10289         /*
    10290          * finalize the requested snapshot object.
    10291          * This will reset the machine state to the state it had right
    10292          * before calling mControl->BeginTakingSnapshot().
    10293          */
    10294         rc = that->mControl->EndTakingSnapshot(TRUE /*aSuccess*/);
    10295         // do not throw rc here because we can't call EndTakingSnapshot() twice
    10296         LogFlowFunc(("EndTakingSnapshot -> %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
    10297     }
    10298     catch (HRESULT rcThrown)
    10299     {
    10300         /* preserve existing error info */
    10301         ErrorInfoKeeper eik;
    10302 
    10303         if (fBeganTakingSnapshot)
    10304             that->mControl->EndTakingSnapshot(FALSE /*aSuccess*/);
    10305 
    10306         rc = rcThrown;
    10307         LogFunc(("Caught %Rhrc [mMachineState=%s]\n", rc, Global::stringifyMachineState(that->mMachineState)));
    10308     }
    10309     Assert(alock.isWriteLockOnCurrentThread());
    10310 
    10311     if (FAILED(rc)) /* Must come before calling setMachineState. */
    10312         pTask->mProgress->i_notifyComplete(rc);
    10313 
    10314     /*
    10315      * Fix up the machine state.
    10316      *
    10317      * For live snapshots we do all the work, for the two other variations we
    10318      * just update the local copy.
    10319      */
    10320     MachineState_T enmMachineState;
    10321     that->mMachine->COMGETTER(State)(&enmMachineState);
    10322     if (   that->mMachineState == MachineState_LiveSnapshotting
    10323         || that->mMachineState == MachineState_Saving)
    10324     {
    10325 
    10326         if (!pTask->fTakingSnapshotOnline)
    10327             that->i_setMachineStateLocally(pTask->lastMachineState);
    10328         else if (SUCCEEDED(rc))
    10329         {
    10330             Assert(   pTask->lastMachineState == MachineState_Running
    10331                    || pTask->lastMachineState == MachineState_Paused);
    10332             Assert(that->mMachineState == MachineState_Saving);
    10333             if (pTask->lastMachineState == MachineState_Running)
    10334             {
    10335                 LogFlowFunc(("VMR3Resume...\n"));
    10336                 SafeVMPtr ptrVM(that);
    10337                 alock.release();
    10338                 int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED);
    10339                 alock.acquire();
    10340                 if (RT_FAILURE(vrc))
    10341                 {
    10342                     rc = i_setErrorStatic(VBOX_E_VM_ERROR, tr("Could not resume the machine execution (%Rrc)"), vrc);
    10343                     pTask->mProgress->i_notifyComplete(rc);
    10344                     if (that->mMachineState == MachineState_Saving)
    10345                         that->i_setMachineStateLocally(MachineState_Paused);
    10346                 }
    10347             }
    10348             else
    10349                 that->i_setMachineStateLocally(MachineState_Paused);
    10350         }
    10351         else
    10352         {
    10353             /** @todo this could probably be made more generic and reused elsewhere. */
    10354             /* paranoid cleanup on for a failed online snapshot. */
    10355             VMSTATE enmVMState = VMR3GetStateU(that->mpUVM);
    10356             switch (enmVMState)
    10357             {
    10358                 case VMSTATE_RUNNING:
    10359                 case VMSTATE_RUNNING_LS:
    10360                 case VMSTATE_DEBUGGING:
    10361                 case VMSTATE_DEBUGGING_LS:
    10362                 case VMSTATE_POWERING_OFF:
    10363                 case VMSTATE_POWERING_OFF_LS:
    10364                 case VMSTATE_RESETTING:
    10365                 case VMSTATE_RESETTING_LS:
    10366                     Assert(!fSuspenededBySave);
    10367                     that->i_setMachineState(MachineState_Running);
    10368                     break;
    10369 
    10370                 case VMSTATE_GURU_MEDITATION:
    10371                 case VMSTATE_GURU_MEDITATION_LS:
    10372                     that->i_setMachineState(MachineState_Stuck);
    10373                     break;
    10374 
    10375                 case VMSTATE_FATAL_ERROR:
    10376                 case VMSTATE_FATAL_ERROR_LS:
    10377                     if (pTask->lastMachineState == MachineState_Paused)
    10378                         that->i_setMachineStateLocally(pTask->lastMachineState);
    10379                     else
    10380                         that->i_setMachineState(MachineState_Paused);
    10381                     break;
    10382 
    10383                 default:
    10384                     AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
    10385                 case VMSTATE_SUSPENDED:
    10386                 case VMSTATE_SUSPENDED_LS:
    10387                 case VMSTATE_SUSPENDING:
    10388                 case VMSTATE_SUSPENDING_LS:
    10389                 case VMSTATE_SUSPENDING_EXT_LS:
    10390                     if (fSuspenededBySave)
    10391                     {
    10392                         Assert(pTask->lastMachineState == MachineState_Running);
    10393                         LogFlowFunc(("VMR3Resume (on failure)...\n"));
    10394                         SafeVMPtr ptrVM(that);
    10395                         alock.release();
    10396                         int vrc = VMR3Resume(ptrVM.rawUVM(), VMRESUMEREASON_STATE_SAVED); AssertLogRelRC(vrc);
    10397                         alock.acquire();
    10398                         if (RT_FAILURE(vrc))
    10399                             that->i_setMachineState(MachineState_Paused);
    10400                     }
    10401                     else if (pTask->lastMachineState == MachineState_Paused)
    10402                         that->i_setMachineStateLocally(pTask->lastMachineState);
    10403                     else
    10404                         that->i_setMachineState(MachineState_Paused);
    10405                     break;
    10406             }
    10407 
    10408         }
    10409     }
    10410     /*else: somebody else has change the state... Leave it. */
    10411 
    10412     /* check the remote state to see that we got it right. */
    10413     that->mMachine->COMGETTER(State)(&enmMachineState);
    10414     AssertLogRelMsg(that->mMachineState == enmMachineState,
    10415                     ("mMachineState=%s enmMachineState=%s\n", Global::stringifyMachineState(that->mMachineState),
    10416                      Global::stringifyMachineState(enmMachineState) ));
    10417 
    10418 
    10419     if (SUCCEEDED(rc)) /* The failure cases are handled above. */
    10420         pTask->mProgress->i_notifyComplete(rc);
    10421 
    10422     delete pTask;
    10423 
    10424     LogFlowFuncLeave();
    10425     return VINF_SUCCESS;
    10426 }
    10427 
    10428 /**
    10429  * Thread for executing the saved state operation.
    10430  *
    10431  * @param   Thread      The thread handle.
    10432  * @param   pvUser      Pointer to a VMSaveTask structure.
    10433  * @return  VINF_SUCCESS (ignored).
    10434  *
    10435  * @note Locks the Console object for writing.
    10436  */
    10437 /*static*/
    10438 DECLCALLBACK(int) Console::i_saveStateThread(RTTHREAD Thread, void *pvUser)
    10439 {
    10440     LogFlowFuncEnter();
    10441 
    10442     std::auto_ptr<VMSaveTask> task(static_cast<VMSaveTask*>(pvUser));
    10443     AssertReturn(task.get(), VERR_INVALID_PARAMETER);
    10444 
    10445     Assert(task->mSavedStateFile.length());
    10446     Assert(task->mProgress.isNull());
    10447     Assert(!task->mServerProgress.isNull());
    10448 
    10449     const ComObjPtr<Console> &that = task->mConsole;
    10450     Utf8Str errMsg;
    10451     HRESULT rc = S_OK;
    10452 
    10453     LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str()));
    10454 
    10455     bool fSuspenededBySave;
    10456     int vrc = VMR3Save(task->mpUVM,
    10457                        task->mSavedStateFile.c_str(),
    10458                        false, /*fContinueAfterwards*/
    10459                        Console::i_stateProgressCallback,
    10460                        static_cast<IProgress *>(task->mServerProgress),
    10461                        &fSuspenededBySave);
    10462     if (RT_FAILURE(vrc))
    10463     {
    10464         errMsg = Utf8StrFmt(Console::tr("Failed to save the machine state to '%s' (%Rrc)"),
    10465                             task->mSavedStateFile.c_str(), vrc);
    10466         rc = E_FAIL;
    10467     }
    10468     Assert(!fSuspenededBySave);
    10469 
    10470     /* lock the console once we're going to access it */
    10471     AutoWriteLock thatLock(that COMMA_LOCKVAL_SRC_POS);
    10472 
    10473     /* synchronize the state with the server */
    10474     if (SUCCEEDED(rc))
    10475     {
    10476         /*
    10477          * The machine has been successfully saved, so power it down
    10478          * (vmstateChangeCallback() will set state to Saved on success).
    10479          * Note: we release the task's VM caller, otherwise it will
    10480          * deadlock.
    10481          */
    10482         task->releaseVMCaller();
    10483         thatLock.release();
    10484         rc = that->i_powerDown();
    10485         thatLock.acquire();
    10486     }
    10487 
    10488     /*
    10489      * If we failed, reset the local machine state.
    10490      */
    10491     if (FAILED(rc))
    10492         that->i_setMachineStateLocally(task->mMachineStateBefore);
    10493 
    10494     /*
    10495      * Finalize the requested save state procedure. In case of failure it will
    10496      * reset the machine state to the state it had right before calling
    10497      * mControl->BeginSavingState(). This must be the last thing because it
    10498      * will set the progress to completed, and that means that the frontend
    10499      * can immediately uninit the associated console object.
    10500      */
    10501     that->mControl->EndSavingState(rc, Bstr(errMsg).raw());
    10502 
    10503     LogFlowFuncLeave();
    10504     return VINF_SUCCESS;
    10505 }
    10506 
    10507 /**
    105089931 * Thread for powering down the Console.
    105099932 *
     
    105609983    /*
    105619984     * For now, just call SaveState.  We should probably try notify the GUI so
    10562      * it can pop up a progress object and stuff.
     9985     * it can pop up a progress object and stuff. The progress object created
     9986     * by the call isn't returned to anyone and thus gets updated without
     9987     * anyone noticing it.
    105639988     */
    10564     HRESULT hrc = pConsole->SaveState(NULL);
     9989    ComPtr<IProgress> pProgress;
     9990    HRESULT hrc = pConsole->mMachine->SaveState(pProgress.asOutParam());
    105659991    return SUCCEEDED(hrc) ? VINF_SUCCESS : Global::vboxStatusCodeFromCOM(hrc);
    105669992}
注意: 瀏覽 TracChangeset 來幫助您使用更動檢視器

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