Changeset 47561 in vbox for trunk/src/VBox
- Timestamp:
- Aug 6, 2013 3:18:17 PM (12 years ago)
- svn:sync-xref-src-repo-rev:
- 87761
- Location:
- trunk/src/VBox/Main
- Files:
-
- 3 added
- 8 edited
- 3 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/Makefile.kmk
r47376 r47561 334 334 src-server/BandwidthGroupImpl.cpp \ 335 335 src-server/BIOSSettingsImpl.cpp \ 336 src-server/ClientWatcher.cpp \ 337 src-server/ClientToken.cpp \ 336 338 src-server/DHCPServerImpl.cpp \ 337 339 src-server/NetworkServiceRunner.cpp \ … … 634 636 src-client/BusAssignmentManager.cpp \ 635 637 $(if $(VBOX_WITH_PCI_PASSTHROUGH),src-client/PCIRawDevImpl.cpp,) \ 638 src-client/ClientTokenHolder.cpp \ 636 639 src-client/ConsoleImpl.cpp \ 637 640 src-client/ConsoleImpl2.cpp \ -
trunk/src/VBox/Main/idl/VirtualBox.xidl
r47485 r47561 3364 3364 <interface 3365 3365 name="IInternalMachineControl" extends="$unknown" 3366 uuid=" dca36a92-703c-4649-98a4-f40c1ef0c336"3366 uuid="aca30121-e06b-4ace-bc22-d18506a8ba2b" 3367 3367 internal="yes" 3368 3368 wsmap="suppress" … … 3386 3386 </desc> 3387 3387 <param name="state" type="MachineState" dir="in"/> 3388 </method>3389 3390 <method name="getIPCId">3391 <param name="id" type="wstring" dir="return"/>3392 3388 </method> 3393 3389 … … 17331 17327 <interface 17332 17328 name="IInternalSessionControl" extends="$unknown" 17333 uuid=" cddf451c-a006-4c33-8245-63b3c9ae6586"17329 uuid="3eeeb8e7-374f-4e0a-a5fd-f548742da8d4" 17334 17330 internal="yes" 17335 17331 wsmap="suppress" … … 17372 17368 <param name="machine" type="IMachine" dir="in"/> 17373 17369 <param name="lockType" type="LockType" dir="in"/> 17370 <param name="tokenId" type="wstring" dir="in"/> 17374 17371 </method> 17375 17372 -
trunk/src/VBox/Main/include/MachineImpl.h
r47409 r47561 783 783 } 784 784 785 #if defined(RT_OS_WINDOWS)786 787 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine,788 ComPtr<IInternalSessionControl> *aControl = NULL,789 HANDLE *aIPCSem = NULL, bool aAllowClosing = false);790 bool isSessionSpawning(RTPROCESS *aPID = NULL);791 792 bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine,793 ComPtr<IInternalSessionControl> *aControl = NULL,794 HANDLE *aIPCSem = NULL)795 { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); }796 797 #elif defined(RT_OS_OS2)798 799 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine,800 ComPtr<IInternalSessionControl> *aControl = NULL,801 HMTX *aIPCSem = NULL, bool aAllowClosing = false);802 803 bool isSessionSpawning(RTPROCESS *aPID = NULL);804 805 bool isSessionOpenOrClosing(ComObjPtr<SessionMachine> &aMachine,806 ComPtr<IInternalSessionControl> *aControl = NULL,807 HMTX *aIPCSem = NULL)808 { return isSessionOpen(aMachine, aControl, aIPCSem, true /* aAllowClosing */); }809 810 #else811 812 785 bool isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 813 786 ComPtr<IInternalSessionControl> *aControl = NULL, … … 819 792 { return isSessionOpen(aMachine, aControl, true /* aAllowClosing */); } 820 793 821 #endif822 823 794 bool checkForSpawnFailure(); 824 795 … … 848 819 849 820 protected: 821 822 class ClientToken; 850 823 851 824 HRESULT checkStateDependency(StateDependency aDepType); … … 1075 1048 STDMETHOD(SetRemoveSavedStateFile)(BOOL aRemove); 1076 1049 STDMETHOD(UpdateState)(MachineState_T machineState); 1077 STDMETHOD(GetIPCId)(BSTR *id);1078 1050 STDMETHOD(BeginPowerUp)(IProgress *aProgress); 1079 1051 STDMETHOD(EndPowerUp)(LONG iResult); … … 1134 1106 bool checkForDeath(); 1135 1107 1108 void getTokenId(Utf8Str &strTokenId); 1109 // getClientToken must be only used by callers who can guarantee that 1110 // the object cannot be deleted in the mean time, i.e. have a caller/lock. 1111 ClientToken *getClientToken(); 1112 1136 1113 HRESULT onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter); 1137 1114 HRESULT onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName, … … 1236 1213 ConsoleTaskData mConsoleTaskData; 1237 1214 1238 /** interprocess semaphore handle for this machine */ 1239 #if defined(RT_OS_WINDOWS) 1240 HANDLE mIPCSem; 1241 Bstr mIPCSemName; 1242 friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 1243 ComPtr<IInternalSessionControl> *aControl, 1244 HANDLE *aIPCSem, bool aAllowClosing); 1245 #elif defined(RT_OS_OS2) 1246 HMTX mIPCSem; 1247 Bstr mIPCSemName; 1248 friend bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 1249 ComPtr<IInternalSessionControl> *aControl, 1250 HMTX *aIPCSem, bool aAllowClosing); 1251 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 1252 int mIPCSem; 1253 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 1254 Bstr mIPCKey; 1255 # endif /*VBOX_WITH_NEW_SYS_V_KEYGEN */ 1256 #else 1257 # error "Port me!" 1258 #endif 1215 /** client token for this machine */ 1216 ClientToken *mClientToken; 1259 1217 1260 1218 static DECLCALLBACK(int) taskHandler(RTTHREAD thread, void *pvUser); -
trunk/src/VBox/Main/include/SessionImpl.h
r46775 r47561 24 24 #ifdef RT_OS_WINDOWS 25 25 # include "win/resource.h" 26 #endif27 28 /** @def VBOX_WITH_SYS_V_IPC_SESSION_WATCHER29 * Use SYS V IPC for watching a session.30 * This is defined in the Makefile since it's also used by MachineImpl.h/cpp.31 */32 #ifdef DOXYGEN_RUNNING33 # define VBOX_WITH_SYS_V_IPC_SESSION_WATCHER34 26 #endif 35 27 … … 81 73 STDMETHOD(GetPID)(ULONG *aPid); 82 74 STDMETHOD(GetRemoteConsole)(IConsole **aConsole); 83 STDMETHOD(AssignMachine)(IMachine *aMachine, LockType_T aLockType );75 STDMETHOD(AssignMachine)(IMachine *aMachine, LockType_T aLockType, IN_BSTR aTokenId); 84 76 STDMETHOD(AssignRemoteMachine)(IMachine *aMachine, IConsole *aConsole); 85 77 STDMETHOD(UpdateMachineState)(MachineState_T aMachineState); … … 124 116 125 117 HRESULT unlockMachine(bool aFinalRelease, bool aFromServer); 126 HRESULT grabIPCSemaphore();127 void releaseIPCSemaphore();128 118 129 119 SessionState_T mState; … … 139 129 ComPtr<IVirtualBox> mVirtualBox; 140 130 141 /* interprocess semaphore handle (id) for the opened machine */ 142 #if defined(RT_OS_WINDOWS) 143 HANDLE mIPCSem; 144 HANDLE mIPCThreadSem; 145 #elif defined(RT_OS_OS2) 146 RTTHREAD mIPCThread; 147 RTSEMEVENT mIPCThreadSem; 148 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 149 int mIPCSem; 150 #else 151 # error "Port me!" 152 #endif 131 class ClientTokenHolder; 132 133 ClientTokenHolder *mClientTokenHolder; 153 134 }; 154 135 -
trunk/src/VBox/Main/include/VirtualBoxImpl.h
r47525 r47561 20 20 21 21 #include "VirtualBoxBase.h" 22 #include "objectslist.h" 22 23 23 24 #ifdef RT_OS_WINDOWS … … 33 34 class SessionMachine; 34 35 class GuestOSType; 35 class SharedFolder;36 36 class Progress; 37 37 class Host; … … 39 39 class DHCPServer; 40 40 class PerformanceCollector; 41 class VirtualBoxCallbackRegistration; /* see VirtualBoxImpl.cpp */42 41 #ifdef VBOX_WITH_EXTPACK 43 42 class ExtPackManager; … … 52 51 class SVCHlpClient; 53 52 #endif 54 55 struct VMClientWatcherData;56 53 57 54 namespace settings … … 71 68 72 69 typedef std::list<ComPtr<IInternalSessionControl> > InternalControlList; 70 typedef ObjectsList<Machine> MachinesOList; 73 71 74 72 class CallbackEvent; … … 216 214 void onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName, IN_BSTR aValue, 217 215 IN_BSTR aFlags); 218 void onMachineUninit(Machine *aMachine);219 216 void onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName, 220 217 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort, … … 234 231 void getOpenedMachines(SessionMachinesList &aMachines, 235 232 InternalControlList *aControls = NULL); 233 MachinesOList &getMachinesList(); 236 234 237 235 HRESULT findMachine(const Guid &aId, … … 322 320 323 321 private: 322 class ClientWatcher; 324 323 325 324 static HRESULT setErrorStatic(HRESULT aResultCode, … … 361 360 static Bstr sAPIVersion; 362 361 363 static DECLCALLBACK(int) ClientWatcher(RTTHREAD thread, void *pvUser);364 362 static DECLCALLBACK(int) AsyncEventHandler(RTTHREAD thread, void *pvUser); 365 363 -
trunk/src/VBox/Main/src-client/ClientTokenHolder.cpp
r47555 r47561 1 /* $Id$ */2 1 /** @file 3 * VBox Client Session COM Class implementation in VBoxC. 2 * 3 * VirtualBox API client token holder (in the client process) 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 2006-201 2Oracle Corporation7 * Copyright (C) 2006-2013 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 16 16 */ 17 17 18 #include <iprt/asm.h> 19 #include <iprt/assert.h> 20 #include <iprt/log.h> 21 #include <iprt/semaphore.h> 22 #include <iprt/process.h> 23 18 24 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER 19 # include <errno.h> 20 # include <sys/types.h> 21 # include <sys/stat.h> 22 # include <sys/ipc.h> 23 # include <sys/sem.h> 24 #endif 25 25 # include <errno.h> 26 # include <sys/types.h> 27 # include <sys/stat.h> 28 # include <sys/ipc.h> 29 # include <sys/sem.h> 30 #endif 31 32 #include <VBox/com/defs.h> 33 34 #include "ClientTokenHolder.h" 26 35 #include "SessionImpl.h" 27 #include "ConsoleImpl.h" 28 #include "Global.h" 29 30 #include "AutoCaller.h" 31 #include "Logging.h" 32 33 #include <VBox/err.h> 34 #include <iprt/process.h> 35 36 #if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2) 37 /** VM IPC mutex holder thread */ 38 static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser); 39 #endif 40 41 /** 42 * Local macro to check whether the session is open and return an error if not. 43 * @note Don't forget to do |Auto[Reader]Lock alock (this);| before using this 44 * macro. 45 */ 46 #define CHECK_OPEN() \ 47 do { \ 48 if (mState != SessionState_Locked) \ 49 return setError(E_UNEXPECTED, tr ("The session is not locked (session state: %s)"), Global::stringifySessionState(mState)); \ 50 } while (0) 51 52 // constructor / destructor 53 ///////////////////////////////////////////////////////////////////////////// 54 55 HRESULT Session::FinalConstruct() 56 { 57 LogFlowThisFunc(("\n")); 58 59 HRESULT rc = init(); 60 61 BaseFinalConstruct(); 62 63 return rc; 64 } 65 66 void Session::FinalRelease() 67 { 68 LogFlowThisFunc(("\n")); 69 70 uninit(); 71 72 BaseFinalRelease(); 73 } 74 75 // public initializer/uninitializer for internal purposes only 76 ///////////////////////////////////////////////////////////////////////////// 77 78 /** 79 * Initializes the Session object. 80 */ 81 HRESULT Session::init() 82 { 83 /* Enclose the state transition NotReady->InInit->Ready */ 84 AutoInitSpan autoInitSpan(this); 85 AssertReturn(autoInitSpan.isOk(), E_FAIL); 86 87 LogFlowThisFuncEnter(); 88 89 mState = SessionState_Unlocked; 90 mType = SessionType_Null; 91 36 37 38 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 39 /** client token holder thread */ 40 static DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser); 41 #endif 42 43 44 Session::ClientTokenHolder::ClientTokenHolder() 45 { 46 AssertReleaseFailed(); 47 } 48 49 Session::ClientTokenHolder::~ClientTokenHolder() 50 { 51 /* release the client token */ 92 52 #if defined(RT_OS_WINDOWS) 93 mIPCSem = NULL; 94 mIPCThreadSem = NULL; 53 54 if (mSem && mThreadSem) 55 { 56 /* 57 * tell the thread holding the token to release it; 58 * it will close mSem handle 59 */ 60 ::SetEvent(mSem); 61 /* wait for the thread to finish */ 62 ::WaitForSingleObject(mThreadSem, INFINITE); 63 ::CloseHandle(mThreadSem); 64 65 mThreadSem = NULL; 66 mSem = NULL; 67 mThread = NIL_RTTHREAD; 68 } 69 95 70 #elif defined(RT_OS_OS2) 96 mIPCThread = NIL_RTTHREAD; 97 mIPCThreadSem = NIL_RTSEMEVENT; 71 72 if (mThread != NIL_RTTHREAD) 73 { 74 Assert(mSem != NIL_RTSEMEVENT); 75 76 /* tell the thread holding the token to release it */ 77 int vrc = RTSemEventSignal(mSem); 78 AssertRC(vrc == NO_ERROR); 79 80 /* wait for the thread to finish */ 81 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT); 82 Assert(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED); 83 84 mThread = NIL_RTTHREAD; 85 } 86 87 if (mSem != NIL_RTSEMEVENT) 88 { 89 RTSemEventDestroy(mSem); 90 mSem = NIL_RTSEMEVENT; 91 } 92 98 93 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 99 mIPCSem = -1; 94 95 if (mSem >= 0) 96 { 97 ::sembuf sop = { 0, 1, SEM_UNDO }; 98 ::semop(mSem, &sop, 1); 99 100 mSem = -1; 101 } 102 100 103 #else 101 104 # error "Port me!" 102 105 #endif 103 104 /* Confirm a successful initialization when it's the case */ 105 autoInitSpan.setSucceeded(); 106 107 LogFlowThisFuncLeave(); 108 109 return S_OK; 110 } 111 112 /** 113 * Uninitializes the Session object. 114 * 115 * @note Locks this object for writing. 116 */ 117 void Session::uninit() 118 { 119 LogFlowThisFuncEnter(); 120 121 /* Enclose the state transition Ready->InUninit->NotReady */ 122 AutoUninitSpan autoUninitSpan(this); 123 if (autoUninitSpan.uninitDone()) 124 { 125 LogFlowThisFunc(("Already uninitialized.\n")); 126 LogFlowThisFuncLeave(); 127 return; 128 } 129 130 /* close() needs write lock */ 131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 132 133 if (mState != SessionState_Unlocked) 134 { 135 Assert(mState == SessionState_Locked || 136 mState == SessionState_Spawning); 137 138 HRESULT rc = unlockMachine(true /* aFinalRelease */, false /* aFromServer */); 139 AssertComRC(rc); 140 } 141 142 LogFlowThisFuncLeave(); 143 } 144 145 // ISession properties 146 ///////////////////////////////////////////////////////////////////////////// 147 148 STDMETHODIMP Session::COMGETTER(State)(SessionState_T *aState) 149 { 150 CheckComArgOutPointerValid(aState); 151 152 AutoCaller autoCaller(this); 153 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 154 155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 156 157 *aState = mState; 158 159 return S_OK; 160 } 161 162 STDMETHODIMP Session::COMGETTER(Type)(SessionType_T *aType) 163 { 164 CheckComArgOutPointerValid(aType); 165 166 AutoCaller autoCaller(this); 167 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 168 169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 170 171 CHECK_OPEN(); 172 173 *aType = mType; 174 return S_OK; 175 } 176 177 STDMETHODIMP Session::COMGETTER(Machine)(IMachine **aMachine) 178 { 179 CheckComArgOutPointerValid(aMachine); 180 181 AutoCaller autoCaller(this); 182 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 183 184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 185 186 CHECK_OPEN(); 187 188 HRESULT rc; 189 if (mConsole) 190 rc = mConsole->machine().queryInterfaceTo(aMachine); 106 } 107 108 Session::ClientTokenHolder::ClientTokenHolder(const Utf8Str &strTokenId) : 109 mClientTokenId(strTokenId) 110 { 111 mSem = CTHSEMARG; 112 mThread = NIL_RTTHREAD; 113 114 #if defined(RT_OS_WINDOWS) 115 mThreadSem = CTHTHREADSEMARG; 116 117 Bstr bstrTokenId(strTokenId); 118 119 /* 120 * Since there is no guarantee that the constructor and destructor will be 121 * called in the same thread, we need a separate thread to hold the token. 122 */ 123 124 mThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL); 125 AssertMsgReturnVoid(mThreadSem, 126 ("Cannot create an event sem, err=%d", ::GetLastError())); 127 128 void *data[3]; 129 data[0] = (void*)(BSTR)bstrTokenId.raw(); 130 data[1] = (void*)mThreadSem; 131 data[2] = 0; /* will get an output from the thread */ 132 133 /* create a thread to hold the token until signalled to release it */ 134 int vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder"); 135 AssertRCReturnVoid(vrc); 136 137 /* wait until thread init is completed */ 138 DWORD wrc = ::WaitForSingleObject(mThreadSem, INFINITE); 139 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError())); 140 Assert(data[2]); 141 142 if (wrc == WAIT_OBJECT_0 && data[2]) 143 { 144 /* memorize the event sem we should signal in close() */ 145 mSem = (HANDLE)data[2]; 146 } 191 147 else 192 rc = mRemoteMachine.queryInterfaceTo(aMachine); 193 if (FAILED(rc)) 194 { 195 /** @todo VBox 3.3: replace E_FAIL with rc here. */ 196 if (mConsole) 197 setError(E_FAIL, tr("Failed to query the session machine (%Rhrc)"), rc); 198 else if (FAILED_DEAD_INTERFACE(rc)) 199 setError(E_FAIL, tr("Peer process crashed")); 200 else 201 setError(E_FAIL, tr("Failed to query the remote session machine (%Rhrc)"), rc); 202 } 203 204 return rc; 205 } 206 207 STDMETHODIMP Session::COMGETTER(Console)(IConsole **aConsole) 208 { 209 CheckComArgOutPointerValid(aConsole); 210 211 AutoCaller autoCaller(this); 212 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 213 214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 215 216 CHECK_OPEN(); 217 218 HRESULT rc; 219 if (mConsole) 220 rc = mConsole.queryInterfaceTo(aConsole); 221 else 222 rc = mRemoteConsole.queryInterfaceTo(aConsole); 223 224 if (FAILED(rc)) 225 { 226 /** @todo VBox 3.3: replace E_FAIL with rc here. */ 227 if (mConsole) 228 setError(E_FAIL, tr("Failed to query the console (%Rhrc)"), rc); 229 else if (FAILED_DEAD_INTERFACE(rc)) 230 setError(E_FAIL, tr("Peer process crashed")); 231 else 232 setError(E_FAIL, tr("Failed to query the remote console (%Rhrc)"), rc); 233 } 234 235 return rc; 236 } 237 238 // ISession methods 239 ///////////////////////////////////////////////////////////////////////////// 240 241 STDMETHODIMP Session::UnlockMachine() 242 { 243 LogFlowThisFunc(("mState=%d, mType=%d\n", mState, mType)); 244 245 AutoCaller autoCaller(this); 246 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 247 248 /* close() needs write lock */ 249 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 250 251 CHECK_OPEN(); 252 253 return unlockMachine(false /* aFinalRelease */, false /* aFromServer */); 254 } 255 256 // IInternalSessionControl methods 257 ///////////////////////////////////////////////////////////////////////////// 258 259 STDMETHODIMP Session::GetPID(ULONG *aPid) 260 { 261 AssertReturn(aPid, E_POINTER); 262 263 AutoCaller autoCaller(this); 264 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 265 266 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 267 268 *aPid = (ULONG)RTProcSelf(); 269 AssertCompile(sizeof(*aPid) == sizeof(RTPROCESS)); 270 271 return S_OK; 272 } 273 274 STDMETHODIMP Session::GetRemoteConsole(IConsole **aConsole) 275 { 276 LogFlowThisFuncEnter(); 277 AssertReturn(aConsole, E_POINTER); 278 279 AutoCaller autoCaller(this); 280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 281 282 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 283 284 AssertReturn(mState != SessionState_Unlocked, VBOX_E_INVALID_VM_STATE); 285 286 AssertMsgReturn(mType == SessionType_WriteLock && !!mConsole, 287 ("This is not a direct session!\n"), 288 VBOX_E_INVALID_OBJECT_STATE); 289 290 /* return a failure if the session already transitioned to Closing 291 * but the server hasn't processed Machine::OnSessionEnd() yet. */ 292 if (mState != SessionState_Locked) 293 return VBOX_E_INVALID_VM_STATE; 294 295 mConsole.queryInterfaceTo(aConsole); 296 297 LogFlowThisFuncLeave(); 298 299 return S_OK; 300 } 301 302 STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType) 303 { 304 LogFlowThisFuncEnter(); 305 LogFlowThisFunc(("aMachine=%p\n", aMachine)); 306 307 AutoCaller autoCaller(this); 308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 309 310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 311 312 AssertReturn(mState == SessionState_Unlocked, VBOX_E_INVALID_VM_STATE); 313 314 if (!aMachine) 315 { 316 /* 317 * A special case: the server informs us that this session has been 318 * passed to IMachine::launchVMProcess() so this session will become 319 * remote (but not existing) when AssignRemoteMachine() is called. 320 */ 321 322 AssertReturn(mType == SessionType_Null, VBOX_E_INVALID_OBJECT_STATE); 323 mType = SessionType_Remote; 324 mState = SessionState_Spawning; 325 326 LogFlowThisFuncLeave(); 327 return S_OK; 328 } 329 330 HRESULT rc = E_FAIL; 331 332 /* query IInternalMachineControl interface */ 333 mControl = aMachine; 334 AssertReturn(!!mControl, E_FAIL); 335 336 rc = mConsole.createObject(); 337 AssertComRCReturn(rc, rc); 338 339 rc = mConsole->init(aMachine, mControl, aLockType); 340 AssertComRCReturn(rc, rc); 341 342 rc = grabIPCSemaphore(); 148 { 149 ::CloseHandle(mThreadSem); 150 mThreadSem = NULL; 151 } 152 #elif defined(RT_OS_OS2) 153 Bstr bstrTokenId(strTokenId); 343 154 344 155 /* 345 * Reference the VirtualBox object to ensure the server is up346 * until the session is closed156 * Since there is no guarantee that the constructor and destructor will be 157 * called in the same thread, we need a separate thread to hold the token. 347 158 */ 348 if (SUCCEEDED(rc)) 349 rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam()); 350 351 if (SUCCEEDED(rc)) 352 { 353 mType = SessionType_WriteLock; 354 mState = SessionState_Locked; 355 } 356 else 357 { 358 /* some cleanup */ 359 mControl.setNull(); 360 if (!mConsole.isNull()) 361 { 362 mConsole->uninit(); 363 mConsole.setNull(); 364 } 365 } 366 367 LogFlowThisFunc(("rc=%08X\n", rc)); 368 LogFlowThisFuncLeave(); 369 370 return rc; 371 } 372 373 STDMETHODIMP Session::AssignRemoteMachine(IMachine *aMachine, IConsole *aConsole) 374 { 375 LogFlowThisFuncEnter(); 376 LogFlowThisFunc(("aMachine=%p, aConsole=%p\n", aMachine, aConsole)); 377 378 AssertReturn(aMachine && aConsole, E_INVALIDARG); 379 380 AutoCaller autoCaller(this); 381 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 382 383 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 384 385 AssertReturn(mState == SessionState_Unlocked || 386 mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE); 387 388 HRESULT rc = E_FAIL; 389 390 /* query IInternalMachineControl interface */ 391 mControl = aMachine; 392 AssertReturn(!!mControl, E_FAIL); 393 394 /// @todo (dmik) 395 // currently, the remote session returns the same machine and 396 // console objects as the direct session, thus giving the 397 // (remote) client full control over the direct session. For the 398 // console, it is the desired behavior (the ability to control 399 // VM execution is a must for the remote session). What about 400 // the machine object, we may want to prevent the remote client 401 // from modifying machine data. In this case, we must: 402 // 1) assign the Machine object (instead of the SessionMachine 403 // object that is passed to this method) to mRemoteMachine; 404 // 2) remove GetMachine() property from the IConsole interface 405 // because it always returns the SessionMachine object 406 // (alternatively, we can supply a separate IConsole 407 // implementation that will return the Machine object in 408 // response to GetMachine()). 409 410 mRemoteMachine = aMachine; 411 mRemoteConsole = aConsole; 412 413 /* 414 * Reference the VirtualBox object to ensure the server is up 415 * until the session is closed 416 */ 417 rc = aMachine->COMGETTER(Parent)(mVirtualBox.asOutParam()); 418 419 if (SUCCEEDED(rc)) 420 { 421 /* 422 * RemoteSession type can be already set by AssignMachine() when its 423 * argument is NULL (a special case) 424 */ 425 if (mType != SessionType_Remote) 426 mType = SessionType_Shared; 427 else 428 Assert(mState == SessionState_Spawning); 429 430 mState = SessionState_Locked; 431 } 432 else 433 { 434 /* some cleanup */ 435 mControl.setNull(); 436 mRemoteMachine.setNull(); 437 mRemoteConsole.setNull(); 438 } 439 440 LogFlowThisFunc(("rc=%08X\n", rc)); 441 LogFlowThisFuncLeave(); 442 443 return rc; 444 } 445 446 STDMETHODIMP Session::UpdateMachineState(MachineState_T aMachineState) 447 { 448 AutoCaller autoCaller(this); 449 450 if (autoCaller.state() != Ready) 451 { 452 /* 453 * We might have already entered Session::uninit() at this point, so 454 * return silently (not interested in the state change during uninit) 455 */ 456 LogFlowThisFunc(("Already uninitialized.\n")); 457 return S_OK; 458 } 459 460 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 461 462 if (mState == SessionState_Unlocking) 463 { 464 LogFlowThisFunc(("Already being unlocked.\n")); 465 return S_OK; 466 } 467 468 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 469 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 470 471 AssertReturn(!mControl.isNull(), E_FAIL); 472 AssertReturn(!mConsole.isNull(), E_FAIL); 473 474 return mConsole->updateMachineState(aMachineState); 475 } 476 477 STDMETHODIMP Session::Uninitialize() 478 { 479 LogFlowThisFuncEnter(); 480 481 AutoCaller autoCaller(this); 482 483 HRESULT rc = S_OK; 484 485 if (autoCaller.state() == Ready) 486 { 487 /* close() needs write lock */ 488 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 489 490 LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType)); 491 492 if (mState == SessionState_Unlocking) 493 { 494 LogFlowThisFunc(("Already being unlocked.\n")); 495 return S_OK; 496 } 497 498 #ifndef DEBUG_andy /* Don't bug me -- now time to fix this at the moment. */ 499 AssertReturn(mState == SessionState_Locked || 500 mState == SessionState_Spawning, VBOX_E_INVALID_VM_STATE); 501 #endif 502 503 /* close ourselves */ 504 rc = unlockMachine(false /* aFinalRelease */, true /* aFromServer */); 505 } 506 else if (autoCaller.state() == InUninit) 507 { 508 /* 509 * We might have already entered Session::uninit() at this point, 510 * return silently 511 */ 512 LogFlowThisFunc(("Already uninitialized.\n")); 513 } 514 else 515 { 516 LogWarningThisFunc(("UNEXPECTED uninitialization!\n")); 517 rc = autoCaller.rc(); 518 } 519 520 LogFlowThisFunc(("rc=%08X\n", rc)); 521 LogFlowThisFuncLeave(); 522 523 return rc; 524 } 525 526 STDMETHODIMP Session::OnNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter) 527 { 528 LogFlowThisFunc(("\n")); 529 530 AutoCaller autoCaller(this); 531 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 532 533 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 534 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 535 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 536 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 537 538 return mConsole->onNetworkAdapterChange(networkAdapter, changeAdapter); 539 } 540 541 STDMETHODIMP Session::OnSerialPortChange(ISerialPort *serialPort) 542 { 543 LogFlowThisFunc(("\n")); 544 545 AutoCaller autoCaller(this); 546 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 547 548 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 549 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 550 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 551 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 552 553 return mConsole->onSerialPortChange(serialPort); 554 } 555 556 STDMETHODIMP Session::OnParallelPortChange(IParallelPort *parallelPort) 557 { 558 LogFlowThisFunc(("\n")); 559 560 AutoCaller autoCaller(this); 561 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 562 563 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 564 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 565 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 566 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 567 568 return mConsole->onParallelPortChange(parallelPort); 569 } 570 571 STDMETHODIMP Session::OnStorageControllerChange() 572 { 573 LogFlowThisFunc(("\n")); 574 575 AutoCaller autoCaller(this); 576 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 577 578 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 579 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 580 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 581 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 582 583 return mConsole->onStorageControllerChange(); 584 } 585 586 STDMETHODIMP Session::OnMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForce) 587 { 588 LogFlowThisFunc(("\n")); 589 590 AutoCaller autoCaller(this); 591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 592 593 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 594 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 595 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 596 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 597 598 return mConsole->onMediumChange(aMediumAttachment, aForce); 599 } 600 601 STDMETHODIMP Session::OnCPUChange(ULONG aCPU, BOOL aRemove) 602 { 603 LogFlowThisFunc(("\n")); 604 605 AutoCaller autoCaller(this); 606 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 607 608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 609 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 610 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 611 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 612 613 return mConsole->onCPUChange(aCPU, aRemove); 614 } 615 616 STDMETHODIMP Session::OnCPUExecutionCapChange(ULONG aExecutionCap) 617 { 618 LogFlowThisFunc(("\n")); 619 620 AutoCaller autoCaller(this); 621 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 622 623 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 624 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 625 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 626 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 627 628 return mConsole->onCPUExecutionCapChange(aExecutionCap); 629 } 630 631 STDMETHODIMP Session::OnVRDEServerChange(BOOL aRestart) 632 { 633 LogFlowThisFunc(("\n")); 634 635 AutoCaller autoCaller(this); 636 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 637 638 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 639 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 640 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 641 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 642 643 return mConsole->onVRDEServerChange(aRestart); 644 } 645 646 STDMETHODIMP Session::OnVideoCaptureChange() 647 { 648 LogFlowThisFunc(("\n")); 649 650 AutoCaller autoCaller(this); 651 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 652 653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 654 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 655 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 656 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 657 658 return mConsole->onVideoCaptureChange(); 659 } 660 661 STDMETHODIMP Session::OnUSBControllerChange() 662 { 663 LogFlowThisFunc(("\n")); 664 665 AutoCaller autoCaller(this); 666 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 667 668 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 669 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 670 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 671 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 672 673 return mConsole->onUSBControllerChange(); 674 } 675 676 STDMETHODIMP Session::OnSharedFolderChange(BOOL aGlobal) 677 { 678 LogFlowThisFunc(("\n")); 679 680 AutoCaller autoCaller(this); 681 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 682 683 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 684 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 685 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 686 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 687 688 return mConsole->onSharedFolderChange(aGlobal); 689 } 690 691 STDMETHODIMP Session::OnClipboardModeChange(ClipboardMode_T aClipboardMode) 692 { 693 LogFlowThisFunc(("\n")); 694 695 AutoCaller autoCaller(this); 696 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 697 698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 699 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 700 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 701 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 702 703 return mConsole->onClipboardModeChange(aClipboardMode); 704 } 705 706 STDMETHODIMP Session::OnDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode) 707 { 708 LogFlowThisFunc(("\n")); 709 710 AutoCaller autoCaller(this); 711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 712 713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 714 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 715 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 716 717 return mConsole->onDragAndDropModeChange(aDragAndDropMode); 718 } 719 720 STDMETHODIMP Session::OnUSBDeviceAttach(IUSBDevice *aDevice, 721 IVirtualBoxErrorInfo *aError, 722 ULONG aMaskedIfs) 723 { 724 LogFlowThisFunc(("\n")); 725 726 AutoCaller autoCaller(this); 727 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 728 729 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 730 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 731 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 732 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 733 734 return mConsole->onUSBDeviceAttach(aDevice, aError, aMaskedIfs); 735 } 736 737 STDMETHODIMP Session::OnUSBDeviceDetach(IN_BSTR aId, 738 IVirtualBoxErrorInfo *aError) 739 { 740 LogFlowThisFunc(("\n")); 741 742 AutoCaller autoCaller(this); 743 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 744 745 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 746 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 747 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 748 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 749 750 return mConsole->onUSBDeviceDetach(aId, aError); 751 } 752 753 STDMETHODIMP Session::OnShowWindow(BOOL aCheck, BOOL *aCanShow, LONG64 *aWinId) 754 { 755 AutoCaller autoCaller(this); 756 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 757 758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 759 760 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 761 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 762 763 if (mState != SessionState_Locked) 764 { 765 /* the call from Machine issued when the session is open can arrive 766 * after the session starts closing or gets closed. Note that when 767 * aCheck is false, we return E_FAIL to indicate that aWinId we return 768 * is not valid */ 769 *aCanShow = FALSE; 770 *aWinId = 0; 771 return aCheck ? S_OK : E_FAIL; 772 } 773 774 return mConsole->onShowWindow(aCheck, aCanShow, aWinId); 775 } 776 777 STDMETHODIMP Session::OnBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) 778 { 779 LogFlowThisFunc(("\n")); 780 781 AutoCaller autoCaller(this); 782 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 783 784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 785 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 786 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 787 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 788 789 return mConsole->onBandwidthGroupChange(aBandwidthGroup); 790 } 791 792 STDMETHODIMP Session::OnStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove, BOOL aSilent) 793 { 794 LogFlowThisFunc(("\n")); 795 796 AutoCaller autoCaller(this); 797 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 798 799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 800 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 801 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 802 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 803 804 return mConsole->onStorageDeviceChange(aMediumAttachment, aRemove, aSilent); 805 } 806 807 STDMETHODIMP Session::AccessGuestProperty(IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags, 808 BOOL aIsSetter, BSTR *aRetValue, LONG64 *aRetTimestamp, BSTR *aRetFlags) 809 { 810 #ifdef VBOX_WITH_GUEST_PROPS 811 AutoCaller autoCaller(this); 812 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 813 814 if (mState != SessionState_Locked) 815 return setError(VBOX_E_INVALID_VM_STATE, 816 tr("Machine is not locked by session (session state: %s)."), 817 Global::stringifySessionState(mState)); 818 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 819 CheckComArgStrNotEmptyOrNull(aName); 820 if (!aIsSetter && !VALID_PTR(aRetValue)) 821 return E_POINTER; 822 if (!aIsSetter && !VALID_PTR(aRetTimestamp)) 823 return E_POINTER; 824 if (!aIsSetter && !VALID_PTR(aRetFlags)) 825 return E_POINTER; 826 /* aValue can be NULL for a setter call if the property is to be deleted. */ 827 if (aIsSetter && (aValue != NULL) && !VALID_PTR(aValue)) 828 return setError(E_INVALIDARG, tr("Invalid value pointer")); 829 /* aFlags can be null if it is to be left as is */ 830 if (aIsSetter && (aFlags != NULL) && !VALID_PTR(aFlags)) 831 return setError(E_INVALIDARG, tr("Invalid flags pointer")); 832 833 /* If this session is not in a VM process fend off the call. The caller 834 * handles this correctly, by doing the operation in VBoxSVC. */ 835 if (!mConsole) 836 return E_ACCESSDENIED; 837 838 if (!aIsSetter) 839 return mConsole->getGuestProperty(aName, aRetValue, aRetTimestamp, aRetFlags); 840 else 841 return mConsole->setGuestProperty(aName, aValue, aFlags); 842 #else /* VBOX_WITH_GUEST_PROPS not defined */ 843 ReturnComNotImplemented(); 844 #endif /* VBOX_WITH_GUEST_PROPS not defined */ 845 } 846 847 STDMETHODIMP Session::EnumerateGuestProperties(IN_BSTR aPatterns, 848 ComSafeArrayOut(BSTR, aNames), 849 ComSafeArrayOut(BSTR, aValues), 850 ComSafeArrayOut(LONG64, aTimestamps), 851 ComSafeArrayOut(BSTR, aFlags)) 852 { 853 #ifdef VBOX_WITH_GUEST_PROPS 854 AutoCaller autoCaller(this); 855 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 856 857 if (mState != SessionState_Locked) 858 return setError(VBOX_E_INVALID_VM_STATE, 859 tr("Machine is not locked by session (session state: %s)."), 860 Global::stringifySessionState(mState)); 861 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 862 if (!VALID_PTR(aPatterns) && (aPatterns != NULL)) 863 return E_POINTER; 864 if (ComSafeArrayOutIsNull(aNames)) 865 return E_POINTER; 866 if (ComSafeArrayOutIsNull(aValues)) 867 return E_POINTER; 868 if (ComSafeArrayOutIsNull(aTimestamps)) 869 return E_POINTER; 870 if (ComSafeArrayOutIsNull(aFlags)) 871 return E_POINTER; 872 873 /* If this session is not in a VM process fend off the call. The caller 874 * handles this correctly, by doing the operation in VBoxSVC. */ 875 if (!mConsole) 876 return E_ACCESSDENIED; 877 878 return mConsole->enumerateGuestProperties(aPatterns, 879 ComSafeArrayOutArg(aNames), 880 ComSafeArrayOutArg(aValues), 881 ComSafeArrayOutArg(aTimestamps), 882 ComSafeArrayOutArg(aFlags)); 883 #else /* VBOX_WITH_GUEST_PROPS not defined */ 884 ReturnComNotImplemented(); 885 #endif /* VBOX_WITH_GUEST_PROPS not defined */ 886 } 887 888 STDMETHODIMP Session::OnlineMergeMedium(IMediumAttachment *aMediumAttachment, 889 ULONG aSourceIdx, ULONG aTargetIdx, 890 IMedium *aSource, IMedium *aTarget, 891 BOOL aMergeForward, 892 IMedium *aParentForTarget, 893 ComSafeArrayIn(IMedium *, aChildrenToReparent), 894 IProgress *aProgress) 895 { 896 AutoCaller autoCaller(this); 897 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 898 899 if (mState != SessionState_Locked) 900 return setError(VBOX_E_INVALID_VM_STATE, 901 tr("Machine is not locked by session (session state: %s)."), 902 Global::stringifySessionState(mState)); 903 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 904 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 905 CheckComArgNotNull(aMediumAttachment); 906 CheckComArgSafeArrayNotNull(aChildrenToReparent); 907 908 return mConsole->onlineMergeMedium(aMediumAttachment, aSourceIdx, 909 aTargetIdx, aSource, aTarget, 910 aMergeForward, aParentForTarget, 911 ComSafeArrayInArg(aChildrenToReparent), 912 aProgress); 913 } 914 915 STDMETHODIMP Session::EnableVMMStatistics(BOOL aEnable) 916 { 917 AutoCaller autoCaller(this); 918 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 919 920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 921 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 922 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 923 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 924 925 mConsole->enableVMMStatistics(aEnable); 926 927 return S_OK; 928 } 929 930 STDMETHODIMP Session::PauseWithReason(Reason_T aReason) 931 { 932 AutoCaller autoCaller(this); 933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 934 935 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 936 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 937 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 938 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 939 940 return mConsole->pause(aReason); 941 } 942 943 STDMETHODIMP Session::ResumeWithReason(Reason_T aReason) 944 { 945 AutoCaller autoCaller(this); 946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 947 948 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 949 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 950 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 951 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 952 953 return mConsole->resume(aReason); 954 } 955 956 STDMETHODIMP Session::SaveStateWithReason(Reason_T aReason, IProgress **aProgress) 957 { 958 AutoCaller autoCaller(this); 959 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 960 961 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 962 AssertReturn(mState == SessionState_Locked, VBOX_E_INVALID_VM_STATE); 963 AssertReturn(mType == SessionType_WriteLock, VBOX_E_INVALID_OBJECT_STATE); 964 AssertReturn(mConsole, VBOX_E_INVALID_OBJECT_STATE); 965 966 return mConsole->saveState(aReason, aProgress); 967 } 968 969 // private methods 970 /////////////////////////////////////////////////////////////////////////////// 971 972 /** 973 * Unlocks a machine associated with the current session. 974 * 975 * @param aFinalRelease called as a result of FinalRelease() 976 * @param aFromServer called as a result of Uninitialize() 977 * 978 * @note To be called only from #uninit(), #UnlockMachine() or #Uninitialize(). 979 * @note Locks this object for writing. 980 */ 981 HRESULT Session::unlockMachine(bool aFinalRelease, bool aFromServer) 982 { 983 LogFlowThisFuncEnter(); 984 LogFlowThisFunc(("aFinalRelease=%d, isFromServer=%d\n", 985 aFinalRelease, aFromServer)); 986 987 AutoCaller autoCaller(this); 988 AssertComRCReturnRC(autoCaller.rc()); 989 990 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 991 992 LogFlowThisFunc(("mState=%s, mType=%d\n", Global::stringifySessionState(mState), mType)); 993 994 if (mState != SessionState_Locked) 995 { 996 Assert(mState == SessionState_Spawning); 997 998 /* The session object is going to be uninitialized before it has been 999 * assigned a direct console of the machine the client requested to open 1000 * a remote session to using IVirtualBox:: openRemoteSession(). It is OK 1001 * only if this close request comes from the server (for example, it 1002 * detected that the VM process it started terminated before opening a 1003 * direct session). Otherwise, it means that the client is too fast and 1004 * trying to close the session before waiting for the progress object it 1005 * got from IVirtualBox:: openRemoteSession() to complete, so assert. */ 1006 Assert(aFromServer); 1007 1008 mState = SessionState_Unlocked; 1009 mType = SessionType_Null; 1010 #if defined(RT_OS_WINDOWS) 1011 Assert(!mIPCSem && !mIPCThreadSem); 1012 #elif defined(RT_OS_OS2) 1013 Assert(mIPCThread == NIL_RTTHREAD && 1014 mIPCThreadSem == NIL_RTSEMEVENT); 159 160 int vrc = RTSemEventCreate(&mSem); 161 AssertRCReturnVoid(vrc); 162 163 void *data[3]; 164 data[0] = (void*)bstrTokenId.raw(); 165 data[1] = (void*)mSem; 166 data[2] = (void*)false; /* will get the thread result here */ 167 168 /* create a thread to hold the token until signalled to release it */ 169 vrc = RTThreadCreate(&mThread, ClientTokenHolderThread, (void *) data, 170 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder"); 171 AssertRCReturnVoid(vrc); 172 /* wait until thread init is completed */ 173 vrc = RTThreadUserWait(mThread, RT_INDEFINITE_WAIT); 174 AssertReturnVoid(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED); 175 176 /* the thread must succeed */ 177 AssertReturnVoid((bool)data[2]); 178 1015 179 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 1016 Assert(mIPCSem == -1); 180 181 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 182 key_t key = RTStrToUInt32(strTokenId.c_str()); 183 AssertMsgReturnVoid(key != 0, 184 ("Key value of 0 is not valid for client token")); 185 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 186 char *pszSemName = NULL; 187 RTStrUtf8ToCurrentCP(&pszSemName, strTokenId); 188 key_t key = ::ftok(pszSemName, 'V'); 189 RTStrFree(pszSemName); 190 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 191 int s = ::semget(key, 0, 0); 192 AssertMsgReturnVoid(s >= 0, 193 ("Cannot open semaphore, errno=%d", errno)); 194 195 /* grab the semaphore */ 196 ::sembuf sop = { 0, -1, SEM_UNDO }; 197 int rv = ::semop(s, &sop, 1); 198 AssertMsgReturnVoid(rv == 0, 199 ("Cannot grab semaphore, errno=%d", errno)); 200 mSem = s; 201 1017 202 #else 1018 203 # error "Port me!" 1019 204 #endif 1020 LogFlowThisFuncLeave(); 1021 return S_OK; 1022 } 1023 1024 /* go to the closing state */ 1025 mState = SessionState_Unlocking; 1026 1027 if (mType == SessionType_WriteLock) 1028 { 1029 if (!mConsole.isNull()) 1030 { 1031 mConsole->uninit(); 1032 mConsole.setNull(); 1033 } 1034 } 1035 else 1036 { 1037 mRemoteMachine.setNull(); 1038 mRemoteConsole.setNull(); 1039 } 1040 1041 ComPtr<IProgress> progress; 1042 1043 if (!aFinalRelease && !aFromServer) 1044 { 1045 /* 1046 * We trigger OnSessionEnd() only when the session closes itself using 1047 * Close(). Note that if isFinalRelease = TRUE here, this means that 1048 * the client process has already initialized the termination procedure 1049 * without issuing Close() and the IPC channel is no more operational -- 1050 * so we cannot call the server's method (it will definitely fail). The 1051 * server will instead simply detect the abnormal client death (since 1052 * OnSessionEnd() is not called) and reset the machine state to Aborted. 1053 */ 1054 1055 /* 1056 * while waiting for OnSessionEnd() to complete one of our methods 1057 * can be called by the server (for example, Uninitialize(), if the 1058 * direct session has initiated a closure just a bit before us) so 1059 * we need to release the lock to avoid deadlocks. The state is already 1060 * SessionState_Closing here, so it's safe. 1061 */ 1062 alock.release(); 1063 1064 LogFlowThisFunc(("Calling mControl->OnSessionEnd()...\n")); 1065 HRESULT rc = mControl->OnSessionEnd(this, progress.asOutParam()); 1066 LogFlowThisFunc(("mControl->OnSessionEnd()=%08X\n", rc)); 1067 1068 alock.acquire(); 1069 1070 /* 1071 * If we get E_UNEXPECTED this means that the direct session has already 1072 * been closed, we're just too late with our notification and nothing more 1073 * 1074 * bird: Seems E_ACCESSDENIED is what gets returned these days; see 1075 * VirtualBoxBase::addCaller. 1076 */ 1077 if (mType != SessionType_WriteLock && (rc == E_UNEXPECTED || rc == E_ACCESSDENIED)) 1078 rc = S_OK; 1079 1080 #ifndef DEBUG_bird /* I don't want clients crashing on me just because VBoxSVC went belly up. */ 1081 AssertComRC(rc); 1082 #endif 1083 } 1084 1085 mControl.setNull(); 1086 1087 if (mType == SessionType_WriteLock) 1088 { 1089 releaseIPCSemaphore(); 1090 if (!aFinalRelease && !aFromServer) 1091 { 1092 /* 1093 * Wait for the server to grab the semaphore and destroy the session 1094 * machine (allowing us to open a new session with the same machine 1095 * once this method returns) 1096 */ 1097 Assert(!!progress); 1098 if (progress) 1099 progress->WaitForCompletion(-1); 1100 } 1101 } 1102 1103 mState = SessionState_Unlocked; 1104 mType = SessionType_Null; 1105 1106 /* release the VirtualBox instance as the very last step */ 1107 mVirtualBox.setNull(); 1108 1109 LogFlowThisFuncLeave(); 1110 return S_OK; 1111 } 1112 1113 /** @note To be called only from #AssignMachine() */ 1114 HRESULT Session::grabIPCSemaphore() 1115 { 1116 HRESULT rc = E_FAIL; 1117 1118 /* open the IPC semaphore based on the sessionId and try to grab it */ 1119 Bstr ipcId; 1120 rc = mControl->GetIPCId(ipcId.asOutParam()); 1121 AssertComRCReturnRC(rc); 1122 1123 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw())); 1124 1125 #if defined(RT_OS_WINDOWS) 1126 1127 /* 1128 * Since Session is an MTA object, this method can be executed on 1129 * any thread, and this thread will not necessarily match the thread on 1130 * which close() will be called later. Therefore, we need a separate 1131 * thread to hold the IPC mutex and then release it in close(). 1132 */ 1133 1134 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL); 1135 AssertMsgReturn(mIPCThreadSem, 1136 ("Cannot create an event sem, err=%d", ::GetLastError()), 1137 E_FAIL); 1138 1139 void *data[3]; 1140 data[0] = (void*)(BSTR)ipcId.raw(); 1141 data[1] = (void*)mIPCThreadSem; 1142 data[2] = 0; /* will get an output from the thread */ 1143 1144 /* create a thread to hold the IPC mutex until signalled to release it */ 1145 RTTHREAD tid; 1146 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder"); 1147 AssertRCReturn(vrc, E_FAIL); 1148 1149 /* wait until thread init is completed */ 1150 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE); 1151 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError())); 1152 Assert(data[2]); 1153 1154 if (wrc == WAIT_OBJECT_0 && data[2]) 1155 { 1156 /* memorize the event sem we should signal in close() */ 1157 mIPCSem = (HANDLE)data[2]; 1158 rc = S_OK; 1159 } 1160 else 1161 { 1162 ::CloseHandle(mIPCThreadSem); 1163 mIPCThreadSem = NULL; 1164 rc = E_FAIL; 1165 } 1166 1167 #elif defined(RT_OS_OS2) 1168 1169 /* We use XPCOM where any message (including close()) can arrive on any 1170 * worker thread (which will not necessarily match this thread that opens 1171 * the mutex). Therefore, we need a separate thread to hold the IPC mutex 1172 * and then release it in close(). */ 1173 1174 int vrc = RTSemEventCreate(&mIPCThreadSem); 1175 AssertRCReturn(vrc, E_FAIL); 1176 1177 void *data[3]; 1178 data[0] = (void*)ipcId.raw(); 1179 data[1] = (void*)mIPCThreadSem; 1180 data[2] = (void*)false; /* will get the thread result here */ 1181 1182 /* create a thread to hold the IPC mutex until signalled to release it */ 1183 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data, 1184 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder"); 1185 AssertRCReturn(vrc, E_FAIL); 1186 1187 /* wait until thread init is completed */ 1188 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT); 1189 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL); 1190 1191 /* the thread must succeed */ 1192 AssertReturn((bool)data[2], E_FAIL); 1193 1194 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 1195 1196 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 1197 Utf8Str ipcKey = ipcId; 1198 key_t key = RTStrToUInt32(ipcKey.c_str()); 1199 AssertMsgReturn (key != 0, 1200 ("Key value of 0 is not valid for IPC semaphore"), 1201 E_FAIL); 1202 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 1203 Utf8Str semName = ipcId; 1204 char *pszSemName = NULL; 1205 RTStrUtf8ToCurrentCP (&pszSemName, semName); 1206 key_t key = ::ftok (pszSemName, 'V'); 1207 RTStrFree (pszSemName); 1208 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 1209 1210 mIPCSem = ::semget (key, 0, 0); 1211 AssertMsgReturn (mIPCSem >= 0, 1212 ("Cannot open IPC semaphore, errno=%d", errno), 1213 E_FAIL); 1214 1215 /* grab the semaphore */ 1216 ::sembuf sop = { 0, -1, SEM_UNDO }; 1217 int rv = ::semop (mIPCSem, &sop, 1); 1218 AssertMsgReturn (rv == 0, 1219 ("Cannot grab IPC semaphore, errno=%d", errno), 1220 E_FAIL); 1221 1222 #else 1223 # error "Port me!" 1224 #endif 1225 1226 return rc; 1227 } 1228 1229 /** @note To be called only from #close() */ 1230 void Session::releaseIPCSemaphore() 1231 { 1232 /* release the IPC semaphore */ 1233 #if defined(RT_OS_WINDOWS) 1234 1235 if (mIPCSem && mIPCThreadSem) 1236 { 1237 /* 1238 * tell the thread holding the IPC mutex to release it; 1239 * it will close mIPCSem handle 1240 */ 1241 ::SetEvent (mIPCSem); 1242 /* wait for the thread to finish */ 1243 ::WaitForSingleObject (mIPCThreadSem, INFINITE); 1244 ::CloseHandle (mIPCThreadSem); 1245 1246 mIPCThreadSem = NULL; 1247 mIPCSem = NULL; 1248 } 1249 1250 #elif defined(RT_OS_OS2) 1251 1252 if (mIPCThread != NIL_RTTHREAD) 1253 { 1254 Assert (mIPCThreadSem != NIL_RTSEMEVENT); 1255 1256 /* tell the thread holding the IPC mutex to release it */ 1257 int vrc = RTSemEventSignal (mIPCThreadSem); 1258 AssertRC(vrc == NO_ERROR); 1259 1260 /* wait for the thread to finish */ 1261 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT); 1262 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED); 1263 1264 mIPCThread = NIL_RTTHREAD; 1265 } 1266 1267 if (mIPCThreadSem != NIL_RTSEMEVENT) 1268 { 1269 RTSemEventDestroy (mIPCThreadSem); 1270 mIPCThreadSem = NIL_RTSEMEVENT; 1271 } 1272 1273 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 1274 1275 if (mIPCSem >= 0) 1276 { 1277 ::sembuf sop = { 0, 1, SEM_UNDO }; 1278 ::semop (mIPCSem, &sop, 1); 1279 1280 mIPCSem = -1; 1281 } 1282 1283 #else 1284 # error "Port me!" 1285 #endif 1286 } 1287 1288 #if defined(RT_OS_WINDOWS) 1289 /** VM IPC mutex holder thread */ 1290 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser) 205 } 206 207 bool Session::ClientTokenHolder::isReady() 208 { 209 return mSem != CTHSEMARG; 210 } 211 212 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 213 /** client token holder thread */ 214 DECLCALLBACK(int) ClientTokenHolderThread(RTTHREAD Thread, void *pvUser) 1291 215 { 1292 216 LogFlowFuncEnter(); 1293 217 1294 Assert (pvUser); 1295 void **data = (void **) pvUser; 1296 218 Assert(pvUser); 219 220 void **data = (void **)pvUser; 221 222 # if defined(RT_OS_WINDOWS) 1297 223 BSTR sessionId = (BSTR)data[0]; 1298 224 HANDLE initDoneSem = (HANDLE)data[1]; 1299 225 1300 HANDLE ipcMutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId);1301 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));1302 1303 if ( ipcMutex)1304 { 1305 /* grab the mutex*/1306 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);1307 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));226 HANDLE mutex = ::OpenMutex(MUTEX_ALL_ACCESS, FALSE, sessionId); 227 AssertMsg(mutex, ("cannot open token, err=%d\n", ::GetLastError())); 228 229 if (mutex) 230 { 231 /* grab the token */ 232 DWORD wrc = ::WaitForSingleObject(mutex, 0); 233 AssertMsg(wrc == WAIT_OBJECT_0, ("cannot grab token, err=%d\n", wrc)); 1308 234 if (wrc == WAIT_OBJECT_0) 1309 235 { 1310 HANDLE finishSem = ::CreateEvent 1311 AssertMsg 236 HANDLE finishSem = ::CreateEvent(NULL, FALSE, FALSE, NULL); 237 AssertMsg(finishSem, ("cannot create event sem, err=%d\n", ::GetLastError())); 1312 238 if (finishSem) 1313 239 { 1314 240 data[2] = (void*)finishSem; 1315 241 /* signal we're done with init */ 1316 ::SetEvent 1317 /* wait until we're signaled to release the IPC mutex*/1318 ::WaitForSingleObject 1319 /* release the IPC mutex*/1320 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));1321 BOOL success = ::ReleaseMutex (ipcMutex);1322 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));1323 ::CloseHandle (ipcMutex);1324 ::CloseHandle 242 ::SetEvent(initDoneSem); 243 /* wait until we're signaled to release the token */ 244 ::WaitForSingleObject(finishSem, INFINITE); 245 /* release the token */ 246 LogFlow(("ClientTokenHolderThread(): releasing token...\n")); 247 BOOL success = ::ReleaseMutex(mutex); 248 AssertMsg(success, ("cannot release token, err=%d\n", ::GetLastError())); 249 ::CloseHandle(mutex); 250 ::CloseHandle(finishSem); 1325 251 } 1326 252 } … … 1328 254 1329 255 /* signal we're done */ 1330 ::SetEvent (initDoneSem); 1331 1332 LogFlowFuncLeave(); 1333 1334 return 0; 1335 } 1336 #endif 1337 1338 #if defined(RT_OS_OS2) 1339 /** VM IPC mutex holder thread */ 1340 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser) 1341 { 1342 LogFlowFuncEnter(); 1343 1344 Assert (pvUser); 1345 void **data = (void **) pvUser; 1346 1347 Utf8Str ipcId = (BSTR)data[0]; 256 ::SetEvent(initDoneSem); 257 # elif defined(RT_OS_OS2) 258 Utf8Str sessionId = (BSTR)data[0]; 1348 259 RTSEMEVENT finishSem = (RTSEMEVENT)data[1]; 1349 260 1350 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));1351 1352 HMTX ipcMutex = NULLHANDLE;1353 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);1354 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));261 LogFlowFunc(("sessionId='%s', finishSem=%p\n", sessionId.raw(), finishSem)); 262 263 HMTX mutex = NULLHANDLE; 264 APIRET arc = ::DosOpenMutexSem((PSZ)sessionId.raw(), &mutex); 265 AssertMsg(arc == NO_ERROR, ("cannot open token, arc=%ld\n", arc)); 1355 266 1356 267 if (arc == NO_ERROR) 1357 268 { 1358 /* grab the mutex*/1359 LogFlowFunc (("grabbing IPC mutex...\n"));1360 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);1361 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));269 /* grab the token */ 270 LogFlowFunc(("grabbing token...\n")); 271 arc = ::DosRequestMutexSem(mutex, SEM_IMMEDIATE_RETURN); 272 AssertMsg(arc == NO_ERROR, ("cannot grab token, arc=%ld\n", arc)); 1362 273 if (arc == NO_ERROR) 1363 274 { … … 1365 276 data[2] = (void*)true; 1366 277 /* signal we're done */ 1367 int vrc = RTThreadUserSignal 278 int vrc = RTThreadUserSignal(Thread); 1368 279 AssertRC(vrc); 1369 280 1370 /* wait until we're signaled to release the IPC mutex*/1371 LogFlowFunc 1372 vrc = RTSemEventWait 1373 Assert 1374 1375 /* release the IPC mutex*/1376 LogFlowFunc (("releasing IPC mutex...\n"));1377 arc = ::DosReleaseMutexSem (ipcMutex);1378 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));281 /* wait until we're signaled to release the token */ 282 LogFlowFunc(("waiting for termination signal..\n")); 283 vrc = RTSemEventWait(finishSem, RT_INDEFINITE_WAIT); 284 Assert(arc == ERROR_INTERRUPT || ERROR_TIMEOUT); 285 286 /* release the token */ 287 LogFlowFunc(("releasing token...\n")); 288 arc = ::DosReleaseMutexSem(mutex); 289 AssertMsg(arc == NO_ERROR, ("cannot release token, arc=%ld\n", arc)); 1379 290 } 1380 1381 ::DosCloseMutexSem (ipcMutex); 291 ::DosCloseMutexSem(mutex); 1382 292 } 1383 293 … … 1385 295 data[1] = (void*)false; 1386 296 /* signal we're done */ 1387 int vrc = RTThreadUserSignal 297 int vrc = RTThreadUserSignal(Thread); 1388 298 AssertRC(vrc); 299 # else 300 # error "Port me!" 301 # endif 1389 302 1390 303 LogFlowFuncLeave(); … … 1393 306 } 1394 307 #endif 308 1395 309 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/src-client/SessionImpl.cpp
r46775 r47561 5 5 6 6 /* 7 * Copyright (C) 2006-201 2Oracle Corporation7 * Copyright (C) 2006-2013 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 16 16 */ 17 17 18 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER19 # include <errno.h>20 # include <sys/types.h>21 # include <sys/stat.h>22 # include <sys/ipc.h>23 # include <sys/sem.h>24 #endif25 26 18 #include "SessionImpl.h" 27 19 #include "ConsoleImpl.h" 28 20 #include "Global.h" 21 #include "ClientTokenHolder.h" 29 22 30 23 #include "AutoCaller.h" … … 33 26 #include <VBox/err.h> 34 27 #include <iprt/process.h> 35 36 #if defined(RT_OS_WINDOWS) || defined (RT_OS_OS2)37 /** VM IPC mutex holder thread */38 static DECLCALLBACK(int) IPCMutexHolderThread(RTTHREAD Thread, void *pvUser);39 #endif40 28 41 29 /** … … 90 78 mType = SessionType_Null; 91 79 92 #if defined(RT_OS_WINDOWS) 93 mIPCSem = NULL; 94 mIPCThreadSem = NULL; 95 #elif defined(RT_OS_OS2) 96 mIPCThread = NIL_RTTHREAD; 97 mIPCThreadSem = NIL_RTSEMEVENT; 98 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 99 mIPCSem = -1; 100 #else 101 # error "Port me!" 102 #endif 80 mClientTokenHolder = NULL; 103 81 104 82 /* Confirm a successful initialization when it's the case */ … … 300 278 } 301 279 302 STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType) 280 STDMETHODIMP Session::AssignMachine(IMachine *aMachine, LockType_T aLockType, 281 IN_BSTR aTokenId) 303 282 { 304 283 LogFlowThisFuncEnter(); … … 340 319 AssertComRCReturn(rc, rc); 341 320 342 rc = grabIPCSemaphore(); 321 Utf8Str strTokenId(aTokenId); 322 Assert(!strTokenId.isEmpty()); 323 /* create the machine client token */ 324 try 325 { 326 mClientTokenHolder = new ClientTokenHolder(strTokenId); 327 if (!mClientTokenHolder->isReady()) 328 { 329 delete mClientTokenHolder; 330 mClientTokenHolder = NULL; 331 rc = E_FAIL; 332 } 333 } 334 catch (std::bad_alloc &) 335 { 336 rc = E_OUTOFMEMORY; 337 } 343 338 344 339 /* … … 1008 1003 mState = SessionState_Unlocked; 1009 1004 mType = SessionType_Null; 1010 #if defined(RT_OS_WINDOWS) 1011 Assert(!mIPCSem && !mIPCThreadSem); 1012 #elif defined(RT_OS_OS2) 1013 Assert(mIPCThread == NIL_RTTHREAD && 1014 mIPCThreadSem == NIL_RTSEMEVENT); 1015 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 1016 Assert(mIPCSem == -1); 1017 #else 1018 # error "Port me!" 1019 #endif 1005 1006 Assert(!mClientTokenHolder); 1007 1020 1008 LogFlowThisFuncLeave(); 1021 1009 return S_OK; … … 1087 1075 if (mType == SessionType_WriteLock) 1088 1076 { 1089 releaseIPCSemaphore(); 1077 if (mClientTokenHolder) 1078 { 1079 delete mClientTokenHolder; 1080 mClientTokenHolder = NULL; 1081 } 1082 1090 1083 if (!aFinalRelease && !aFromServer) 1091 1084 { … … 1111 1104 } 1112 1105 1113 /** @note To be called only from #AssignMachine() */1114 HRESULT Session::grabIPCSemaphore()1115 {1116 HRESULT rc = E_FAIL;1117 1118 /* open the IPC semaphore based on the sessionId and try to grab it */1119 Bstr ipcId;1120 rc = mControl->GetIPCId(ipcId.asOutParam());1121 AssertComRCReturnRC(rc);1122 1123 LogFlowThisFunc(("ipcId='%ls'\n", ipcId.raw()));1124 1125 #if defined(RT_OS_WINDOWS)1126 1127 /*1128 * Since Session is an MTA object, this method can be executed on1129 * any thread, and this thread will not necessarily match the thread on1130 * which close() will be called later. Therefore, we need a separate1131 * thread to hold the IPC mutex and then release it in close().1132 */1133 1134 mIPCThreadSem = ::CreateEvent(NULL, FALSE, FALSE, NULL);1135 AssertMsgReturn(mIPCThreadSem,1136 ("Cannot create an event sem, err=%d", ::GetLastError()),1137 E_FAIL);1138 1139 void *data[3];1140 data[0] = (void*)(BSTR)ipcId.raw();1141 data[1] = (void*)mIPCThreadSem;1142 data[2] = 0; /* will get an output from the thread */1143 1144 /* create a thread to hold the IPC mutex until signalled to release it */1145 RTTHREAD tid;1146 int vrc = RTThreadCreate(&tid, IPCMutexHolderThread, (void*)data, 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");1147 AssertRCReturn(vrc, E_FAIL);1148 1149 /* wait until thread init is completed */1150 DWORD wrc = ::WaitForSingleObject(mIPCThreadSem, INFINITE);1151 AssertMsg(wrc == WAIT_OBJECT_0, ("Wait failed, err=%d\n", ::GetLastError()));1152 Assert(data[2]);1153 1154 if (wrc == WAIT_OBJECT_0 && data[2])1155 {1156 /* memorize the event sem we should signal in close() */1157 mIPCSem = (HANDLE)data[2];1158 rc = S_OK;1159 }1160 else1161 {1162 ::CloseHandle(mIPCThreadSem);1163 mIPCThreadSem = NULL;1164 rc = E_FAIL;1165 }1166 1167 #elif defined(RT_OS_OS2)1168 1169 /* We use XPCOM where any message (including close()) can arrive on any1170 * worker thread (which will not necessarily match this thread that opens1171 * the mutex). Therefore, we need a separate thread to hold the IPC mutex1172 * and then release it in close(). */1173 1174 int vrc = RTSemEventCreate(&mIPCThreadSem);1175 AssertRCReturn(vrc, E_FAIL);1176 1177 void *data[3];1178 data[0] = (void*)ipcId.raw();1179 data[1] = (void*)mIPCThreadSem;1180 data[2] = (void*)false; /* will get the thread result here */1181 1182 /* create a thread to hold the IPC mutex until signalled to release it */1183 vrc = RTThreadCreate(&mIPCThread, IPCMutexHolderThread, (void *) data,1184 0, RTTHREADTYPE_MAIN_WORKER, 0, "IPCHolder");1185 AssertRCReturn(vrc, E_FAIL);1186 1187 /* wait until thread init is completed */1188 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);1189 AssertReturn(RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED, E_FAIL);1190 1191 /* the thread must succeed */1192 AssertReturn((bool)data[2], E_FAIL);1193 1194 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)1195 1196 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN1197 Utf8Str ipcKey = ipcId;1198 key_t key = RTStrToUInt32(ipcKey.c_str());1199 AssertMsgReturn (key != 0,1200 ("Key value of 0 is not valid for IPC semaphore"),1201 E_FAIL);1202 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */1203 Utf8Str semName = ipcId;1204 char *pszSemName = NULL;1205 RTStrUtf8ToCurrentCP (&pszSemName, semName);1206 key_t key = ::ftok (pszSemName, 'V');1207 RTStrFree (pszSemName);1208 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */1209 1210 mIPCSem = ::semget (key, 0, 0);1211 AssertMsgReturn (mIPCSem >= 0,1212 ("Cannot open IPC semaphore, errno=%d", errno),1213 E_FAIL);1214 1215 /* grab the semaphore */1216 ::sembuf sop = { 0, -1, SEM_UNDO };1217 int rv = ::semop (mIPCSem, &sop, 1);1218 AssertMsgReturn (rv == 0,1219 ("Cannot grab IPC semaphore, errno=%d", errno),1220 E_FAIL);1221 1222 #else1223 # error "Port me!"1224 #endif1225 1226 return rc;1227 }1228 1229 /** @note To be called only from #close() */1230 void Session::releaseIPCSemaphore()1231 {1232 /* release the IPC semaphore */1233 #if defined(RT_OS_WINDOWS)1234 1235 if (mIPCSem && mIPCThreadSem)1236 {1237 /*1238 * tell the thread holding the IPC mutex to release it;1239 * it will close mIPCSem handle1240 */1241 ::SetEvent (mIPCSem);1242 /* wait for the thread to finish */1243 ::WaitForSingleObject (mIPCThreadSem, INFINITE);1244 ::CloseHandle (mIPCThreadSem);1245 1246 mIPCThreadSem = NULL;1247 mIPCSem = NULL;1248 }1249 1250 #elif defined(RT_OS_OS2)1251 1252 if (mIPCThread != NIL_RTTHREAD)1253 {1254 Assert (mIPCThreadSem != NIL_RTSEMEVENT);1255 1256 /* tell the thread holding the IPC mutex to release it */1257 int vrc = RTSemEventSignal (mIPCThreadSem);1258 AssertRC(vrc == NO_ERROR);1259 1260 /* wait for the thread to finish */1261 vrc = RTThreadUserWait (mIPCThread, RT_INDEFINITE_WAIT);1262 Assert (RT_SUCCESS(vrc) || vrc == VERR_INTERRUPTED);1263 1264 mIPCThread = NIL_RTTHREAD;1265 }1266 1267 if (mIPCThreadSem != NIL_RTSEMEVENT)1268 {1269 RTSemEventDestroy (mIPCThreadSem);1270 mIPCThreadSem = NIL_RTSEMEVENT;1271 }1272 1273 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)1274 1275 if (mIPCSem >= 0)1276 {1277 ::sembuf sop = { 0, 1, SEM_UNDO };1278 ::semop (mIPCSem, &sop, 1);1279 1280 mIPCSem = -1;1281 }1282 1283 #else1284 # error "Port me!"1285 #endif1286 }1287 1288 #if defined(RT_OS_WINDOWS)1289 /** VM IPC mutex holder thread */1290 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)1291 {1292 LogFlowFuncEnter();1293 1294 Assert (pvUser);1295 void **data = (void **) pvUser;1296 1297 BSTR sessionId = (BSTR)data[0];1298 HANDLE initDoneSem = (HANDLE)data[1];1299 1300 HANDLE ipcMutex = ::OpenMutex (MUTEX_ALL_ACCESS, FALSE, sessionId);1301 AssertMsg (ipcMutex, ("cannot open IPC mutex, err=%d\n", ::GetLastError()));1302 1303 if (ipcMutex)1304 {1305 /* grab the mutex */1306 DWORD wrc = ::WaitForSingleObject (ipcMutex, 0);1307 AssertMsg (wrc == WAIT_OBJECT_0, ("cannot grab IPC mutex, err=%d\n", wrc));1308 if (wrc == WAIT_OBJECT_0)1309 {1310 HANDLE finishSem = ::CreateEvent (NULL, FALSE, FALSE, NULL);1311 AssertMsg (finishSem, ("cannot create event sem, err=%d\n", ::GetLastError()));1312 if (finishSem)1313 {1314 data[2] = (void*)finishSem;1315 /* signal we're done with init */1316 ::SetEvent (initDoneSem);1317 /* wait until we're signaled to release the IPC mutex */1318 ::WaitForSingleObject (finishSem, INFINITE);1319 /* release the IPC mutex */1320 LogFlow (("IPCMutexHolderThread(): releasing IPC mutex...\n"));1321 BOOL success = ::ReleaseMutex (ipcMutex);1322 AssertMsg (success, ("cannot release mutex, err=%d\n", ::GetLastError()));1323 ::CloseHandle (ipcMutex);1324 ::CloseHandle (finishSem);1325 }1326 }1327 }1328 1329 /* signal we're done */1330 ::SetEvent (initDoneSem);1331 1332 LogFlowFuncLeave();1333 1334 return 0;1335 }1336 #endif1337 1338 #if defined(RT_OS_OS2)1339 /** VM IPC mutex holder thread */1340 DECLCALLBACK(int) IPCMutexHolderThread (RTTHREAD Thread, void *pvUser)1341 {1342 LogFlowFuncEnter();1343 1344 Assert (pvUser);1345 void **data = (void **) pvUser;1346 1347 Utf8Str ipcId = (BSTR)data[0];1348 RTSEMEVENT finishSem = (RTSEMEVENT)data[1];1349 1350 LogFlowFunc (("ipcId='%s', finishSem=%p\n", ipcId.raw(), finishSem));1351 1352 HMTX ipcMutex = NULLHANDLE;1353 APIRET arc = ::DosOpenMutexSem ((PSZ) ipcId.raw(), &ipcMutex);1354 AssertMsg (arc == NO_ERROR, ("cannot open IPC mutex, arc=%ld\n", arc));1355 1356 if (arc == NO_ERROR)1357 {1358 /* grab the mutex */1359 LogFlowFunc (("grabbing IPC mutex...\n"));1360 arc = ::DosRequestMutexSem (ipcMutex, SEM_IMMEDIATE_RETURN);1361 AssertMsg (arc == NO_ERROR, ("cannot grab IPC mutex, arc=%ld\n", arc));1362 if (arc == NO_ERROR)1363 {1364 /* store the answer */1365 data[2] = (void*)true;1366 /* signal we're done */1367 int vrc = RTThreadUserSignal (Thread);1368 AssertRC(vrc);1369 1370 /* wait until we're signaled to release the IPC mutex */1371 LogFlowFunc (("waiting for termination signal..\n"));1372 vrc = RTSemEventWait (finishSem, RT_INDEFINITE_WAIT);1373 Assert (arc == ERROR_INTERRUPT || ERROR_TIMEOUT);1374 1375 /* release the IPC mutex */1376 LogFlowFunc (("releasing IPC mutex...\n"));1377 arc = ::DosReleaseMutexSem (ipcMutex);1378 AssertMsg (arc == NO_ERROR, ("cannot release mutex, arc=%ld\n", arc));1379 }1380 1381 ::DosCloseMutexSem (ipcMutex);1382 }1383 1384 /* store the answer */1385 data[1] = (void*)false;1386 /* signal we're done */1387 int vrc = RTThreadUserSignal (Thread);1388 AssertRC(vrc);1389 1390 LogFlowFuncLeave();1391 1392 return 0;1393 }1394 #endif1395 1106 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/src-server/ClientToken.cpp
r47555 r47561 1 /* $Id$ */2 1 /** @file 3 * Implementation of IMachine in VBoxSVC. 2 * 3 * VirtualBox API client crash token handling 4 4 */ 5 5 … … 16 16 */ 17 17 18 /* Make sure all the stdint.h macros are included - must come first! */ 19 #ifndef __STDC_LIMIT_MACROS 20 # define __STDC_LIMIT_MACROS 21 #endif 22 #ifndef __STDC_CONSTANT_MACROS 23 # define __STDC_CONSTANT_MACROS 24 #endif 18 #include <iprt/asm.h> 19 #include <iprt/assert.h> 20 #include <iprt/log.h> 21 #include <iprt/semaphore.h> 22 #include <iprt/process.h> 25 23 26 24 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER … … 32 30 #endif 33 31 34 #include "Logging.h" 35 #include "VirtualBoxImpl.h" 32 #include <VBox/com/defs.h> 33 34 #include <vector> 35 36 #include "VirtualBoxBase.h" 37 #include "AutoCaller.h" 38 #include "ClientToken.h" 36 39 #include "MachineImpl.h" 37 #include "ProgressImpl.h" 38 #include "ProgressProxyImpl.h" 39 #include "MediumAttachmentImpl.h" 40 #include "MediumImpl.h" 41 #include "MediumLock.h" 42 #include "USBControllerImpl.h" 43 #include "USBDeviceFiltersImpl.h" 44 #include "HostImpl.h" 45 #include "SharedFolderImpl.h" 46 #include "GuestOSTypeImpl.h" 47 #include "VirtualBoxErrorInfoImpl.h" 48 #include "GuestImpl.h" 49 #include "StorageControllerImpl.h" 50 #include "DisplayImpl.h" 51 #include "DisplayUtils.h" 52 #include "MachineImplCloneVM.h" 53 #include "AutostartDb.h" 54 #include "SystemPropertiesImpl.h" 55 56 // generated header 57 #include "VBoxEvents.h" 58 59 #ifdef VBOX_WITH_USB 60 # include "USBProxyService.h" 61 #endif 62 63 #include "AutoCaller.h" 64 #include "HashedPw.h" 65 #include "Performance.h" 66 67 #include <iprt/asm.h> 68 #include <iprt/path.h> 69 #include <iprt/dir.h> 70 #include <iprt/env.h> 71 #include <iprt/lockvalidator.h> 72 #include <iprt/process.h> 73 #include <iprt/cpp/utils.h> 74 #include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */ 75 #include <iprt/sha.h> 76 #include <iprt/string.h> 77 #include <iprt/base64.h> 78 79 #include <VBox/com/array.h> 80 #include <VBox/com/list.h> 81 82 #include <VBox/err.h> 83 #include <VBox/param.h> 84 #include <VBox/settings.h> 85 #include <VBox/vmm/ssm.h> 86 87 #ifdef VBOX_WITH_GUEST_PROPS 88 # include <VBox/HostServices/GuestPropertySvc.h> 89 # include <VBox/com/array.h> 90 #endif 91 92 #include "VBox/com/MultiResult.h" 93 94 #include <algorithm> 95 96 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 97 # define HOSTSUFF_EXE ".exe" 98 #else /* !RT_OS_WINDOWS */ 99 # define HOSTSUFF_EXE "" 100 #endif /* !RT_OS_WINDOWS */ 101 102 // defines / prototypes 103 ///////////////////////////////////////////////////////////////////////////// 104 105 ///////////////////////////////////////////////////////////////////////////// 106 // Machine::Data structure 107 ///////////////////////////////////////////////////////////////////////////// 108 109 Machine::Data::Data() 110 { 111 mRegistered = FALSE; 112 pMachineConfigFile = NULL; 113 /* Contains hints on what has changed when the user is using the VM (config 114 * changes, running the VM, ...). This is used to decide if a config needs 115 * to be written to disk. */ 116 flModifications = 0; 117 /* VM modification usually also trigger setting the current state to 118 * "Modified". Although this is not always the case. An e.g. is the VM 119 * initialization phase or when snapshot related data is changed. The 120 * actually behavior is controlled by the following flag. */ 121 m_fAllowStateModification = false; 122 mAccessible = FALSE; 123 /* mUuid is initialized in Machine::init() */ 124 125 mMachineState = MachineState_PoweredOff; 126 RTTimeNow(&mLastStateChange); 127 128 mMachineStateDeps = 0; 129 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 130 mMachineStateChangePending = 0; 131 132 mCurrentStateModified = TRUE; 133 mGuestPropertiesModified = FALSE; 134 135 mSession.mPID = NIL_RTPROCESS; 136 mSession.mState = SessionState_Unlocked; 137 } 138 139 Machine::Data::~Data() 140 { 141 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI) 142 { 143 RTSemEventMultiDestroy(mMachineStateDepsSem); 144 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 145 } 146 if (pMachineConfigFile) 147 { 148 delete pMachineConfigFile; 149 pMachineConfigFile = NULL; 150 } 151 } 152 153 ///////////////////////////////////////////////////////////////////////////// 154 // Machine::HWData structure 155 ///////////////////////////////////////////////////////////////////////////// 156 157 Machine::HWData::HWData() 158 { 159 /* default values for a newly created machine */ 160 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */ 161 mMemorySize = 128; 162 mCPUCount = 1; 163 mCPUHotPlugEnabled = false; 164 mMemoryBalloonSize = 0; 165 mPageFusionEnabled = false; 166 mGraphicsControllerType = GraphicsControllerType_VBoxVGA; 167 mVRAMSize = 8; 168 mAccelerate3DEnabled = false; 169 mAccelerate2DVideoEnabled = false; 170 mMonitorCount = 1; 171 mVideoCaptureWidth = 1024; 172 mVideoCaptureHeight = 768; 173 mVideoCaptureRate = 512; 174 mVideoCaptureFPS = 25; 175 mVideoCaptureEnabled = false; 176 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++) 177 maVideoCaptureScreens[i] = true; 178 179 mHWVirtExEnabled = true; 180 mHWVirtExNestedPagingEnabled = true; 181 #if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX) 182 mHWVirtExLargePagesEnabled = true; 183 #else 184 /* Not supported on 32 bits hosts. */ 185 mHWVirtExLargePagesEnabled = false; 186 #endif 187 mHWVirtExVPIDEnabled = true; 188 mHWVirtExUXEnabled = true; 189 mHWVirtExForceEnabled = false; 190 #if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) 191 mHWVirtExExclusive = false; 192 #else 193 mHWVirtExExclusive = true; 194 #endif 195 #if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) 196 mPAEEnabled = true; 197 #else 198 mPAEEnabled = false; 199 #endif 200 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled; 201 mSyntheticCpu = false; 202 mHPETEnabled = false; 203 204 /* default boot order: floppy - DVD - HDD */ 205 mBootOrder[0] = DeviceType_Floppy; 206 mBootOrder[1] = DeviceType_DVD; 207 mBootOrder[2] = DeviceType_HardDisk; 208 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i) 209 mBootOrder[i] = DeviceType_Null; 210 211 mClipboardMode = ClipboardMode_Disabled; 212 mDragAndDropMode = DragAndDropMode_Disabled; 213 mGuestPropertyNotificationPatterns = ""; 214 215 mFirmwareType = FirmwareType_BIOS; 216 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard; 217 mPointingHIDType = PointingHIDType_PS2Mouse; 218 mChipsetType = ChipsetType_PIIX3; 219 mEmulatedUSBWebcamEnabled = FALSE; 220 mEmulatedUSBCardReaderEnabled = FALSE; 221 222 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++) 223 mCPUAttached[i] = false; 224 225 mIOCacheEnabled = true; 226 mIOCacheSize = 5; /* 5MB */ 227 228 /* Maximum CPU execution cap by default. */ 229 mCpuExecutionCap = 100; 230 } 231 232 Machine::HWData::~HWData() 233 { 234 } 235 236 ///////////////////////////////////////////////////////////////////////////// 237 // Machine::HDData structure 238 ///////////////////////////////////////////////////////////////////////////// 239 240 Machine::MediaData::MediaData() 241 { 242 } 243 244 Machine::MediaData::~MediaData() 245 { 246 } 247 248 ///////////////////////////////////////////////////////////////////////////// 249 // Machine class 250 ///////////////////////////////////////////////////////////////////////////// 251 252 // constructor / destructor 253 ///////////////////////////////////////////////////////////////////////////// 254 255 Machine::Machine() : 256 #ifdef VBOX_WITH_RESOURCE_USAGE_API 257 mCollectorGuest(NULL), 258 #endif 259 mPeer(NULL), 260 mParent(NULL), 261 mSerialPorts(), 262 mParallelPorts(), 263 uRegistryNeedsSaving(0) 264 {} 265 266 Machine::~Machine() 267 {} 268 269 HRESULT Machine::FinalConstruct() 270 { 271 LogFlowThisFunc(("\n")); 272 return BaseFinalConstruct(); 273 } 274 275 void Machine::FinalRelease() 276 { 277 LogFlowThisFunc(("\n")); 278 uninit(); 279 BaseFinalRelease(); 280 } 281 282 /** 283 * Initializes a new machine instance; this init() variant creates a new, empty machine. 284 * This gets called from VirtualBox::CreateMachine(). 285 * 286 * @param aParent Associated parent object 287 * @param strConfigFile Local file system path to the VM settings file (can 288 * be relative to the VirtualBox config directory). 289 * @param strName name for the machine 290 * @param llGroups list of groups for the machine 291 * @param aOsType OS Type of this machine or NULL. 292 * @param aId UUID for the new machine. 293 * @param fForceOverwrite Whether to overwrite an existing machine settings file. 294 * 295 * @return Success indicator. if not S_OK, the machine object is invalid 296 */ 297 HRESULT Machine::init(VirtualBox *aParent, 298 const Utf8Str &strConfigFile, 299 const Utf8Str &strName, 300 const StringsList &llGroups, 301 GuestOSType *aOsType, 302 const Guid &aId, 303 bool fForceOverwrite, 304 bool fDirectoryIncludesUUID) 305 { 306 LogFlowThisFuncEnter(); 307 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str())); 308 309 /* Enclose the state transition NotReady->InInit->Ready */ 310 AutoInitSpan autoInitSpan(this); 311 AssertReturn(autoInitSpan.isOk(), E_FAIL); 312 313 HRESULT rc = initImpl(aParent, strConfigFile); 314 if (FAILED(rc)) return rc; 315 316 rc = tryCreateMachineConfigFile(fForceOverwrite); 317 if (FAILED(rc)) return rc; 318 319 if (SUCCEEDED(rc)) 320 { 321 // create an empty machine config 322 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 323 324 rc = initDataAndChildObjects(); 325 } 326 327 if (SUCCEEDED(rc)) 328 { 329 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 330 mData->mAccessible = TRUE; 331 332 unconst(mData->mUuid) = aId; 333 334 mUserData->s.strName = strName; 335 336 mUserData->s.llGroups = llGroups; 337 338 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID; 339 // the "name sync" flag determines whether the machine directory gets renamed along 340 // with the machine file; say so if the settings file name is the same as the 341 // settings file parent directory (machine directory) 342 mUserData->s.fNameSync = isInOwnDir(); 343 344 // initialize the default snapshots folder 345 rc = COMSETTER(SnapshotFolder)(NULL); 346 AssertComRC(rc); 347 348 if (aOsType) 349 { 350 /* Store OS type */ 351 mUserData->s.strOsType = aOsType->id(); 352 353 /* Apply BIOS defaults */ 354 mBIOSSettings->applyDefaults(aOsType); 355 356 /* Apply network adapters defaults */ 357 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot) 358 mNetworkAdapters[slot]->applyDefaults(aOsType); 359 360 /* Apply serial port defaults */ 361 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot) 362 mSerialPorts[slot]->applyDefaults(aOsType); 363 364 /* Let the OS type select 64-bit ness. */ 365 mHWData->mLongMode = aOsType->is64Bit() 366 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled; 367 } 368 369 /* At this point the changing of the current state modification 370 * flag is allowed. */ 371 allowStateModification(); 372 373 /* commit all changes made during the initialization */ 374 commit(); 375 } 376 377 /* Confirm a successful initialization when it's the case */ 378 if (SUCCEEDED(rc)) 379 { 380 if (mData->mAccessible) 381 autoInitSpan.setSucceeded(); 382 else 383 autoInitSpan.setLimited(); 384 } 385 386 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n", 387 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 388 mData->mRegistered, 389 mData->mAccessible, 390 rc)); 391 392 LogFlowThisFuncLeave(); 393 394 return rc; 395 } 396 397 /** 398 * Initializes a new instance with data from machine XML (formerly Init_Registered). 399 * Gets called in two modes: 400 * 401 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the 402 * UUID is specified and we mark the machine as "registered"; 403 * 404 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL 405 * and the machine remains unregistered until RegisterMachine() is called. 406 * 407 * @param aParent Associated parent object 408 * @param aConfigFile Local file system path to the VM settings file (can 409 * be relative to the VirtualBox config directory). 410 * @param aId UUID of the machine or NULL (see above). 411 * 412 * @return Success indicator. if not S_OK, the machine object is invalid 413 */ 414 HRESULT Machine::initFromSettings(VirtualBox *aParent, 415 const Utf8Str &strConfigFile, 416 const Guid *aId) 417 { 418 LogFlowThisFuncEnter(); 419 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str())); 420 421 /* Enclose the state transition NotReady->InInit->Ready */ 422 AutoInitSpan autoInitSpan(this); 423 AssertReturn(autoInitSpan.isOk(), E_FAIL); 424 425 HRESULT rc = initImpl(aParent, strConfigFile); 426 if (FAILED(rc)) return rc; 427 428 if (aId) 429 { 430 // loading a registered VM: 431 unconst(mData->mUuid) = *aId; 432 mData->mRegistered = TRUE; 433 // now load the settings from XML: 434 rc = registeredInit(); 435 // this calls initDataAndChildObjects() and loadSettings() 436 } 437 else 438 { 439 // opening an unregistered VM (VirtualBox::OpenMachine()): 440 rc = initDataAndChildObjects(); 441 442 if (SUCCEEDED(rc)) 443 { 444 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 445 mData->mAccessible = TRUE; 446 447 try 448 { 449 // load and parse machine XML; this will throw on XML or logic errors 450 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 451 452 // reject VM UUID duplicates, they can happen if someone 453 // tries to register an already known VM config again 454 if (aParent->findMachine(mData->pMachineConfigFile->uuid, 455 true /* fPermitInaccessible */, 456 false /* aDoSetError */, 457 NULL) != VBOX_E_OBJECT_NOT_FOUND) 458 { 459 throw setError(E_FAIL, 460 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"), 461 mData->m_strConfigFile.c_str()); 462 } 463 464 // use UUID from machine config 465 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid; 466 467 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile, 468 NULL /* puuidRegistry */); 469 if (FAILED(rc)) throw rc; 470 471 /* At this point the changing of the current state modification 472 * flag is allowed. */ 473 allowStateModification(); 474 475 commit(); 476 } 477 catch (HRESULT err) 478 { 479 /* we assume that error info is set by the thrower */ 480 rc = err; 481 } 482 catch (...) 483 { 484 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 485 } 486 } 487 } 488 489 /* Confirm a successful initialization when it's the case */ 490 if (SUCCEEDED(rc)) 491 { 492 if (mData->mAccessible) 493 autoInitSpan.setSucceeded(); 494 else 495 { 496 autoInitSpan.setLimited(); 497 498 // uninit media from this machine's media registry, or else 499 // reloading the settings will fail 500 mParent->unregisterMachineMedia(getId()); 501 } 502 } 503 504 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 505 "rc=%08X\n", 506 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 507 mData->mRegistered, mData->mAccessible, rc)); 508 509 LogFlowThisFuncLeave(); 510 511 return rc; 512 } 513 514 /** 515 * Initializes a new instance from a machine config that is already in memory 516 * (import OVF case). Since we are importing, the UUID in the machine 517 * config is ignored and we always generate a fresh one. 518 * 519 * @param strName Name for the new machine; this overrides what is specified in config and is used 520 * for the settings file as well. 521 * @param config Machine configuration loaded and parsed from XML. 522 * 523 * @return Success indicator. if not S_OK, the machine object is invalid 524 */ 525 HRESULT Machine::init(VirtualBox *aParent, 526 const Utf8Str &strName, 527 const settings::MachineConfigFile &config) 528 { 529 LogFlowThisFuncEnter(); 530 531 /* Enclose the state transition NotReady->InInit->Ready */ 532 AutoInitSpan autoInitSpan(this); 533 AssertReturn(autoInitSpan.isOk(), E_FAIL); 534 535 Utf8Str strConfigFile; 536 aParent->getDefaultMachineFolder(strConfigFile); 537 strConfigFile.append(RTPATH_DELIMITER); 538 strConfigFile.append(strName); 539 strConfigFile.append(RTPATH_DELIMITER); 540 strConfigFile.append(strName); 541 strConfigFile.append(".vbox"); 542 543 HRESULT rc = initImpl(aParent, strConfigFile); 544 if (FAILED(rc)) return rc; 545 546 rc = tryCreateMachineConfigFile(false /* fForceOverwrite */); 547 if (FAILED(rc)) return rc; 548 549 rc = initDataAndChildObjects(); 550 551 if (SUCCEEDED(rc)) 552 { 553 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 554 mData->mAccessible = TRUE; 555 556 // create empty machine config for instance data 557 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 558 559 // generate fresh UUID, ignore machine config 560 unconst(mData->mUuid).create(); 561 562 rc = loadMachineDataFromSettings(config, 563 &mData->mUuid); // puuidRegistry: initialize media with this registry ID 564 565 // override VM name as well, it may be different 566 mUserData->s.strName = strName; 567 568 if (SUCCEEDED(rc)) 569 { 570 /* At this point the changing of the current state modification 571 * flag is allowed. */ 572 allowStateModification(); 573 574 /* commit all changes made during the initialization */ 575 commit(); 576 } 577 } 578 579 /* Confirm a successful initialization when it's the case */ 580 if (SUCCEEDED(rc)) 581 { 582 if (mData->mAccessible) 583 autoInitSpan.setSucceeded(); 584 else 585 { 586 autoInitSpan.setLimited(); 587 588 // uninit media from this machine's media registry, or else 589 // reloading the settings will fail 590 mParent->unregisterMachineMedia(getId()); 591 } 592 } 593 594 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 595 "rc=%08X\n", 596 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 597 mData->mRegistered, mData->mAccessible, rc)); 598 599 LogFlowThisFuncLeave(); 600 601 return rc; 602 } 603 604 /** 605 * Shared code between the various init() implementations. 606 * @param aParent 607 * @return 608 */ 609 HRESULT Machine::initImpl(VirtualBox *aParent, 610 const Utf8Str &strConfigFile) 611 { 612 LogFlowThisFuncEnter(); 613 614 AssertReturn(aParent, E_INVALIDARG); 615 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG); 616 617 HRESULT rc = S_OK; 618 619 /* share the parent weakly */ 620 unconst(mParent) = aParent; 621 622 /* allocate the essential machine data structure (the rest will be 623 * allocated later by initDataAndChildObjects() */ 624 mData.allocate(); 625 626 /* memorize the config file name (as provided) */ 627 mData->m_strConfigFile = strConfigFile; 628 629 /* get the full file name */ 630 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull); 631 if (RT_FAILURE(vrc1)) 632 return setError(VBOX_E_FILE_ERROR, 633 tr("Invalid machine settings file name '%s' (%Rrc)"), 634 strConfigFile.c_str(), 635 vrc1); 636 637 LogFlowThisFuncLeave(); 638 639 return rc; 640 } 641 642 /** 643 * Tries to create a machine settings file in the path stored in the machine 644 * instance data. Used when a new machine is created to fail gracefully if 645 * the settings file could not be written (e.g. because machine dir is read-only). 646 * @return 647 */ 648 HRESULT Machine::tryCreateMachineConfigFile(bool fForceOverwrite) 649 { 650 HRESULT rc = S_OK; 651 652 // when we create a new machine, we must be able to create the settings file 653 RTFILE f = NIL_RTFILE; 654 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); 655 if ( RT_SUCCESS(vrc) 656 || vrc == VERR_SHARING_VIOLATION 657 ) 658 { 659 if (RT_SUCCESS(vrc)) 660 RTFileClose(f); 661 if (!fForceOverwrite) 662 rc = setError(VBOX_E_FILE_ERROR, 663 tr("Machine settings file '%s' already exists"), 664 mData->m_strConfigFileFull.c_str()); 665 else 666 { 667 /* try to delete the config file, as otherwise the creation 668 * of a new settings file will fail. */ 669 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str()); 670 if (RT_FAILURE(vrc2)) 671 rc = setError(VBOX_E_FILE_ERROR, 672 tr("Could not delete the existing settings file '%s' (%Rrc)"), 673 mData->m_strConfigFileFull.c_str(), vrc2); 674 } 675 } 676 else if ( vrc != VERR_FILE_NOT_FOUND 677 && vrc != VERR_PATH_NOT_FOUND 678 ) 679 rc = setError(VBOX_E_FILE_ERROR, 680 tr("Invalid machine settings file name '%s' (%Rrc)"), 681 mData->m_strConfigFileFull.c_str(), 682 vrc); 683 return rc; 684 } 685 686 /** 687 * Initializes the registered machine by loading the settings file. 688 * This method is separated from #init() in order to make it possible to 689 * retry the operation after VirtualBox startup instead of refusing to 690 * startup the whole VirtualBox server in case if the settings file of some 691 * registered VM is invalid or inaccessible. 692 * 693 * @note Must be always called from this object's write lock 694 * (unless called from #init() that doesn't need any locking). 695 * @note Locks the mUSBController method for writing. 696 * @note Subclasses must not call this method. 697 */ 698 HRESULT Machine::registeredInit() 699 { 700 AssertReturn(!isSessionMachine(), E_FAIL); 701 AssertReturn(!isSnapshotMachine(), E_FAIL); 702 AssertReturn(mData->mUuid.isValid(), E_FAIL); 703 AssertReturn(!mData->mAccessible, E_FAIL); 704 705 HRESULT rc = initDataAndChildObjects(); 706 707 if (SUCCEEDED(rc)) 708 { 709 /* Temporarily reset the registered flag in order to let setters 710 * potentially called from loadSettings() succeed (isMutable() used in 711 * all setters will return FALSE for a Machine instance if mRegistered 712 * is TRUE). */ 713 mData->mRegistered = FALSE; 714 715 try 716 { 717 // load and parse machine XML; this will throw on XML or logic errors 718 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 719 720 if (mData->mUuid != mData->pMachineConfigFile->uuid) 721 throw setError(E_FAIL, 722 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"), 723 mData->pMachineConfigFile->uuid.raw(), 724 mData->m_strConfigFileFull.c_str(), 725 mData->mUuid.toString().c_str(), 726 mParent->settingsFilePath().c_str()); 727 728 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile, 729 NULL /* const Guid *puuidRegistry */); 730 if (FAILED(rc)) throw rc; 731 } 732 catch (HRESULT err) 733 { 734 /* we assume that error info is set by the thrower */ 735 rc = err; 736 } 737 catch (...) 738 { 739 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 740 } 741 742 /* Restore the registered flag (even on failure) */ 743 mData->mRegistered = TRUE; 744 } 745 746 if (SUCCEEDED(rc)) 747 { 748 /* Set mAccessible to TRUE only if we successfully locked and loaded 749 * the settings file */ 750 mData->mAccessible = TRUE; 751 752 /* commit all changes made during loading the settings file */ 753 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive 754 /// @todo r=klaus for some reason the settings loading logic backs up 755 // the settings, and therefore a commit is needed. Should probably be changed. 756 } 757 else 758 { 759 /* If the machine is registered, then, instead of returning a 760 * failure, we mark it as inaccessible and set the result to 761 * success to give it a try later */ 762 763 /* fetch the current error info */ 764 mData->mAccessError = com::ErrorInfo(); 765 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n", 766 mData->mUuid.raw(), 767 mData->mAccessError.getText().raw())); 768 769 /* rollback all changes */ 770 rollback(false /* aNotify */); 771 772 // uninit media from this machine's media registry, or else 773 // reloading the settings will fail 774 mParent->unregisterMachineMedia(getId()); 775 776 /* uninitialize the common part to make sure all data is reset to 777 * default (null) values */ 778 uninitDataAndChildObjects(); 779 780 rc = S_OK; 781 } 782 783 return rc; 784 } 785 786 /** 787 * Uninitializes the instance. 788 * Called either from FinalRelease() or by the parent when it gets destroyed. 789 * 790 * @note The caller of this method must make sure that this object 791 * a) doesn't have active callers on the current thread and b) is not locked 792 * by the current thread; otherwise uninit() will hang either a) due to 793 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to 794 * a dead-lock caused by this thread waiting for all callers on the other 795 * threads are done but preventing them from doing so by holding a lock. 796 */ 797 void Machine::uninit() 798 { 799 LogFlowThisFuncEnter(); 800 801 Assert(!isWriteLockOnCurrentThread()); 802 803 Assert(!uRegistryNeedsSaving); 804 if (uRegistryNeedsSaving) 805 { 806 AutoCaller autoCaller(this); 807 if (SUCCEEDED(autoCaller.rc())) 808 { 809 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 810 saveSettings(NULL, Machine::SaveS_Force); 811 } 812 } 813 814 /* Enclose the state transition Ready->InUninit->NotReady */ 815 AutoUninitSpan autoUninitSpan(this); 816 if (autoUninitSpan.uninitDone()) 817 return; 818 819 Assert(!isSnapshotMachine()); 820 Assert(!isSessionMachine()); 821 Assert(!!mData); 822 823 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed())); 824 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered)); 825 826 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 827 828 if (!mData->mSession.mMachine.isNull()) 829 { 830 /* Theoretically, this can only happen if the VirtualBox server has been 831 * terminated while there were clients running that owned open direct 832 * sessions. Since in this case we are definitely called by 833 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit() 834 * won't happen on the client watcher thread (because it does 835 * VirtualBox::addCaller() for the duration of the 836 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit() 837 * cannot happen until the VirtualBox caller is released). This is 838 * important, because SessionMachine::uninit() cannot correctly operate 839 * after we return from this method (it expects the Machine instance is 840 * still valid). We'll call it ourselves below. 841 */ 842 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n", 843 (SessionMachine*)mData->mSession.mMachine)); 844 845 if (Global::IsOnlineOrTransient(mData->mMachineState)) 846 { 847 LogWarningThisFunc(("Setting state to Aborted!\n")); 848 /* set machine state using SessionMachine reimplementation */ 849 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted); 850 } 851 852 /* 853 * Uninitialize SessionMachine using public uninit() to indicate 854 * an unexpected uninitialization. 855 */ 856 mData->mSession.mMachine->uninit(); 857 /* SessionMachine::uninit() must set mSession.mMachine to null */ 858 Assert(mData->mSession.mMachine.isNull()); 859 } 860 861 // uninit media from this machine's media registry, if they're still there 862 Guid uuidMachine(getId()); 863 864 /* the lock is no more necessary (SessionMachine is uninitialized) */ 865 alock.release(); 866 867 /* XXX This will fail with 868 * "cannot be closed because it is still attached to 1 virtual machines" 869 * because at this point we did not call uninitDataAndChildObjects() yet 870 * and therefore also removeBackReference() for all these mediums was not called! */ 871 872 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init 873 mParent->unregisterMachineMedia(uuidMachine); 874 875 // has machine been modified? 876 if (mData->flModifications) 877 { 878 LogWarningThisFunc(("Discarding unsaved settings changes!\n")); 879 rollback(false /* aNotify */); 880 } 881 882 if (mData->mAccessible) 883 uninitDataAndChildObjects(); 884 885 /* free the essential data structure last */ 886 mData.free(); 887 888 LogFlowThisFuncLeave(); 889 } 890 891 // IMachine properties 892 ///////////////////////////////////////////////////////////////////////////// 893 894 STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent) 895 { 896 CheckComArgOutPointerValid(aParent); 897 898 AutoLimitedCaller autoCaller(this); 899 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 900 901 /* mParent is constant during life time, no need to lock */ 902 ComObjPtr<VirtualBox> pVirtualBox(mParent); 903 pVirtualBox.queryInterfaceTo(aParent); 904 905 return S_OK; 906 } 907 908 STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible) 909 { 910 CheckComArgOutPointerValid(aAccessible); 911 912 AutoLimitedCaller autoCaller(this); 913 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 914 915 LogFlowThisFunc(("ENTER\n")); 916 917 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 918 919 HRESULT rc = S_OK; 920 921 if (!mData->mAccessible) 922 { 923 /* try to initialize the VM once more if not accessible */ 924 925 AutoReinitSpan autoReinitSpan(this); 926 AssertReturn(autoReinitSpan.isOk(), E_FAIL); 927 928 #ifdef DEBUG 929 LogFlowThisFunc(("Dumping media backreferences\n")); 930 mParent->dumpAllBackRefs(); 931 #endif 932 933 if (mData->pMachineConfigFile) 934 { 935 // reset the XML file to force loadSettings() (called from registeredInit()) 936 // to parse it again; the file might have changed 937 delete mData->pMachineConfigFile; 938 mData->pMachineConfigFile = NULL; 939 } 940 941 rc = registeredInit(); 942 943 if (SUCCEEDED(rc) && mData->mAccessible) 944 { 945 autoReinitSpan.setSucceeded(); 946 947 /* make sure interesting parties will notice the accessibility 948 * state change */ 949 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState); 950 mParent->onMachineDataChange(mData->mUuid); 951 } 952 } 953 954 if (SUCCEEDED(rc)) 955 *aAccessible = mData->mAccessible; 956 957 LogFlowThisFuncLeave(); 958 959 return rc; 960 } 961 962 STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError) 963 { 964 CheckComArgOutPointerValid(aAccessError); 965 966 AutoLimitedCaller autoCaller(this); 967 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 968 969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 970 971 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable()) 972 { 973 /* return shortly */ 974 aAccessError = NULL; 975 return S_OK; 976 } 977 978 HRESULT rc = S_OK; 979 980 ComObjPtr<VirtualBoxErrorInfo> errorInfo; 981 rc = errorInfo.createObject(); 982 if (SUCCEEDED(rc)) 983 { 984 errorInfo->init(mData->mAccessError.getResultCode(), 985 mData->mAccessError.getInterfaceID().ref(), 986 Utf8Str(mData->mAccessError.getComponent()).c_str(), 987 Utf8Str(mData->mAccessError.getText())); 988 rc = errorInfo.queryInterfaceTo(aAccessError); 989 } 990 991 return rc; 992 } 993 994 STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName) 995 { 996 CheckComArgOutPointerValid(aName); 997 998 AutoCaller autoCaller(this); 999 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1000 1001 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1002 1003 mUserData->s.strName.cloneTo(aName); 1004 1005 return S_OK; 1006 } 1007 1008 STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName) 1009 { 1010 CheckComArgStrNotEmptyOrNull(aName); 1011 1012 AutoCaller autoCaller(this); 1013 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1014 1015 // prohibit setting a UUID only as the machine name, or else it can 1016 // never be found by findMachine() 1017 Guid test(aName); 1018 1019 if (test.isValid()) 1020 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name")); 1021 1022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1023 1024 HRESULT rc = checkStateDependency(MutableStateDep); 1025 if (FAILED(rc)) return rc; 1026 1027 setModified(IsModified_MachineData); 1028 mUserData.backup(); 1029 mUserData->s.strName = aName; 1030 1031 return S_OK; 1032 } 1033 1034 STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription) 1035 { 1036 CheckComArgOutPointerValid(aDescription); 1037 1038 AutoCaller autoCaller(this); 1039 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1040 1041 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1042 1043 mUserData->s.strDescription.cloneTo(aDescription); 1044 1045 return S_OK; 1046 } 1047 1048 STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription) 1049 { 1050 AutoCaller autoCaller(this); 1051 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1052 1053 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1054 1055 // this can be done in principle in any state as it doesn't affect the VM 1056 // significantly, but play safe by not messing around while complex 1057 // activities are going on 1058 HRESULT rc = checkStateDependency(MutableOrSavedStateDep); 1059 if (FAILED(rc)) return rc; 1060 1061 setModified(IsModified_MachineData); 1062 mUserData.backup(); 1063 mUserData->s.strDescription = aDescription; 1064 1065 return S_OK; 1066 } 1067 1068 STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId) 1069 { 1070 CheckComArgOutPointerValid(aId); 1071 1072 AutoLimitedCaller autoCaller(this); 1073 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1074 1075 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1076 1077 mData->mUuid.toUtf16().cloneTo(aId); 1078 1079 return S_OK; 1080 } 1081 1082 STDMETHODIMP Machine::COMGETTER(Groups)(ComSafeArrayOut(BSTR, aGroups)) 1083 { 1084 CheckComArgOutSafeArrayPointerValid(aGroups); 1085 1086 AutoCaller autoCaller(this); 1087 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1088 1089 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1090 SafeArray<BSTR> groups(mUserData->s.llGroups.size()); 1091 size_t i = 0; 1092 for (StringsList::const_iterator it = mUserData->s.llGroups.begin(); 1093 it != mUserData->s.llGroups.end(); 1094 ++it, i++) 1095 { 1096 Bstr tmp = *it; 1097 tmp.cloneTo(&groups[i]); 1098 } 1099 groups.detachTo(ComSafeArrayOutArg(aGroups)); 1100 1101 return S_OK; 1102 } 1103 1104 STDMETHODIMP Machine::COMSETTER(Groups)(ComSafeArrayIn(IN_BSTR, aGroups)) 1105 { 1106 AutoCaller autoCaller(this); 1107 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1108 1109 StringsList llGroups; 1110 HRESULT rc = mParent->convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups); 1111 if (FAILED(rc)) 1112 return rc; 1113 1114 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1115 1116 // changing machine groups is possible while the VM is offline 1117 rc = checkStateDependency(OfflineStateDep); 1118 if (FAILED(rc)) return rc; 1119 1120 setModified(IsModified_MachineData); 1121 mUserData.backup(); 1122 mUserData->s.llGroups = llGroups; 1123 1124 return S_OK; 1125 } 1126 1127 STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId) 1128 { 1129 CheckComArgOutPointerValid(aOSTypeId); 1130 1131 AutoCaller autoCaller(this); 1132 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1133 1134 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1135 1136 mUserData->s.strOsType.cloneTo(aOSTypeId); 1137 1138 return S_OK; 1139 } 1140 1141 STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId) 1142 { 1143 CheckComArgStrNotEmptyOrNull(aOSTypeId); 1144 1145 AutoCaller autoCaller(this); 1146 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1147 1148 /* look up the object by Id to check it is valid */ 1149 ComPtr<IGuestOSType> guestOSType; 1150 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam()); 1151 if (FAILED(rc)) return rc; 1152 1153 /* when setting, always use the "etalon" value for consistency -- lookup 1154 * by ID is case-insensitive and the input value may have different case */ 1155 Bstr osTypeId; 1156 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam()); 1157 if (FAILED(rc)) return rc; 1158 1159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1160 1161 rc = checkStateDependency(MutableStateDep); 1162 if (FAILED(rc)) return rc; 1163 1164 setModified(IsModified_MachineData); 1165 mUserData.backup(); 1166 mUserData->s.strOsType = osTypeId; 1167 1168 return S_OK; 1169 } 1170 1171 1172 STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType) 1173 { 1174 CheckComArgOutPointerValid(aFirmwareType); 1175 1176 AutoCaller autoCaller(this); 1177 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1178 1179 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1180 1181 *aFirmwareType = mHWData->mFirmwareType; 1182 1183 return S_OK; 1184 } 1185 1186 STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType) 1187 { 1188 AutoCaller autoCaller(this); 1189 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1190 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1191 1192 HRESULT rc = checkStateDependency(MutableStateDep); 1193 if (FAILED(rc)) return rc; 1194 1195 setModified(IsModified_MachineData); 1196 mHWData.backup(); 1197 mHWData->mFirmwareType = aFirmwareType; 1198 1199 return S_OK; 1200 } 1201 1202 STDMETHODIMP Machine::COMGETTER(KeyboardHIDType)(KeyboardHIDType_T *aKeyboardHIDType) 1203 { 1204 CheckComArgOutPointerValid(aKeyboardHIDType); 1205 1206 AutoCaller autoCaller(this); 1207 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1208 1209 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1210 1211 *aKeyboardHIDType = mHWData->mKeyboardHIDType; 1212 1213 return S_OK; 1214 } 1215 1216 STDMETHODIMP Machine::COMSETTER(KeyboardHIDType)(KeyboardHIDType_T aKeyboardHIDType) 1217 { 1218 AutoCaller autoCaller(this); 1219 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1220 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1221 1222 HRESULT rc = checkStateDependency(MutableStateDep); 1223 if (FAILED(rc)) return rc; 1224 1225 setModified(IsModified_MachineData); 1226 mHWData.backup(); 1227 mHWData->mKeyboardHIDType = aKeyboardHIDType; 1228 1229 return S_OK; 1230 } 1231 1232 STDMETHODIMP Machine::COMGETTER(PointingHIDType)(PointingHIDType_T *aPointingHIDType) 1233 { 1234 CheckComArgOutPointerValid(aPointingHIDType); 1235 1236 AutoCaller autoCaller(this); 1237 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1238 1239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1240 1241 *aPointingHIDType = mHWData->mPointingHIDType; 1242 1243 return S_OK; 1244 } 1245 1246 STDMETHODIMP Machine::COMSETTER(PointingHIDType)(PointingHIDType_T aPointingHIDType) 1247 { 1248 AutoCaller autoCaller(this); 1249 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1250 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1251 1252 HRESULT rc = checkStateDependency(MutableStateDep); 1253 if (FAILED(rc)) return rc; 1254 1255 setModified(IsModified_MachineData); 1256 mHWData.backup(); 1257 mHWData->mPointingHIDType = aPointingHIDType; 1258 1259 return S_OK; 1260 } 1261 1262 STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType) 1263 { 1264 CheckComArgOutPointerValid(aChipsetType); 1265 1266 AutoCaller autoCaller(this); 1267 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1268 1269 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1270 1271 *aChipsetType = mHWData->mChipsetType; 1272 1273 return S_OK; 1274 } 1275 1276 STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType) 1277 { 1278 AutoCaller autoCaller(this); 1279 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1280 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1281 1282 HRESULT rc = checkStateDependency(MutableStateDep); 1283 if (FAILED(rc)) return rc; 1284 1285 if (aChipsetType != mHWData->mChipsetType) 1286 { 1287 setModified(IsModified_MachineData); 1288 mHWData.backup(); 1289 mHWData->mChipsetType = aChipsetType; 1290 1291 // Resize network adapter array, to be finalized on commit/rollback. 1292 // We must not throw away entries yet, otherwise settings are lost 1293 // without a way to roll back. 1294 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType); 1295 size_t oldCount = mNetworkAdapters.size(); 1296 if (newCount > oldCount) 1297 { 1298 mNetworkAdapters.resize(newCount); 1299 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++) 1300 { 1301 unconst(mNetworkAdapters[slot]).createObject(); 1302 mNetworkAdapters[slot]->init(this, slot); 1303 } 1304 } 1305 } 1306 1307 return S_OK; 1308 } 1309 1310 STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion) 1311 { 1312 CheckComArgOutPointerValid(aHWVersion); 1313 1314 AutoCaller autoCaller(this); 1315 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1316 1317 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1318 1319 mHWData->mHWVersion.cloneTo(aHWVersion); 1320 1321 return S_OK; 1322 } 1323 1324 STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion) 1325 { 1326 /* check known version */ 1327 Utf8Str hwVersion = aHWVersion; 1328 if ( hwVersion.compare("1") != 0 1329 && hwVersion.compare("2") != 0) 1330 return setError(E_INVALIDARG, 1331 tr("Invalid hardware version: %ls\n"), aHWVersion); 1332 1333 AutoCaller autoCaller(this); 1334 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1335 1336 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1337 1338 HRESULT rc = checkStateDependency(MutableStateDep); 1339 if (FAILED(rc)) return rc; 1340 1341 setModified(IsModified_MachineData); 1342 mHWData.backup(); 1343 mHWData->mHWVersion = hwVersion; 1344 1345 return S_OK; 1346 } 1347 1348 STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID) 1349 { 1350 CheckComArgOutPointerValid(aUUID); 1351 1352 AutoCaller autoCaller(this); 1353 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1354 1355 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1356 1357 if (mHWData->mHardwareUUID.isValid()) 1358 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID); 1359 else 1360 mData->mUuid.toUtf16().cloneTo(aUUID); 1361 1362 return S_OK; 1363 } 1364 1365 STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID) 1366 { 1367 Guid hardwareUUID(aUUID); 1368 if (!hardwareUUID.isValid()) 1369 return E_INVALIDARG; 1370 1371 AutoCaller autoCaller(this); 1372 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1373 1374 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1375 1376 HRESULT rc = checkStateDependency(MutableStateDep); 1377 if (FAILED(rc)) return rc; 1378 1379 setModified(IsModified_MachineData); 1380 mHWData.backup(); 1381 if (hardwareUUID == mData->mUuid) 1382 mHWData->mHardwareUUID.clear(); 1383 else 1384 mHWData->mHardwareUUID = hardwareUUID; 1385 1386 return S_OK; 1387 } 1388 1389 STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize) 1390 { 1391 CheckComArgOutPointerValid(memorySize); 1392 1393 AutoCaller autoCaller(this); 1394 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1395 1396 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1397 1398 *memorySize = mHWData->mMemorySize; 1399 1400 return S_OK; 1401 } 1402 1403 STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize) 1404 { 1405 /* check RAM limits */ 1406 if ( memorySize < MM_RAM_MIN_IN_MB 1407 || memorySize > MM_RAM_MAX_IN_MB 1408 ) 1409 return setError(E_INVALIDARG, 1410 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"), 1411 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB); 1412 1413 AutoCaller autoCaller(this); 1414 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1415 1416 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1417 1418 HRESULT rc = checkStateDependency(MutableStateDep); 1419 if (FAILED(rc)) return rc; 1420 1421 setModified(IsModified_MachineData); 1422 mHWData.backup(); 1423 mHWData->mMemorySize = memorySize; 1424 1425 return S_OK; 1426 } 1427 1428 STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount) 1429 { 1430 CheckComArgOutPointerValid(CPUCount); 1431 1432 AutoCaller autoCaller(this); 1433 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1434 1435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1436 1437 *CPUCount = mHWData->mCPUCount; 1438 1439 return S_OK; 1440 } 1441 1442 STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount) 1443 { 1444 /* check CPU limits */ 1445 if ( CPUCount < SchemaDefs::MinCPUCount 1446 || CPUCount > SchemaDefs::MaxCPUCount 1447 ) 1448 return setError(E_INVALIDARG, 1449 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"), 1450 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount); 1451 1452 AutoCaller autoCaller(this); 1453 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1454 1455 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1456 1457 /* We cant go below the current number of CPUs attached if hotplug is enabled*/ 1458 if (mHWData->mCPUHotPlugEnabled) 1459 { 1460 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++) 1461 { 1462 if (mHWData->mCPUAttached[idx]) 1463 return setError(E_INVALIDARG, 1464 tr("There is still a CPU attached to socket %lu." 1465 "Detach the CPU before removing the socket"), 1466 CPUCount, idx+1); 1467 } 1468 } 1469 1470 HRESULT rc = checkStateDependency(MutableStateDep); 1471 if (FAILED(rc)) return rc; 1472 1473 setModified(IsModified_MachineData); 1474 mHWData.backup(); 1475 mHWData->mCPUCount = CPUCount; 1476 1477 return S_OK; 1478 } 1479 1480 STDMETHODIMP Machine::COMGETTER(CPUExecutionCap)(ULONG *aExecutionCap) 1481 { 1482 CheckComArgOutPointerValid(aExecutionCap); 1483 1484 AutoCaller autoCaller(this); 1485 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1486 1487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1488 1489 *aExecutionCap = mHWData->mCpuExecutionCap; 1490 1491 return S_OK; 1492 } 1493 1494 STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap) 1495 { 1496 HRESULT rc = S_OK; 1497 1498 /* check throttle limits */ 1499 if ( aExecutionCap < 1 1500 || aExecutionCap > 100 1501 ) 1502 return setError(E_INVALIDARG, 1503 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"), 1504 aExecutionCap, 1, 100); 1505 1506 AutoCaller autoCaller(this); 1507 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1508 1509 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1510 1511 alock.release(); 1512 rc = onCPUExecutionCapChange(aExecutionCap); 1513 alock.acquire(); 1514 if (FAILED(rc)) return rc; 1515 1516 setModified(IsModified_MachineData); 1517 mHWData.backup(); 1518 mHWData->mCpuExecutionCap = aExecutionCap; 1519 1520 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 1521 if (Global::IsOnline(mData->mMachineState)) 1522 saveSettings(NULL); 1523 1524 return S_OK; 1525 } 1526 1527 1528 STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled) 1529 { 1530 CheckComArgOutPointerValid(aEnabled); 1531 1532 AutoCaller autoCaller(this); 1533 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1534 1535 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1536 1537 *aEnabled = mHWData->mCPUHotPlugEnabled; 1538 1539 return S_OK; 1540 } 1541 1542 STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled) 1543 { 1544 HRESULT rc = S_OK; 1545 1546 AutoCaller autoCaller(this); 1547 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1548 1549 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1550 1551 rc = checkStateDependency(MutableStateDep); 1552 if (FAILED(rc)) return rc; 1553 1554 if (mHWData->mCPUHotPlugEnabled != aEnabled) 1555 { 1556 if (aEnabled) 1557 { 1558 setModified(IsModified_MachineData); 1559 mHWData.backup(); 1560 1561 /* Add the amount of CPUs currently attached */ 1562 for (unsigned i = 0; i < mHWData->mCPUCount; i++) 1563 { 1564 mHWData->mCPUAttached[i] = true; 1565 } 1566 } 1567 else 1568 { 1569 /* 1570 * We can disable hotplug only if the amount of maximum CPUs is equal 1571 * to the amount of attached CPUs 1572 */ 1573 unsigned cCpusAttached = 0; 1574 unsigned iHighestId = 0; 1575 1576 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++) 1577 { 1578 if (mHWData->mCPUAttached[i]) 1579 { 1580 cCpusAttached++; 1581 iHighestId = i; 1582 } 1583 } 1584 1585 if ( (cCpusAttached != mHWData->mCPUCount) 1586 || (iHighestId >= mHWData->mCPUCount)) 1587 return setError(E_INVALIDARG, 1588 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached")); 1589 1590 setModified(IsModified_MachineData); 1591 mHWData.backup(); 1592 } 1593 } 1594 1595 mHWData->mCPUHotPlugEnabled = aEnabled; 1596 1597 return rc; 1598 } 1599 1600 STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled) 1601 { 1602 #ifdef VBOX_WITH_USB_CARDREADER 1603 CheckComArgOutPointerValid(aEnabled); 1604 1605 AutoCaller autoCaller(this); 1606 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1607 1608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1609 1610 *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled; 1611 1612 return S_OK; 1613 #else 1614 NOREF(aEnabled); 1615 return E_NOTIMPL; 1616 #endif 1617 } 1618 1619 STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled) 1620 { 1621 #ifdef VBOX_WITH_USB_CARDREADER 1622 AutoCaller autoCaller(this); 1623 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1624 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1625 1626 HRESULT rc = checkStateDependency(MutableStateDep); 1627 if (FAILED(rc)) return rc; 1628 1629 setModified(IsModified_MachineData); 1630 mHWData.backup(); 1631 mHWData->mEmulatedUSBCardReaderEnabled = aEnabled; 1632 1633 return S_OK; 1634 #else 1635 NOREF(aEnabled); 1636 return E_NOTIMPL; 1637 #endif 1638 } 1639 1640 STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *aEnabled) 1641 { 1642 #ifdef VBOX_WITH_USB_VIDEO 1643 CheckComArgOutPointerValid(aEnabled); 1644 1645 AutoCaller autoCaller(this); 1646 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1647 1648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1649 1650 *aEnabled = mHWData->mEmulatedUSBWebcamEnabled; 1651 1652 return S_OK; 1653 #else 1654 NOREF(aEnabled); 1655 return E_NOTIMPL; 1656 #endif 1657 } 1658 1659 STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL aEnabled) 1660 { 1661 #ifdef VBOX_WITH_USB_VIDEO 1662 AutoCaller autoCaller(this); 1663 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1664 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1665 1666 HRESULT rc = checkStateDependency(MutableStateDep); 1667 if (FAILED(rc)) return rc; 1668 1669 setModified(IsModified_MachineData); 1670 mHWData.backup(); 1671 mHWData->mEmulatedUSBWebcamEnabled = aEnabled; 1672 1673 return S_OK; 1674 #else 1675 NOREF(aEnabled); 1676 return E_NOTIMPL; 1677 #endif 1678 } 1679 1680 STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled) 1681 { 1682 CheckComArgOutPointerValid(aEnabled); 1683 1684 AutoCaller autoCaller(this); 1685 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1686 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1687 1688 *aEnabled = mHWData->mHPETEnabled; 1689 1690 return S_OK; 1691 } 1692 1693 STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled) 1694 { 1695 HRESULT rc = S_OK; 1696 1697 AutoCaller autoCaller(this); 1698 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1699 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1700 1701 rc = checkStateDependency(MutableStateDep); 1702 if (FAILED(rc)) return rc; 1703 1704 setModified(IsModified_MachineData); 1705 mHWData.backup(); 1706 1707 mHWData->mHPETEnabled = aEnabled; 1708 1709 return rc; 1710 } 1711 1712 STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled) 1713 { 1714 AutoCaller autoCaller(this); 1715 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1716 1717 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1718 1719 *fEnabled = mHWData->mVideoCaptureEnabled; 1720 return S_OK; 1721 } 1722 1723 STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled) 1724 { 1725 HRESULT rc = S_OK; 1726 1727 AutoCaller autoCaller(this); 1728 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1729 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1730 1731 setModified(IsModified_MachineData); 1732 mHWData.backup(); 1733 mHWData->mVideoCaptureEnabled = fEnabled; 1734 1735 alock.release(); 1736 rc = onVideoCaptureChange(); 1737 alock.acquire(); 1738 if (FAILED(rc)) 1739 { 1740 /* 1741 * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded. 1742 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to 1743 * determine if it should start or stop capturing. Therefore we need to manually 1744 * undo change. 1745 */ 1746 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled; 1747 return rc; 1748 } 1749 1750 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 1751 if (Global::IsOnline(mData->mMachineState)) 1752 saveSettings(NULL); 1753 1754 return rc; 1755 } 1756 1757 STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens)) 1758 { 1759 CheckComArgOutSafeArrayPointerValid(aScreens); 1760 1761 AutoCaller autoCaller(this); 1762 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1763 1764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1765 1766 SafeArray<BOOL> screens(mHWData->mMonitorCount); 1767 for (unsigned i = 0; i < screens.size(); i++) 1768 screens[i] = mHWData->maVideoCaptureScreens[i]; 1769 screens.detachTo(ComSafeArrayOutArg(aScreens)); 1770 return S_OK; 1771 } 1772 1773 STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens)) 1774 { 1775 SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens)); 1776 AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG); 1777 bool fChanged = false; 1778 1779 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1780 1781 for (unsigned i = 0; i < screens.size(); i++) 1782 { 1783 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i])) 1784 { 1785 mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]); 1786 fChanged = true; 1787 } 1788 } 1789 if (fChanged) 1790 { 1791 alock.release(); 1792 HRESULT rc = onVideoCaptureChange(); 1793 alock.acquire(); 1794 if (FAILED(rc)) return rc; 1795 setModified(IsModified_MachineData); 1796 1797 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 1798 if (Global::IsOnline(mData->mMachineState)) 1799 saveSettings(NULL); 1800 } 1801 1802 return S_OK; 1803 } 1804 1805 STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile) 1806 { 1807 AutoCaller autoCaller(this); 1808 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1809 1810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1811 if (mHWData->mVideoCaptureFile.isEmpty()) 1812 { 1813 Utf8Str defaultFile; 1814 getDefaultVideoCaptureFile(defaultFile); 1815 defaultFile.cloneTo(apFile); 1816 } 1817 else 1818 mHWData->mVideoCaptureFile.cloneTo(apFile); 1819 return S_OK; 1820 } 1821 1822 STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile) 1823 { 1824 Utf8Str strFile(aFile); 1825 AutoCaller autoCaller(this); 1826 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1827 1828 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1829 1830 if ( Global::IsOnline(mData->mMachineState) 1831 && mHWData->mVideoCaptureEnabled) 1832 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); 1833 1834 if (!RTPathStartsWithRoot(strFile.c_str())) 1835 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str()); 1836 1837 if (!strFile.isEmpty()) 1838 { 1839 Utf8Str defaultFile; 1840 getDefaultVideoCaptureFile(defaultFile); 1841 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str())) 1842 strFile.setNull(); 1843 } 1844 1845 setModified(IsModified_MachineData); 1846 mHWData.backup(); 1847 mHWData->mVideoCaptureFile = strFile; 1848 1849 return S_OK; 1850 } 1851 1852 STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes) 1853 { 1854 AutoCaller autoCaller(this); 1855 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1856 1857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1858 *aHorzRes = mHWData->mVideoCaptureWidth; 1859 return S_OK; 1860 } 1861 1862 STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes) 1863 { 1864 AutoCaller autoCaller(this); 1865 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1866 1867 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1868 1869 if ( Global::IsOnline(mData->mMachineState) 1870 && mHWData->mVideoCaptureEnabled) 1871 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); 1872 1873 setModified(IsModified_MachineData); 1874 mHWData.backup(); 1875 mHWData->mVideoCaptureWidth = aHorzRes; 1876 1877 return S_OK; 1878 } 1879 1880 STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes) 1881 { 1882 AutoCaller autoCaller(this); 1883 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1884 1885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1886 *aVertRes = mHWData->mVideoCaptureHeight; 1887 return S_OK; 1888 } 1889 1890 STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes) 1891 { 1892 AutoCaller autoCaller(this); 1893 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1894 1895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1896 1897 if ( Global::IsOnline(mData->mMachineState) 1898 && mHWData->mVideoCaptureEnabled) 1899 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); 1900 1901 setModified(IsModified_MachineData); 1902 mHWData.backup(); 1903 mHWData->mVideoCaptureHeight = aVertRes; 1904 1905 return S_OK; 1906 } 1907 1908 STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate) 1909 { 1910 AutoCaller autoCaller(this); 1911 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1912 1913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1914 *aRate = mHWData->mVideoCaptureRate; 1915 return S_OK; 1916 } 1917 1918 STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate) 1919 { 1920 AutoCaller autoCaller(this); 1921 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1922 1923 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1924 1925 if ( Global::IsOnline(mData->mMachineState) 1926 && mHWData->mVideoCaptureEnabled) 1927 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); 1928 1929 setModified(IsModified_MachineData); 1930 mHWData.backup(); 1931 mHWData->mVideoCaptureRate = aRate; 1932 1933 return S_OK; 1934 } 1935 1936 STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS) 1937 { 1938 AutoCaller autoCaller(this); 1939 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1940 1941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1942 *aFPS = mHWData->mVideoCaptureFPS; 1943 return S_OK; 1944 } 1945 1946 STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS) 1947 { 1948 AutoCaller autoCaller(this); 1949 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1950 1951 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1952 1953 if ( Global::IsOnline(mData->mMachineState) 1954 && mHWData->mVideoCaptureEnabled) 1955 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); 1956 1957 setModified(IsModified_MachineData); 1958 mHWData.backup(); 1959 mHWData->mVideoCaptureFPS = aFPS; 1960 1961 return S_OK; 1962 } 1963 1964 STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType) 1965 { 1966 CheckComArgOutPointerValid(aGraphicsControllerType); 1967 1968 AutoCaller autoCaller(this); 1969 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1970 1971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1972 1973 *aGraphicsControllerType = mHWData->mGraphicsControllerType; 1974 1975 return S_OK; 1976 } 1977 1978 STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType) 1979 { 1980 switch (aGraphicsControllerType) 1981 { 1982 case GraphicsControllerType_Null: 1983 case GraphicsControllerType_VBoxVGA: 1984 break; 1985 default: 1986 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType); 1987 } 1988 1989 AutoCaller autoCaller(this); 1990 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1991 1992 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1993 1994 HRESULT rc = checkStateDependency(MutableStateDep); 1995 if (FAILED(rc)) return rc; 1996 1997 setModified(IsModified_MachineData); 1998 mHWData.backup(); 1999 mHWData->mGraphicsControllerType = aGraphicsControllerType; 2000 2001 return S_OK; 2002 } 2003 2004 STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize) 2005 { 2006 CheckComArgOutPointerValid(memorySize); 2007 2008 AutoCaller autoCaller(this); 2009 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2010 2011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2012 2013 *memorySize = mHWData->mVRAMSize; 2014 2015 return S_OK; 2016 } 2017 2018 STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize) 2019 { 2020 /* check VRAM limits */ 2021 if (memorySize < SchemaDefs::MinGuestVRAM || 2022 memorySize > SchemaDefs::MaxGuestVRAM) 2023 return setError(E_INVALIDARG, 2024 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"), 2025 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM); 2026 2027 AutoCaller autoCaller(this); 2028 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2029 2030 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2031 2032 HRESULT rc = checkStateDependency(MutableStateDep); 2033 if (FAILED(rc)) return rc; 2034 2035 setModified(IsModified_MachineData); 2036 mHWData.backup(); 2037 mHWData->mVRAMSize = memorySize; 2038 2039 return S_OK; 2040 } 2041 2042 /** @todo this method should not be public */ 2043 STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize) 2044 { 2045 CheckComArgOutPointerValid(memoryBalloonSize); 2046 2047 AutoCaller autoCaller(this); 2048 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2049 2050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2051 2052 *memoryBalloonSize = mHWData->mMemoryBalloonSize; 2053 2054 return S_OK; 2055 } 2056 2057 /** 2058 * Set the memory balloon size. 2059 * 2060 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so 2061 * we have to make sure that we never call IGuest from here. 2062 */ 2063 STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize) 2064 { 2065 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */ 2066 #if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) 2067 /* check limits */ 2068 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)) 2069 return setError(E_INVALIDARG, 2070 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"), 2071 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)); 2072 2073 AutoCaller autoCaller(this); 2074 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2075 2076 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2077 2078 setModified(IsModified_MachineData); 2079 mHWData.backup(); 2080 mHWData->mMemoryBalloonSize = memoryBalloonSize; 2081 2082 return S_OK; 2083 #else 2084 NOREF(memoryBalloonSize); 2085 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts")); 2086 #endif 2087 } 2088 2089 STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled) 2090 { 2091 CheckComArgOutPointerValid(aEnabled); 2092 2093 AutoCaller autoCaller(this); 2094 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2095 2096 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2097 2098 *aEnabled = mHWData->mPageFusionEnabled; 2099 return S_OK; 2100 } 2101 2102 STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled) 2103 { 2104 #ifdef VBOX_WITH_PAGE_SHARING 2105 AutoCaller autoCaller(this); 2106 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2107 2108 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2109 2110 /** @todo must support changes for running vms and keep this in sync with IGuest. */ 2111 setModified(IsModified_MachineData); 2112 mHWData.backup(); 2113 mHWData->mPageFusionEnabled = aEnabled; 2114 return S_OK; 2115 #else 2116 NOREF(aEnabled); 2117 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts")); 2118 #endif 2119 } 2120 2121 STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled) 2122 { 2123 CheckComArgOutPointerValid(aEnabled); 2124 2125 AutoCaller autoCaller(this); 2126 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2127 2128 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2129 2130 *aEnabled = mHWData->mAccelerate3DEnabled; 2131 2132 return S_OK; 2133 } 2134 2135 STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable) 2136 { 2137 AutoCaller autoCaller(this); 2138 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2139 2140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2141 2142 HRESULT rc = checkStateDependency(MutableStateDep); 2143 if (FAILED(rc)) return rc; 2144 2145 /** @todo check validity! */ 2146 2147 setModified(IsModified_MachineData); 2148 mHWData.backup(); 2149 mHWData->mAccelerate3DEnabled = enable; 2150 2151 return S_OK; 2152 } 2153 2154 2155 STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled) 2156 { 2157 CheckComArgOutPointerValid(aEnabled); 2158 2159 AutoCaller autoCaller(this); 2160 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2161 2162 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2163 2164 *aEnabled = mHWData->mAccelerate2DVideoEnabled; 2165 2166 return S_OK; 2167 } 2168 2169 STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable) 2170 { 2171 AutoCaller autoCaller(this); 2172 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2173 2174 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2175 2176 HRESULT rc = checkStateDependency(MutableStateDep); 2177 if (FAILED(rc)) return rc; 2178 2179 /** @todo check validity! */ 2180 2181 setModified(IsModified_MachineData); 2182 mHWData.backup(); 2183 mHWData->mAccelerate2DVideoEnabled = enable; 2184 2185 return S_OK; 2186 } 2187 2188 STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount) 2189 { 2190 CheckComArgOutPointerValid(monitorCount); 2191 2192 AutoCaller autoCaller(this); 2193 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2194 2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2196 2197 *monitorCount = mHWData->mMonitorCount; 2198 2199 return S_OK; 2200 } 2201 2202 STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount) 2203 { 2204 /* make sure monitor count is a sensible number */ 2205 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors) 2206 return setError(E_INVALIDARG, 2207 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"), 2208 monitorCount, 1, SchemaDefs::MaxGuestMonitors); 2209 2210 AutoCaller autoCaller(this); 2211 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2212 2213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2214 2215 HRESULT rc = checkStateDependency(MutableStateDep); 2216 if (FAILED(rc)) return rc; 2217 2218 setModified(IsModified_MachineData); 2219 mHWData.backup(); 2220 mHWData->mMonitorCount = monitorCount; 2221 2222 return S_OK; 2223 } 2224 2225 STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings) 2226 { 2227 CheckComArgOutPointerValid(biosSettings); 2228 2229 AutoCaller autoCaller(this); 2230 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2231 2232 /* mBIOSSettings is constant during life time, no need to lock */ 2233 mBIOSSettings.queryInterfaceTo(biosSettings); 2234 2235 return S_OK; 2236 } 2237 2238 STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal) 2239 { 2240 CheckComArgOutPointerValid(aVal); 2241 2242 AutoCaller autoCaller(this); 2243 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2244 2245 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2246 2247 switch (property) 2248 { 2249 case CPUPropertyType_PAE: 2250 *aVal = mHWData->mPAEEnabled; 2251 break; 2252 2253 case CPUPropertyType_Synthetic: 2254 *aVal = mHWData->mSyntheticCpu; 2255 break; 2256 2257 case CPUPropertyType_LongMode: 2258 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled) 2259 *aVal = TRUE; 2260 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled) 2261 *aVal = FALSE; 2262 #if HC_ARCH_BITS == 64 2263 else 2264 *aVal = TRUE; 2265 #else 2266 else 2267 { 2268 *aVal = FALSE; 2269 2270 ComPtr<IGuestOSType> ptrGuestOSType; 2271 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam()); 2272 if (SUCCEEDED(hrc2)) 2273 { 2274 BOOL fIs64Bit = FALSE; 2275 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2); 2276 if (SUCCEEDED(hrc2) && fIs64Bit) 2277 { 2278 ComObjPtr<Host> ptrHost = mParent->host(); 2279 alock.release(); 2280 2281 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2); 2282 if (FAILED(hrc2)) 2283 *aVal = FALSE; 2284 } 2285 } 2286 } 2287 #endif 2288 break; 2289 2290 default: 2291 return E_INVALIDARG; 2292 } 2293 return S_OK; 2294 } 2295 2296 STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal) 2297 { 2298 AutoCaller autoCaller(this); 2299 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2300 2301 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2302 2303 HRESULT rc = checkStateDependency(MutableStateDep); 2304 if (FAILED(rc)) return rc; 2305 2306 switch (property) 2307 { 2308 case CPUPropertyType_PAE: 2309 setModified(IsModified_MachineData); 2310 mHWData.backup(); 2311 mHWData->mPAEEnabled = !!aVal; 2312 break; 2313 2314 case CPUPropertyType_Synthetic: 2315 setModified(IsModified_MachineData); 2316 mHWData.backup(); 2317 mHWData->mSyntheticCpu = !!aVal; 2318 break; 2319 2320 case CPUPropertyType_LongMode: 2321 setModified(IsModified_MachineData); 2322 mHWData.backup(); 2323 mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled; 2324 break; 2325 2326 default: 2327 return E_INVALIDARG; 2328 } 2329 return S_OK; 2330 } 2331 2332 STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx) 2333 { 2334 CheckComArgOutPointerValid(aValEax); 2335 CheckComArgOutPointerValid(aValEbx); 2336 CheckComArgOutPointerValid(aValEcx); 2337 CheckComArgOutPointerValid(aValEdx); 2338 2339 AutoCaller autoCaller(this); 2340 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2341 2342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2343 2344 switch(aId) 2345 { 2346 case 0x0: 2347 case 0x1: 2348 case 0x2: 2349 case 0x3: 2350 case 0x4: 2351 case 0x5: 2352 case 0x6: 2353 case 0x7: 2354 case 0x8: 2355 case 0x9: 2356 case 0xA: 2357 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId) 2358 return E_INVALIDARG; 2359 2360 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax; 2361 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx; 2362 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx; 2363 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx; 2364 break; 2365 2366 case 0x80000000: 2367 case 0x80000001: 2368 case 0x80000002: 2369 case 0x80000003: 2370 case 0x80000004: 2371 case 0x80000005: 2372 case 0x80000006: 2373 case 0x80000007: 2374 case 0x80000008: 2375 case 0x80000009: 2376 case 0x8000000A: 2377 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId) 2378 return E_INVALIDARG; 2379 2380 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax; 2381 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx; 2382 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx; 2383 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx; 2384 break; 2385 2386 default: 2387 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 2388 } 2389 return S_OK; 2390 } 2391 2392 STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx) 2393 { 2394 AutoCaller autoCaller(this); 2395 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2396 2397 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2398 2399 HRESULT rc = checkStateDependency(MutableStateDep); 2400 if (FAILED(rc)) return rc; 2401 2402 switch(aId) 2403 { 2404 case 0x0: 2405 case 0x1: 2406 case 0x2: 2407 case 0x3: 2408 case 0x4: 2409 case 0x5: 2410 case 0x6: 2411 case 0x7: 2412 case 0x8: 2413 case 0x9: 2414 case 0xA: 2415 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB); 2416 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 2417 setModified(IsModified_MachineData); 2418 mHWData.backup(); 2419 mHWData->mCpuIdStdLeafs[aId].ulId = aId; 2420 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax; 2421 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx; 2422 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx; 2423 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx; 2424 break; 2425 2426 case 0x80000000: 2427 case 0x80000001: 2428 case 0x80000002: 2429 case 0x80000003: 2430 case 0x80000004: 2431 case 0x80000005: 2432 case 0x80000006: 2433 case 0x80000007: 2434 case 0x80000008: 2435 case 0x80000009: 2436 case 0x8000000A: 2437 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB); 2438 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 2439 setModified(IsModified_MachineData); 2440 mHWData.backup(); 2441 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId; 2442 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax; 2443 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx; 2444 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx; 2445 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx; 2446 break; 2447 2448 default: 2449 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 2450 } 2451 return S_OK; 2452 } 2453 2454 STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId) 2455 { 2456 AutoCaller autoCaller(this); 2457 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2458 2459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2460 2461 HRESULT rc = checkStateDependency(MutableStateDep); 2462 if (FAILED(rc)) return rc; 2463 2464 switch(aId) 2465 { 2466 case 0x0: 2467 case 0x1: 2468 case 0x2: 2469 case 0x3: 2470 case 0x4: 2471 case 0x5: 2472 case 0x6: 2473 case 0x7: 2474 case 0x8: 2475 case 0x9: 2476 case 0xA: 2477 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB); 2478 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 2479 setModified(IsModified_MachineData); 2480 mHWData.backup(); 2481 /* Invalidate leaf. */ 2482 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX; 2483 break; 2484 2485 case 0x80000000: 2486 case 0x80000001: 2487 case 0x80000002: 2488 case 0x80000003: 2489 case 0x80000004: 2490 case 0x80000005: 2491 case 0x80000006: 2492 case 0x80000007: 2493 case 0x80000008: 2494 case 0x80000009: 2495 case 0x8000000A: 2496 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB); 2497 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 2498 setModified(IsModified_MachineData); 2499 mHWData.backup(); 2500 /* Invalidate leaf. */ 2501 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX; 2502 break; 2503 2504 default: 2505 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 2506 } 2507 return S_OK; 2508 } 2509 2510 STDMETHODIMP Machine::RemoveAllCPUIDLeaves() 2511 { 2512 AutoCaller autoCaller(this); 2513 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2514 2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2516 2517 HRESULT rc = checkStateDependency(MutableStateDep); 2518 if (FAILED(rc)) return rc; 2519 2520 setModified(IsModified_MachineData); 2521 mHWData.backup(); 2522 2523 /* Invalidate all standard leafs. */ 2524 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++) 2525 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX; 2526 2527 /* Invalidate all extended leafs. */ 2528 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++) 2529 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX; 2530 2531 return S_OK; 2532 } 2533 2534 STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal) 2535 { 2536 CheckComArgOutPointerValid(aVal); 2537 2538 AutoCaller autoCaller(this); 2539 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2540 2541 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2542 2543 switch(property) 2544 { 2545 case HWVirtExPropertyType_Enabled: 2546 *aVal = mHWData->mHWVirtExEnabled; 2547 break; 2548 2549 case HWVirtExPropertyType_Exclusive: 2550 *aVal = mHWData->mHWVirtExExclusive; 2551 break; 2552 2553 case HWVirtExPropertyType_VPID: 2554 *aVal = mHWData->mHWVirtExVPIDEnabled; 2555 break; 2556 2557 case HWVirtExPropertyType_NestedPaging: 2558 *aVal = mHWData->mHWVirtExNestedPagingEnabled; 2559 break; 2560 2561 case HWVirtExPropertyType_UnrestrictedExecution: 2562 *aVal = mHWData->mHWVirtExUXEnabled; 2563 break; 2564 2565 case HWVirtExPropertyType_LargePages: 2566 *aVal = mHWData->mHWVirtExLargePagesEnabled; 2567 #if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */ 2568 *aVal = FALSE; 2569 #endif 2570 break; 2571 2572 case HWVirtExPropertyType_Force: 2573 *aVal = mHWData->mHWVirtExForceEnabled; 2574 break; 2575 2576 default: 2577 return E_INVALIDARG; 2578 } 2579 return S_OK; 2580 } 2581 2582 STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal) 2583 { 2584 AutoCaller autoCaller(this); 2585 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2586 2587 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2588 2589 HRESULT rc = checkStateDependency(MutableStateDep); 2590 if (FAILED(rc)) return rc; 2591 2592 switch(property) 2593 { 2594 case HWVirtExPropertyType_Enabled: 2595 setModified(IsModified_MachineData); 2596 mHWData.backup(); 2597 mHWData->mHWVirtExEnabled = !!aVal; 2598 break; 2599 2600 case HWVirtExPropertyType_Exclusive: 2601 setModified(IsModified_MachineData); 2602 mHWData.backup(); 2603 mHWData->mHWVirtExExclusive = !!aVal; 2604 break; 2605 2606 case HWVirtExPropertyType_VPID: 2607 setModified(IsModified_MachineData); 2608 mHWData.backup(); 2609 mHWData->mHWVirtExVPIDEnabled = !!aVal; 2610 break; 2611 2612 case HWVirtExPropertyType_NestedPaging: 2613 setModified(IsModified_MachineData); 2614 mHWData.backup(); 2615 mHWData->mHWVirtExNestedPagingEnabled = !!aVal; 2616 break; 2617 2618 case HWVirtExPropertyType_UnrestrictedExecution: 2619 setModified(IsModified_MachineData); 2620 mHWData.backup(); 2621 mHWData->mHWVirtExUXEnabled = !!aVal; 2622 break; 2623 2624 case HWVirtExPropertyType_LargePages: 2625 setModified(IsModified_MachineData); 2626 mHWData.backup(); 2627 mHWData->mHWVirtExLargePagesEnabled = !!aVal; 2628 break; 2629 2630 case HWVirtExPropertyType_Force: 2631 setModified(IsModified_MachineData); 2632 mHWData.backup(); 2633 mHWData->mHWVirtExForceEnabled = !!aVal; 2634 break; 2635 2636 default: 2637 return E_INVALIDARG; 2638 } 2639 2640 return S_OK; 2641 } 2642 2643 STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder) 2644 { 2645 CheckComArgOutPointerValid(aSnapshotFolder); 2646 2647 AutoCaller autoCaller(this); 2648 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2649 2650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2651 2652 Utf8Str strFullSnapshotFolder; 2653 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 2654 strFullSnapshotFolder.cloneTo(aSnapshotFolder); 2655 2656 return S_OK; 2657 } 2658 2659 STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder) 2660 { 2661 /* @todo (r=dmik): 2662 * 1. Allow to change the name of the snapshot folder containing snapshots 2663 * 2. Rename the folder on disk instead of just changing the property 2664 * value (to be smart and not to leave garbage). Note that it cannot be 2665 * done here because the change may be rolled back. Thus, the right 2666 * place is #saveSettings(). 2667 */ 2668 2669 AutoCaller autoCaller(this); 2670 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2671 2672 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2673 2674 HRESULT rc = checkStateDependency(MutableStateDep); 2675 if (FAILED(rc)) return rc; 2676 2677 if (!mData->mCurrentSnapshot.isNull()) 2678 return setError(E_FAIL, 2679 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)")); 2680 2681 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original 2682 2683 Utf8Str strSnapshotFolder(strSnapshotFolder0); 2684 if (strSnapshotFolder.isEmpty()) 2685 strSnapshotFolder = "Snapshots"; 2686 int vrc = calculateFullPath(strSnapshotFolder, 2687 strSnapshotFolder); 2688 if (RT_FAILURE(vrc)) 2689 return setError(E_FAIL, 2690 tr("Invalid snapshot folder '%ls' (%Rrc)"), 2691 aSnapshotFolder, vrc); 2692 2693 setModified(IsModified_MachineData); 2694 mUserData.backup(); 2695 2696 copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder); 2697 2698 return S_OK; 2699 } 2700 2701 STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments)) 2702 { 2703 CheckComArgOutSafeArrayPointerValid(aAttachments); 2704 2705 AutoCaller autoCaller(this); 2706 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2707 2708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2709 2710 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments); 2711 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 2712 2713 return S_OK; 2714 } 2715 2716 STDMETHODIMP Machine::COMGETTER(VRDEServer)(IVRDEServer **vrdeServer) 2717 { 2718 CheckComArgOutPointerValid(vrdeServer); 2719 2720 AutoCaller autoCaller(this); 2721 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2722 2723 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2724 2725 Assert(!!mVRDEServer); 2726 mVRDEServer.queryInterfaceTo(vrdeServer); 2727 2728 return S_OK; 2729 } 2730 2731 STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter) 2732 { 2733 CheckComArgOutPointerValid(audioAdapter); 2734 2735 AutoCaller autoCaller(this); 2736 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2737 2738 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2739 2740 mAudioAdapter.queryInterfaceTo(audioAdapter); 2741 return S_OK; 2742 } 2743 2744 STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers)) 2745 { 2746 #ifdef VBOX_WITH_VUSB 2747 CheckComArgOutPointerValid(aUSBControllers); 2748 2749 AutoCaller autoCaller(this); 2750 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2751 2752 clearError(); 2753 MultiResult rc(S_OK); 2754 2755 # ifdef VBOX_WITH_USB 2756 rc = mParent->host()->checkUSBProxyService(); 2757 if (FAILED(rc)) return rc; 2758 # endif 2759 2760 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2761 2762 SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data()); 2763 ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers)); 2764 return S_OK; 2765 #else 2766 /* Note: The GUI depends on this method returning E_NOTIMPL with no 2767 * extended error info to indicate that USB is simply not available 2768 * (w/o treating it as a failure), for example, as in OSE */ 2769 NOREF(aUSBControllers); 2770 ReturnComNotImplemented(); 2771 #endif /* VBOX_WITH_VUSB */ 2772 } 2773 2774 STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters) 2775 { 2776 #ifdef VBOX_WITH_VUSB 2777 CheckComArgOutPointerValid(aUSBDeviceFilters); 2778 2779 AutoCaller autoCaller(this); 2780 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2781 2782 clearError(); 2783 MultiResult rc(S_OK); 2784 2785 # ifdef VBOX_WITH_USB 2786 rc = mParent->host()->checkUSBProxyService(); 2787 if (FAILED(rc)) return rc; 2788 # endif 2789 2790 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2791 2792 return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters); 2793 #else 2794 /* Note: The GUI depends on this method returning E_NOTIMPL with no 2795 * extended error info to indicate that USB is simply not available 2796 * (w/o treating it as a failure), for example, as in OSE */ 2797 NOREF(aUSBDeviceFilters); 2798 ReturnComNotImplemented(); 2799 #endif /* VBOX_WITH_VUSB */ 2800 } 2801 2802 STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath) 2803 { 2804 CheckComArgOutPointerValid(aFilePath); 2805 2806 AutoLimitedCaller autoCaller(this); 2807 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2808 2809 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2810 2811 mData->m_strConfigFileFull.cloneTo(aFilePath); 2812 return S_OK; 2813 } 2814 2815 STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified) 2816 { 2817 CheckComArgOutPointerValid(aModified); 2818 2819 AutoCaller autoCaller(this); 2820 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2821 2822 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2823 2824 HRESULT rc = checkStateDependency(MutableStateDep); 2825 if (FAILED(rc)) return rc; 2826 2827 if (!mData->pMachineConfigFile->fileExists()) 2828 // this is a new machine, and no config file exists yet: 2829 *aModified = TRUE; 2830 else 2831 *aModified = (mData->flModifications != 0); 2832 2833 return S_OK; 2834 } 2835 2836 STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState) 2837 { 2838 CheckComArgOutPointerValid(aSessionState); 2839 2840 AutoCaller autoCaller(this); 2841 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2842 2843 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2844 2845 *aSessionState = mData->mSession.mState; 2846 2847 return S_OK; 2848 } 2849 2850 STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType) 2851 { 2852 CheckComArgOutPointerValid(aSessionType); 2853 2854 AutoCaller autoCaller(this); 2855 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2856 2857 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2858 2859 mData->mSession.mType.cloneTo(aSessionType); 2860 2861 return S_OK; 2862 } 2863 2864 STDMETHODIMP Machine::COMGETTER(SessionPID)(ULONG *aSessionPID) 2865 { 2866 CheckComArgOutPointerValid(aSessionPID); 2867 2868 AutoCaller autoCaller(this); 2869 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2870 2871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2872 2873 *aSessionPID = mData->mSession.mPID; 2874 2875 return S_OK; 2876 } 2877 2878 STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState) 2879 { 2880 CheckComArgOutPointerValid(machineState); 2881 2882 AutoCaller autoCaller(this); 2883 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2884 2885 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2886 2887 *machineState = mData->mMachineState; 2888 2889 return S_OK; 2890 } 2891 2892 STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange) 2893 { 2894 CheckComArgOutPointerValid(aLastStateChange); 2895 2896 AutoCaller autoCaller(this); 2897 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2898 2899 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2900 2901 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange); 2902 2903 return S_OK; 2904 } 2905 2906 STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath) 2907 { 2908 CheckComArgOutPointerValid(aStateFilePath); 2909 2910 AutoCaller autoCaller(this); 2911 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2912 2913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2914 2915 mSSData->strStateFilePath.cloneTo(aStateFilePath); 2916 2917 return S_OK; 2918 } 2919 2920 STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder) 2921 { 2922 CheckComArgOutPointerValid(aLogFolder); 2923 2924 AutoCaller autoCaller(this); 2925 AssertComRCReturnRC(autoCaller.rc()); 2926 2927 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2928 2929 Utf8Str logFolder; 2930 getLogFolder(logFolder); 2931 logFolder.cloneTo(aLogFolder); 2932 2933 return S_OK; 2934 } 2935 2936 STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot) 2937 { 2938 CheckComArgOutPointerValid(aCurrentSnapshot); 2939 2940 AutoCaller autoCaller(this); 2941 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2942 2943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2944 2945 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot); 2946 2947 return S_OK; 2948 } 2949 2950 STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount) 2951 { 2952 CheckComArgOutPointerValid(aSnapshotCount); 2953 2954 AutoCaller autoCaller(this); 2955 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2956 2957 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2958 2959 *aSnapshotCount = mData->mFirstSnapshot.isNull() 2960 ? 0 2961 : mData->mFirstSnapshot->getAllChildrenCount() + 1; 2962 2963 return S_OK; 2964 } 2965 2966 STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified) 2967 { 2968 CheckComArgOutPointerValid(aCurrentStateModified); 2969 2970 AutoCaller autoCaller(this); 2971 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2972 2973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2974 2975 /* Note: for machines with no snapshots, we always return FALSE 2976 * (mData->mCurrentStateModified will be TRUE in this case, for historical 2977 * reasons :) */ 2978 2979 *aCurrentStateModified = mData->mFirstSnapshot.isNull() 2980 ? FALSE 2981 : mData->mCurrentStateModified; 2982 2983 return S_OK; 2984 } 2985 2986 STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders)) 2987 { 2988 CheckComArgOutSafeArrayPointerValid(aSharedFolders); 2989 2990 AutoCaller autoCaller(this); 2991 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2992 2993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2994 2995 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders); 2996 folders.detachTo(ComSafeArrayOutArg(aSharedFolders)); 2997 2998 return S_OK; 2999 } 3000 3001 STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode) 3002 { 3003 CheckComArgOutPointerValid(aClipboardMode); 3004 3005 AutoCaller autoCaller(this); 3006 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3007 3008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3009 3010 *aClipboardMode = mHWData->mClipboardMode; 3011 3012 return S_OK; 3013 } 3014 3015 STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) 3016 { 3017 HRESULT rc = S_OK; 3018 3019 AutoCaller autoCaller(this); 3020 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3021 3022 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3023 3024 alock.release(); 3025 rc = onClipboardModeChange(aClipboardMode); 3026 alock.acquire(); 3027 if (FAILED(rc)) return rc; 3028 3029 setModified(IsModified_MachineData); 3030 mHWData.backup(); 3031 mHWData->mClipboardMode = aClipboardMode; 3032 3033 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 3034 if (Global::IsOnline(mData->mMachineState)) 3035 saveSettings(NULL); 3036 3037 return S_OK; 3038 } 3039 3040 STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDropMode) 3041 { 3042 CheckComArgOutPointerValid(aDragAndDropMode); 3043 3044 AutoCaller autoCaller(this); 3045 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3046 3047 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3048 3049 *aDragAndDropMode = mHWData->mDragAndDropMode; 3050 3051 return S_OK; 3052 } 3053 3054 STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode) 3055 { 3056 HRESULT rc = S_OK; 3057 3058 AutoCaller autoCaller(this); 3059 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3060 3061 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3062 3063 alock.release(); 3064 rc = onDragAndDropModeChange(aDragAndDropMode); 3065 alock.acquire(); 3066 if (FAILED(rc)) return rc; 3067 3068 setModified(IsModified_MachineData); 3069 mHWData.backup(); 3070 mHWData->mDragAndDropMode = aDragAndDropMode; 3071 3072 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 3073 if (Global::IsOnline(mData->mMachineState)) 3074 saveSettings(NULL); 3075 3076 return S_OK; 3077 } 3078 3079 STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) 3080 { 3081 CheckComArgOutPointerValid(aPatterns); 3082 3083 AutoCaller autoCaller(this); 3084 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3085 3086 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3087 3088 try 3089 { 3090 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns); 3091 } 3092 catch (...) 3093 { 3094 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 3095 } 3096 3097 return S_OK; 3098 } 3099 3100 STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) 3101 { 3102 AutoCaller autoCaller(this); 3103 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3104 3105 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3106 3107 HRESULT rc = checkStateDependency(MutableStateDep); 3108 if (FAILED(rc)) return rc; 3109 3110 setModified(IsModified_MachineData); 3111 mHWData.backup(); 3112 mHWData->mGuestPropertyNotificationPatterns = aPatterns; 3113 return rc; 3114 } 3115 3116 STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers)) 3117 { 3118 CheckComArgOutSafeArrayPointerValid(aStorageControllers); 3119 3120 AutoCaller autoCaller(this); 3121 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3122 3123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3124 3125 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data()); 3126 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers)); 3127 3128 return S_OK; 3129 } 3130 3131 STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled) 3132 { 3133 CheckComArgOutPointerValid(aEnabled); 3134 3135 AutoCaller autoCaller(this); 3136 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3137 3138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3139 3140 *aEnabled = mUserData->s.fTeleporterEnabled; 3141 3142 return S_OK; 3143 } 3144 3145 STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled) 3146 { 3147 AutoCaller autoCaller(this); 3148 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3149 3150 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3151 3152 /* Only allow it to be set to true when PoweredOff or Aborted. 3153 (Clearing it is always permitted.) */ 3154 if ( aEnabled 3155 && mData->mRegistered 3156 && ( !isSessionMachine() 3157 || ( mData->mMachineState != MachineState_PoweredOff 3158 && mData->mMachineState != MachineState_Teleported 3159 && mData->mMachineState != MachineState_Aborted 3160 ) 3161 ) 3162 ) 3163 return setError(VBOX_E_INVALID_VM_STATE, 3164 tr("The machine is not powered off (state is %s)"), 3165 Global::stringifyMachineState(mData->mMachineState)); 3166 3167 setModified(IsModified_MachineData); 3168 mUserData.backup(); 3169 mUserData->s.fTeleporterEnabled = !!aEnabled; 3170 3171 return S_OK; 3172 } 3173 3174 STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort) 3175 { 3176 CheckComArgOutPointerValid(aPort); 3177 3178 AutoCaller autoCaller(this); 3179 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3180 3181 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3182 3183 *aPort = (ULONG)mUserData->s.uTeleporterPort; 3184 3185 return S_OK; 3186 } 3187 3188 STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort) 3189 { 3190 if (aPort >= _64K) 3191 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort); 3192 3193 AutoCaller autoCaller(this); 3194 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3195 3196 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3197 3198 HRESULT rc = checkStateDependency(MutableStateDep); 3199 if (FAILED(rc)) return rc; 3200 3201 setModified(IsModified_MachineData); 3202 mUserData.backup(); 3203 mUserData->s.uTeleporterPort = (uint32_t)aPort; 3204 3205 return S_OK; 3206 } 3207 3208 STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress) 3209 { 3210 CheckComArgOutPointerValid(aAddress); 3211 3212 AutoCaller autoCaller(this); 3213 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3214 3215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3216 3217 mUserData->s.strTeleporterAddress.cloneTo(aAddress); 3218 3219 return S_OK; 3220 } 3221 3222 STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress) 3223 { 3224 AutoCaller autoCaller(this); 3225 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3226 3227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3228 3229 HRESULT rc = checkStateDependency(MutableStateDep); 3230 if (FAILED(rc)) return rc; 3231 3232 setModified(IsModified_MachineData); 3233 mUserData.backup(); 3234 mUserData->s.strTeleporterAddress = aAddress; 3235 3236 return S_OK; 3237 } 3238 3239 STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword) 3240 { 3241 CheckComArgOutPointerValid(aPassword); 3242 3243 AutoCaller autoCaller(this); 3244 HRESULT hrc = autoCaller.rc(); 3245 if (SUCCEEDED(hrc)) 3246 { 3247 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3248 mUserData->s.strTeleporterPassword.cloneTo(aPassword); 3249 } 3250 3251 return hrc; 3252 } 3253 3254 STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword) 3255 { 3256 /* 3257 * Hash the password first. 3258 */ 3259 Utf8Str strPassword(aPassword); 3260 if (!strPassword.isEmpty()) 3261 { 3262 if (VBoxIsPasswordHashed(&strPassword)) 3263 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please")); 3264 VBoxHashPassword(&strPassword); 3265 } 3266 3267 /* 3268 * Do the update. 3269 */ 3270 AutoCaller autoCaller(this); 3271 HRESULT hrc = autoCaller.rc(); 3272 if (SUCCEEDED(hrc)) 3273 { 3274 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3275 hrc = checkStateDependency(MutableStateDep); 3276 if (SUCCEEDED(hrc)) 3277 { 3278 setModified(IsModified_MachineData); 3279 mUserData.backup(); 3280 mUserData->s.strTeleporterPassword = strPassword; 3281 } 3282 } 3283 3284 return hrc; 3285 } 3286 3287 STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState) 3288 { 3289 CheckComArgOutPointerValid(aState); 3290 3291 AutoCaller autoCaller(this); 3292 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3293 3294 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3295 3296 *aState = mUserData->s.enmFaultToleranceState; 3297 return S_OK; 3298 } 3299 3300 STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState) 3301 { 3302 AutoCaller autoCaller(this); 3303 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3304 3305 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3306 3307 /* @todo deal with running state change. */ 3308 HRESULT rc = checkStateDependency(MutableStateDep); 3309 if (FAILED(rc)) return rc; 3310 3311 setModified(IsModified_MachineData); 3312 mUserData.backup(); 3313 mUserData->s.enmFaultToleranceState = aState; 3314 return S_OK; 3315 } 3316 3317 STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress) 3318 { 3319 CheckComArgOutPointerValid(aAddress); 3320 3321 AutoCaller autoCaller(this); 3322 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3323 3324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3325 3326 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress); 3327 return S_OK; 3328 } 3329 3330 STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress) 3331 { 3332 AutoCaller autoCaller(this); 3333 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3334 3335 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3336 3337 /* @todo deal with running state change. */ 3338 HRESULT rc = checkStateDependency(MutableStateDep); 3339 if (FAILED(rc)) return rc; 3340 3341 setModified(IsModified_MachineData); 3342 mUserData.backup(); 3343 mUserData->s.strFaultToleranceAddress = aAddress; 3344 return S_OK; 3345 } 3346 3347 STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort) 3348 { 3349 CheckComArgOutPointerValid(aPort); 3350 3351 AutoCaller autoCaller(this); 3352 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3353 3354 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3355 3356 *aPort = mUserData->s.uFaultTolerancePort; 3357 return S_OK; 3358 } 3359 3360 STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort) 3361 { 3362 AutoCaller autoCaller(this); 3363 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3364 3365 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3366 3367 /* @todo deal with running state change. */ 3368 HRESULT rc = checkStateDependency(MutableStateDep); 3369 if (FAILED(rc)) return rc; 3370 3371 setModified(IsModified_MachineData); 3372 mUserData.backup(); 3373 mUserData->s.uFaultTolerancePort = aPort; 3374 return S_OK; 3375 } 3376 3377 STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword) 3378 { 3379 CheckComArgOutPointerValid(aPassword); 3380 3381 AutoCaller autoCaller(this); 3382 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3383 3384 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3385 3386 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword); 3387 3388 return S_OK; 3389 } 3390 3391 STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword) 3392 { 3393 AutoCaller autoCaller(this); 3394 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3395 3396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3397 3398 /* @todo deal with running state change. */ 3399 HRESULT rc = checkStateDependency(MutableStateDep); 3400 if (FAILED(rc)) return rc; 3401 3402 setModified(IsModified_MachineData); 3403 mUserData.backup(); 3404 mUserData->s.strFaultTolerancePassword = aPassword; 3405 3406 return S_OK; 3407 } 3408 3409 STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval) 3410 { 3411 CheckComArgOutPointerValid(aInterval); 3412 3413 AutoCaller autoCaller(this); 3414 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3415 3416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3417 3418 *aInterval = mUserData->s.uFaultToleranceInterval; 3419 return S_OK; 3420 } 3421 3422 STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval) 3423 { 3424 AutoCaller autoCaller(this); 3425 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3426 3427 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3428 3429 /* @todo deal with running state change. */ 3430 HRESULT rc = checkStateDependency(MutableStateDep); 3431 if (FAILED(rc)) return rc; 3432 3433 setModified(IsModified_MachineData); 3434 mUserData.backup(); 3435 mUserData->s.uFaultToleranceInterval = aInterval; 3436 return S_OK; 3437 } 3438 3439 STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled) 3440 { 3441 CheckComArgOutPointerValid(aEnabled); 3442 3443 AutoCaller autoCaller(this); 3444 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3445 3446 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3447 3448 *aEnabled = mUserData->s.fRTCUseUTC; 3449 3450 return S_OK; 3451 } 3452 3453 STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled) 3454 { 3455 AutoCaller autoCaller(this); 3456 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3457 3458 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3459 3460 /* Only allow it to be set to true when PoweredOff or Aborted. 3461 (Clearing it is always permitted.) */ 3462 if ( aEnabled 3463 && mData->mRegistered 3464 && ( !isSessionMachine() 3465 || ( mData->mMachineState != MachineState_PoweredOff 3466 && mData->mMachineState != MachineState_Teleported 3467 && mData->mMachineState != MachineState_Aborted 3468 ) 3469 ) 3470 ) 3471 return setError(VBOX_E_INVALID_VM_STATE, 3472 tr("The machine is not powered off (state is %s)"), 3473 Global::stringifyMachineState(mData->mMachineState)); 3474 3475 setModified(IsModified_MachineData); 3476 mUserData.backup(); 3477 mUserData->s.fRTCUseUTC = !!aEnabled; 3478 3479 return S_OK; 3480 } 3481 3482 STDMETHODIMP Machine::COMGETTER(IOCacheEnabled)(BOOL *aEnabled) 3483 { 3484 CheckComArgOutPointerValid(aEnabled); 3485 3486 AutoCaller autoCaller(this); 3487 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3488 3489 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3490 3491 *aEnabled = mHWData->mIOCacheEnabled; 3492 3493 return S_OK; 3494 } 3495 3496 STDMETHODIMP Machine::COMSETTER(IOCacheEnabled)(BOOL aEnabled) 3497 { 3498 AutoCaller autoCaller(this); 3499 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3500 3501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3502 3503 HRESULT rc = checkStateDependency(MutableStateDep); 3504 if (FAILED(rc)) return rc; 3505 3506 setModified(IsModified_MachineData); 3507 mHWData.backup(); 3508 mHWData->mIOCacheEnabled = aEnabled; 3509 3510 return S_OK; 3511 } 3512 3513 STDMETHODIMP Machine::COMGETTER(IOCacheSize)(ULONG *aIOCacheSize) 3514 { 3515 CheckComArgOutPointerValid(aIOCacheSize); 3516 3517 AutoCaller autoCaller(this); 3518 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3519 3520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3521 3522 *aIOCacheSize = mHWData->mIOCacheSize; 3523 3524 return S_OK; 3525 } 3526 3527 STDMETHODIMP Machine::COMSETTER(IOCacheSize)(ULONG aIOCacheSize) 3528 { 3529 AutoCaller autoCaller(this); 3530 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3531 3532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3533 3534 HRESULT rc = checkStateDependency(MutableStateDep); 3535 if (FAILED(rc)) return rc; 3536 3537 setModified(IsModified_MachineData); 3538 mHWData.backup(); 3539 mHWData->mIOCacheSize = aIOCacheSize; 3540 3541 return S_OK; 3542 } 3543 3544 3545 /** 3546 * @note Locks objects! 3547 */ 3548 STDMETHODIMP Machine::LockMachine(ISession *aSession, 3549 LockType_T lockType) 3550 { 3551 CheckComArgNotNull(aSession); 3552 3553 AutoCaller autoCaller(this); 3554 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3555 3556 /* check the session state */ 3557 SessionState_T state; 3558 HRESULT rc = aSession->COMGETTER(State)(&state); 3559 if (FAILED(rc)) return rc; 3560 3561 if (state != SessionState_Unlocked) 3562 return setError(VBOX_E_INVALID_OBJECT_STATE, 3563 tr("The given session is busy")); 3564 3565 // get the client's IInternalSessionControl interface 3566 ComPtr<IInternalSessionControl> pSessionControl = aSession; 3567 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"), 3568 E_INVALIDARG); 3569 3570 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3571 3572 if (!mData->mRegistered) 3573 return setError(E_UNEXPECTED, 3574 tr("The machine '%s' is not registered"), 3575 mUserData->s.strName.c_str()); 3576 3577 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 3578 3579 SessionState_T oldState = mData->mSession.mState; 3580 /* Hack: in case the session is closing and there is a progress object 3581 * which allows waiting for the session to be closed, take the opportunity 3582 * and do a limited wait (max. 1 second). This helps a lot when the system 3583 * is busy and thus session closing can take a little while. */ 3584 if ( mData->mSession.mState == SessionState_Unlocking 3585 && mData->mSession.mProgress) 3586 { 3587 alock.release(); 3588 mData->mSession.mProgress->WaitForCompletion(1000); 3589 alock.acquire(); 3590 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 3591 } 3592 3593 // try again now 3594 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists) 3595 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock: 3596 ) 3597 { 3598 // OK, share the session... we are now dealing with three processes: 3599 // 1) VBoxSVC (where this code runs); 3600 // 2) process C: the caller's client process (who wants a shared session); 3601 // 3) process W: the process which already holds the write lock on the machine (write-locking session) 3602 3603 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL) 3604 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl; 3605 ComAssertRet(!pSessionW.isNull(), E_FAIL); 3606 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine; 3607 AssertReturn(!pSessionMachine.isNull(), E_FAIL); 3608 3609 /* 3610 * Release the lock before calling the client process. It's safe here 3611 * since the only thing to do after we get the lock again is to add 3612 * the remote control to the list (which doesn't directly influence 3613 * anything). 3614 */ 3615 alock.release(); 3616 3617 // get the console of the session holding the write lock (this is a remote call) 3618 ComPtr<IConsole> pConsoleW; 3619 LogFlowThisFunc(("Calling GetRemoteConsole()...\n")); 3620 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam()); 3621 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc)); 3622 if (FAILED(rc)) 3623 // the failure may occur w/o any error info (from RPC), so provide one 3624 return setError(VBOX_E_VM_ERROR, 3625 tr("Failed to get a console object from the direct session (%Rrc)"), rc); 3626 3627 ComAssertRet(!pConsoleW.isNull(), E_FAIL); 3628 3629 // share the session machine and W's console with the caller's session 3630 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 3631 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW); 3632 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 3633 3634 if (FAILED(rc)) 3635 // the failure may occur w/o any error info (from RPC), so provide one 3636 return setError(VBOX_E_VM_ERROR, 3637 tr("Failed to assign the machine to the session (%Rrc)"), rc); 3638 alock.acquire(); 3639 3640 // need to revalidate the state after acquiring the lock again 3641 if (mData->mSession.mState != SessionState_Locked) 3642 { 3643 pSessionControl->Uninitialize(); 3644 return setError(VBOX_E_INVALID_SESSION_STATE, 3645 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"), 3646 mUserData->s.strName.c_str()); 3647 } 3648 3649 // add the caller's session to the list 3650 mData->mSession.mRemoteControls.push_back(pSessionControl); 3651 } 3652 else if ( mData->mSession.mState == SessionState_Locked 3653 || mData->mSession.mState == SessionState_Unlocking 3654 ) 3655 { 3656 // sharing not permitted, or machine still unlocking: 3657 return setError(VBOX_E_INVALID_OBJECT_STATE, 3658 tr("The machine '%s' is already locked for a session (or being unlocked)"), 3659 mUserData->s.strName.c_str()); 3660 } 3661 else 3662 { 3663 // machine is not locked: then write-lock the machine (create the session machine) 3664 3665 // must not be busy 3666 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 3667 3668 // get the caller's session PID 3669 RTPROCESS pid = NIL_RTPROCESS; 3670 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS)); 3671 pSessionControl->GetPID((ULONG*)&pid); 3672 Assert(pid != NIL_RTPROCESS); 3673 3674 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning); 3675 3676 if (fLaunchingVMProcess) 3677 { 3678 // this machine is awaiting for a spawning session to be opened: 3679 // then the calling process must be the one that got started by 3680 // LaunchVMProcess() 3681 3682 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID)); 3683 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid)); 3684 3685 if (mData->mSession.mPID != pid) 3686 return setError(E_ACCESSDENIED, 3687 tr("An unexpected process (PID=0x%08X) has tried to lock the " 3688 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"), 3689 pid, mUserData->s.strName.c_str(), mData->mSession.mPID); 3690 } 3691 3692 // create the mutable SessionMachine from the current machine 3693 ComObjPtr<SessionMachine> sessionMachine; 3694 sessionMachine.createObject(); 3695 rc = sessionMachine->init(this); 3696 AssertComRC(rc); 3697 3698 /* NOTE: doing return from this function after this point but 3699 * before the end is forbidden since it may call SessionMachine::uninit() 3700 * (through the ComObjPtr's destructor) which requests the VirtualBox write 3701 * lock while still holding the Machine lock in alock so that a deadlock 3702 * is possible due to the wrong lock order. */ 3703 3704 if (SUCCEEDED(rc)) 3705 { 3706 /* 3707 * Set the session state to Spawning to protect against subsequent 3708 * attempts to open a session and to unregister the machine after 3709 * we release the lock. 3710 */ 3711 SessionState_T origState = mData->mSession.mState; 3712 mData->mSession.mState = SessionState_Spawning; 3713 3714 /* 3715 * Release the lock before calling the client process -- it will call 3716 * Machine/SessionMachine methods. Releasing the lock here is quite safe 3717 * because the state is Spawning, so that LaunchVMProcess() and 3718 * LockMachine() calls will fail. This method, called before we 3719 * acquire the lock again, will fail because of the wrong PID. 3720 * 3721 * Note that mData->mSession.mRemoteControls accessed outside 3722 * the lock may not be modified when state is Spawning, so it's safe. 3723 */ 3724 alock.release(); 3725 3726 LogFlowThisFunc(("Calling AssignMachine()...\n")); 3727 rc = pSessionControl->AssignMachine(sessionMachine, lockType); 3728 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc)); 3729 3730 /* The failure may occur w/o any error info (from RPC), so provide one */ 3731 if (FAILED(rc)) 3732 setError(VBOX_E_VM_ERROR, 3733 tr("Failed to assign the machine to the session (%Rrc)"), rc); 3734 3735 if ( SUCCEEDED(rc) 3736 && fLaunchingVMProcess 3737 ) 3738 { 3739 /* complete the remote session initialization */ 3740 3741 /* get the console from the direct session */ 3742 ComPtr<IConsole> console; 3743 rc = pSessionControl->GetRemoteConsole(console.asOutParam()); 3744 ComAssertComRC(rc); 3745 3746 if (SUCCEEDED(rc) && !console) 3747 { 3748 ComAssert(!!console); 3749 rc = E_FAIL; 3750 } 3751 3752 /* assign machine & console to the remote session */ 3753 if (SUCCEEDED(rc)) 3754 { 3755 /* 3756 * after LaunchVMProcess(), the first and the only 3757 * entry in remoteControls is that remote session 3758 */ 3759 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 3760 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console); 3761 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 3762 3763 /* The failure may occur w/o any error info (from RPC), so provide one */ 3764 if (FAILED(rc)) 3765 setError(VBOX_E_VM_ERROR, 3766 tr("Failed to assign the machine to the remote session (%Rrc)"), rc); 3767 } 3768 3769 if (FAILED(rc)) 3770 pSessionControl->Uninitialize(); 3771 } 3772 3773 /* acquire the lock again */ 3774 alock.acquire(); 3775 3776 /* Restore the session state */ 3777 mData->mSession.mState = origState; 3778 } 3779 3780 // finalize spawning anyway (this is why we don't return on errors above) 3781 if (fLaunchingVMProcess) 3782 { 3783 /* Note that the progress object is finalized later */ 3784 /** @todo Consider checking mData->mSession.mProgress for cancellation 3785 * around here. */ 3786 3787 /* We don't reset mSession.mPID here because it is necessary for 3788 * SessionMachine::uninit() to reap the child process later. */ 3789 3790 if (FAILED(rc)) 3791 { 3792 /* Close the remote session, remove the remote control from the list 3793 * and reset session state to Closed (@note keep the code in sync 3794 * with the relevant part in openSession()). */ 3795 3796 Assert(mData->mSession.mRemoteControls.size() == 1); 3797 if (mData->mSession.mRemoteControls.size() == 1) 3798 { 3799 ErrorInfoKeeper eik; 3800 mData->mSession.mRemoteControls.front()->Uninitialize(); 3801 } 3802 3803 mData->mSession.mRemoteControls.clear(); 3804 mData->mSession.mState = SessionState_Unlocked; 3805 } 3806 } 3807 else 3808 { 3809 /* memorize PID of the directly opened session */ 3810 if (SUCCEEDED(rc)) 3811 mData->mSession.mPID = pid; 3812 } 3813 3814 if (SUCCEEDED(rc)) 3815 { 3816 /* memorize the direct session control and cache IUnknown for it */ 3817 mData->mSession.mDirectControl = pSessionControl; 3818 mData->mSession.mState = SessionState_Locked; 3819 /* associate the SessionMachine with this Machine */ 3820 mData->mSession.mMachine = sessionMachine; 3821 3822 /* request an IUnknown pointer early from the remote party for later 3823 * identity checks (it will be internally cached within mDirectControl 3824 * at least on XPCOM) */ 3825 ComPtr<IUnknown> unk = mData->mSession.mDirectControl; 3826 NOREF(unk); 3827 } 3828 3829 /* Release the lock since SessionMachine::uninit() locks VirtualBox which 3830 * would break the lock order */ 3831 alock.release(); 3832 3833 /* uninitialize the created session machine on failure */ 3834 if (FAILED(rc)) 3835 sessionMachine->uninit(); 3836 3837 } 3838 3839 if (SUCCEEDED(rc)) 3840 { 3841 /* 3842 * tell the client watcher thread to update the set of 3843 * machines that have open sessions 3844 */ 3845 mParent->updateClientWatcher(); 3846 3847 if (oldState != SessionState_Locked) 3848 /* fire an event */ 3849 mParent->onSessionStateChange(getId(), SessionState_Locked); 3850 } 3851 3852 return rc; 3853 } 3854 3855 /** 3856 * @note Locks objects! 3857 */ 3858 STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, 3859 IN_BSTR aFrontend, 3860 IN_BSTR aEnvironment, 3861 IProgress **aProgress) 3862 { 3863 CheckComArgStr(aFrontend); 3864 Utf8Str strFrontend(aFrontend); 3865 Utf8Str strEnvironment(aEnvironment); 3866 /* "emergencystop" doesn't need the session, so skip the checks/interface 3867 * retrieval. This code doesn't quite fit in here, but introducing a 3868 * special API method would be even more effort, and would require explicit 3869 * support by every API client. It's better to hide the feature a bit. */ 3870 if (strFrontend != "emergencystop") 3871 CheckComArgNotNull(aSession); 3872 CheckComArgOutPointerValid(aProgress); 3873 3874 AutoCaller autoCaller(this); 3875 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3876 3877 HRESULT rc = S_OK; 3878 if (strFrontend.isEmpty()) 3879 { 3880 Bstr bstrFrontend; 3881 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam()); 3882 if (FAILED(rc)) 3883 return rc; 3884 strFrontend = bstrFrontend; 3885 if (strFrontend.isEmpty()) 3886 { 3887 ComPtr<ISystemProperties> systemProperties; 3888 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam()); 3889 if (FAILED(rc)) 3890 return rc; 3891 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam()); 3892 if (FAILED(rc)) 3893 return rc; 3894 strFrontend = bstrFrontend; 3895 } 3896 /* paranoia - emergencystop is not a valid default */ 3897 if (strFrontend == "emergencystop") 3898 strFrontend = Utf8Str::Empty; 3899 } 3900 /* default frontend: Qt GUI */ 3901 if (strFrontend.isEmpty()) 3902 strFrontend = "GUI/Qt"; 3903 3904 if (strFrontend != "emergencystop") 3905 { 3906 /* check the session state */ 3907 SessionState_T state; 3908 rc = aSession->COMGETTER(State)(&state); 3909 if (FAILED(rc)) 3910 return rc; 3911 3912 if (state != SessionState_Unlocked) 3913 return setError(VBOX_E_INVALID_OBJECT_STATE, 3914 tr("The given session is busy")); 3915 3916 /* get the IInternalSessionControl interface */ 3917 ComPtr<IInternalSessionControl> control(aSession); 3918 ComAssertMsgRet(!control.isNull(), 3919 ("No IInternalSessionControl interface"), 3920 E_INVALIDARG); 3921 3922 /* get the teleporter enable state for the progress object init. */ 3923 BOOL fTeleporterEnabled; 3924 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled); 3925 if (FAILED(rc)) 3926 return rc; 3927 3928 /* create a progress object */ 3929 ComObjPtr<ProgressProxy> progress; 3930 progress.createObject(); 3931 rc = progress->init(mParent, 3932 static_cast<IMachine*>(this), 3933 Bstr(tr("Starting VM")).raw(), 3934 TRUE /* aCancelable */, 3935 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */, 3936 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(), 3937 2 /* uFirstOperationWeight */, 3938 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */); 3939 3940 if (SUCCEEDED(rc)) 3941 { 3942 rc = launchVMProcess(control, strFrontend, strEnvironment, progress); 3943 if (SUCCEEDED(rc)) 3944 { 3945 progress.queryInterfaceTo(aProgress); 3946 3947 /* signal the client watcher thread */ 3948 mParent->updateClientWatcher(); 3949 3950 /* fire an event */ 3951 mParent->onSessionStateChange(getId(), SessionState_Spawning); 3952 } 3953 } 3954 } 3955 else 3956 { 3957 /* no progress object - either instant success or failure */ 3958 *aProgress = NULL; 3959 3960 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3961 3962 if (mData->mSession.mState != SessionState_Locked) 3963 return setError(VBOX_E_INVALID_OBJECT_STATE, 3964 tr("The machine '%s' is not locked by a session"), 3965 mUserData->s.strName.c_str()); 3966 3967 /* must have a VM process associated - do not kill normal API clients 3968 * with an open session */ 3969 if (!Global::IsOnline(mData->mMachineState)) 3970 return setError(VBOX_E_INVALID_OBJECT_STATE, 3971 tr("The machine '%s' does not have a VM process"), 3972 mUserData->s.strName.c_str()); 3973 3974 /* forcibly terminate the VM process */ 3975 if (mData->mSession.mPID != NIL_RTPROCESS) 3976 RTProcTerminate(mData->mSession.mPID); 3977 3978 /* signal the client watcher thread, as most likely the client has 3979 * been terminated */ 3980 mParent->updateClientWatcher(); 3981 } 3982 3983 return rc; 3984 } 3985 3986 STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice) 3987 { 3988 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 3989 return setError(E_INVALIDARG, 3990 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 3991 aPosition, SchemaDefs::MaxBootPosition); 3992 3993 if (aDevice == DeviceType_USB) 3994 return setError(E_NOTIMPL, 3995 tr("Booting from USB device is currently not supported")); 3996 3997 AutoCaller autoCaller(this); 3998 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3999 4000 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4001 4002 HRESULT rc = checkStateDependency(MutableStateDep); 4003 if (FAILED(rc)) return rc; 4004 4005 setModified(IsModified_MachineData); 4006 mHWData.backup(); 4007 mHWData->mBootOrder[aPosition - 1] = aDevice; 4008 4009 return S_OK; 4010 } 4011 4012 STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice) 4013 { 4014 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 4015 return setError(E_INVALIDARG, 4016 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 4017 aPosition, SchemaDefs::MaxBootPosition); 4018 4019 AutoCaller autoCaller(this); 4020 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4021 4022 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4023 4024 *aDevice = mHWData->mBootOrder[aPosition - 1]; 4025 4026 return S_OK; 4027 } 4028 4029 STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, 4030 LONG aControllerPort, 4031 LONG aDevice, 4032 DeviceType_T aType, 4033 IMedium *aMedium) 4034 { 4035 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n", 4036 aControllerName, aControllerPort, aDevice, aType, aMedium)); 4037 4038 CheckComArgStrNotEmptyOrNull(aControllerName); 4039 4040 AutoCaller autoCaller(this); 4041 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4042 4043 // request the host lock first, since might be calling Host methods for getting host drives; 4044 // next, protect the media tree all the while we're in here, as well as our member variables 4045 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS); 4046 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4047 4048 HRESULT rc = checkStateDependency(MutableStateDep); 4049 if (FAILED(rc)) return rc; 4050 4051 /// @todo NEWMEDIA implicit machine registration 4052 if (!mData->mRegistered) 4053 return setError(VBOX_E_INVALID_OBJECT_STATE, 4054 tr("Cannot attach storage devices to an unregistered machine")); 4055 4056 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4057 4058 /* Check for an existing controller. */ 4059 ComObjPtr<StorageController> ctl; 4060 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */); 4061 if (FAILED(rc)) return rc; 4062 4063 StorageControllerType_T ctrlType; 4064 rc = ctl->COMGETTER(ControllerType)(&ctrlType); 4065 if (FAILED(rc)) 4066 return setError(E_FAIL, 4067 tr("Could not get type of controller '%ls'"), 4068 aControllerName); 4069 4070 bool fSilent = false; 4071 Utf8Str strReconfig; 4072 4073 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */ 4074 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused")); 4075 if (FAILED(rc)) 4076 return rc; 4077 if ( mData->mMachineState == MachineState_Paused 4078 && strReconfig == "1") 4079 fSilent = true; 4080 4081 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */ 4082 bool fHotplug = false; 4083 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState)) 4084 fHotplug = true; 4085 4086 if (fHotplug && !isControllerHotplugCapable(ctrlType)) 4087 return setError(VBOX_E_INVALID_VM_STATE, 4088 tr("Controller '%ls' does not support hotplugging"), 4089 aControllerName); 4090 4091 // check that the port and device are not out of range 4092 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice); 4093 if (FAILED(rc)) return rc; 4094 4095 /* check if the device slot is already busy */ 4096 MediumAttachment *pAttachTemp; 4097 if ((pAttachTemp = findAttachment(mMediaData->mAttachments, 4098 aControllerName, 4099 aControllerPort, 4100 aDevice))) 4101 { 4102 Medium *pMedium = pAttachTemp->getMedium(); 4103 if (pMedium) 4104 { 4105 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 4106 return setError(VBOX_E_OBJECT_IN_USE, 4107 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 4108 pMedium->getLocationFull().c_str(), 4109 aControllerPort, 4110 aDevice, 4111 aControllerName); 4112 } 4113 else 4114 return setError(VBOX_E_OBJECT_IN_USE, 4115 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 4116 aControllerPort, aDevice, aControllerName); 4117 } 4118 4119 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium); 4120 if (aMedium && medium.isNull()) 4121 return setError(E_INVALIDARG, "The given medium pointer is invalid"); 4122 4123 AutoCaller mediumCaller(medium); 4124 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 4125 4126 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS); 4127 4128 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium)) 4129 && !medium.isNull() 4130 ) 4131 return setError(VBOX_E_OBJECT_IN_USE, 4132 tr("Medium '%s' is already attached to this virtual machine"), 4133 medium->getLocationFull().c_str()); 4134 4135 if (!medium.isNull()) 4136 { 4137 MediumType_T mtype = medium->getType(); 4138 // MediumType_Readonly is also new, but only applies to DVDs and floppies. 4139 // For DVDs it's not written to the config file, so needs no global config 4140 // version bump. For floppies it's a new attribute "type", which is ignored 4141 // by older VirtualBox version, so needs no global config version bump either. 4142 // For hard disks this type is not accepted. 4143 if (mtype == MediumType_MultiAttach) 4144 { 4145 // This type is new with VirtualBox 4.0 and therefore requires settings 4146 // version 1.11 in the settings backend. Unfortunately it is not enough to do 4147 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for 4148 // two reasons: The medium type is a property of the media registry tree, which 4149 // can reside in the global config file (for pre-4.0 media); we would therefore 4150 // possibly need to bump the global config version. We don't want to do that though 4151 // because that might make downgrading to pre-4.0 impossible. 4152 // As a result, we can only use these two new types if the medium is NOT in the 4153 // global registry: 4154 const Guid &uuidGlobalRegistry = mParent->getGlobalRegistryId(); 4155 if ( medium->isInRegistry(uuidGlobalRegistry) 4156 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry() 4157 ) 4158 return setError(VBOX_E_INVALID_OBJECT_STATE, 4159 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached " 4160 "to machines that were created with VirtualBox 4.0 or later"), 4161 medium->getLocationFull().c_str()); 4162 } 4163 } 4164 4165 bool fIndirect = false; 4166 if (!medium.isNull()) 4167 fIndirect = medium->isReadOnly(); 4168 bool associate = true; 4169 4170 do 4171 { 4172 if ( aType == DeviceType_HardDisk 4173 && mMediaData.isBackedUp()) 4174 { 4175 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 4176 4177 /* check if the medium was attached to the VM before we started 4178 * changing attachments in which case the attachment just needs to 4179 * be restored */ 4180 if ((pAttachTemp = findAttachment(oldAtts, medium))) 4181 { 4182 AssertReturn(!fIndirect, E_FAIL); 4183 4184 /* see if it's the same bus/channel/device */ 4185 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice)) 4186 { 4187 /* the simplest case: restore the whole attachment 4188 * and return, nothing else to do */ 4189 mMediaData->mAttachments.push_back(pAttachTemp); 4190 4191 /* Reattach the medium to the VM. */ 4192 if (fHotplug || fSilent) 4193 { 4194 mediumLock.release(); 4195 treeLock.release(); 4196 alock.release(); 4197 4198 MediumLockList *pMediumLockList(new MediumLockList()); 4199 4200 rc = medium->createMediumLockList(true /* fFailIfInaccessible */, 4201 true /* fMediumLockWrite */, 4202 NULL, 4203 *pMediumLockList); 4204 alock.acquire(); 4205 if (FAILED(rc)) 4206 delete pMediumLockList; 4207 else 4208 { 4209 mData->mSession.mLockedMedia.Unlock(); 4210 alock.release(); 4211 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList); 4212 mData->mSession.mLockedMedia.Lock(); 4213 alock.acquire(); 4214 } 4215 alock.release(); 4216 4217 if (SUCCEEDED(rc)) 4218 { 4219 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent); 4220 /* Remove lock list in case of error. */ 4221 if (FAILED(rc)) 4222 { 4223 mData->mSession.mLockedMedia.Unlock(); 4224 mData->mSession.mLockedMedia.Remove(pAttachTemp); 4225 mData->mSession.mLockedMedia.Lock(); 4226 } 4227 } 4228 } 4229 4230 return S_OK; 4231 } 4232 4233 /* bus/channel/device differ; we need a new attachment object, 4234 * but don't try to associate it again */ 4235 associate = false; 4236 break; 4237 } 4238 } 4239 4240 /* go further only if the attachment is to be indirect */ 4241 if (!fIndirect) 4242 break; 4243 4244 /* perform the so called smart attachment logic for indirect 4245 * attachments. Note that smart attachment is only applicable to base 4246 * hard disks. */ 4247 4248 if (medium->getParent().isNull()) 4249 { 4250 /* first, investigate the backup copy of the current hard disk 4251 * attachments to make it possible to re-attach existing diffs to 4252 * another device slot w/o losing their contents */ 4253 if (mMediaData.isBackedUp()) 4254 { 4255 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 4256 4257 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end(); 4258 uint32_t foundLevel = 0; 4259 4260 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 4261 it != oldAtts.end(); 4262 ++it) 4263 { 4264 uint32_t level = 0; 4265 MediumAttachment *pAttach = *it; 4266 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 4267 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 4268 if (pMedium.isNull()) 4269 continue; 4270 4271 if (pMedium->getBase(&level) == medium) 4272 { 4273 /* skip the hard disk if its currently attached (we 4274 * cannot attach the same hard disk twice) */ 4275 if (findAttachment(mMediaData->mAttachments, 4276 pMedium)) 4277 continue; 4278 4279 /* matched device, channel and bus (i.e. attached to the 4280 * same place) will win and immediately stop the search; 4281 * otherwise the attachment that has the youngest 4282 * descendant of medium will be used 4283 */ 4284 if (pAttach->matches(aControllerName, aControllerPort, aDevice)) 4285 { 4286 /* the simplest case: restore the whole attachment 4287 * and return, nothing else to do */ 4288 mMediaData->mAttachments.push_back(*it); 4289 4290 /* Reattach the medium to the VM. */ 4291 if (fHotplug || fSilent) 4292 { 4293 mediumLock.release(); 4294 treeLock.release(); 4295 alock.release(); 4296 4297 MediumLockList *pMediumLockList(new MediumLockList()); 4298 4299 rc = medium->createMediumLockList(true /* fFailIfInaccessible */, 4300 true /* fMediumLockWrite */, 4301 NULL, 4302 *pMediumLockList); 4303 alock.acquire(); 4304 if (FAILED(rc)) 4305 delete pMediumLockList; 4306 else 4307 { 4308 mData->mSession.mLockedMedia.Unlock(); 4309 alock.release(); 4310 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList); 4311 mData->mSession.mLockedMedia.Lock(); 4312 alock.acquire(); 4313 } 4314 alock.release(); 4315 4316 if (SUCCEEDED(rc)) 4317 { 4318 rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent); 4319 /* Remove lock list in case of error. */ 4320 if (FAILED(rc)) 4321 { 4322 mData->mSession.mLockedMedia.Unlock(); 4323 mData->mSession.mLockedMedia.Remove(pAttachTemp); 4324 mData->mSession.mLockedMedia.Lock(); 4325 } 4326 } 4327 } 4328 4329 return S_OK; 4330 } 4331 else if ( foundIt == oldAtts.end() 4332 || level > foundLevel /* prefer younger */ 4333 ) 4334 { 4335 foundIt = it; 4336 foundLevel = level; 4337 } 4338 } 4339 } 4340 4341 if (foundIt != oldAtts.end()) 4342 { 4343 /* use the previously attached hard disk */ 4344 medium = (*foundIt)->getMedium(); 4345 mediumCaller.attach(medium); 4346 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 4347 mediumLock.attach(medium); 4348 /* not implicit, doesn't require association with this VM */ 4349 fIndirect = false; 4350 associate = false; 4351 /* go right to the MediumAttachment creation */ 4352 break; 4353 } 4354 } 4355 4356 /* must give up the medium lock and medium tree lock as below we 4357 * go over snapshots, which needs a lock with higher lock order. */ 4358 mediumLock.release(); 4359 treeLock.release(); 4360 4361 /* then, search through snapshots for the best diff in the given 4362 * hard disk's chain to base the new diff on */ 4363 4364 ComObjPtr<Medium> base; 4365 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot; 4366 while (snap) 4367 { 4368 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS); 4369 4370 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments; 4371 4372 MediumAttachment *pAttachFound = NULL; 4373 uint32_t foundLevel = 0; 4374 4375 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); 4376 it != snapAtts.end(); 4377 ++it) 4378 { 4379 MediumAttachment *pAttach = *it; 4380 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 4381 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 4382 if (pMedium.isNull()) 4383 continue; 4384 4385 uint32_t level = 0; 4386 if (pMedium->getBase(&level) == medium) 4387 { 4388 /* matched device, channel and bus (i.e. attached to the 4389 * same place) will win and immediately stop the search; 4390 * otherwise the attachment that has the youngest 4391 * descendant of medium will be used 4392 */ 4393 if ( pAttach->getDevice() == aDevice 4394 && pAttach->getPort() == aControllerPort 4395 && pAttach->getControllerName() == aControllerName 4396 ) 4397 { 4398 pAttachFound = pAttach; 4399 break; 4400 } 4401 else if ( !pAttachFound 4402 || level > foundLevel /* prefer younger */ 4403 ) 4404 { 4405 pAttachFound = pAttach; 4406 foundLevel = level; 4407 } 4408 } 4409 } 4410 4411 if (pAttachFound) 4412 { 4413 base = pAttachFound->getMedium(); 4414 break; 4415 } 4416 4417 snap = snap->getParent(); 4418 } 4419 4420 /* re-lock medium tree and the medium, as we need it below */ 4421 treeLock.acquire(); 4422 mediumLock.acquire(); 4423 4424 /* found a suitable diff, use it as a base */ 4425 if (!base.isNull()) 4426 { 4427 medium = base; 4428 mediumCaller.attach(medium); 4429 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 4430 mediumLock.attach(medium); 4431 } 4432 } 4433 4434 Utf8Str strFullSnapshotFolder; 4435 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 4436 4437 ComObjPtr<Medium> diff; 4438 diff.createObject(); 4439 // store this diff in the same registry as the parent 4440 Guid uuidRegistryParent; 4441 if (!medium->getFirstRegistryMachineId(uuidRegistryParent)) 4442 { 4443 // parent image has no registry: this can happen if we're attaching a new immutable 4444 // image that has not yet been attached (medium then points to the base and we're 4445 // creating the diff image for the immutable, and the parent is not yet registered); 4446 // put the parent in the machine registry then 4447 mediumLock.release(); 4448 treeLock.release(); 4449 alock.release(); 4450 addMediumToRegistry(medium); 4451 alock.acquire(); 4452 treeLock.acquire(); 4453 mediumLock.acquire(); 4454 medium->getFirstRegistryMachineId(uuidRegistryParent); 4455 } 4456 rc = diff->init(mParent, 4457 medium->getPreferredDiffFormat(), 4458 strFullSnapshotFolder.append(RTPATH_SLASH_STR), 4459 uuidRegistryParent); 4460 if (FAILED(rc)) return rc; 4461 4462 /* Apply the normal locking logic to the entire chain. */ 4463 MediumLockList *pMediumLockList(new MediumLockList()); 4464 mediumLock.release(); 4465 treeLock.release(); 4466 rc = diff->createMediumLockList(true /* fFailIfInaccessible */, 4467 true /* fMediumLockWrite */, 4468 medium, 4469 *pMediumLockList); 4470 treeLock.acquire(); 4471 mediumLock.acquire(); 4472 if (SUCCEEDED(rc)) 4473 { 4474 mediumLock.release(); 4475 treeLock.release(); 4476 rc = pMediumLockList->Lock(); 4477 treeLock.acquire(); 4478 mediumLock.acquire(); 4479 if (FAILED(rc)) 4480 setError(rc, 4481 tr("Could not lock medium when creating diff '%s'"), 4482 diff->getLocationFull().c_str()); 4483 else 4484 { 4485 /* will release the lock before the potentially lengthy 4486 * operation, so protect with the special state */ 4487 MachineState_T oldState = mData->mMachineState; 4488 setMachineState(MachineState_SettingUp); 4489 4490 mediumLock.release(); 4491 treeLock.release(); 4492 alock.release(); 4493 4494 rc = medium->createDiffStorage(diff, 4495 MediumVariant_Standard, 4496 pMediumLockList, 4497 NULL /* aProgress */, 4498 true /* aWait */); 4499 4500 alock.acquire(); 4501 treeLock.acquire(); 4502 mediumLock.acquire(); 4503 4504 setMachineState(oldState); 4505 } 4506 } 4507 4508 /* Unlock the media and free the associated memory. */ 4509 delete pMediumLockList; 4510 4511 if (FAILED(rc)) return rc; 4512 4513 /* use the created diff for the actual attachment */ 4514 medium = diff; 4515 mediumCaller.attach(medium); 4516 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 4517 mediumLock.attach(medium); 4518 } 4519 while (0); 4520 4521 ComObjPtr<MediumAttachment> attachment; 4522 attachment.createObject(); 4523 rc = attachment->init(this, 4524 medium, 4525 aControllerName, 4526 aControllerPort, 4527 aDevice, 4528 aType, 4529 fIndirect, 4530 false /* fPassthrough */, 4531 false /* fTempEject */, 4532 false /* fNonRotational */, 4533 false /* fDiscard */, 4534 Utf8Str::Empty); 4535 if (FAILED(rc)) return rc; 4536 4537 if (associate && !medium.isNull()) 4538 { 4539 // as the last step, associate the medium to the VM 4540 rc = medium->addBackReference(mData->mUuid); 4541 // here we can fail because of Deleting, or being in process of creating a Diff 4542 if (FAILED(rc)) return rc; 4543 4544 mediumLock.release(); 4545 treeLock.release(); 4546 alock.release(); 4547 addMediumToRegistry(medium); 4548 alock.acquire(); 4549 treeLock.acquire(); 4550 mediumLock.acquire(); 4551 } 4552 4553 /* success: finally remember the attachment */ 4554 setModified(IsModified_Storage); 4555 mMediaData.backup(); 4556 mMediaData->mAttachments.push_back(attachment); 4557 4558 mediumLock.release(); 4559 treeLock.release(); 4560 alock.release(); 4561 4562 if (fHotplug || fSilent) 4563 { 4564 if (!medium.isNull()) 4565 { 4566 MediumLockList *pMediumLockList(new MediumLockList()); 4567 4568 rc = medium->createMediumLockList(true /* fFailIfInaccessible */, 4569 true /* fMediumLockWrite */, 4570 NULL, 4571 *pMediumLockList); 4572 alock.acquire(); 4573 if (FAILED(rc)) 4574 delete pMediumLockList; 4575 else 4576 { 4577 mData->mSession.mLockedMedia.Unlock(); 4578 alock.release(); 4579 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList); 4580 mData->mSession.mLockedMedia.Lock(); 4581 alock.acquire(); 4582 } 4583 alock.release(); 4584 } 4585 4586 if (SUCCEEDED(rc)) 4587 { 4588 rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent); 4589 /* Remove lock list in case of error. */ 4590 if (FAILED(rc)) 4591 { 4592 mData->mSession.mLockedMedia.Unlock(); 4593 mData->mSession.mLockedMedia.Remove(attachment); 4594 mData->mSession.mLockedMedia.Lock(); 4595 } 4596 } 4597 } 4598 4599 mParent->saveModifiedRegistries(); 4600 4601 return rc; 4602 } 4603 4604 STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort, 4605 LONG aDevice) 4606 { 4607 CheckComArgStrNotEmptyOrNull(aControllerName); 4608 4609 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 4610 aControllerName, aControllerPort, aDevice)); 4611 4612 AutoCaller autoCaller(this); 4613 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4614 4615 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4616 4617 HRESULT rc = checkStateDependency(MutableStateDep); 4618 if (FAILED(rc)) return rc; 4619 4620 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4621 4622 /* Check for an existing controller. */ 4623 ComObjPtr<StorageController> ctl; 4624 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */); 4625 if (FAILED(rc)) return rc; 4626 4627 StorageControllerType_T ctrlType; 4628 rc = ctl->COMGETTER(ControllerType)(&ctrlType); 4629 if (FAILED(rc)) 4630 return setError(E_FAIL, 4631 tr("Could not get type of controller '%ls'"), 4632 aControllerName); 4633 4634 bool fSilent = false; 4635 Utf8Str strReconfig; 4636 4637 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */ 4638 strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused")); 4639 if (FAILED(rc)) 4640 return rc; 4641 if ( mData->mMachineState == MachineState_Paused 4642 && strReconfig == "1") 4643 fSilent = true; 4644 4645 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */ 4646 bool fHotplug = false; 4647 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState)) 4648 fHotplug = true; 4649 4650 if (fHotplug && !isControllerHotplugCapable(ctrlType)) 4651 return setError(VBOX_E_INVALID_VM_STATE, 4652 tr("Controller '%ls' does not support hotplugging"), 4653 aControllerName); 4654 4655 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4656 aControllerName, 4657 aControllerPort, 4658 aDevice); 4659 if (!pAttach) 4660 return setError(VBOX_E_OBJECT_NOT_FOUND, 4661 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4662 aDevice, aControllerPort, aControllerName); 4663 4664 /* 4665 * The VM has to detach the device before we delete any implicit diffs. 4666 * If this fails we can roll back without loosing data. 4667 */ 4668 if (fHotplug || fSilent) 4669 { 4670 alock.release(); 4671 rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent); 4672 alock.acquire(); 4673 } 4674 if (FAILED(rc)) return rc; 4675 4676 /* If we are here everything went well and we can delete the implicit now. */ 4677 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */); 4678 4679 alock.release(); 4680 4681 mParent->saveModifiedRegistries(); 4682 4683 return rc; 4684 } 4685 4686 STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort, 4687 LONG aDevice, BOOL aPassthrough) 4688 { 4689 CheckComArgStrNotEmptyOrNull(aControllerName); 4690 4691 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n", 4692 aControllerName, aControllerPort, aDevice, aPassthrough)); 4693 4694 AutoCaller autoCaller(this); 4695 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4696 4697 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4698 4699 HRESULT rc = checkStateDependency(MutableStateDep); 4700 if (FAILED(rc)) return rc; 4701 4702 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4703 4704 if (Global::IsOnlineOrTransient(mData->mMachineState)) 4705 return setError(VBOX_E_INVALID_VM_STATE, 4706 tr("Invalid machine state: %s"), 4707 Global::stringifyMachineState(mData->mMachineState)); 4708 4709 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4710 aControllerName, 4711 aControllerPort, 4712 aDevice); 4713 if (!pAttach) 4714 return setError(VBOX_E_OBJECT_NOT_FOUND, 4715 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4716 aDevice, aControllerPort, aControllerName); 4717 4718 4719 setModified(IsModified_Storage); 4720 mMediaData.backup(); 4721 4722 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4723 4724 if (pAttach->getType() != DeviceType_DVD) 4725 return setError(E_INVALIDARG, 4726 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"), 4727 aDevice, aControllerPort, aControllerName); 4728 pAttach->updatePassthrough(!!aPassthrough); 4729 4730 return S_OK; 4731 } 4732 4733 STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort, 4734 LONG aDevice, BOOL aTemporaryEject) 4735 { 4736 CheckComArgStrNotEmptyOrNull(aControllerName); 4737 4738 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n", 4739 aControllerName, aControllerPort, aDevice, aTemporaryEject)); 4740 4741 AutoCaller autoCaller(this); 4742 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4743 4744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4745 4746 HRESULT rc = checkStateDependency(MutableStateDep); 4747 if (FAILED(rc)) return rc; 4748 4749 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4750 aControllerName, 4751 aControllerPort, 4752 aDevice); 4753 if (!pAttach) 4754 return setError(VBOX_E_OBJECT_NOT_FOUND, 4755 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4756 aDevice, aControllerPort, aControllerName); 4757 4758 4759 setModified(IsModified_Storage); 4760 mMediaData.backup(); 4761 4762 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4763 4764 if (pAttach->getType() != DeviceType_DVD) 4765 return setError(E_INVALIDARG, 4766 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"), 4767 aDevice, aControllerPort, aControllerName); 4768 pAttach->updateTempEject(!!aTemporaryEject); 4769 4770 return S_OK; 4771 } 4772 4773 STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort, 4774 LONG aDevice, BOOL aNonRotational) 4775 { 4776 CheckComArgStrNotEmptyOrNull(aControllerName); 4777 4778 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n", 4779 aControllerName, aControllerPort, aDevice, aNonRotational)); 4780 4781 AutoCaller autoCaller(this); 4782 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4783 4784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4785 4786 HRESULT rc = checkStateDependency(MutableStateDep); 4787 if (FAILED(rc)) return rc; 4788 4789 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4790 4791 if (Global::IsOnlineOrTransient(mData->mMachineState)) 4792 return setError(VBOX_E_INVALID_VM_STATE, 4793 tr("Invalid machine state: %s"), 4794 Global::stringifyMachineState(mData->mMachineState)); 4795 4796 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4797 aControllerName, 4798 aControllerPort, 4799 aDevice); 4800 if (!pAttach) 4801 return setError(VBOX_E_OBJECT_NOT_FOUND, 4802 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4803 aDevice, aControllerPort, aControllerName); 4804 4805 4806 setModified(IsModified_Storage); 4807 mMediaData.backup(); 4808 4809 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4810 4811 if (pAttach->getType() != DeviceType_HardDisk) 4812 return setError(E_INVALIDARG, 4813 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"), 4814 aDevice, aControllerPort, aControllerName); 4815 pAttach->updateNonRotational(!!aNonRotational); 4816 4817 return S_OK; 4818 } 4819 4820 STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aControllerPort, 4821 LONG aDevice, BOOL aDiscard) 4822 { 4823 CheckComArgStrNotEmptyOrNull(aControllerName); 4824 4825 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aDiscard=%d\n", 4826 aControllerName, aControllerPort, aDevice, aDiscard)); 4827 4828 AutoCaller autoCaller(this); 4829 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4830 4831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4832 4833 HRESULT rc = checkStateDependency(MutableStateDep); 4834 if (FAILED(rc)) return rc; 4835 4836 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4837 4838 if (Global::IsOnlineOrTransient(mData->mMachineState)) 4839 return setError(VBOX_E_INVALID_VM_STATE, 4840 tr("Invalid machine state: %s"), 4841 Global::stringifyMachineState(mData->mMachineState)); 4842 4843 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4844 aControllerName, 4845 aControllerPort, 4846 aDevice); 4847 if (!pAttach) 4848 return setError(VBOX_E_OBJECT_NOT_FOUND, 4849 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4850 aDevice, aControllerPort, aControllerName); 4851 4852 4853 setModified(IsModified_Storage); 4854 mMediaData.backup(); 4855 4856 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4857 4858 if (pAttach->getType() != DeviceType_HardDisk) 4859 return setError(E_INVALIDARG, 4860 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"), 4861 aDevice, aControllerPort, aControllerName); 4862 pAttach->updateDiscard(!!aDiscard); 4863 4864 return S_OK; 4865 } 4866 4867 STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort, 4868 LONG aDevice) 4869 { 4870 int rc = S_OK; 4871 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 4872 aControllerName, aControllerPort, aDevice)); 4873 4874 rc = SetBandwidthGroupForDevice(aControllerName, aControllerPort, aDevice, NULL); 4875 4876 return rc; 4877 } 4878 4879 STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort, 4880 LONG aDevice, IBandwidthGroup *aBandwidthGroup) 4881 { 4882 CheckComArgStrNotEmptyOrNull(aControllerName); 4883 4884 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 4885 aControllerName, aControllerPort, aDevice)); 4886 4887 AutoCaller autoCaller(this); 4888 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4889 4890 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4891 4892 HRESULT rc = checkStateDependency(MutableStateDep); 4893 if (FAILED(rc)) return rc; 4894 4895 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 4896 4897 if (Global::IsOnlineOrTransient(mData->mMachineState)) 4898 return setError(VBOX_E_INVALID_VM_STATE, 4899 tr("Invalid machine state: %s"), 4900 Global::stringifyMachineState(mData->mMachineState)); 4901 4902 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 4903 aControllerName, 4904 aControllerPort, 4905 aDevice); 4906 if (!pAttach) 4907 return setError(VBOX_E_OBJECT_NOT_FOUND, 4908 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 4909 aDevice, aControllerPort, aControllerName); 4910 4911 4912 setModified(IsModified_Storage); 4913 mMediaData.backup(); 4914 4915 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(aBandwidthGroup); 4916 if (aBandwidthGroup && group.isNull()) 4917 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid"); 4918 4919 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 4920 4921 const Utf8Str strBandwidthGroupOld = pAttach->getBandwidthGroup(); 4922 if (strBandwidthGroupOld.isNotEmpty()) 4923 { 4924 /* Get the bandwidth group object and release it - this must not fail. */ 4925 ComObjPtr<BandwidthGroup> pBandwidthGroupOld; 4926 rc = getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false); 4927 Assert(SUCCEEDED(rc)); 4928 4929 pBandwidthGroupOld->release(); 4930 pAttach->updateBandwidthGroup(Utf8Str::Empty); 4931 } 4932 4933 if (!group.isNull()) 4934 { 4935 group->reference(); 4936 pAttach->updateBandwidthGroup(group->getName()); 4937 } 4938 4939 return S_OK; 4940 } 4941 4942 STDMETHODIMP Machine::AttachDeviceWithoutMedium(IN_BSTR aControllerName, 4943 LONG aControllerPort, 4944 LONG aDevice, 4945 DeviceType_T aType) 4946 { 4947 HRESULT rc = S_OK; 4948 4949 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n", 4950 aControllerName, aControllerPort, aDevice, aType)); 4951 4952 rc = AttachDevice(aControllerName, aControllerPort, aDevice, aType, NULL); 4953 4954 return rc; 4955 } 4956 4957 4958 4959 STDMETHODIMP Machine::UnmountMedium(IN_BSTR aControllerName, 4960 LONG aControllerPort, 4961 LONG aDevice, 4962 BOOL aForce) 4963 { 4964 int rc = S_OK; 4965 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d", 4966 aControllerName, aControllerPort, aForce)); 4967 4968 rc = MountMedium(aControllerName, aControllerPort, aDevice, NULL, aForce); 4969 4970 return rc; 4971 } 4972 4973 STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName, 4974 LONG aControllerPort, 4975 LONG aDevice, 4976 IMedium *aMedium, 4977 BOOL aForce) 4978 { 4979 int rc = S_OK; 4980 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n", 4981 aControllerName, aControllerPort, aDevice, aForce)); 4982 4983 CheckComArgStrNotEmptyOrNull(aControllerName); 4984 4985 AutoCaller autoCaller(this); 4986 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4987 4988 // request the host lock first, since might be calling Host methods for getting host drives; 4989 // next, protect the media tree all the while we're in here, as well as our member variables 4990 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(), 4991 this->lockHandle(), 4992 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4993 4994 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 4995 aControllerName, 4996 aControllerPort, 4997 aDevice); 4998 if (pAttach.isNull()) 4999 return setError(VBOX_E_OBJECT_NOT_FOUND, 5000 tr("No drive attached to device slot %d on port %d of controller '%ls'"), 5001 aDevice, aControllerPort, aControllerName); 5002 5003 /* Remember previously mounted medium. The medium before taking the 5004 * backup is not necessarily the same thing. */ 5005 ComObjPtr<Medium> oldmedium; 5006 oldmedium = pAttach->getMedium(); 5007 5008 ComObjPtr<Medium> pMedium = static_cast<Medium*>(aMedium); 5009 if (aMedium && pMedium.isNull()) 5010 return setError(E_INVALIDARG, "The given medium pointer is invalid"); 5011 5012 AutoCaller mediumCaller(pMedium); 5013 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 5014 5015 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 5016 if (pMedium) 5017 { 5018 DeviceType_T mediumType = pAttach->getType(); 5019 switch (mediumType) 5020 { 5021 case DeviceType_DVD: 5022 case DeviceType_Floppy: 5023 break; 5024 5025 default: 5026 return setError(VBOX_E_INVALID_OBJECT_STATE, 5027 tr("The device at port %d, device %d of controller '%ls' of this virtual machine is not removeable"), 5028 aControllerPort, 5029 aDevice, 5030 aControllerName); 5031 } 5032 } 5033 5034 setModified(IsModified_Storage); 5035 mMediaData.backup(); 5036 5037 { 5038 // The backup operation makes the pAttach reference point to the 5039 // old settings. Re-get the correct reference. 5040 pAttach = findAttachment(mMediaData->mAttachments, 5041 aControllerName, 5042 aControllerPort, 5043 aDevice); 5044 if (!oldmedium.isNull()) 5045 oldmedium->removeBackReference(mData->mUuid); 5046 if (!pMedium.isNull()) 5047 { 5048 pMedium->addBackReference(mData->mUuid); 5049 5050 mediumLock.release(); 5051 multiLock.release(); 5052 addMediumToRegistry(pMedium); 5053 multiLock.acquire(); 5054 mediumLock.acquire(); 5055 } 5056 5057 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 5058 pAttach->updateMedium(pMedium); 5059 } 5060 5061 setModified(IsModified_Storage); 5062 5063 mediumLock.release(); 5064 multiLock.release(); 5065 rc = onMediumChange(pAttach, aForce); 5066 multiLock.acquire(); 5067 mediumLock.acquire(); 5068 5069 /* On error roll back this change only. */ 5070 if (FAILED(rc)) 5071 { 5072 if (!pMedium.isNull()) 5073 pMedium->removeBackReference(mData->mUuid); 5074 pAttach = findAttachment(mMediaData->mAttachments, 5075 aControllerName, 5076 aControllerPort, 5077 aDevice); 5078 /* If the attachment is gone in the meantime, bail out. */ 5079 if (pAttach.isNull()) 5080 return rc; 5081 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 5082 if (!oldmedium.isNull()) 5083 oldmedium->addBackReference(mData->mUuid); 5084 pAttach->updateMedium(oldmedium); 5085 } 5086 5087 mediumLock.release(); 5088 multiLock.release(); 5089 5090 mParent->saveModifiedRegistries(); 5091 5092 return rc; 5093 } 5094 5095 STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName, 5096 LONG aControllerPort, 5097 LONG aDevice, 5098 IMedium **aMedium) 5099 { 5100 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 5101 aControllerName, aControllerPort, aDevice)); 5102 5103 CheckComArgStrNotEmptyOrNull(aControllerName); 5104 CheckComArgOutPointerValid(aMedium); 5105 5106 AutoCaller autoCaller(this); 5107 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5108 5109 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5110 5111 *aMedium = NULL; 5112 5113 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 5114 aControllerName, 5115 aControllerPort, 5116 aDevice); 5117 if (pAttach.isNull()) 5118 return setError(VBOX_E_OBJECT_NOT_FOUND, 5119 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 5120 aDevice, aControllerPort, aControllerName); 5121 5122 pAttach->getMedium().queryInterfaceTo(aMedium); 5123 5124 return S_OK; 5125 } 5126 5127 STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port) 5128 { 5129 CheckComArgOutPointerValid(port); 5130 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts)); 5131 5132 AutoCaller autoCaller(this); 5133 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5134 5135 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5136 5137 mSerialPorts[slot].queryInterfaceTo(port); 5138 5139 return S_OK; 5140 } 5141 5142 STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port) 5143 { 5144 CheckComArgOutPointerValid(port); 5145 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts)); 5146 5147 AutoCaller autoCaller(this); 5148 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5149 5150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5151 5152 mParallelPorts[slot].queryInterfaceTo(port); 5153 5154 return S_OK; 5155 } 5156 5157 STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter) 5158 { 5159 CheckComArgOutPointerValid(adapter); 5160 /* Do not assert if slot is out of range, just return the advertised 5161 status. testdriver/vbox.py triggers this in logVmInfo. */ 5162 if (slot >= mNetworkAdapters.size()) 5163 return setError(E_INVALIDARG, 5164 tr("No network adapter in slot %RU32 (total %RU32 adapters)"), 5165 slot, mNetworkAdapters.size()); 5166 5167 AutoCaller autoCaller(this); 5168 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5169 5170 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5171 5172 mNetworkAdapters[slot].queryInterfaceTo(adapter); 5173 5174 return S_OK; 5175 } 5176 5177 STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys)) 5178 { 5179 CheckComArgOutSafeArrayPointerValid(aKeys); 5180 5181 AutoCaller autoCaller(this); 5182 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5183 5184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5185 5186 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size()); 5187 int i = 0; 5188 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin(); 5189 it != mData->pMachineConfigFile->mapExtraDataItems.end(); 5190 ++it, ++i) 5191 { 5192 const Utf8Str &strKey = it->first; 5193 strKey.cloneTo(&saKeys[i]); 5194 } 5195 saKeys.detachTo(ComSafeArrayOutArg(aKeys)); 5196 5197 return S_OK; 5198 } 5199 5200 /** 5201 * @note Locks this object for reading. 5202 */ 5203 STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey, 5204 BSTR *aValue) 5205 { 5206 CheckComArgStrNotEmptyOrNull(aKey); 5207 CheckComArgOutPointerValid(aValue); 5208 5209 AutoCaller autoCaller(this); 5210 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5211 5212 /* start with nothing found */ 5213 Bstr bstrResult(""); 5214 5215 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5216 5217 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey)); 5218 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 5219 // found: 5220 bstrResult = it->second; // source is a Utf8Str 5221 5222 /* return the result to caller (may be empty) */ 5223 bstrResult.cloneTo(aValue); 5224 5225 return S_OK; 5226 } 5227 5228 /** 5229 * @note Locks mParent for writing + this object for writing. 5230 */ 5231 STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue) 5232 { 5233 CheckComArgStrNotEmptyOrNull(aKey); 5234 5235 AutoCaller autoCaller(this); 5236 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5237 5238 Utf8Str strKey(aKey); 5239 Utf8Str strValue(aValue); 5240 Utf8Str strOldValue; // empty 5241 5242 // locking note: we only hold the read lock briefly to look up the old value, 5243 // then release it and call the onExtraCanChange callbacks. There is a small 5244 // chance of a race insofar as the callback might be called twice if two callers 5245 // change the same key at the same time, but that's a much better solution 5246 // than the deadlock we had here before. The actual changing of the extradata 5247 // is then performed under the write lock and race-free. 5248 5249 // look up the old value first; if nothing has changed then we need not do anything 5250 { 5251 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up 5252 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey); 5253 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 5254 strOldValue = it->second; 5255 } 5256 5257 bool fChanged; 5258 if ((fChanged = (strOldValue != strValue))) 5259 { 5260 // ask for permission from all listeners outside the locks; 5261 // onExtraDataCanChange() only briefly requests the VirtualBox 5262 // lock to copy the list of callbacks to invoke 5263 Bstr error; 5264 Bstr bstrValue(aValue); 5265 5266 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue.raw(), error)) 5267 { 5268 const char *sep = error.isEmpty() ? "" : ": "; 5269 CBSTR err = error.raw(); 5270 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n", 5271 sep, err)); 5272 return setError(E_ACCESSDENIED, 5273 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"), 5274 aKey, 5275 bstrValue.raw(), 5276 sep, 5277 err); 5278 } 5279 5280 // data is changing and change not vetoed: then write it out under the lock 5281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5282 5283 if (isSnapshotMachine()) 5284 { 5285 HRESULT rc = checkStateDependency(MutableStateDep); 5286 if (FAILED(rc)) return rc; 5287 } 5288 5289 if (strValue.isEmpty()) 5290 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey); 5291 else 5292 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue; 5293 // creates a new key if needed 5294 5295 bool fNeedsGlobalSaveSettings = false; 5296 saveSettings(&fNeedsGlobalSaveSettings); 5297 5298 if (fNeedsGlobalSaveSettings) 5299 { 5300 // save the global settings; for that we should hold only the VirtualBox lock 5301 alock.release(); 5302 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS); 5303 mParent->saveSettings(); 5304 } 5305 } 5306 5307 // fire notification outside the lock 5308 if (fChanged) 5309 mParent->onExtraDataChange(mData->mUuid, aKey, aValue); 5310 5311 return S_OK; 5312 } 5313 5314 STDMETHODIMP Machine::SaveSettings() 5315 { 5316 AutoCaller autoCaller(this); 5317 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5318 5319 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS); 5320 5321 /* when there was auto-conversion, we want to save the file even if 5322 * the VM is saved */ 5323 HRESULT rc = checkStateDependency(MutableOrSavedStateDep); 5324 if (FAILED(rc)) return rc; 5325 5326 /* the settings file path may never be null */ 5327 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL); 5328 5329 /* save all VM data excluding snapshots */ 5330 bool fNeedsGlobalSaveSettings = false; 5331 rc = saveSettings(&fNeedsGlobalSaveSettings); 5332 mlock.release(); 5333 5334 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings) 5335 { 5336 // save the global settings; for that we should hold only the VirtualBox lock 5337 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS); 5338 rc = mParent->saveSettings(); 5339 } 5340 5341 return rc; 5342 } 5343 5344 STDMETHODIMP Machine::DiscardSettings() 5345 { 5346 AutoCaller autoCaller(this); 5347 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5348 5349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5350 5351 HRESULT rc = checkStateDependency(MutableStateDep); 5352 if (FAILED(rc)) return rc; 5353 5354 /* 5355 * during this rollback, the session will be notified if data has 5356 * been actually changed 5357 */ 5358 rollback(true /* aNotify */); 5359 5360 return S_OK; 5361 } 5362 5363 /** @note Locks objects! */ 5364 STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode, 5365 ComSafeArrayOut(IMedium*, aMedia)) 5366 { 5367 // use AutoLimitedCaller because this call is valid on inaccessible machines as well 5368 AutoLimitedCaller autoCaller(this); 5369 AssertComRCReturnRC(autoCaller.rc()); 5370 5371 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5372 5373 Guid id(getId()); 5374 5375 if (mData->mSession.mState != SessionState_Unlocked) 5376 return setError(VBOX_E_INVALID_OBJECT_STATE, 5377 tr("Cannot unregister the machine '%s' while it is locked"), 5378 mUserData->s.strName.c_str()); 5379 5380 // wait for state dependents to drop to zero 5381 ensureNoStateDependencies(); 5382 5383 if (!mData->mAccessible) 5384 { 5385 // inaccessible maschines can only be unregistered; uninitialize ourselves 5386 // here because currently there may be no unregistered that are inaccessible 5387 // (this state combination is not supported). Note releasing the caller and 5388 // leaving the lock before calling uninit() 5389 alock.release(); 5390 autoCaller.release(); 5391 5392 uninit(); 5393 5394 mParent->unregisterMachine(this, id); 5395 // calls VirtualBox::saveSettings() 5396 5397 return S_OK; 5398 } 5399 5400 HRESULT rc = S_OK; 5401 5402 // discard saved state 5403 if (mData->mMachineState == MachineState_Saved) 5404 { 5405 // add the saved state file to the list of files the caller should delete 5406 Assert(!mSSData->strStateFilePath.isEmpty()); 5407 mData->llFilesToDelete.push_back(mSSData->strStateFilePath); 5408 5409 mSSData->strStateFilePath.setNull(); 5410 5411 // unconditionally set the machine state to powered off, we now 5412 // know no session has locked the machine 5413 mData->mMachineState = MachineState_PoweredOff; 5414 } 5415 5416 size_t cSnapshots = 0; 5417 if (mData->mFirstSnapshot) 5418 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1; 5419 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly) 5420 // fail now before we start detaching media 5421 return setError(VBOX_E_INVALID_OBJECT_STATE, 5422 tr("Cannot unregister the machine '%s' because it has %d snapshots"), 5423 mUserData->s.strName.c_str(), cSnapshots); 5424 5425 // This list collects the medium objects from all medium attachments 5426 // which we will detach from the machine and its snapshots, in a specific 5427 // order which allows for closing all media without getting "media in use" 5428 // errors, simply by going through the list from the front to the back: 5429 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots 5430 // and must be closed before the parent media from the snapshots, or closing the parents 5431 // will fail because they still have children); 5432 // 2) media from the youngest snapshots followed by those from the parent snapshots until 5433 // the root ("first") snapshot of the machine. 5434 MediaList llMedia; 5435 5436 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible 5437 && mMediaData->mAttachments.size() 5438 ) 5439 { 5440 // we have media attachments: detach them all and add the Medium objects to our list 5441 if (cleanupMode != CleanupMode_UnregisterOnly) 5442 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia); 5443 else 5444 return setError(VBOX_E_INVALID_OBJECT_STATE, 5445 tr("Cannot unregister the machine '%s' because it has %d media attachments"), 5446 mUserData->s.strName.c_str(), mMediaData->mAttachments.size()); 5447 } 5448 5449 if (cSnapshots) 5450 { 5451 // autoCleanup must be true here, or we would have failed above 5452 5453 // add the media from the medium attachments of the snapshots to llMedia 5454 // as well, after the "main" machine media; Snapshot::uninitRecursively() 5455 // calls Machine::detachAllMedia() for the snapshot machine, recursing 5456 // into the children first 5457 5458 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this 5459 MachineState_T oldState = mData->mMachineState; 5460 mData->mMachineState = MachineState_DeletingSnapshot; 5461 5462 // make a copy of the first snapshot so the refcount does not drop to 0 5463 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs 5464 // because of the AutoCaller voodoo) 5465 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot; 5466 5467 // GO! 5468 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete); 5469 5470 mData->mMachineState = oldState; 5471 } 5472 5473 if (FAILED(rc)) 5474 { 5475 rollbackMedia(); 5476 return rc; 5477 } 5478 5479 // commit all the media changes made above 5480 commitMedia(); 5481 5482 mData->mRegistered = false; 5483 5484 // machine lock no longer needed 5485 alock.release(); 5486 5487 // return media to caller 5488 SafeIfaceArray<IMedium> sfaMedia(llMedia); 5489 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia)); 5490 5491 mParent->unregisterMachine(this, id); 5492 // calls VirtualBox::saveSettings() and VirtualBox::saveModifiedRegistries() 5493 5494 return S_OK; 5495 } 5496 5497 struct Machine::DeleteTask 5498 { 5499 ComObjPtr<Machine> pMachine; 5500 RTCList<ComPtr<IMedium> > llMediums; 5501 StringsList llFilesToDelete; 5502 ComObjPtr<Progress> pProgress; 5503 }; 5504 5505 STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress) 5506 { 5507 LogFlowFuncEnter(); 5508 5509 AutoCaller autoCaller(this); 5510 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5511 5512 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5513 5514 HRESULT rc = checkStateDependency(MutableStateDep); 5515 if (FAILED(rc)) return rc; 5516 5517 if (mData->mRegistered) 5518 return setError(VBOX_E_INVALID_VM_STATE, 5519 tr("Cannot delete settings of a registered machine")); 5520 5521 DeleteTask *pTask = new DeleteTask; 5522 pTask->pMachine = this; 5523 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia)); 5524 5525 // collect files to delete 5526 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister() 5527 5528 for (size_t i = 0; i < sfaMedia.size(); ++i) 5529 { 5530 IMedium *pIMedium(sfaMedia[i]); 5531 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium); 5532 if (pMedium.isNull()) 5533 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i); 5534 SafeArray<BSTR> ids; 5535 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids)); 5536 if (FAILED(rc)) return rc; 5537 /* At this point the medium should not have any back references 5538 * anymore. If it has it is attached to another VM and *must* not 5539 * deleted. */ 5540 if (ids.size() < 1) 5541 pTask->llMediums.append(pMedium); 5542 } 5543 if (mData->pMachineConfigFile->fileExists()) 5544 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull); 5545 5546 pTask->pProgress.createObject(); 5547 pTask->pProgress->init(getVirtualBox(), 5548 static_cast<IMachine*>(this) /* aInitiator */, 5549 Bstr(tr("Deleting files")).raw(), 5550 true /* fCancellable */, 5551 pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations 5552 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw()); 5553 5554 int vrc = RTThreadCreate(NULL, 5555 Machine::deleteThread, 5556 (void*)pTask, 5557 0, 5558 RTTHREADTYPE_MAIN_WORKER, 5559 0, 5560 "MachineDelete"); 5561 5562 pTask->pProgress.queryInterfaceTo(aProgress); 5563 5564 if (RT_FAILURE(vrc)) 5565 { 5566 delete pTask; 5567 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc); 5568 } 5569 5570 LogFlowFuncLeave(); 5571 5572 return S_OK; 5573 } 5574 5575 /** 5576 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then 5577 * calls Machine::deleteTaskWorker() on the actual machine object. 5578 * @param Thread 5579 * @param pvUser 5580 * @return 5581 */ 5582 /*static*/ 5583 DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser) 5584 { 5585 LogFlowFuncEnter(); 5586 5587 DeleteTask *pTask = (DeleteTask*)pvUser; 5588 Assert(pTask); 5589 Assert(pTask->pMachine); 5590 Assert(pTask->pProgress); 5591 5592 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask); 5593 pTask->pProgress->notifyComplete(rc); 5594 5595 delete pTask; 5596 5597 LogFlowFuncLeave(); 5598 5599 NOREF(Thread); 5600 5601 return VINF_SUCCESS; 5602 } 5603 5604 /** 5605 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread(). 5606 * @param task 5607 * @return 5608 */ 5609 HRESULT Machine::deleteTaskWorker(DeleteTask &task) 5610 { 5611 AutoCaller autoCaller(this); 5612 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5613 5614 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5615 5616 HRESULT rc = S_OK; 5617 5618 try 5619 { 5620 ULONG uLogHistoryCount = 3; 5621 ComPtr<ISystemProperties> systemProperties; 5622 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam()); 5623 if (FAILED(rc)) throw rc; 5624 5625 if (!systemProperties.isNull()) 5626 { 5627 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount); 5628 if (FAILED(rc)) throw rc; 5629 } 5630 5631 MachineState_T oldState = mData->mMachineState; 5632 setMachineState(MachineState_SettingUp); 5633 alock.release(); 5634 for (size_t i = 0; i < task.llMediums.size(); ++i) 5635 { 5636 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i); 5637 { 5638 AutoCaller mac(pMedium); 5639 if (FAILED(mac.rc())) throw mac.rc(); 5640 Utf8Str strLocation = pMedium->getLocationFull(); 5641 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1); 5642 if (FAILED(rc)) throw rc; 5643 LogFunc(("Deleting file %s\n", strLocation.c_str())); 5644 } 5645 ComPtr<IProgress> pProgress2; 5646 rc = pMedium->DeleteStorage(pProgress2.asOutParam()); 5647 if (FAILED(rc)) throw rc; 5648 rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2); 5649 if (FAILED(rc)) throw rc; 5650 /* Check the result of the asynchrony process. */ 5651 LONG iRc; 5652 rc = pProgress2->COMGETTER(ResultCode)(&iRc); 5653 if (FAILED(rc)) throw rc; 5654 /* If the thread of the progress object has an error, then 5655 * retrieve the error info from there, or it'll be lost. */ 5656 if (FAILED(iRc)) 5657 throw setError(ProgressErrorInfo(pProgress2)); 5658 } 5659 setMachineState(oldState); 5660 alock.acquire(); 5661 5662 // delete the files pushed on the task list by Machine::Delete() 5663 // (this includes saved states of the machine and snapshots and 5664 // medium storage files from the IMedium list passed in, and the 5665 // machine XML file) 5666 StringsList::const_iterator it = task.llFilesToDelete.begin(); 5667 while (it != task.llFilesToDelete.end()) 5668 { 5669 const Utf8Str &strFile = *it; 5670 LogFunc(("Deleting file %s\n", strFile.c_str())); 5671 int vrc = RTFileDelete(strFile.c_str()); 5672 if (RT_FAILURE(vrc)) 5673 throw setError(VBOX_E_IPRT_ERROR, 5674 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc); 5675 5676 ++it; 5677 if (it == task.llFilesToDelete.end()) 5678 { 5679 rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1); 5680 if (FAILED(rc)) throw rc; 5681 break; 5682 } 5683 5684 rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1); 5685 if (FAILED(rc)) throw rc; 5686 } 5687 5688 /* delete the settings only when the file actually exists */ 5689 if (mData->pMachineConfigFile->fileExists()) 5690 { 5691 /* Delete any backup or uncommitted XML files. Ignore failures. 5692 See the fSafe parameter of xml::XmlFileWriter::write for details. */ 5693 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */ 5694 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff); 5695 RTFileDelete(otherXml.c_str()); 5696 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff); 5697 RTFileDelete(otherXml.c_str()); 5698 5699 /* delete the Logs folder, nothing important should be left 5700 * there (we don't check for errors because the user might have 5701 * some private files there that we don't want to delete) */ 5702 Utf8Str logFolder; 5703 getLogFolder(logFolder); 5704 Assert(logFolder.length()); 5705 if (RTDirExists(logFolder.c_str())) 5706 { 5707 /* Delete all VBox.log[.N] files from the Logs folder 5708 * (this must be in sync with the rotation logic in 5709 * Console::powerUpThread()). Also, delete the VBox.png[.N] 5710 * files that may have been created by the GUI. */ 5711 Utf8Str log = Utf8StrFmt("%s%cVBox.log", 5712 logFolder.c_str(), RTPATH_DELIMITER); 5713 RTFileDelete(log.c_str()); 5714 log = Utf8StrFmt("%s%cVBox.png", 5715 logFolder.c_str(), RTPATH_DELIMITER); 5716 RTFileDelete(log.c_str()); 5717 for (int i = uLogHistoryCount; i > 0; i--) 5718 { 5719 log = Utf8StrFmt("%s%cVBox.log.%d", 5720 logFolder.c_str(), RTPATH_DELIMITER, i); 5721 RTFileDelete(log.c_str()); 5722 log = Utf8StrFmt("%s%cVBox.png.%d", 5723 logFolder.c_str(), RTPATH_DELIMITER, i); 5724 RTFileDelete(log.c_str()); 5725 } 5726 5727 RTDirRemove(logFolder.c_str()); 5728 } 5729 5730 /* delete the Snapshots folder, nothing important should be left 5731 * there (we don't check for errors because the user might have 5732 * some private files there that we don't want to delete) */ 5733 Utf8Str strFullSnapshotFolder; 5734 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 5735 Assert(!strFullSnapshotFolder.isEmpty()); 5736 if (RTDirExists(strFullSnapshotFolder.c_str())) 5737 RTDirRemove(strFullSnapshotFolder.c_str()); 5738 5739 // delete the directory that contains the settings file, but only 5740 // if it matches the VM name 5741 Utf8Str settingsDir; 5742 if (isInOwnDir(&settingsDir)) 5743 RTDirRemove(settingsDir.c_str()); 5744 } 5745 5746 alock.release(); 5747 5748 mParent->saveModifiedRegistries(); 5749 } 5750 catch (HRESULT aRC) { rc = aRC; } 5751 5752 return rc; 5753 } 5754 5755 STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot) 5756 { 5757 CheckComArgOutPointerValid(aSnapshot); 5758 5759 AutoCaller autoCaller(this); 5760 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5761 5762 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5763 5764 ComObjPtr<Snapshot> pSnapshot; 5765 HRESULT rc; 5766 5767 if (!aNameOrId || !*aNameOrId) 5768 // null case (caller wants root snapshot): findSnapshotById() handles this 5769 rc = findSnapshotById(Guid(), pSnapshot, true /* aSetError */); 5770 else 5771 { 5772 Guid uuid(aNameOrId); 5773 if (uuid.isValid()) 5774 rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */); 5775 else 5776 rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */); 5777 } 5778 pSnapshot.queryInterfaceTo(aSnapshot); 5779 5780 return rc; 5781 } 5782 5783 STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount) 5784 { 5785 CheckComArgStrNotEmptyOrNull(aName); 5786 CheckComArgStrNotEmptyOrNull(aHostPath); 5787 5788 AutoCaller autoCaller(this); 5789 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5790 5791 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5792 5793 HRESULT rc = checkStateDependency(MutableStateDep); 5794 if (FAILED(rc)) return rc; 5795 5796 Utf8Str strName(aName); 5797 5798 ComObjPtr<SharedFolder> sharedFolder; 5799 rc = findSharedFolder(strName, sharedFolder, false /* aSetError */); 5800 if (SUCCEEDED(rc)) 5801 return setError(VBOX_E_OBJECT_IN_USE, 5802 tr("Shared folder named '%s' already exists"), 5803 strName.c_str()); 5804 5805 sharedFolder.createObject(); 5806 rc = sharedFolder->init(getMachine(), 5807 strName, 5808 aHostPath, 5809 !!aWritable, 5810 !!aAutoMount, 5811 true /* fFailOnError */); 5812 if (FAILED(rc)) return rc; 5813 5814 setModified(IsModified_SharedFolders); 5815 mHWData.backup(); 5816 mHWData->mSharedFolders.push_back(sharedFolder); 5817 5818 /* inform the direct session if any */ 5819 alock.release(); 5820 onSharedFolderChange(); 5821 5822 return S_OK; 5823 } 5824 5825 STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName) 5826 { 5827 CheckComArgStrNotEmptyOrNull(aName); 5828 5829 AutoCaller autoCaller(this); 5830 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5831 5832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5833 5834 HRESULT rc = checkStateDependency(MutableStateDep); 5835 if (FAILED(rc)) return rc; 5836 5837 ComObjPtr<SharedFolder> sharedFolder; 5838 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */); 5839 if (FAILED(rc)) return rc; 5840 5841 setModified(IsModified_SharedFolders); 5842 mHWData.backup(); 5843 mHWData->mSharedFolders.remove(sharedFolder); 5844 5845 /* inform the direct session if any */ 5846 alock.release(); 5847 onSharedFolderChange(); 5848 5849 return S_OK; 5850 } 5851 5852 STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow) 5853 { 5854 CheckComArgOutPointerValid(aCanShow); 5855 5856 /* start with No */ 5857 *aCanShow = FALSE; 5858 5859 AutoCaller autoCaller(this); 5860 AssertComRCReturnRC(autoCaller.rc()); 5861 5862 ComPtr<IInternalSessionControl> directControl; 5863 { 5864 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5865 5866 if (mData->mSession.mState != SessionState_Locked) 5867 return setError(VBOX_E_INVALID_VM_STATE, 5868 tr("Machine is not locked for session (session state: %s)"), 5869 Global::stringifySessionState(mData->mSession.mState)); 5870 5871 directControl = mData->mSession.mDirectControl; 5872 } 5873 5874 /* ignore calls made after #OnSessionEnd() is called */ 5875 if (!directControl) 5876 return S_OK; 5877 5878 LONG64 dummy; 5879 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy); 5880 } 5881 5882 STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId) 5883 { 5884 CheckComArgOutPointerValid(aWinId); 5885 5886 AutoCaller autoCaller(this); 5887 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 5888 5889 ComPtr<IInternalSessionControl> directControl; 5890 { 5891 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5892 5893 if (mData->mSession.mState != SessionState_Locked) 5894 return setError(E_FAIL, 5895 tr("Machine is not locked for session (session state: %s)"), 5896 Global::stringifySessionState(mData->mSession.mState)); 5897 5898 directControl = mData->mSession.mDirectControl; 5899 } 5900 5901 /* ignore calls made after #OnSessionEnd() is called */ 5902 if (!directControl) 5903 return S_OK; 5904 5905 BOOL dummy; 5906 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId); 5907 } 5908 5909 #ifdef VBOX_WITH_GUEST_PROPS 5910 /** 5911 * Look up a guest property in VBoxSVC's internal structures. 5912 */ 5913 HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName, 5914 BSTR *aValue, 5915 LONG64 *aTimestamp, 5916 BSTR *aFlags) const 5917 { 5918 using namespace guestProp; 5919 5920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5921 Utf8Str strName(aName); 5922 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName); 5923 5924 if (it != mHWData->mGuestProperties.end()) 5925 { 5926 char szFlags[MAX_FLAGS_LEN + 1]; 5927 it->second.strValue.cloneTo(aValue); 5928 *aTimestamp = it->second.mTimestamp; 5929 writeFlags(it->second.mFlags, szFlags); 5930 Bstr(szFlags).cloneTo(aFlags); 5931 } 5932 5933 return S_OK; 5934 } 5935 5936 /** 5937 * Query the VM that a guest property belongs to for the property. 5938 * @returns E_ACCESSDENIED if the VM process is not available or not 5939 * currently handling queries and the lookup should then be done in 5940 * VBoxSVC. 5941 */ 5942 HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName, 5943 BSTR *aValue, 5944 LONG64 *aTimestamp, 5945 BSTR *aFlags) const 5946 { 5947 HRESULT rc; 5948 ComPtr<IInternalSessionControl> directControl; 5949 directControl = mData->mSession.mDirectControl; 5950 5951 /* fail if we were called after #OnSessionEnd() is called. This is a 5952 * silly race condition. */ 5953 5954 if (!directControl) 5955 rc = E_ACCESSDENIED; 5956 else 5957 rc = directControl->AccessGuestProperty(aName, NULL, NULL, 5958 false /* isSetter */, 5959 aValue, aTimestamp, aFlags); 5960 return rc; 5961 } 5962 #endif // VBOX_WITH_GUEST_PROPS 5963 5964 STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName, 5965 BSTR *aValue, 5966 LONG64 *aTimestamp, 5967 BSTR *aFlags) 5968 { 5969 #ifndef VBOX_WITH_GUEST_PROPS 5970 ReturnComNotImplemented(); 5971 #else // VBOX_WITH_GUEST_PROPS 5972 CheckComArgStrNotEmptyOrNull(aName); 5973 CheckComArgOutPointerValid(aValue); 5974 CheckComArgOutPointerValid(aTimestamp); 5975 CheckComArgOutPointerValid(aFlags); 5976 5977 AutoCaller autoCaller(this); 5978 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5979 5980 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags); 5981 if (rc == E_ACCESSDENIED) 5982 /* The VM is not running or the service is not (yet) accessible */ 5983 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags); 5984 return rc; 5985 #endif // VBOX_WITH_GUEST_PROPS 5986 } 5987 5988 STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue) 5989 { 5990 LONG64 dummyTimestamp; 5991 Bstr dummyFlags; 5992 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam()); 5993 } 5994 5995 STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp) 5996 { 5997 Bstr dummyValue; 5998 Bstr dummyFlags; 5999 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam()); 6000 } 6001 6002 #ifdef VBOX_WITH_GUEST_PROPS 6003 /** 6004 * Set a guest property in VBoxSVC's internal structures. 6005 */ 6006 HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, 6007 IN_BSTR aFlags) 6008 { 6009 using namespace guestProp; 6010 6011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6012 HRESULT rc = S_OK; 6013 6014 rc = checkStateDependency(MutableStateDep); 6015 if (FAILED(rc)) return rc; 6016 6017 try 6018 { 6019 Utf8Str utf8Name(aName); 6020 Utf8Str utf8Flags(aFlags); 6021 uint32_t fFlags = NILFLAG; 6022 if ( aFlags != NULL 6023 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))) 6024 return setError(E_INVALIDARG, 6025 tr("Invalid guest property flag values: '%ls'"), 6026 aFlags); 6027 6028 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0'; 6029 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name); 6030 if (it == mHWData->mGuestProperties.end()) 6031 { 6032 if (!fDelete) 6033 { 6034 setModified(IsModified_MachineData); 6035 mHWData.backupEx(); 6036 6037 RTTIMESPEC time; 6038 HWData::GuestProperty prop; 6039 prop.strValue = aValue; 6040 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 6041 prop.mFlags = fFlags; 6042 mHWData->mGuestProperties[Utf8Str(aName)] = prop; 6043 } 6044 } 6045 else 6046 { 6047 if (it->second.mFlags & (RDONLYHOST)) 6048 { 6049 rc = setError(E_ACCESSDENIED, 6050 tr("The property '%ls' cannot be changed by the host"), 6051 aName); 6052 } 6053 else 6054 { 6055 setModified(IsModified_MachineData); 6056 mHWData.backupEx(); 6057 6058 /* The backupEx() operation invalidates our iterator, 6059 * so get a new one. */ 6060 it = mHWData->mGuestProperties.find(utf8Name); 6061 Assert(it != mHWData->mGuestProperties.end()); 6062 6063 if (!fDelete) 6064 { 6065 RTTIMESPEC time; 6066 it->second.strValue = aValue; 6067 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 6068 it->second.mFlags = fFlags; 6069 } 6070 else 6071 mHWData->mGuestProperties.erase(it); 6072 } 6073 } 6074 6075 if ( SUCCEEDED(rc) 6076 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty() 6077 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(), 6078 RTSTR_MAX, 6079 utf8Name.c_str(), 6080 RTSTR_MAX, 6081 NULL) 6082 ) 6083 ) 6084 { 6085 alock.release(); 6086 6087 mParent->onGuestPropertyChange(mData->mUuid, aName, 6088 aValue ? aValue : Bstr("").raw(), 6089 aFlags ? aFlags : Bstr("").raw()); 6090 } 6091 } 6092 catch (std::bad_alloc &) 6093 { 6094 rc = E_OUTOFMEMORY; 6095 } 6096 6097 return rc; 6098 } 6099 6100 /** 6101 * Set a property on the VM that that property belongs to. 6102 * @returns E_ACCESSDENIED if the VM process is not available or not 6103 * currently handling queries and the setting should then be done in 6104 * VBoxSVC. 6105 */ 6106 HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue, 6107 IN_BSTR aFlags) 6108 { 6109 HRESULT rc; 6110 6111 try 6112 { 6113 ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl; 6114 6115 BSTR dummy = NULL; /* will not be changed (setter) */ 6116 LONG64 dummy64; 6117 if (!directControl) 6118 rc = E_ACCESSDENIED; 6119 else 6120 /** @todo Fix when adding DeleteGuestProperty(), see defect. */ 6121 rc = directControl->AccessGuestProperty(aName, aValue, aFlags, 6122 true /* isSetter */, 6123 &dummy, &dummy64, &dummy); 6124 } 6125 catch (std::bad_alloc &) 6126 { 6127 rc = E_OUTOFMEMORY; 6128 } 6129 6130 return rc; 6131 } 6132 #endif // VBOX_WITH_GUEST_PROPS 6133 6134 STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue, 6135 IN_BSTR aFlags) 6136 { 6137 #ifndef VBOX_WITH_GUEST_PROPS 6138 ReturnComNotImplemented(); 6139 #else // VBOX_WITH_GUEST_PROPS 6140 CheckComArgStrNotEmptyOrNull(aName); 6141 CheckComArgMaybeNull(aFlags); 6142 CheckComArgMaybeNull(aValue); 6143 6144 AutoCaller autoCaller(this); 6145 if (FAILED(autoCaller.rc())) 6146 return autoCaller.rc(); 6147 6148 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags); 6149 if (rc == E_ACCESSDENIED) 6150 /* The VM is not running or the service is not (yet) accessible */ 6151 rc = setGuestPropertyToService(aName, aValue, aFlags); 6152 return rc; 6153 #endif // VBOX_WITH_GUEST_PROPS 6154 } 6155 6156 STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue) 6157 { 6158 return SetGuestProperty(aName, aValue, NULL); 6159 } 6160 6161 STDMETHODIMP Machine::DeleteGuestProperty(IN_BSTR aName) 6162 { 6163 return SetGuestProperty(aName, NULL, NULL); 6164 } 6165 6166 #ifdef VBOX_WITH_GUEST_PROPS 6167 /** 6168 * Enumerate the guest properties in VBoxSVC's internal structures. 6169 */ 6170 HRESULT Machine::enumerateGuestPropertiesInService 6171 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 6172 ComSafeArrayOut(BSTR, aValues), 6173 ComSafeArrayOut(LONG64, aTimestamps), 6174 ComSafeArrayOut(BSTR, aFlags)) 6175 { 6176 using namespace guestProp; 6177 6178 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6179 Utf8Str strPatterns(aPatterns); 6180 6181 HWData::GuestPropertyMap propMap; 6182 6183 /* 6184 * Look for matching patterns and build up a list. 6185 */ 6186 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin(); 6187 while (it != mHWData->mGuestProperties.end()) 6188 { 6189 if ( strPatterns.isEmpty() 6190 || RTStrSimplePatternMultiMatch(strPatterns.c_str(), 6191 RTSTR_MAX, 6192 it->first.c_str(), 6193 RTSTR_MAX, 6194 NULL) 6195 ) 6196 { 6197 propMap.insert(*it); 6198 } 6199 6200 it++; 6201 } 6202 6203 alock.release(); 6204 6205 /* 6206 * And build up the arrays for returning the property information. 6207 */ 6208 size_t cEntries = propMap.size(); 6209 SafeArray<BSTR> names(cEntries); 6210 SafeArray<BSTR> values(cEntries); 6211 SafeArray<LONG64> timestamps(cEntries); 6212 SafeArray<BSTR> flags(cEntries); 6213 size_t iProp = 0; 6214 6215 it = propMap.begin(); 6216 while (it != propMap.end()) 6217 { 6218 char szFlags[MAX_FLAGS_LEN + 1]; 6219 it->first.cloneTo(&names[iProp]); 6220 it->second.strValue.cloneTo(&values[iProp]); 6221 timestamps[iProp] = it->second.mTimestamp; 6222 writeFlags(it->second.mFlags, szFlags); 6223 Bstr(szFlags).cloneTo(&flags[iProp++]); 6224 it++; 6225 } 6226 names.detachTo(ComSafeArrayOutArg(aNames)); 6227 values.detachTo(ComSafeArrayOutArg(aValues)); 6228 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps)); 6229 flags.detachTo(ComSafeArrayOutArg(aFlags)); 6230 return S_OK; 6231 } 6232 6233 /** 6234 * Enumerate the properties managed by a VM. 6235 * @returns E_ACCESSDENIED if the VM process is not available or not 6236 * currently handling queries and the setting should then be done in 6237 * VBoxSVC. 6238 */ 6239 HRESULT Machine::enumerateGuestPropertiesOnVM 6240 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 6241 ComSafeArrayOut(BSTR, aValues), 6242 ComSafeArrayOut(LONG64, aTimestamps), 6243 ComSafeArrayOut(BSTR, aFlags)) 6244 { 6245 HRESULT rc; 6246 ComPtr<IInternalSessionControl> directControl; 6247 directControl = mData->mSession.mDirectControl; 6248 6249 if (!directControl) 6250 rc = E_ACCESSDENIED; 6251 else 6252 rc = directControl->EnumerateGuestProperties 6253 (aPatterns, ComSafeArrayOutArg(aNames), 6254 ComSafeArrayOutArg(aValues), 6255 ComSafeArrayOutArg(aTimestamps), 6256 ComSafeArrayOutArg(aFlags)); 6257 return rc; 6258 } 6259 #endif // VBOX_WITH_GUEST_PROPS 6260 6261 STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns, 6262 ComSafeArrayOut(BSTR, aNames), 6263 ComSafeArrayOut(BSTR, aValues), 6264 ComSafeArrayOut(LONG64, aTimestamps), 6265 ComSafeArrayOut(BSTR, aFlags)) 6266 { 6267 #ifndef VBOX_WITH_GUEST_PROPS 6268 ReturnComNotImplemented(); 6269 #else // VBOX_WITH_GUEST_PROPS 6270 CheckComArgMaybeNull(aPatterns); 6271 CheckComArgOutSafeArrayPointerValid(aNames); 6272 CheckComArgOutSafeArrayPointerValid(aValues); 6273 CheckComArgOutSafeArrayPointerValid(aTimestamps); 6274 CheckComArgOutSafeArrayPointerValid(aFlags); 6275 6276 AutoCaller autoCaller(this); 6277 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6278 6279 HRESULT rc = enumerateGuestPropertiesOnVM 6280 (aPatterns, ComSafeArrayOutArg(aNames), 6281 ComSafeArrayOutArg(aValues), 6282 ComSafeArrayOutArg(aTimestamps), 6283 ComSafeArrayOutArg(aFlags)); 6284 if (rc == E_ACCESSDENIED) 6285 /* The VM is not running or the service is not (yet) accessible */ 6286 rc = enumerateGuestPropertiesInService 6287 (aPatterns, ComSafeArrayOutArg(aNames), 6288 ComSafeArrayOutArg(aValues), 6289 ComSafeArrayOutArg(aTimestamps), 6290 ComSafeArrayOutArg(aFlags)); 6291 return rc; 6292 #endif // VBOX_WITH_GUEST_PROPS 6293 } 6294 6295 STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName, 6296 ComSafeArrayOut(IMediumAttachment*, aAttachments)) 6297 { 6298 MediaData::AttachmentList atts; 6299 6300 HRESULT rc = getMediumAttachmentsOfController(aName, atts); 6301 if (FAILED(rc)) return rc; 6302 6303 SafeIfaceArray<IMediumAttachment> attachments(atts); 6304 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 6305 6306 return S_OK; 6307 } 6308 6309 STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName, 6310 LONG aControllerPort, 6311 LONG aDevice, 6312 IMediumAttachment **aAttachment) 6313 { 6314 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 6315 aControllerName, aControllerPort, aDevice)); 6316 6317 CheckComArgStrNotEmptyOrNull(aControllerName); 6318 CheckComArgOutPointerValid(aAttachment); 6319 6320 AutoCaller autoCaller(this); 6321 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6322 6323 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6324 6325 *aAttachment = NULL; 6326 6327 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 6328 aControllerName, 6329 aControllerPort, 6330 aDevice); 6331 if (pAttach.isNull()) 6332 return setError(VBOX_E_OBJECT_NOT_FOUND, 6333 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 6334 aDevice, aControllerPort, aControllerName); 6335 6336 pAttach.queryInterfaceTo(aAttachment); 6337 6338 return S_OK; 6339 } 6340 6341 STDMETHODIMP Machine::AddStorageController(IN_BSTR aName, 6342 StorageBus_T aConnectionType, 6343 IStorageController **controller) 6344 { 6345 CheckComArgStrNotEmptyOrNull(aName); 6346 6347 if ( (aConnectionType <= StorageBus_Null) 6348 || (aConnectionType > StorageBus_SAS)) 6349 return setError(E_INVALIDARG, 6350 tr("Invalid connection type: %d"), 6351 aConnectionType); 6352 6353 AutoCaller autoCaller(this); 6354 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6355 6356 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6357 6358 HRESULT rc = checkStateDependency(MutableStateDep); 6359 if (FAILED(rc)) return rc; 6360 6361 /* try to find one with the name first. */ 6362 ComObjPtr<StorageController> ctrl; 6363 6364 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */); 6365 if (SUCCEEDED(rc)) 6366 return setError(VBOX_E_OBJECT_IN_USE, 6367 tr("Storage controller named '%ls' already exists"), 6368 aName); 6369 6370 ctrl.createObject(); 6371 6372 /* get a new instance number for the storage controller */ 6373 ULONG ulInstance = 0; 6374 bool fBootable = true; 6375 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 6376 it != mStorageControllers->end(); 6377 ++it) 6378 { 6379 if ((*it)->getStorageBus() == aConnectionType) 6380 { 6381 ULONG ulCurInst = (*it)->getInstance(); 6382 6383 if (ulCurInst >= ulInstance) 6384 ulInstance = ulCurInst + 1; 6385 6386 /* Only one controller of each type can be marked as bootable. */ 6387 if ((*it)->getBootable()) 6388 fBootable = false; 6389 } 6390 } 6391 6392 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable); 6393 if (FAILED(rc)) return rc; 6394 6395 setModified(IsModified_Storage); 6396 mStorageControllers.backup(); 6397 mStorageControllers->push_back(ctrl); 6398 6399 ctrl.queryInterfaceTo(controller); 6400 6401 /* inform the direct session if any */ 6402 alock.release(); 6403 onStorageControllerChange(); 6404 6405 return S_OK; 6406 } 6407 6408 STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName, 6409 IStorageController **aStorageController) 6410 { 6411 CheckComArgStrNotEmptyOrNull(aName); 6412 6413 AutoCaller autoCaller(this); 6414 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6415 6416 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6417 6418 ComObjPtr<StorageController> ctrl; 6419 6420 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 6421 if (SUCCEEDED(rc)) 6422 ctrl.queryInterfaceTo(aStorageController); 6423 6424 return rc; 6425 } 6426 6427 STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance, 6428 IStorageController **aStorageController) 6429 { 6430 AutoCaller autoCaller(this); 6431 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6432 6433 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6434 6435 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 6436 it != mStorageControllers->end(); 6437 ++it) 6438 { 6439 if ((*it)->getInstance() == aInstance) 6440 { 6441 (*it).queryInterfaceTo(aStorageController); 6442 return S_OK; 6443 } 6444 } 6445 6446 return setError(VBOX_E_OBJECT_NOT_FOUND, 6447 tr("Could not find a storage controller with instance number '%lu'"), 6448 aInstance); 6449 } 6450 6451 STDMETHODIMP Machine::SetStorageControllerBootable(IN_BSTR aName, BOOL fBootable) 6452 { 6453 AutoCaller autoCaller(this); 6454 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6455 6456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6457 6458 HRESULT rc = checkStateDependency(MutableStateDep); 6459 if (FAILED(rc)) return rc; 6460 6461 ComObjPtr<StorageController> ctrl; 6462 6463 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 6464 if (SUCCEEDED(rc)) 6465 { 6466 /* Ensure that only one controller of each type is marked as bootable. */ 6467 if (fBootable == TRUE) 6468 { 6469 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 6470 it != mStorageControllers->end(); 6471 ++it) 6472 { 6473 ComObjPtr<StorageController> aCtrl = (*it); 6474 6475 if ( (aCtrl->getName() != Utf8Str(aName)) 6476 && aCtrl->getBootable() == TRUE 6477 && aCtrl->getStorageBus() == ctrl->getStorageBus() 6478 && aCtrl->getControllerType() == ctrl->getControllerType()) 6479 { 6480 aCtrl->setBootable(FALSE); 6481 break; 6482 } 6483 } 6484 } 6485 6486 if (SUCCEEDED(rc)) 6487 { 6488 ctrl->setBootable(fBootable); 6489 setModified(IsModified_Storage); 6490 } 6491 } 6492 6493 if (SUCCEEDED(rc)) 6494 { 6495 /* inform the direct session if any */ 6496 alock.release(); 6497 onStorageControllerChange(); 6498 } 6499 6500 return rc; 6501 } 6502 6503 STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName) 6504 { 6505 CheckComArgStrNotEmptyOrNull(aName); 6506 6507 AutoCaller autoCaller(this); 6508 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6509 6510 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6511 6512 HRESULT rc = checkStateDependency(MutableStateDep); 6513 if (FAILED(rc)) return rc; 6514 6515 ComObjPtr<StorageController> ctrl; 6516 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 6517 if (FAILED(rc)) return rc; 6518 6519 { 6520 /* find all attached devices to the appropriate storage controller and detach them all */ 6521 // make a temporary list because detachDevice invalidates iterators into 6522 // mMediaData->mAttachments 6523 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments; 6524 6525 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); 6526 it != llAttachments2.end(); 6527 ++it) 6528 { 6529 MediumAttachment *pAttachTemp = *it; 6530 6531 AutoCaller localAutoCaller(pAttachTemp); 6532 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc(); 6533 6534 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS); 6535 6536 if (pAttachTemp->getControllerName() == aName) 6537 { 6538 rc = detachDevice(pAttachTemp, alock, NULL); 6539 if (FAILED(rc)) return rc; 6540 } 6541 } 6542 } 6543 6544 /* We can remove it now. */ 6545 setModified(IsModified_Storage); 6546 mStorageControllers.backup(); 6547 6548 ctrl->unshare(); 6549 6550 mStorageControllers->remove(ctrl); 6551 6552 /* inform the direct session if any */ 6553 alock.release(); 6554 onStorageControllerChange(); 6555 6556 return S_OK; 6557 } 6558 6559 STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType, 6560 IUSBController **controller) 6561 { 6562 if ( (aType <= USBControllerType_Null) 6563 || (aType >= USBControllerType_Last)) 6564 return setError(E_INVALIDARG, 6565 tr("Invalid USB controller type: %d"), 6566 aType); 6567 6568 AutoCaller autoCaller(this); 6569 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6570 6571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6572 6573 HRESULT rc = checkStateDependency(MutableStateDep); 6574 if (FAILED(rc)) return rc; 6575 6576 /* try to find one with the same type first. */ 6577 ComObjPtr<USBController> ctrl; 6578 6579 rc = getUSBControllerByName(aName, ctrl, false /* aSetError */); 6580 if (SUCCEEDED(rc)) 6581 return setError(VBOX_E_OBJECT_IN_USE, 6582 tr("USB controller named '%ls' already exists"), 6583 aName); 6584 6585 /* Check that we don't exceed the maximum number of USB controllers for the given type. */ 6586 ULONG maxInstances; 6587 rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances); 6588 if (FAILED(rc)) 6589 return rc; 6590 6591 ULONG cInstances = getUSBControllerCountByType(aType); 6592 if (cInstances >= maxInstances) 6593 return setError(E_INVALIDARG, 6594 tr("Too many USB controllers of this type")); 6595 6596 ctrl.createObject(); 6597 6598 rc = ctrl->init(this, aName, aType); 6599 if (FAILED(rc)) return rc; 6600 6601 setModified(IsModified_USB); 6602 mUSBControllers.backup(); 6603 mUSBControllers->push_back(ctrl); 6604 6605 ctrl.queryInterfaceTo(controller); 6606 6607 /* inform the direct session if any */ 6608 alock.release(); 6609 onUSBControllerChange(); 6610 6611 return S_OK; 6612 } 6613 6614 STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController) 6615 { 6616 CheckComArgStrNotEmptyOrNull(aName); 6617 6618 AutoCaller autoCaller(this); 6619 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6620 6621 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6622 6623 ComObjPtr<USBController> ctrl; 6624 6625 HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */); 6626 if (SUCCEEDED(rc)) 6627 ctrl.queryInterfaceTo(aUSBController); 6628 6629 return rc; 6630 } 6631 6632 STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType, 6633 ULONG *aControllers) 6634 { 6635 CheckComArgOutPointerValid(aControllers); 6636 6637 if ( (aType <= USBControllerType_Null) 6638 || (aType >= USBControllerType_Last)) 6639 return setError(E_INVALIDARG, 6640 tr("Invalid USB controller type: %d"), 6641 aType); 6642 6643 AutoCaller autoCaller(this); 6644 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6645 6646 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6647 6648 ComObjPtr<USBController> ctrl; 6649 6650 *aControllers = getUSBControllerCountByType(aType); 6651 6652 return S_OK; 6653 } 6654 6655 STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName) 6656 { 6657 CheckComArgStrNotEmptyOrNull(aName); 6658 6659 AutoCaller autoCaller(this); 6660 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6661 6662 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6663 6664 HRESULT rc = checkStateDependency(MutableStateDep); 6665 if (FAILED(rc)) return rc; 6666 6667 ComObjPtr<USBController> ctrl; 6668 rc = getUSBControllerByName(aName, ctrl, true /* aSetError */); 6669 if (FAILED(rc)) return rc; 6670 6671 setModified(IsModified_USB); 6672 mUSBControllers.backup(); 6673 6674 ctrl->unshare(); 6675 6676 mUSBControllers->remove(ctrl); 6677 6678 /* inform the direct session if any */ 6679 alock.release(); 6680 onUSBControllerChange(); 6681 6682 return S_OK; 6683 } 6684 6685 STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId, 6686 ULONG *puOriginX, 6687 ULONG *puOriginY, 6688 ULONG *puWidth, 6689 ULONG *puHeight, 6690 BOOL *pfEnabled) 6691 { 6692 LogFlowThisFunc(("\n")); 6693 6694 CheckComArgNotNull(puOriginX); 6695 CheckComArgNotNull(puOriginY); 6696 CheckComArgNotNull(puWidth); 6697 CheckComArgNotNull(puHeight); 6698 CheckComArgNotNull(pfEnabled); 6699 6700 uint32_t u32OriginX= 0; 6701 uint32_t u32OriginY= 0; 6702 uint32_t u32Width = 0; 6703 uint32_t u32Height = 0; 6704 uint16_t u16Flags = 0; 6705 6706 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, uScreenId, 6707 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags); 6708 if (RT_FAILURE(vrc)) 6709 { 6710 #ifdef RT_OS_WINDOWS 6711 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails. 6712 * This works with XPCOM. But Windows COM sets all output parameters to zero. 6713 * So just assign fEnable to TRUE again. 6714 * The right fix would be to change GUI API wrappers to make sure that parameters 6715 * are changed only if API succeeds. 6716 */ 6717 *pfEnabled = TRUE; 6718 #endif 6719 return setError(VBOX_E_IPRT_ERROR, 6720 tr("Saved guest size is not available (%Rrc)"), 6721 vrc); 6722 } 6723 6724 *puOriginX = u32OriginX; 6725 *puOriginY = u32OriginY; 6726 *puWidth = u32Width; 6727 *puHeight = u32Height; 6728 *pfEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0; 6729 6730 return S_OK; 6731 } 6732 6733 STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight) 6734 { 6735 LogFlowThisFunc(("\n")); 6736 6737 CheckComArgNotNull(aSize); 6738 CheckComArgNotNull(aWidth); 6739 CheckComArgNotNull(aHeight); 6740 6741 if (aScreenId != 0) 6742 return E_NOTIMPL; 6743 6744 AutoCaller autoCaller(this); 6745 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6746 6747 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6748 6749 uint8_t *pu8Data = NULL; 6750 uint32_t cbData = 0; 6751 uint32_t u32Width = 0; 6752 uint32_t u32Height = 0; 6753 6754 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 6755 6756 if (RT_FAILURE(vrc)) 6757 return setError(VBOX_E_IPRT_ERROR, 6758 tr("Saved screenshot data is not available (%Rrc)"), 6759 vrc); 6760 6761 *aSize = cbData; 6762 *aWidth = u32Width; 6763 *aHeight = u32Height; 6764 6765 freeSavedDisplayScreenshot(pu8Data); 6766 6767 return S_OK; 6768 } 6769 6770 STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 6771 { 6772 LogFlowThisFunc(("\n")); 6773 6774 CheckComArgNotNull(aWidth); 6775 CheckComArgNotNull(aHeight); 6776 CheckComArgOutSafeArrayPointerValid(aData); 6777 6778 if (aScreenId != 0) 6779 return E_NOTIMPL; 6780 6781 AutoCaller autoCaller(this); 6782 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6783 6784 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6785 6786 uint8_t *pu8Data = NULL; 6787 uint32_t cbData = 0; 6788 uint32_t u32Width = 0; 6789 uint32_t u32Height = 0; 6790 6791 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 6792 6793 if (RT_FAILURE(vrc)) 6794 return setError(VBOX_E_IPRT_ERROR, 6795 tr("Saved screenshot data is not available (%Rrc)"), 6796 vrc); 6797 6798 *aWidth = u32Width; 6799 *aHeight = u32Height; 6800 6801 com::SafeArray<BYTE> bitmap(cbData); 6802 /* Convert pixels to format expected by the API caller. */ 6803 if (aBGR) 6804 { 6805 /* [0] B, [1] G, [2] R, [3] A. */ 6806 for (unsigned i = 0; i < cbData; i += 4) 6807 { 6808 bitmap[i] = pu8Data[i]; 6809 bitmap[i + 1] = pu8Data[i + 1]; 6810 bitmap[i + 2] = pu8Data[i + 2]; 6811 bitmap[i + 3] = 0xff; 6812 } 6813 } 6814 else 6815 { 6816 /* [0] R, [1] G, [2] B, [3] A. */ 6817 for (unsigned i = 0; i < cbData; i += 4) 6818 { 6819 bitmap[i] = pu8Data[i + 2]; 6820 bitmap[i + 1] = pu8Data[i + 1]; 6821 bitmap[i + 2] = pu8Data[i]; 6822 bitmap[i + 3] = 0xff; 6823 } 6824 } 6825 bitmap.detachTo(ComSafeArrayOutArg(aData)); 6826 6827 freeSavedDisplayScreenshot(pu8Data); 6828 6829 return S_OK; 6830 } 6831 6832 6833 STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 6834 { 6835 LogFlowThisFunc(("\n")); 6836 6837 CheckComArgNotNull(aWidth); 6838 CheckComArgNotNull(aHeight); 6839 CheckComArgOutSafeArrayPointerValid(aData); 6840 6841 if (aScreenId != 0) 6842 return E_NOTIMPL; 6843 6844 AutoCaller autoCaller(this); 6845 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6846 6847 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6848 6849 uint8_t *pu8Data = NULL; 6850 uint32_t cbData = 0; 6851 uint32_t u32Width = 0; 6852 uint32_t u32Height = 0; 6853 6854 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 6855 6856 if (RT_FAILURE(vrc)) 6857 return setError(VBOX_E_IPRT_ERROR, 6858 tr("Saved screenshot data is not available (%Rrc)"), 6859 vrc); 6860 6861 *aWidth = u32Width; 6862 *aHeight = u32Height; 6863 6864 HRESULT rc = S_OK; 6865 uint8_t *pu8PNG = NULL; 6866 uint32_t cbPNG = 0; 6867 uint32_t cxPNG = 0; 6868 uint32_t cyPNG = 0; 6869 6870 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0); 6871 6872 if (RT_SUCCESS(vrc)) 6873 { 6874 com::SafeArray<BYTE> screenData(cbPNG); 6875 screenData.initFrom(pu8PNG, cbPNG); 6876 if (pu8PNG) 6877 RTMemFree(pu8PNG); 6878 screenData.detachTo(ComSafeArrayOutArg(aData)); 6879 } 6880 else 6881 { 6882 if (pu8PNG) 6883 RTMemFree(pu8PNG); 6884 return setError(VBOX_E_IPRT_ERROR, 6885 tr("Could not convert screenshot to PNG (%Rrc)"), 6886 vrc); 6887 } 6888 6889 freeSavedDisplayScreenshot(pu8Data); 6890 6891 return rc; 6892 } 6893 6894 STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight) 6895 { 6896 LogFlowThisFunc(("\n")); 6897 6898 CheckComArgNotNull(aSize); 6899 CheckComArgNotNull(aWidth); 6900 CheckComArgNotNull(aHeight); 6901 6902 if (aScreenId != 0) 6903 return E_NOTIMPL; 6904 6905 AutoCaller autoCaller(this); 6906 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6907 6908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6909 6910 uint8_t *pu8Data = NULL; 6911 uint32_t cbData = 0; 6912 uint32_t u32Width = 0; 6913 uint32_t u32Height = 0; 6914 6915 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 6916 6917 if (RT_FAILURE(vrc)) 6918 return setError(VBOX_E_IPRT_ERROR, 6919 tr("Saved screenshot data is not available (%Rrc)"), 6920 vrc); 6921 6922 *aSize = cbData; 6923 *aWidth = u32Width; 6924 *aHeight = u32Height; 6925 6926 freeSavedDisplayScreenshot(pu8Data); 6927 6928 return S_OK; 6929 } 6930 6931 STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 6932 { 6933 LogFlowThisFunc(("\n")); 6934 6935 CheckComArgNotNull(aWidth); 6936 CheckComArgNotNull(aHeight); 6937 CheckComArgOutSafeArrayPointerValid(aData); 6938 6939 if (aScreenId != 0) 6940 return E_NOTIMPL; 6941 6942 AutoCaller autoCaller(this); 6943 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6944 6945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6946 6947 uint8_t *pu8Data = NULL; 6948 uint32_t cbData = 0; 6949 uint32_t u32Width = 0; 6950 uint32_t u32Height = 0; 6951 6952 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 6953 6954 if (RT_FAILURE(vrc)) 6955 return setError(VBOX_E_IPRT_ERROR, 6956 tr("Saved screenshot thumbnail data is not available (%Rrc)"), 6957 vrc); 6958 6959 *aWidth = u32Width; 6960 *aHeight = u32Height; 6961 6962 com::SafeArray<BYTE> png(cbData); 6963 png.initFrom(pu8Data, cbData); 6964 png.detachTo(ComSafeArrayOutArg(aData)); 6965 6966 freeSavedDisplayScreenshot(pu8Data); 6967 6968 return S_OK; 6969 } 6970 6971 STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu) 6972 { 6973 HRESULT rc = S_OK; 6974 LogFlowThisFunc(("\n")); 6975 6976 AutoCaller autoCaller(this); 6977 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 6978 6979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6980 6981 if (!mHWData->mCPUHotPlugEnabled) 6982 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 6983 6984 if (aCpu >= mHWData->mCPUCount) 6985 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1); 6986 6987 if (mHWData->mCPUAttached[aCpu]) 6988 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu); 6989 6990 alock.release(); 6991 rc = onCPUChange(aCpu, false); 6992 alock.acquire(); 6993 if (FAILED(rc)) return rc; 6994 6995 setModified(IsModified_MachineData); 6996 mHWData.backup(); 6997 mHWData->mCPUAttached[aCpu] = true; 6998 6999 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 7000 if (Global::IsOnline(mData->mMachineState)) 7001 saveSettings(NULL); 7002 7003 return S_OK; 7004 } 7005 7006 STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu) 7007 { 7008 HRESULT rc = S_OK; 7009 LogFlowThisFunc(("\n")); 7010 7011 AutoCaller autoCaller(this); 7012 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7013 7014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7015 7016 if (!mHWData->mCPUHotPlugEnabled) 7017 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 7018 7019 if (aCpu >= SchemaDefs::MaxCPUCount) 7020 return setError(E_INVALIDARG, 7021 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"), 7022 SchemaDefs::MaxCPUCount); 7023 7024 if (!mHWData->mCPUAttached[aCpu]) 7025 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu); 7026 7027 /* CPU 0 can't be detached */ 7028 if (aCpu == 0) 7029 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0")); 7030 7031 alock.release(); 7032 rc = onCPUChange(aCpu, true); 7033 alock.acquire(); 7034 if (FAILED(rc)) return rc; 7035 7036 setModified(IsModified_MachineData); 7037 mHWData.backup(); 7038 mHWData->mCPUAttached[aCpu] = false; 7039 7040 /** Save settings if online - @todo why is this required? -- @bugref{6818} */ 7041 if (Global::IsOnline(mData->mMachineState)) 7042 saveSettings(NULL); 7043 7044 return S_OK; 7045 } 7046 7047 STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached) 7048 { 7049 LogFlowThisFunc(("\n")); 7050 7051 CheckComArgNotNull(aCpuAttached); 7052 7053 *aCpuAttached = false; 7054 7055 AutoCaller autoCaller(this); 7056 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7057 7058 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7059 7060 /* If hotplug is enabled the CPU is always enabled. */ 7061 if (!mHWData->mCPUHotPlugEnabled) 7062 { 7063 if (aCpu < mHWData->mCPUCount) 7064 *aCpuAttached = true; 7065 } 7066 else 7067 { 7068 if (aCpu < SchemaDefs::MaxCPUCount) 7069 *aCpuAttached = mHWData->mCPUAttached[aCpu]; 7070 } 7071 7072 return S_OK; 7073 } 7074 7075 STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName) 7076 { 7077 CheckComArgOutPointerValid(aName); 7078 7079 AutoCaller autoCaller(this); 7080 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7081 7082 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7083 7084 Utf8Str log = queryLogFilename(aIdx); 7085 if (!RTFileExists(log.c_str())) 7086 log.setNull(); 7087 log.cloneTo(aName); 7088 7089 return S_OK; 7090 } 7091 7092 STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData)) 7093 { 7094 LogFlowThisFunc(("\n")); 7095 CheckComArgOutSafeArrayPointerValid(aData); 7096 if (aSize < 0) 7097 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize); 7098 7099 AutoCaller autoCaller(this); 7100 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7101 7102 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7103 7104 HRESULT rc = S_OK; 7105 Utf8Str log = queryLogFilename(aIdx); 7106 7107 /* do not unnecessarily hold the lock while doing something which does 7108 * not need the lock and potentially takes a long time. */ 7109 alock.release(); 7110 7111 /* Limit the chunk size to 32K for now, as that gives better performance 7112 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice. 7113 * One byte expands to approx. 25 bytes of breathtaking XML. */ 7114 size_t cbData = (size_t)RT_MIN(aSize, 32768); 7115 com::SafeArray<BYTE> logData(cbData); 7116 7117 RTFILE LogFile; 7118 int vrc = RTFileOpen(&LogFile, log.c_str(), 7119 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); 7120 if (RT_SUCCESS(vrc)) 7121 { 7122 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData); 7123 if (RT_SUCCESS(vrc)) 7124 logData.resize(cbData); 7125 else 7126 rc = setError(VBOX_E_IPRT_ERROR, 7127 tr("Could not read log file '%s' (%Rrc)"), 7128 log.c_str(), vrc); 7129 RTFileClose(LogFile); 7130 } 7131 else 7132 rc = setError(VBOX_E_IPRT_ERROR, 7133 tr("Could not open log file '%s' (%Rrc)"), 7134 log.c_str(), vrc); 7135 7136 if (FAILED(rc)) 7137 logData.resize(0); 7138 logData.detachTo(ComSafeArrayOutArg(aData)); 7139 7140 return rc; 7141 } 7142 7143 7144 /** 7145 * Currently this method doesn't attach device to the running VM, 7146 * just makes sure it's plugged on next VM start. 7147 */ 7148 STDMETHODIMP Machine::AttachHostPCIDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/) 7149 { 7150 AutoCaller autoCaller(this); 7151 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7152 7153 // lock scope 7154 { 7155 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7156 7157 HRESULT rc = checkStateDependency(MutableStateDep); 7158 if (FAILED(rc)) return rc; 7159 7160 ChipsetType_T aChipset = ChipsetType_PIIX3; 7161 COMGETTER(ChipsetType)(&aChipset); 7162 7163 if (aChipset != ChipsetType_ICH9) 7164 { 7165 return setError(E_INVALIDARG, 7166 tr("Host PCI attachment only supported with ICH9 chipset")); 7167 } 7168 7169 // check if device with this host PCI address already attached 7170 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin(); 7171 it != mHWData->mPCIDeviceAssignments.end(); 7172 ++it) 7173 { 7174 LONG iHostAddress = -1; 7175 ComPtr<PCIDeviceAttachment> pAttach; 7176 pAttach = *it; 7177 pAttach->COMGETTER(HostAddress)(&iHostAddress); 7178 if (iHostAddress == hostAddress) 7179 return setError(E_INVALIDARG, 7180 tr("Device with host PCI address already attached to this VM")); 7181 } 7182 7183 ComObjPtr<PCIDeviceAttachment> pda; 7184 char name[32]; 7185 7186 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7); 7187 Bstr bname(name); 7188 pda.createObject(); 7189 pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE); 7190 setModified(IsModified_MachineData); 7191 mHWData.backup(); 7192 mHWData->mPCIDeviceAssignments.push_back(pda); 7193 } 7194 7195 return S_OK; 7196 } 7197 7198 /** 7199 * Currently this method doesn't detach device from the running VM, 7200 * just makes sure it's not plugged on next VM start. 7201 */ 7202 STDMETHODIMP Machine::DetachHostPCIDevice(LONG hostAddress) 7203 { 7204 AutoCaller autoCaller(this); 7205 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7206 7207 ComObjPtr<PCIDeviceAttachment> pAttach; 7208 bool fRemoved = false; 7209 HRESULT rc; 7210 7211 // lock scope 7212 { 7213 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7214 7215 rc = checkStateDependency(MutableStateDep); 7216 if (FAILED(rc)) return rc; 7217 7218 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin(); 7219 it != mHWData->mPCIDeviceAssignments.end(); 7220 ++it) 7221 { 7222 LONG iHostAddress = -1; 7223 pAttach = *it; 7224 pAttach->COMGETTER(HostAddress)(&iHostAddress); 7225 if (iHostAddress != -1 && iHostAddress == hostAddress) 7226 { 7227 setModified(IsModified_MachineData); 7228 mHWData.backup(); 7229 mHWData->mPCIDeviceAssignments.remove(pAttach); 7230 fRemoved = true; 7231 break; 7232 } 7233 } 7234 } 7235 7236 7237 /* Fire event outside of the lock */ 7238 if (fRemoved) 7239 { 7240 Assert(!pAttach.isNull()); 7241 ComPtr<IEventSource> es; 7242 rc = mParent->COMGETTER(EventSource)(es.asOutParam()); 7243 Assert(SUCCEEDED(rc)); 7244 Bstr mid; 7245 rc = this->COMGETTER(Id)(mid.asOutParam()); 7246 Assert(SUCCEEDED(rc)); 7247 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL); 7248 } 7249 7250 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND, 7251 tr("No host PCI device %08x attached"), 7252 hostAddress 7253 ); 7254 } 7255 7256 STDMETHODIMP Machine::COMGETTER(PCIDeviceAssignments)(ComSafeArrayOut(IPCIDeviceAttachment *, aAssignments)) 7257 { 7258 CheckComArgOutSafeArrayPointerValid(aAssignments); 7259 7260 AutoCaller autoCaller(this); 7261 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7262 7263 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7264 7265 SafeIfaceArray<IPCIDeviceAttachment> assignments(mHWData->mPCIDeviceAssignments); 7266 assignments.detachTo(ComSafeArrayOutArg(aAssignments)); 7267 7268 return S_OK; 7269 } 7270 7271 STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidthControl) 7272 { 7273 CheckComArgOutPointerValid(aBandwidthControl); 7274 7275 AutoCaller autoCaller(this); 7276 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7277 7278 mBandwidthControl.queryInterfaceTo(aBandwidthControl); 7279 7280 return S_OK; 7281 } 7282 7283 STDMETHODIMP Machine::COMGETTER(TracingEnabled)(BOOL *pfEnabled) 7284 { 7285 CheckComArgOutPointerValid(pfEnabled); 7286 AutoCaller autoCaller(this); 7287 HRESULT hrc = autoCaller.rc(); 7288 if (SUCCEEDED(hrc)) 7289 { 7290 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7291 *pfEnabled = mHWData->mDebugging.fTracingEnabled; 7292 } 7293 return hrc; 7294 } 7295 7296 STDMETHODIMP Machine::COMSETTER(TracingEnabled)(BOOL fEnabled) 7297 { 7298 AutoCaller autoCaller(this); 7299 HRESULT hrc = autoCaller.rc(); 7300 if (SUCCEEDED(hrc)) 7301 { 7302 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7303 hrc = checkStateDependency(MutableStateDep); 7304 if (SUCCEEDED(hrc)) 7305 { 7306 hrc = mHWData.backupEx(); 7307 if (SUCCEEDED(hrc)) 7308 { 7309 setModified(IsModified_MachineData); 7310 mHWData->mDebugging.fTracingEnabled = fEnabled != FALSE; 7311 } 7312 } 7313 } 7314 return hrc; 7315 } 7316 7317 STDMETHODIMP Machine::COMGETTER(TracingConfig)(BSTR *pbstrConfig) 7318 { 7319 CheckComArgOutPointerValid(pbstrConfig); 7320 AutoCaller autoCaller(this); 7321 HRESULT hrc = autoCaller.rc(); 7322 if (SUCCEEDED(hrc)) 7323 { 7324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7325 hrc = mHWData->mDebugging.strTracingConfig.cloneToEx(pbstrConfig); 7326 } 7327 return hrc; 7328 } 7329 7330 STDMETHODIMP Machine::COMSETTER(TracingConfig)(IN_BSTR bstrConfig) 7331 { 7332 CheckComArgStr(bstrConfig); 7333 AutoCaller autoCaller(this); 7334 HRESULT hrc = autoCaller.rc(); 7335 if (SUCCEEDED(hrc)) 7336 { 7337 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7338 hrc = checkStateDependency(MutableStateDep); 7339 if (SUCCEEDED(hrc)) 7340 { 7341 hrc = mHWData.backupEx(); 7342 if (SUCCEEDED(hrc)) 7343 { 7344 hrc = mHWData->mDebugging.strTracingConfig.cloneEx(bstrConfig); 7345 if (SUCCEEDED(hrc)) 7346 setModified(IsModified_MachineData); 7347 } 7348 } 7349 } 7350 return hrc; 7351 7352 } 7353 7354 STDMETHODIMP Machine::COMGETTER(AllowTracingToAccessVM)(BOOL *pfAllow) 7355 { 7356 CheckComArgOutPointerValid(pfAllow); 7357 AutoCaller autoCaller(this); 7358 HRESULT hrc = autoCaller.rc(); 7359 if (SUCCEEDED(hrc)) 7360 { 7361 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7362 *pfAllow = mHWData->mDebugging.fAllowTracingToAccessVM; 7363 } 7364 return hrc; 7365 } 7366 7367 STDMETHODIMP Machine::COMSETTER(AllowTracingToAccessVM)(BOOL fAllow) 7368 { 7369 AutoCaller autoCaller(this); 7370 HRESULT hrc = autoCaller.rc(); 7371 if (SUCCEEDED(hrc)) 7372 { 7373 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7374 hrc = checkStateDependency(MutableStateDep); 7375 if (SUCCEEDED(hrc)) 7376 { 7377 hrc = mHWData.backupEx(); 7378 if (SUCCEEDED(hrc)) 7379 { 7380 setModified(IsModified_MachineData); 7381 mHWData->mDebugging.fAllowTracingToAccessVM = fAllow != FALSE; 7382 } 7383 } 7384 } 7385 return hrc; 7386 } 7387 7388 STDMETHODIMP Machine::COMGETTER(AutostartEnabled)(BOOL *pfEnabled) 7389 { 7390 CheckComArgOutPointerValid(pfEnabled); 7391 AutoCaller autoCaller(this); 7392 HRESULT hrc = autoCaller.rc(); 7393 if (SUCCEEDED(hrc)) 7394 { 7395 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7396 *pfEnabled = mHWData->mAutostart.fAutostartEnabled; 7397 } 7398 return hrc; 7399 } 7400 7401 STDMETHODIMP Machine::COMSETTER(AutostartEnabled)(BOOL fEnabled) 7402 { 7403 AutoCaller autoCaller(this); 7404 HRESULT hrc = autoCaller.rc(); 7405 if (SUCCEEDED(hrc)) 7406 { 7407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7408 hrc = checkStateDependency(MutableStateDep); 7409 if ( SUCCEEDED(hrc) 7410 && mHWData->mAutostart.fAutostartEnabled != !!fEnabled) 7411 { 7412 AutostartDb *autostartDb = mParent->getAutostartDb(); 7413 int vrc; 7414 7415 if (fEnabled) 7416 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str()); 7417 else 7418 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str()); 7419 7420 if (RT_SUCCESS(vrc)) 7421 { 7422 hrc = mHWData.backupEx(); 7423 if (SUCCEEDED(hrc)) 7424 { 7425 setModified(IsModified_MachineData); 7426 mHWData->mAutostart.fAutostartEnabled = fEnabled != FALSE; 7427 } 7428 } 7429 else if (vrc == VERR_NOT_SUPPORTED) 7430 hrc = setError(VBOX_E_NOT_SUPPORTED, 7431 tr("The VM autostart feature is not supported on this platform")); 7432 else if (vrc == VERR_PATH_NOT_FOUND) 7433 hrc = setError(E_FAIL, 7434 tr("The path to the autostart database is not set")); 7435 else 7436 hrc = setError(E_UNEXPECTED, 7437 tr("%s machine '%s' to the autostart database failed with %Rrc"), 7438 fEnabled ? "Adding" : "Removing", 7439 mUserData->s.strName.c_str(), vrc); 7440 } 7441 } 7442 return hrc; 7443 } 7444 7445 STDMETHODIMP Machine::COMGETTER(AutostartDelay)(ULONG *puDelay) 7446 { 7447 CheckComArgOutPointerValid(puDelay); 7448 AutoCaller autoCaller(this); 7449 HRESULT hrc = autoCaller.rc(); 7450 if (SUCCEEDED(hrc)) 7451 { 7452 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7453 *puDelay = mHWData->mAutostart.uAutostartDelay; 7454 } 7455 return hrc; 7456 } 7457 7458 STDMETHODIMP Machine::COMSETTER(AutostartDelay)(ULONG uDelay) 7459 { 7460 AutoCaller autoCaller(this); 7461 HRESULT hrc = autoCaller.rc(); 7462 if (SUCCEEDED(hrc)) 7463 { 7464 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7465 hrc = checkStateDependency(MutableStateDep); 7466 if (SUCCEEDED(hrc)) 7467 { 7468 hrc = mHWData.backupEx(); 7469 if (SUCCEEDED(hrc)) 7470 { 7471 setModified(IsModified_MachineData); 7472 mHWData->mAutostart.uAutostartDelay = uDelay; 7473 } 7474 } 7475 } 7476 return hrc; 7477 } 7478 7479 STDMETHODIMP Machine::COMGETTER(AutostopType)(AutostopType_T *penmAutostopType) 7480 { 7481 CheckComArgOutPointerValid(penmAutostopType); 7482 AutoCaller autoCaller(this); 7483 HRESULT hrc = autoCaller.rc(); 7484 if (SUCCEEDED(hrc)) 7485 { 7486 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7487 *penmAutostopType = mHWData->mAutostart.enmAutostopType; 7488 } 7489 return hrc; 7490 } 7491 7492 STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType) 7493 { 7494 AutoCaller autoCaller(this); 7495 HRESULT hrc = autoCaller.rc(); 7496 if (SUCCEEDED(hrc)) 7497 { 7498 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7499 hrc = checkStateDependency(MutableStateDep); 7500 if ( SUCCEEDED(hrc) 7501 && mHWData->mAutostart.enmAutostopType != enmAutostopType) 7502 { 7503 AutostartDb *autostartDb = mParent->getAutostartDb(); 7504 int vrc; 7505 7506 if (enmAutostopType != AutostopType_Disabled) 7507 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str()); 7508 else 7509 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str()); 7510 7511 if (RT_SUCCESS(vrc)) 7512 { 7513 hrc = mHWData.backupEx(); 7514 if (SUCCEEDED(hrc)) 7515 { 7516 setModified(IsModified_MachineData); 7517 mHWData->mAutostart.enmAutostopType = enmAutostopType; 7518 } 7519 } 7520 else if (vrc == VERR_NOT_SUPPORTED) 7521 hrc = setError(VBOX_E_NOT_SUPPORTED, 7522 tr("The VM autostop feature is not supported on this platform")); 7523 else if (vrc == VERR_PATH_NOT_FOUND) 7524 hrc = setError(E_FAIL, 7525 tr("The path to the autostart database is not set")); 7526 else 7527 hrc = setError(E_UNEXPECTED, 7528 tr("%s machine '%s' to the autostop database failed with %Rrc"), 7529 enmAutostopType != AutostopType_Disabled ? "Adding" : "Removing", 7530 mUserData->s.strName.c_str(), vrc); 7531 } 7532 } 7533 return hrc; 7534 } 7535 7536 STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend) 7537 { 7538 CheckComArgOutPointerValid(aDefaultFrontend); 7539 AutoCaller autoCaller(this); 7540 HRESULT hrc = autoCaller.rc(); 7541 if (SUCCEEDED(hrc)) 7542 { 7543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7544 mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend); 7545 } 7546 return hrc; 7547 } 7548 7549 STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend) 7550 { 7551 CheckComArgStr(aDefaultFrontend); 7552 AutoCaller autoCaller(this); 7553 HRESULT hrc = autoCaller.rc(); 7554 if (SUCCEEDED(hrc)) 7555 { 7556 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7557 hrc = checkStateDependency(MutableOrSavedStateDep); 7558 if (SUCCEEDED(hrc)) 7559 { 7560 hrc = mHWData.backupEx(); 7561 if (SUCCEEDED(hrc)) 7562 { 7563 setModified(IsModified_MachineData); 7564 mHWData->mDefaultFrontend = aDefaultFrontend; 7565 } 7566 } 7567 } 7568 return hrc; 7569 } 7570 7571 STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon)) 7572 { 7573 CheckComArgSafeArrayNotNull(aIcon); 7574 CheckComArgOutSafeArrayPointerValid(aIcon); 7575 AutoCaller autoCaller(this); 7576 HRESULT hrc = autoCaller.rc(); 7577 if (SUCCEEDED(hrc)) 7578 { 7579 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7580 com::SafeArray<BYTE> icon(mUserData->mIcon.size()); 7581 memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size()); 7582 icon.detachTo(ComSafeArrayOutArg(aIcon)); 7583 } 7584 return hrc; 7585 } 7586 7587 STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon)) 7588 { 7589 CheckComArgSafeArrayNotNull(aIcon); 7590 AutoCaller autoCaller(this); 7591 HRESULT hrc = autoCaller.rc(); 7592 if (SUCCEEDED(hrc)) 7593 { 7594 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7595 hrc = checkStateDependency(MutableOrSavedStateDep); 7596 if (SUCCEEDED(hrc)) 7597 { 7598 setModified(IsModified_MachineData); 7599 mUserData.backup(); 7600 com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon)); 7601 mUserData->mIcon.clear(); 7602 memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size()); 7603 } 7604 } 7605 return hrc; 7606 } 7607 7608 STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable) 7609 { 7610 CheckComArgOutPointerValid(aAvailable); 7611 7612 AutoCaller autoCaller(this); 7613 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7614 7615 #ifdef VBOX_WITH_USB 7616 *aAvailable = true; 7617 #else 7618 *aAvailable = false; 7619 #endif 7620 return S_OK; 7621 } 7622 7623 STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress) 7624 { 7625 LogFlowFuncEnter(); 7626 7627 CheckComArgNotNull(pTarget); 7628 CheckComArgOutPointerValid(pProgress); 7629 7630 /* Convert the options. */ 7631 RTCList<CloneOptions_T> optList; 7632 if (options != NULL) 7633 optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList(); 7634 7635 if (optList.contains(CloneOptions_Link)) 7636 { 7637 if (!isSnapshotMachine()) 7638 return setError(E_INVALIDARG, 7639 tr("Linked clone can only be created from a snapshot")); 7640 if (mode != CloneMode_MachineState) 7641 return setError(E_INVALIDARG, 7642 tr("Linked clone can only be created for a single machine state")); 7643 } 7644 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG); 7645 7646 AutoCaller autoCaller(this); 7647 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7648 7649 7650 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList); 7651 7652 HRESULT rc = pWorker->start(pProgress); 7653 7654 LogFlowFuncLeave(); 7655 7656 return rc; 7657 } 7658 7659 // public methods for internal purposes 7660 ///////////////////////////////////////////////////////////////////////////// 7661 7662 /** 7663 * Adds the given IsModified_* flag to the dirty flags of the machine. 7664 * This must be called either during loadSettings or under the machine write lock. 7665 * @param fl 7666 */ 7667 void Machine::setModified(uint32_t fl, bool fAllowStateModification /* = true */) 7668 { 7669 mData->flModifications |= fl; 7670 if (fAllowStateModification && isStateModificationAllowed()) 7671 mData->mCurrentStateModified = true; 7672 } 7673 7674 /** 7675 * Adds the given IsModified_* flag to the dirty flags of the machine, taking 7676 * care of the write locking. 7677 * 7678 * @param fModifications The flag to add. 7679 */ 7680 void Machine::setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */) 7681 { 7682 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7683 setModified(fModification, fAllowStateModification); 7684 } 7685 7686 /** 7687 * Saves the registry entry of this machine to the given configuration node. 7688 * 7689 * @param aEntryNode Node to save the registry entry to. 7690 * 7691 * @note locks this object for reading. 7692 */ 7693 HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data) 7694 { 7695 AutoLimitedCaller autoCaller(this); 7696 AssertComRCReturnRC(autoCaller.rc()); 7697 7698 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7699 7700 data.uuid = mData->mUuid; 7701 data.strSettingsFile = mData->m_strConfigFile; 7702 7703 return S_OK; 7704 } 7705 7706 /** 7707 * Calculates the absolute path of the given path taking the directory of the 7708 * machine settings file as the current directory. 7709 * 7710 * @param aPath Path to calculate the absolute path for. 7711 * @param aResult Where to put the result (used only on success, can be the 7712 * same Utf8Str instance as passed in @a aPath). 7713 * @return IPRT result. 7714 * 7715 * @note Locks this object for reading. 7716 */ 7717 int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult) 7718 { 7719 AutoCaller autoCaller(this); 7720 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 7721 7722 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7723 7724 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE); 7725 7726 Utf8Str strSettingsDir = mData->m_strConfigFileFull; 7727 7728 strSettingsDir.stripFilename(); 7729 char folder[RTPATH_MAX]; 7730 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder)); 7731 if (RT_SUCCESS(vrc)) 7732 aResult = folder; 7733 7734 return vrc; 7735 } 7736 7737 /** 7738 * Copies strSource to strTarget, making it relative to the machine folder 7739 * if it is a subdirectory thereof, or simply copying it otherwise. 7740 * 7741 * @param strSource Path to evaluate and copy. 7742 * @param strTarget Buffer to receive target path. 7743 * 7744 * @note Locks this object for reading. 7745 */ 7746 void Machine::copyPathRelativeToMachine(const Utf8Str &strSource, 7747 Utf8Str &strTarget) 7748 { 7749 AutoCaller autoCaller(this); 7750 AssertComRCReturn(autoCaller.rc(), (void)0); 7751 7752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7753 7754 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty()); 7755 // use strTarget as a temporary buffer to hold the machine settings dir 7756 strTarget = mData->m_strConfigFileFull; 7757 strTarget.stripFilename(); 7758 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str())) 7759 { 7760 // is relative: then append what's left 7761 strTarget = strSource.substr(strTarget.length() + 1); // skip '/' 7762 // for empty paths (only possible for subdirs) use "." to avoid 7763 // triggering default settings for not present config attributes. 7764 if (strTarget.isEmpty()) 7765 strTarget = "."; 7766 } 7767 else 7768 // is not relative: then overwrite 7769 strTarget = strSource; 7770 } 7771 7772 /** 7773 * Returns the full path to the machine's log folder in the 7774 * \a aLogFolder argument. 7775 */ 7776 void Machine::getLogFolder(Utf8Str &aLogFolder) 7777 { 7778 AutoCaller autoCaller(this); 7779 AssertComRCReturnVoid(autoCaller.rc()); 7780 7781 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7782 7783 char szTmp[RTPATH_MAX]; 7784 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL); 7785 if (RT_SUCCESS(vrc)) 7786 { 7787 if (szTmp[0] && !mUserData.isNull()) 7788 { 7789 char szTmp2[RTPATH_MAX]; 7790 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2)); 7791 if (RT_SUCCESS(vrc)) 7792 aLogFolder = BstrFmt("%s%c%s", 7793 szTmp2, 7794 RTPATH_DELIMITER, 7795 mUserData->s.strName.c_str()); // path/to/logfolder/vmname 7796 } 7797 else 7798 vrc = VERR_PATH_IS_RELATIVE; 7799 } 7800 7801 if (RT_FAILURE(vrc)) 7802 { 7803 // fallback if VBOX_USER_LOGHOME is not set or invalid 7804 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox 7805 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname 7806 aLogFolder.append(RTPATH_DELIMITER); 7807 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs 7808 } 7809 } 7810 7811 /** 7812 * Returns the full path to the machine's log file for an given index. 7813 */ 7814 Utf8Str Machine::queryLogFilename(ULONG idx) 7815 { 7816 Utf8Str logFolder; 7817 getLogFolder(logFolder); 7818 Assert(logFolder.length()); 7819 Utf8Str log; 7820 if (idx == 0) 7821 log = Utf8StrFmt("%s%cVBox.log", 7822 logFolder.c_str(), RTPATH_DELIMITER); 7823 else 7824 log = Utf8StrFmt("%s%cVBox.log.%d", 7825 logFolder.c_str(), RTPATH_DELIMITER, idx); 7826 return log; 7827 } 7828 7829 /** 7830 * Composes a unique saved state filename based on the current system time. The filename is 7831 * granular to the second so this will work so long as no more than one snapshot is taken on 7832 * a machine per second. 7833 * 7834 * Before version 4.1, we used this formula for saved state files: 7835 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw()) 7836 * which no longer works because saved state files can now be shared between the saved state of the 7837 * "saved" machine and an online snapshot, and the following would cause problems: 7838 * 1) save machine 7839 * 2) create online snapshot from that machine state --> reusing saved state file 7840 * 3) save machine again --> filename would be reused, breaking the online snapshot 7841 * 7842 * So instead we now use a timestamp. 7843 * 7844 * @param str 7845 */ 7846 void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath) 7847 { 7848 AutoCaller autoCaller(this); 7849 AssertComRCReturnVoid(autoCaller.rc()); 7850 7851 { 7852 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7853 calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath); 7854 } 7855 7856 RTTIMESPEC ts; 7857 RTTimeNow(&ts); 7858 RTTIME time; 7859 RTTimeExplode(&time, &ts); 7860 7861 strStateFilePath += RTPATH_DELIMITER; 7862 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav", 7863 time.i32Year, time.u8Month, time.u8MonthDay, 7864 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond); 7865 } 7866 7867 /** 7868 * Returns the full path to the default video capture file. 7869 */ 7870 void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile) 7871 { 7872 AutoCaller autoCaller(this); 7873 AssertComRCReturnVoid(autoCaller.rc()); 7874 7875 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7876 7877 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox 7878 strFile.stripExt(); // path/to/machinesfolder/vmname/vmname 7879 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm 7880 } 7881 7882 /** 7883 * Returns whether at least one USB controller is present for the VM. 7884 */ 7885 bool Machine::isUSBControllerPresent() 7886 { 7887 AutoCaller autoCaller(this); 7888 AssertComRCReturn(autoCaller.rc(), false); 7889 7890 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7891 7892 return (mUSBControllers->size() > 0); 7893 } 7894 7895 /** 7896 * @note Locks this object for writing, calls the client process 7897 * (inside the lock). 7898 */ 7899 HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, 7900 const Utf8Str &strFrontend, 7901 const Utf8Str &strEnvironment, 7902 ProgressProxy *aProgress) 7903 { 7904 LogFlowThisFuncEnter(); 7905 7906 AssertReturn(aControl, E_FAIL); 7907 AssertReturn(aProgress, E_FAIL); 7908 AssertReturn(!strFrontend.isEmpty(), E_FAIL); 7909 7910 AutoCaller autoCaller(this); 7911 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7912 7913 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 7914 7915 if (!mData->mRegistered) 7916 return setError(E_UNEXPECTED, 7917 tr("The machine '%s' is not registered"), 7918 mUserData->s.strName.c_str()); 7919 7920 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 7921 7922 if ( mData->mSession.mState == SessionState_Locked 7923 || mData->mSession.mState == SessionState_Spawning 7924 || mData->mSession.mState == SessionState_Unlocking) 7925 return setError(VBOX_E_INVALID_OBJECT_STATE, 7926 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"), 7927 mUserData->s.strName.c_str()); 7928 7929 /* may not be busy */ 7930 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 7931 7932 /* get the path to the executable */ 7933 char szPath[RTPATH_MAX]; 7934 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1); 7935 size_t sz = strlen(szPath); 7936 szPath[sz++] = RTPATH_DELIMITER; 7937 szPath[sz] = 0; 7938 char *cmd = szPath + sz; 7939 sz = RTPATH_MAX - sz; 7940 7941 int vrc = VINF_SUCCESS; 7942 RTPROCESS pid = NIL_RTPROCESS; 7943 7944 RTENV env = RTENV_DEFAULT; 7945 7946 if (!strEnvironment.isEmpty()) 7947 { 7948 char *newEnvStr = NULL; 7949 7950 do 7951 { 7952 /* clone the current environment */ 7953 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT); 7954 AssertRCBreakStmt(vrc2, vrc = vrc2); 7955 7956 newEnvStr = RTStrDup(strEnvironment.c_str()); 7957 AssertPtrBreakStmt(newEnvStr, vrc = vrc2); 7958 7959 /* put new variables to the environment 7960 * (ignore empty variable names here since RTEnv API 7961 * intentionally doesn't do that) */ 7962 char *var = newEnvStr; 7963 for (char *p = newEnvStr; *p; ++p) 7964 { 7965 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\')) 7966 { 7967 *p = '\0'; 7968 if (*var) 7969 { 7970 char *val = strchr(var, '='); 7971 if (val) 7972 { 7973 *val++ = '\0'; 7974 vrc2 = RTEnvSetEx(env, var, val); 7975 } 7976 else 7977 vrc2 = RTEnvUnsetEx(env, var); 7978 if (RT_FAILURE(vrc2)) 7979 break; 7980 } 7981 var = p + 1; 7982 } 7983 } 7984 if (RT_SUCCESS(vrc2) && *var) 7985 vrc2 = RTEnvPutEx(env, var); 7986 7987 AssertRCBreakStmt(vrc2, vrc = vrc2); 7988 } 7989 while (0); 7990 7991 if (newEnvStr != NULL) 7992 RTStrFree(newEnvStr); 7993 } 7994 7995 #ifdef VBOX_WITH_QTGUI 7996 if (strFrontend == "gui" || strFrontend == "GUI/Qt") 7997 { 7998 # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */ 7999 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"; 8000 # else 8001 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE; 8002 # endif 8003 Assert(sz >= sizeof(VirtualBox_exe)); 8004 strcpy(cmd, VirtualBox_exe); 8005 8006 Utf8Str idStr = mData->mUuid.toString(); 8007 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 }; 8008 vrc = RTProcCreate(szPath, args, env, 0, &pid); 8009 } 8010 #else /* !VBOX_WITH_QTGUI */ 8011 if (0) 8012 ; 8013 #endif /* VBOX_WITH_QTGUI */ 8014 8015 else 8016 8017 #ifdef VBOX_WITH_VBOXSDL 8018 if (strFrontend == "sdl" || strFrontend == "GUI/SDL") 8019 { 8020 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE; 8021 Assert(sz >= sizeof(VBoxSDL_exe)); 8022 strcpy(cmd, VBoxSDL_exe); 8023 8024 Utf8Str idStr = mData->mUuid.toString(); 8025 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 }; 8026 vrc = RTProcCreate(szPath, args, env, 0, &pid); 8027 } 8028 #else /* !VBOX_WITH_VBOXSDL */ 8029 if (0) 8030 ; 8031 #endif /* !VBOX_WITH_VBOXSDL */ 8032 8033 else 8034 8035 #ifdef VBOX_WITH_HEADLESS 8036 if ( strFrontend == "headless" 8037 || strFrontend == "capture" 8038 || strFrontend == "vrdp" /* Deprecated. Same as headless. */ 8039 ) 8040 { 8041 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE, 8042 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional, 8043 * and a VM works even if the server has not been installed. 8044 * So in 4.0 the "headless" behavior remains the same for default VBox installations. 8045 * Only if a VRDE has been installed and the VM enables it, the "headless" will work 8046 * differently in 4.0 and 3.x. 8047 */ 8048 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE; 8049 Assert(sz >= sizeof(VBoxHeadless_exe)); 8050 strcpy(cmd, VBoxHeadless_exe); 8051 8052 Utf8Str idStr = mData->mUuid.toString(); 8053 /* Leave space for "--capture" arg. */ 8054 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), 8055 "--startvm", idStr.c_str(), 8056 "--vrde", "config", 8057 0, /* For "--capture". */ 8058 0 }; 8059 if (strFrontend == "capture") 8060 { 8061 unsigned pos = RT_ELEMENTS(args) - 2; 8062 args[pos] = "--capture"; 8063 } 8064 vrc = RTProcCreate(szPath, args, env, 8065 #ifdef RT_OS_WINDOWS 8066 RTPROC_FLAGS_NO_WINDOW 8067 #else 8068 0 8069 #endif 8070 , &pid); 8071 } 8072 #else /* !VBOX_WITH_HEADLESS */ 8073 if (0) 8074 ; 8075 #endif /* !VBOX_WITH_HEADLESS */ 8076 else 8077 { 8078 RTEnvDestroy(env); 8079 return setError(E_INVALIDARG, 8080 tr("Invalid frontend name: '%s'"), 8081 strFrontend.c_str()); 8082 } 8083 8084 RTEnvDestroy(env); 8085 8086 if (RT_FAILURE(vrc)) 8087 return setError(VBOX_E_IPRT_ERROR, 8088 tr("Could not launch a process for the machine '%s' (%Rrc)"), 8089 mUserData->s.strName.c_str(), vrc); 8090 8091 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid)); 8092 8093 /* 8094 * Note that we don't release the lock here before calling the client, 8095 * because it doesn't need to call us back if called with a NULL argument. 8096 * Releasing the lock here is dangerous because we didn't prepare the 8097 * launch data yet, but the client we've just started may happen to be 8098 * too fast and call openSession() that will fail (because of PID, etc.), 8099 * so that the Machine will never get out of the Spawning session state. 8100 */ 8101 8102 /* inform the session that it will be a remote one */ 8103 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n")); 8104 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write); 8105 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc)); 8106 8107 if (FAILED(rc)) 8108 { 8109 /* restore the session state */ 8110 mData->mSession.mState = SessionState_Unlocked; 8111 /* The failure may occur w/o any error info (from RPC), so provide one */ 8112 return setError(VBOX_E_VM_ERROR, 8113 tr("Failed to assign the machine to the session (%Rrc)"), rc); 8114 } 8115 8116 /* attach launch data to the machine */ 8117 Assert(mData->mSession.mPID == NIL_RTPROCESS); 8118 mData->mSession.mRemoteControls.push_back(aControl); 8119 mData->mSession.mProgress = aProgress; 8120 mData->mSession.mPID = pid; 8121 mData->mSession.mState = SessionState_Spawning; 8122 mData->mSession.mType = strFrontend; 8123 8124 LogFlowThisFuncLeave(); 8125 return S_OK; 8126 } 8127 8128 /** 8129 * Returns @c true if the given machine has an open direct session and returns 8130 * the session machine instance and additional session data (on some platforms) 8131 * if so. 8132 * 8133 * Note that when the method returns @c false, the arguments remain unchanged. 8134 * 8135 * @param aMachine Session machine object. 8136 * @param aControl Direct session control object (optional). 8137 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional). 8138 * 8139 * @note locks this object for reading. 8140 */ 40 41 Machine::ClientToken::ClientToken() 42 { 43 AssertReleaseFailed(); 44 } 45 46 Machine::ClientToken::~ClientToken() 47 { 8141 48 #if defined(RT_OS_WINDOWS) 8142 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 8143 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 8144 HANDLE *aIPCSem /*= NULL*/, 8145 bool aAllowClosing /*= false*/) 49 if (mClientToken) 50 ::CloseHandle(mClientToken); 8146 51 #elif defined(RT_OS_OS2) 8147 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 8148 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 8149 HMTX *aIPCSem /*= NULL*/, 8150 bool aAllowClosing /*= false*/) 8151 #else 8152 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 8153 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 8154 bool aAllowClosing /*= false*/) 8155 #endif 8156 { 8157 AutoLimitedCaller autoCaller(this); 8158 AssertComRCReturn(autoCaller.rc(), false); 8159 8160 /* just return false for inaccessible machines */ 8161 if (autoCaller.state() != Ready) 8162 return false; 8163 8164 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 8165 8166 if ( mData->mSession.mState == SessionState_Locked 8167 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking) 8168 ) 8169 { 8170 AssertReturn(!mData->mSession.mMachine.isNull(), false); 8171 8172 aMachine = mData->mSession.mMachine; 8173 8174 if (aControl != NULL) 8175 *aControl = mData->mSession.mDirectControl; 8176 8177 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 8178 /* Additional session data */ 8179 if (aIPCSem != NULL) 8180 *aIPCSem = aMachine->mIPCSem; 8181 #endif 8182 return true; 8183 } 8184 8185 return false; 8186 } 8187 8188 /** 8189 * Returns @c true if the given machine has an spawning direct session and 8190 * returns and additional session data (on some platforms) if so. 8191 * 8192 * Note that when the method returns @c false, the arguments remain unchanged. 8193 * 8194 * @param aPID PID of the spawned direct session process. 8195 * 8196 * @note locks this object for reading. 8197 */ 8198 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 8199 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/) 8200 #else 8201 bool Machine::isSessionSpawning() 8202 #endif 8203 { 8204 AutoLimitedCaller autoCaller(this); 8205 AssertComRCReturn(autoCaller.rc(), false); 8206 8207 /* just return false for inaccessible machines */ 8208 if (autoCaller.state() != Ready) 8209 return false; 8210 8211 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 8212 8213 if (mData->mSession.mState == SessionState_Spawning) 8214 { 8215 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 8216 /* Additional session data */ 8217 if (aPID != NULL) 8218 { 8219 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false); 8220 *aPID = mData->mSession.mPID; 8221 } 8222 #endif 8223 return true; 8224 } 8225 8226 return false; 8227 } 8228 8229 /** 8230 * Called from the client watcher thread to check for unexpected client process 8231 * death during Session_Spawning state (e.g. before it successfully opened a 8232 * direct session). 8233 * 8234 * On Win32 and on OS/2, this method is called only when we've got the 8235 * direct client's process termination notification, so it always returns @c 8236 * true. 8237 * 8238 * On other platforms, this method returns @c true if the client process is 8239 * terminated and @c false if it's still alive. 8240 * 8241 * @note Locks this object for writing. 8242 */ 8243 bool Machine::checkForSpawnFailure() 8244 { 8245 AutoCaller autoCaller(this); 8246 if (!autoCaller.isOk()) 8247 { 8248 /* nothing to do */ 8249 LogFlowThisFunc(("Already uninitialized!\n")); 8250 return true; 8251 } 8252 8253 /* VirtualBox::addProcessToReap() needs a write lock */ 8254 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); 8255 8256 if (mData->mSession.mState != SessionState_Spawning) 8257 { 8258 /* nothing to do */ 8259 LogFlowThisFunc(("Not spawning any more!\n")); 8260 return true; 8261 } 8262 8263 HRESULT rc = S_OK; 8264 8265 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 8266 8267 /* the process was already unexpectedly terminated, we just need to set an 8268 * error and finalize session spawning */ 8269 rc = setError(E_FAIL, 8270 tr("The virtual machine '%s' has terminated unexpectedly during startup"), 8271 getName().c_str()); 8272 #else 8273 8274 /* PID not yet initialized, skip check. */ 8275 if (mData->mSession.mPID == NIL_RTPROCESS) 8276 return false; 8277 8278 RTPROCSTATUS status; 8279 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, 8280 &status); 8281 8282 if (vrc != VERR_PROCESS_RUNNING) 8283 { 8284 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL) 8285 rc = setError(E_FAIL, 8286 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"), 8287 getName().c_str(), status.iStatus); 8288 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL) 8289 rc = setError(E_FAIL, 8290 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"), 8291 getName().c_str(), status.iStatus); 8292 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND) 8293 rc = setError(E_FAIL, 8294 tr("The virtual machine '%s' has terminated abnormally"), 8295 getName().c_str(), status.iStatus); 8296 else 8297 rc = setError(E_FAIL, 8298 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"), 8299 getName().c_str(), rc); 8300 } 8301 8302 #endif 8303 8304 if (FAILED(rc)) 8305 { 8306 /* Close the remote session, remove the remote control from the list 8307 * and reset session state to Closed (@note keep the code in sync with 8308 * the relevant part in checkForSpawnFailure()). */ 8309 8310 Assert(mData->mSession.mRemoteControls.size() == 1); 8311 if (mData->mSession.mRemoteControls.size() == 1) 8312 { 8313 ErrorInfoKeeper eik; 8314 mData->mSession.mRemoteControls.front()->Uninitialize(); 8315 } 8316 8317 mData->mSession.mRemoteControls.clear(); 8318 mData->mSession.mState = SessionState_Unlocked; 8319 8320 /* finalize the progress after setting the state */ 8321 if (!mData->mSession.mProgress.isNull()) 8322 { 8323 mData->mSession.mProgress->notifyComplete(rc); 8324 mData->mSession.mProgress.setNull(); 8325 } 8326 8327 mParent->addProcessToReap(mData->mSession.mPID); 8328 mData->mSession.mPID = NIL_RTPROCESS; 8329 8330 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); 8331 return true; 8332 } 8333 8334 return false; 8335 } 8336 8337 /** 8338 * Checks whether the machine can be registered. If so, commits and saves 8339 * all settings. 8340 * 8341 * @note Must be called from mParent's write lock. Locks this object and 8342 * children for writing. 8343 */ 8344 HRESULT Machine::prepareRegister() 8345 { 8346 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL); 8347 8348 AutoLimitedCaller autoCaller(this); 8349 AssertComRCReturnRC(autoCaller.rc()); 8350 8351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8352 8353 /* wait for state dependents to drop to zero */ 8354 ensureNoStateDependencies(); 8355 8356 if (!mData->mAccessible) 8357 return setError(VBOX_E_INVALID_OBJECT_STATE, 8358 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"), 8359 mUserData->s.strName.c_str(), 8360 mData->mUuid.toString().c_str()); 8361 8362 AssertReturn(autoCaller.state() == Ready, E_FAIL); 8363 8364 if (mData->mRegistered) 8365 return setError(VBOX_E_INVALID_OBJECT_STATE, 8366 tr("The machine '%s' with UUID {%s} is already registered"), 8367 mUserData->s.strName.c_str(), 8368 mData->mUuid.toString().c_str()); 8369 8370 HRESULT rc = S_OK; 8371 8372 // Ensure the settings are saved. If we are going to be registered and 8373 // no config file exists yet, create it by calling saveSettings() too. 8374 if ( (mData->flModifications) 8375 || (!mData->pMachineConfigFile->fileExists()) 8376 ) 8377 { 8378 rc = saveSettings(NULL); 8379 // no need to check whether VirtualBox.xml needs saving too since 8380 // we can't have a machine XML file rename pending 8381 if (FAILED(rc)) return rc; 8382 } 8383 8384 /* more config checking goes here */ 8385 8386 if (SUCCEEDED(rc)) 8387 { 8388 /* we may have had implicit modifications we want to fix on success */ 8389 commit(); 8390 8391 mData->mRegistered = true; 8392 } 8393 else 8394 { 8395 /* we may have had implicit modifications we want to cancel on failure*/ 8396 rollback(false /* aNotify */); 8397 } 8398 8399 return rc; 8400 } 8401 8402 /** 8403 * Increases the number of objects dependent on the machine state or on the 8404 * registered state. Guarantees that these two states will not change at least 8405 * until #releaseStateDependency() is called. 8406 * 8407 * Depending on the @a aDepType value, additional state checks may be made. 8408 * These checks will set extended error info on failure. See 8409 * #checkStateDependency() for more info. 8410 * 8411 * If this method returns a failure, the dependency is not added and the caller 8412 * is not allowed to rely on any particular machine state or registration state 8413 * value and may return the failed result code to the upper level. 8414 * 8415 * @param aDepType Dependency type to add. 8416 * @param aState Current machine state (NULL if not interested). 8417 * @param aRegistered Current registered state (NULL if not interested). 8418 * 8419 * @note Locks this object for writing. 8420 */ 8421 HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */, 8422 MachineState_T *aState /* = NULL */, 8423 BOOL *aRegistered /* = NULL */) 8424 { 8425 AutoCaller autoCaller(this); 8426 AssertComRCReturnRC(autoCaller.rc()); 8427 8428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8429 8430 HRESULT rc = checkStateDependency(aDepType); 8431 if (FAILED(rc)) return rc; 8432 8433 { 8434 if (mData->mMachineStateChangePending != 0) 8435 { 8436 /* ensureNoStateDependencies() is waiting for state dependencies to 8437 * drop to zero so don't add more. It may make sense to wait a bit 8438 * and retry before reporting an error (since the pending state 8439 * transition should be really quick) but let's just assert for 8440 * now to see if it ever happens on practice. */ 8441 8442 AssertFailed(); 8443 8444 return setError(E_ACCESSDENIED, 8445 tr("Machine state change is in progress. Please retry the operation later.")); 8446 } 8447 8448 ++mData->mMachineStateDeps; 8449 Assert(mData->mMachineStateDeps != 0 /* overflow */); 8450 } 8451 8452 if (aState) 8453 *aState = mData->mMachineState; 8454 if (aRegistered) 8455 *aRegistered = mData->mRegistered; 8456 8457 return S_OK; 8458 } 8459 8460 /** 8461 * Decreases the number of objects dependent on the machine state. 8462 * Must always complete the #addStateDependency() call after the state 8463 * dependency is no more necessary. 8464 */ 8465 void Machine::releaseStateDependency() 8466 { 8467 AutoCaller autoCaller(this); 8468 AssertComRCReturnVoid(autoCaller.rc()); 8469 8470 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8471 8472 /* releaseStateDependency() w/o addStateDependency()? */ 8473 AssertReturnVoid(mData->mMachineStateDeps != 0); 8474 -- mData->mMachineStateDeps; 8475 8476 if (mData->mMachineStateDeps == 0) 8477 { 8478 /* inform ensureNoStateDependencies() that there are no more deps */ 8479 if (mData->mMachineStateChangePending != 0) 8480 { 8481 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI); 8482 RTSemEventMultiSignal (mData->mMachineStateDepsSem); 8483 } 8484 } 8485 } 8486 8487 Utf8Str Machine::getExtraData(const Utf8Str &strKey) 8488 { 8489 /* start with nothing found */ 8490 Utf8Str strResult(""); 8491 8492 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 8493 8494 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey); 8495 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 8496 // found: 8497 strResult = it->second; // source is a Utf8Str 8498 8499 return strResult; 8500 } 8501 8502 // protected methods 8503 ///////////////////////////////////////////////////////////////////////////// 8504 8505 /** 8506 * Performs machine state checks based on the @a aDepType value. If a check 8507 * fails, this method will set extended error info, otherwise it will return 8508 * S_OK. It is supposed, that on failure, the caller will immediately return 8509 * the return value of this method to the upper level. 8510 * 8511 * When @a aDepType is AnyStateDep, this method always returns S_OK. 8512 * 8513 * When @a aDepType is MutableStateDep, this method returns S_OK only if the 8514 * current state of this machine object allows to change settings of the 8515 * machine (i.e. the machine is not registered, or registered but not running 8516 * and not saved). It is useful to call this method from Machine setters 8517 * before performing any change. 8518 * 8519 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same 8520 * as for MutableStateDep except that if the machine is saved, S_OK is also 8521 * returned. This is useful in setters which allow changing machine 8522 * properties when it is in the saved state. 8523 * 8524 * When @a aDepType is OfflineStateDep, this method returns S_OK if the 8525 * state is one of the 4 offline states (PoweredOff, Saved, Teleported, 8526 * Aborted). 8527 * 8528 * @param aDepType Dependency type to check. 8529 * 8530 * @note Non Machine based classes should use #addStateDependency() and 8531 * #releaseStateDependency() methods or the smart AutoStateDependency 8532 * template. 8533 * 8534 * @note This method must be called from under this object's read or write 8535 * lock. 8536 */ 8537 HRESULT Machine::checkStateDependency(StateDependency aDepType) 8538 { 8539 switch (aDepType) 8540 { 8541 case AnyStateDep: 8542 { 8543 break; 8544 } 8545 case MutableStateDep: 8546 { 8547 if ( mData->mRegistered 8548 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */ 8549 || ( mData->mMachineState != MachineState_Paused 8550 && mData->mMachineState != MachineState_Running 8551 && mData->mMachineState != MachineState_Aborted 8552 && mData->mMachineState != MachineState_Teleported 8553 && mData->mMachineState != MachineState_PoweredOff 8554 ) 8555 ) 8556 ) 8557 return setError(VBOX_E_INVALID_VM_STATE, 8558 tr("The machine is not mutable (state is %s)"), 8559 Global::stringifyMachineState(mData->mMachineState)); 8560 break; 8561 } 8562 case MutableOrSavedStateDep: 8563 { 8564 if ( mData->mRegistered 8565 && ( !isSessionMachine() /** @todo This was just converted raw; Check if Running and Paused should actually be included here... (Live Migration) */ 8566 || ( mData->mMachineState != MachineState_Paused 8567 && mData->mMachineState != MachineState_Running 8568 && mData->mMachineState != MachineState_Aborted 8569 && mData->mMachineState != MachineState_Teleported 8570 && mData->mMachineState != MachineState_Saved 8571 && mData->mMachineState != MachineState_PoweredOff 8572 ) 8573 ) 8574 ) 8575 return setError(VBOX_E_INVALID_VM_STATE, 8576 tr("The machine is not mutable (state is %s)"), 8577 Global::stringifyMachineState(mData->mMachineState)); 8578 break; 8579 } 8580 case OfflineStateDep: 8581 { 8582 if ( mData->mRegistered 8583 && ( !isSessionMachine() 8584 || ( mData->mMachineState != MachineState_PoweredOff 8585 && mData->mMachineState != MachineState_Saved 8586 && mData->mMachineState != MachineState_Aborted 8587 && mData->mMachineState != MachineState_Teleported 8588 ) 8589 ) 8590 ) 8591 return setError(VBOX_E_INVALID_VM_STATE, 8592 tr("The machine is not offline (state is %s)"), 8593 Global::stringifyMachineState(mData->mMachineState)); 8594 break; 8595 } 8596 } 8597 8598 return S_OK; 8599 } 8600 8601 /** 8602 * Helper to initialize all associated child objects and allocate data 8603 * structures. 8604 * 8605 * This method must be called as a part of the object's initialization procedure 8606 * (usually done in the #init() method). 8607 * 8608 * @note Must be called only from #init() or from #registeredInit(). 8609 */ 8610 HRESULT Machine::initDataAndChildObjects() 8611 { 8612 AutoCaller autoCaller(this); 8613 AssertComRCReturnRC(autoCaller.rc()); 8614 AssertComRCReturn(autoCaller.state() == InInit || 8615 autoCaller.state() == Limited, E_FAIL); 8616 8617 AssertReturn(!mData->mAccessible, E_FAIL); 8618 8619 /* allocate data structures */ 8620 mSSData.allocate(); 8621 mUserData.allocate(); 8622 mHWData.allocate(); 8623 mMediaData.allocate(); 8624 mStorageControllers.allocate(); 8625 mUSBControllers.allocate(); 8626 8627 /* initialize mOSTypeId */ 8628 mUserData->s.strOsType = mParent->getUnknownOSType()->id(); 8629 8630 /* create associated BIOS settings object */ 8631 unconst(mBIOSSettings).createObject(); 8632 mBIOSSettings->init(this); 8633 8634 /* create an associated VRDE object (default is disabled) */ 8635 unconst(mVRDEServer).createObject(); 8636 mVRDEServer->init(this); 8637 8638 /* create associated serial port objects */ 8639 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 8640 { 8641 unconst(mSerialPorts[slot]).createObject(); 8642 mSerialPorts[slot]->init(this, slot); 8643 } 8644 8645 /* create associated parallel port objects */ 8646 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 8647 { 8648 unconst(mParallelPorts[slot]).createObject(); 8649 mParallelPorts[slot]->init(this, slot); 8650 } 8651 8652 /* create the audio adapter object (always present, default is disabled) */ 8653 unconst(mAudioAdapter).createObject(); 8654 mAudioAdapter->init(this); 8655 8656 /* create the USB device filters object (always present) */ 8657 unconst(mUSBDeviceFilters).createObject(); 8658 mUSBDeviceFilters->init(this); 8659 8660 /* create associated network adapter objects */ 8661 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType)); 8662 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) 8663 { 8664 unconst(mNetworkAdapters[slot]).createObject(); 8665 mNetworkAdapters[slot]->init(this, slot); 8666 } 8667 8668 /* create the bandwidth control */ 8669 unconst(mBandwidthControl).createObject(); 8670 mBandwidthControl->init(this); 8671 8672 return S_OK; 8673 } 8674 8675 /** 8676 * Helper to uninitialize all associated child objects and to free all data 8677 * structures. 8678 * 8679 * This method must be called as a part of the object's uninitialization 8680 * procedure (usually done in the #uninit() method). 8681 * 8682 * @note Must be called only from #uninit() or from #registeredInit(). 8683 */ 8684 void Machine::uninitDataAndChildObjects() 8685 { 8686 AutoCaller autoCaller(this); 8687 AssertComRCReturnVoid(autoCaller.rc()); 8688 AssertComRCReturnVoid( autoCaller.state() == InUninit 8689 || autoCaller.state() == Limited); 8690 8691 /* tell all our other child objects we've been uninitialized */ 8692 if (mBandwidthControl) 8693 { 8694 mBandwidthControl->uninit(); 8695 unconst(mBandwidthControl).setNull(); 8696 } 8697 8698 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) 8699 { 8700 if (mNetworkAdapters[slot]) 8701 { 8702 mNetworkAdapters[slot]->uninit(); 8703 unconst(mNetworkAdapters[slot]).setNull(); 8704 } 8705 } 8706 8707 if (mUSBDeviceFilters) 8708 { 8709 mUSBDeviceFilters->uninit(); 8710 unconst(mUSBDeviceFilters).setNull(); 8711 } 8712 8713 if (mAudioAdapter) 8714 { 8715 mAudioAdapter->uninit(); 8716 unconst(mAudioAdapter).setNull(); 8717 } 8718 8719 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 8720 { 8721 if (mParallelPorts[slot]) 8722 { 8723 mParallelPorts[slot]->uninit(); 8724 unconst(mParallelPorts[slot]).setNull(); 8725 } 8726 } 8727 8728 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 8729 { 8730 if (mSerialPorts[slot]) 8731 { 8732 mSerialPorts[slot]->uninit(); 8733 unconst(mSerialPorts[slot]).setNull(); 8734 } 8735 } 8736 8737 if (mVRDEServer) 8738 { 8739 mVRDEServer->uninit(); 8740 unconst(mVRDEServer).setNull(); 8741 } 8742 8743 if (mBIOSSettings) 8744 { 8745 mBIOSSettings->uninit(); 8746 unconst(mBIOSSettings).setNull(); 8747 } 8748 8749 /* Deassociate media (only when a real Machine or a SnapshotMachine 8750 * instance is uninitialized; SessionMachine instances refer to real 8751 * Machine media). This is necessary for a clean re-initialization of 8752 * the VM after successfully re-checking the accessibility state. Note 8753 * that in case of normal Machine or SnapshotMachine uninitialization (as 8754 * a result of unregistering or deleting the snapshot), outdated media 8755 * attachments will already be uninitialized and deleted, so this 8756 * code will not affect them. */ 8757 if ( !!mMediaData 8758 && (!isSessionMachine()) 8759 ) 8760 { 8761 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 8762 it != mMediaData->mAttachments.end(); 8763 ++it) 8764 { 8765 ComObjPtr<Medium> pMedium = (*it)->getMedium(); 8766 if (pMedium.isNull()) 8767 continue; 8768 HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId()); 8769 AssertComRC(rc); 8770 } 8771 } 8772 8773 if (!isSessionMachine() && !isSnapshotMachine()) 8774 { 8775 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively) 8776 if (mData->mFirstSnapshot) 8777 { 8778 // snapshots tree is protected by machine write lock; strictly 8779 // this isn't necessary here since we're deleting the entire 8780 // machine, but otherwise we assert in Snapshot::uninit() 8781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8782 mData->mFirstSnapshot->uninit(); 8783 mData->mFirstSnapshot.setNull(); 8784 } 8785 8786 mData->mCurrentSnapshot.setNull(); 8787 } 8788 8789 /* free data structures (the essential mData structure is not freed here 8790 * since it may be still in use) */ 8791 mMediaData.free(); 8792 mStorageControllers.free(); 8793 mUSBControllers.free(); 8794 mHWData.free(); 8795 mUserData.free(); 8796 mSSData.free(); 8797 } 8798 8799 /** 8800 * Returns a pointer to the Machine object for this machine that acts like a 8801 * parent for complex machine data objects such as shared folders, etc. 8802 * 8803 * For primary Machine objects and for SnapshotMachine objects, returns this 8804 * object's pointer itself. For SessionMachine objects, returns the peer 8805 * (primary) machine pointer. 8806 */ 8807 Machine* Machine::getMachine() 8808 { 8809 if (isSessionMachine()) 8810 return (Machine*)mPeer; 8811 return this; 8812 } 8813 8814 /** 8815 * Makes sure that there are no machine state dependents. If necessary, waits 8816 * for the number of dependents to drop to zero. 8817 * 8818 * Make sure this method is called from under this object's write lock to 8819 * guarantee that no new dependents may be added when this method returns 8820 * control to the caller. 8821 * 8822 * @note Locks this object for writing. The lock will be released while waiting 8823 * (if necessary). 8824 * 8825 * @warning To be used only in methods that change the machine state! 8826 */ 8827 void Machine::ensureNoStateDependencies() 8828 { 8829 AssertReturnVoid(isWriteLockOnCurrentThread()); 8830 8831 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8832 8833 /* Wait for all state dependents if necessary */ 8834 if (mData->mMachineStateDeps != 0) 8835 { 8836 /* lazy semaphore creation */ 8837 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI) 8838 RTSemEventMultiCreate(&mData->mMachineStateDepsSem); 8839 8840 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n", 8841 mData->mMachineStateDeps)); 8842 8843 ++mData->mMachineStateChangePending; 8844 8845 /* reset the semaphore before waiting, the last dependent will signal 8846 * it */ 8847 RTSemEventMultiReset(mData->mMachineStateDepsSem); 8848 8849 alock.release(); 8850 8851 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT); 8852 8853 alock.acquire(); 8854 8855 -- mData->mMachineStateChangePending; 8856 } 8857 } 8858 8859 /** 8860 * Changes the machine state and informs callbacks. 8861 * 8862 * This method is not intended to fail so it either returns S_OK or asserts (and 8863 * returns a failure). 8864 * 8865 * @note Locks this object for writing. 8866 */ 8867 HRESULT Machine::setMachineState(MachineState_T aMachineState) 8868 { 8869 LogFlowThisFuncEnter(); 8870 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) )); 8871 8872 AutoCaller autoCaller(this); 8873 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 8874 8875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8876 8877 /* wait for state dependents to drop to zero */ 8878 ensureNoStateDependencies(); 8879 8880 if (mData->mMachineState != aMachineState) 8881 { 8882 mData->mMachineState = aMachineState; 8883 8884 RTTimeNow(&mData->mLastStateChange); 8885 8886 mParent->onMachineStateChange(mData->mUuid, aMachineState); 8887 } 8888 8889 LogFlowThisFuncLeave(); 8890 return S_OK; 8891 } 8892 8893 /** 8894 * Searches for a shared folder with the given logical name 8895 * in the collection of shared folders. 8896 * 8897 * @param aName logical name of the shared folder 8898 * @param aSharedFolder where to return the found object 8899 * @param aSetError whether to set the error info if the folder is 8900 * not found 8901 * @return 8902 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found 8903 * 8904 * @note 8905 * must be called from under the object's lock! 8906 */ 8907 HRESULT Machine::findSharedFolder(const Utf8Str &aName, 8908 ComObjPtr<SharedFolder> &aSharedFolder, 8909 bool aSetError /* = false */) 8910 { 8911 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND; 8912 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 8913 it != mHWData->mSharedFolders.end(); 8914 ++it) 8915 { 8916 SharedFolder *pSF = *it; 8917 AutoCaller autoCaller(pSF); 8918 if (pSF->getName() == aName) 8919 { 8920 aSharedFolder = pSF; 8921 rc = S_OK; 8922 break; 8923 } 8924 } 8925 8926 if (aSetError && FAILED(rc)) 8927 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str()); 8928 8929 return rc; 8930 } 8931 8932 /** 8933 * Initializes all machine instance data from the given settings structures 8934 * from XML. The exception is the machine UUID which needs special handling 8935 * depending on the caller's use case, so the caller needs to set that herself. 8936 * 8937 * This gets called in several contexts during machine initialization: 8938 * 8939 * -- When machine XML exists on disk already and needs to be loaded into memory, 8940 * for example, from registeredInit() to load all registered machines on 8941 * VirtualBox startup. In this case, puuidRegistry is NULL because the media 8942 * attached to the machine should be part of some media registry already. 8943 * 8944 * -- During OVF import, when a machine config has been constructed from an 8945 * OVF file. In this case, puuidRegistry is set to the machine UUID to 8946 * ensure that the media listed as attachments in the config (which have 8947 * been imported from the OVF) receive the correct registry ID. 8948 * 8949 * -- During VM cloning. 8950 * 8951 * @param config Machine settings from XML. 8952 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config. 8953 * @return 8954 */ 8955 HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config, 8956 const Guid *puuidRegistry) 8957 { 8958 // copy name, description, OS type, teleporter, UTC etc. 8959 #define DECODE_STR_MAX _1M 8960 mUserData->s = config.machineUserData; 8961 8962 // Decode the Icon overide data from config userdata and set onto Machine. 8963 const char* pszStr = config.machineUserData.ovIcon.c_str(); 8964 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL); 8965 if (cbOut > DECODE_STR_MAX) 8966 return setError(E_FAIL, 8967 tr("Icon Data too long.'%d' > '%d'"), 8968 cbOut, 8969 DECODE_STR_MAX); 8970 com::SafeArray<BYTE> iconByte(cbOut); 8971 HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL); 8972 if (FAILED(rc)) 8973 return setError(E_FAIL, 8974 tr("Failure to Decode Icon Data. '%s' (%d)"), 8975 pszStr, 8976 rc); 8977 COMSETTER(Icon)(ComSafeArrayAsInParam(iconByte)); 8978 8979 // look up the object by Id to check it is valid 8980 ComPtr<IGuestOSType> guestOSType; 8981 rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), 8982 guestOSType.asOutParam()); 8983 if (FAILED(rc)) return rc; 8984 8985 // stateFile (optional) 8986 if (config.strStateFile.isEmpty()) 8987 mSSData->strStateFilePath.setNull(); 8988 else 8989 { 8990 Utf8Str stateFilePathFull(config.strStateFile); 8991 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull); 8992 if (RT_FAILURE(vrc)) 8993 return setError(E_FAIL, 8994 tr("Invalid saved state file path '%s' (%Rrc)"), 8995 config.strStateFile.c_str(), 8996 vrc); 8997 mSSData->strStateFilePath = stateFilePathFull; 8998 } 8999 9000 // snapshot folder needs special processing so set it again 9001 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw()); 9002 if (FAILED(rc)) return rc; 9003 9004 /* Copy the extra data items (Not in any case config is already the same as 9005 * mData->pMachineConfigFile, like when the xml files are read from disk. So 9006 * make sure the extra data map is copied). */ 9007 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems; 9008 9009 /* currentStateModified (optional, default is true) */ 9010 mData->mCurrentStateModified = config.fCurrentStateModified; 9011 9012 mData->mLastStateChange = config.timeLastStateChange; 9013 9014 /* 9015 * note: all mUserData members must be assigned prior this point because 9016 * we need to commit changes in order to let mUserData be shared by all 9017 * snapshot machine instances. 9018 */ 9019 mUserData.commitCopy(); 9020 9021 // machine registry, if present (must be loaded before snapshots) 9022 if (config.canHaveOwnMediaRegistry()) 9023 { 9024 // determine machine folder 9025 Utf8Str strMachineFolder = getSettingsFileFull(); 9026 strMachineFolder.stripFilename(); 9027 rc = mParent->initMedia(getId(), // media registry ID == machine UUID 9028 config.mediaRegistry, 9029 strMachineFolder); 9030 if (FAILED(rc)) return rc; 9031 } 9032 9033 /* Snapshot node (optional) */ 9034 size_t cRootSnapshots; 9035 if ((cRootSnapshots = config.llFirstSnapshot.size())) 9036 { 9037 // there must be only one root snapshot 9038 Assert(cRootSnapshots == 1); 9039 9040 const settings::Snapshot &snap = config.llFirstSnapshot.front(); 9041 9042 rc = loadSnapshot(snap, 9043 config.uuidCurrentSnapshot, 9044 NULL); // no parent == first snapshot 9045 if (FAILED(rc)) return rc; 9046 } 9047 9048 // hardware data 9049 rc = loadHardware(config.hardwareMachine, &config.debugging, &config.autostart); 9050 if (FAILED(rc)) return rc; 9051 9052 // load storage controllers 9053 rc = loadStorageControllers(config.storageMachine, 9054 puuidRegistry, 9055 NULL /* puuidSnapshot */); 9056 if (FAILED(rc)) return rc; 9057 9058 /* 9059 * NOTE: the assignment below must be the last thing to do, 9060 * otherwise it will be not possible to change the settings 9061 * somewhere in the code above because all setters will be 9062 * blocked by checkStateDependency(MutableStateDep). 9063 */ 9064 9065 /* set the machine state to Aborted or Saved when appropriate */ 9066 if (config.fAborted) 9067 { 9068 mSSData->strStateFilePath.setNull(); 9069 9070 /* no need to use setMachineState() during init() */ 9071 mData->mMachineState = MachineState_Aborted; 9072 } 9073 else if (!mSSData->strStateFilePath.isEmpty()) 9074 { 9075 /* no need to use setMachineState() during init() */ 9076 mData->mMachineState = MachineState_Saved; 9077 } 9078 9079 // after loading settings, we are no longer different from the XML on disk 9080 mData->flModifications = 0; 9081 9082 return S_OK; 9083 } 9084 9085 /** 9086 * Recursively loads all snapshots starting from the given. 9087 * 9088 * @param aNode <Snapshot> node. 9089 * @param aCurSnapshotId Current snapshot ID from the settings file. 9090 * @param aParentSnapshot Parent snapshot. 9091 */ 9092 HRESULT Machine::loadSnapshot(const settings::Snapshot &data, 9093 const Guid &aCurSnapshotId, 9094 Snapshot *aParentSnapshot) 9095 { 9096 AssertReturn(!isSnapshotMachine(), E_FAIL); 9097 AssertReturn(!isSessionMachine(), E_FAIL); 9098 9099 HRESULT rc = S_OK; 9100 9101 Utf8Str strStateFile; 9102 if (!data.strStateFile.isEmpty()) 9103 { 9104 /* optional */ 9105 strStateFile = data.strStateFile; 9106 int vrc = calculateFullPath(strStateFile, strStateFile); 9107 if (RT_FAILURE(vrc)) 9108 return setError(E_FAIL, 9109 tr("Invalid saved state file path '%s' (%Rrc)"), 9110 strStateFile.c_str(), 9111 vrc); 9112 } 9113 9114 /* create a snapshot machine object */ 9115 ComObjPtr<SnapshotMachine> pSnapshotMachine; 9116 pSnapshotMachine.createObject(); 9117 rc = pSnapshotMachine->initFromSettings(this, 9118 data.hardware, 9119 &data.debugging, 9120 &data.autostart, 9121 data.storage, 9122 data.uuid.ref(), 9123 strStateFile); 9124 if (FAILED(rc)) return rc; 9125 9126 /* create a snapshot object */ 9127 ComObjPtr<Snapshot> pSnapshot; 9128 pSnapshot.createObject(); 9129 /* initialize the snapshot */ 9130 rc = pSnapshot->init(mParent, // VirtualBox object 9131 data.uuid, 9132 data.strName, 9133 data.strDescription, 9134 data.timestamp, 9135 pSnapshotMachine, 9136 aParentSnapshot); 9137 if (FAILED(rc)) return rc; 9138 9139 /* memorize the first snapshot if necessary */ 9140 if (!mData->mFirstSnapshot) 9141 mData->mFirstSnapshot = pSnapshot; 9142 9143 /* memorize the current snapshot when appropriate */ 9144 if ( !mData->mCurrentSnapshot 9145 && pSnapshot->getId() == aCurSnapshotId 9146 ) 9147 mData->mCurrentSnapshot = pSnapshot; 9148 9149 // now create the children 9150 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin(); 9151 it != data.llChildSnapshots.end(); 9152 ++it) 9153 { 9154 const settings::Snapshot &childData = *it; 9155 // recurse 9156 rc = loadSnapshot(childData, 9157 aCurSnapshotId, 9158 pSnapshot); // parent = the one we created above 9159 if (FAILED(rc)) return rc; 9160 } 9161 9162 return rc; 9163 } 9164 9165 /** 9166 * Loads settings into mHWData. 9167 * 9168 * @param data Reference to the hardware settings. 9169 * @param pDbg Pointer to the debugging settings. 9170 * @param pAutostart Pointer to the autostart settings. 9171 */ 9172 HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg, 9173 const settings::Autostart *pAutostart) 9174 { 9175 AssertReturn(!isSessionMachine(), E_FAIL); 9176 9177 HRESULT rc = S_OK; 9178 9179 try 9180 { 9181 /* The hardware version attribute (optional). */ 9182 mHWData->mHWVersion = data.strVersion; 9183 mHWData->mHardwareUUID = data.uuid; 9184 9185 mHWData->mHWVirtExEnabled = data.fHardwareVirt; 9186 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive; 9187 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging; 9188 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages; 9189 mHWData->mHWVirtExVPIDEnabled = data.fVPID; 9190 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution; 9191 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce; 9192 mHWData->mPAEEnabled = data.fPAE; 9193 mHWData->mSyntheticCpu = data.fSyntheticCpu; 9194 mHWData->mLongMode = data.enmLongMode; 9195 mHWData->mCPUCount = data.cCPUs; 9196 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug; 9197 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap; 9198 9199 // cpu 9200 if (mHWData->mCPUHotPlugEnabled) 9201 { 9202 for (settings::CpuList::const_iterator it = data.llCpus.begin(); 9203 it != data.llCpus.end(); 9204 ++it) 9205 { 9206 const settings::Cpu &cpu = *it; 9207 9208 mHWData->mCPUAttached[cpu.ulId] = true; 9209 } 9210 } 9211 9212 // cpuid leafs 9213 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin(); 9214 it != data.llCpuIdLeafs.end(); 9215 ++it) 9216 { 9217 const settings::CpuIdLeaf &leaf = *it; 9218 9219 switch (leaf.ulId) 9220 { 9221 case 0x0: 9222 case 0x1: 9223 case 0x2: 9224 case 0x3: 9225 case 0x4: 9226 case 0x5: 9227 case 0x6: 9228 case 0x7: 9229 case 0x8: 9230 case 0x9: 9231 case 0xA: 9232 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf; 9233 break; 9234 9235 case 0x80000000: 9236 case 0x80000001: 9237 case 0x80000002: 9238 case 0x80000003: 9239 case 0x80000004: 9240 case 0x80000005: 9241 case 0x80000006: 9242 case 0x80000007: 9243 case 0x80000008: 9244 case 0x80000009: 9245 case 0x8000000A: 9246 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf; 9247 break; 9248 9249 default: 9250 /* just ignore */ 9251 break; 9252 } 9253 } 9254 9255 mHWData->mMemorySize = data.ulMemorySizeMB; 9256 mHWData->mPageFusionEnabled = data.fPageFusionEnabled; 9257 9258 // boot order 9259 for (size_t i = 0; 9260 i < RT_ELEMENTS(mHWData->mBootOrder); 9261 i++) 9262 { 9263 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i); 9264 if (it == data.mapBootOrder.end()) 9265 mHWData->mBootOrder[i] = DeviceType_Null; 9266 else 9267 mHWData->mBootOrder[i] = it->second; 9268 } 9269 9270 mHWData->mGraphicsControllerType = data.graphicsControllerType; 9271 mHWData->mVRAMSize = data.ulVRAMSizeMB; 9272 mHWData->mMonitorCount = data.cMonitors; 9273 mHWData->mAccelerate3DEnabled = data.fAccelerate3D; 9274 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo; 9275 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes; 9276 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes; 9277 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled; 9278 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++) 9279 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i); 9280 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8); 9281 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate; 9282 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS; 9283 if (!data.strVideoCaptureFile.isEmpty()) 9284 calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile); 9285 else 9286 mHWData->mVideoCaptureFile.setNull(); 9287 mHWData->mFirmwareType = data.firmwareType; 9288 mHWData->mPointingHIDType = data.pointingHIDType; 9289 mHWData->mKeyboardHIDType = data.keyboardHIDType; 9290 mHWData->mChipsetType = data.chipsetType; 9291 mHWData->mEmulatedUSBWebcamEnabled = data.fEmulatedUSBWebcam; 9292 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader; 9293 mHWData->mHPETEnabled = data.fHPETEnabled; 9294 9295 /* VRDEServer */ 9296 rc = mVRDEServer->loadSettings(data.vrdeSettings); 9297 if (FAILED(rc)) return rc; 9298 9299 /* BIOS */ 9300 rc = mBIOSSettings->loadSettings(data.biosSettings); 9301 if (FAILED(rc)) return rc; 9302 9303 // Bandwidth control (must come before network adapters) 9304 rc = mBandwidthControl->loadSettings(data.ioSettings); 9305 if (FAILED(rc)) return rc; 9306 9307 /* Shared folders */ 9308 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin(); 9309 it != data.usbSettings.llUSBControllers.end(); 9310 ++it) 9311 { 9312 const settings::USBController &settingsCtrl = *it; 9313 ComObjPtr<USBController> newCtrl; 9314 9315 newCtrl.createObject(); 9316 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType); 9317 mUSBControllers->push_back(newCtrl); 9318 } 9319 9320 /* USB device filters */ 9321 rc = mUSBDeviceFilters->loadSettings(data.usbSettings); 9322 if (FAILED(rc)) return rc; 9323 9324 // network adapters 9325 uint32_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType); 9326 uint32_t oldCount = mNetworkAdapters.size(); 9327 if (newCount > oldCount) 9328 { 9329 mNetworkAdapters.resize(newCount); 9330 for (ULONG slot = oldCount; slot < mNetworkAdapters.size(); slot++) 9331 { 9332 unconst(mNetworkAdapters[slot]).createObject(); 9333 mNetworkAdapters[slot]->init(this, slot); 9334 } 9335 } 9336 else if (newCount < oldCount) 9337 mNetworkAdapters.resize(newCount); 9338 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin(); 9339 it != data.llNetworkAdapters.end(); 9340 ++it) 9341 { 9342 const settings::NetworkAdapter &nic = *it; 9343 9344 /* slot unicity is guaranteed by XML Schema */ 9345 AssertBreak(nic.ulSlot < mNetworkAdapters.size()); 9346 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic); 9347 if (FAILED(rc)) return rc; 9348 } 9349 9350 // serial ports 9351 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin(); 9352 it != data.llSerialPorts.end(); 9353 ++it) 9354 { 9355 const settings::SerialPort &s = *it; 9356 9357 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts)); 9358 rc = mSerialPorts[s.ulSlot]->loadSettings(s); 9359 if (FAILED(rc)) return rc; 9360 } 9361 9362 // parallel ports (optional) 9363 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin(); 9364 it != data.llParallelPorts.end(); 9365 ++it) 9366 { 9367 const settings::ParallelPort &p = *it; 9368 9369 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts)); 9370 rc = mParallelPorts[p.ulSlot]->loadSettings(p); 9371 if (FAILED(rc)) return rc; 9372 } 9373 9374 /* AudioAdapter */ 9375 rc = mAudioAdapter->loadSettings(data.audioAdapter); 9376 if (FAILED(rc)) return rc; 9377 9378 /* Shared folders */ 9379 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin(); 9380 it != data.llSharedFolders.end(); 9381 ++it) 9382 { 9383 const settings::SharedFolder &sf = *it; 9384 9385 ComObjPtr<SharedFolder> sharedFolder; 9386 /* Check for double entries. Not allowed! */ 9387 rc = findSharedFolder(sf.strName, sharedFolder, false /* aSetError */); 9388 if (SUCCEEDED(rc)) 9389 return setError(VBOX_E_OBJECT_IN_USE, 9390 tr("Shared folder named '%s' already exists"), 9391 sf.strName.c_str()); 9392 9393 /* Create the new shared folder. Don't break on error. This will be 9394 * reported when the machine starts. */ 9395 sharedFolder.createObject(); 9396 rc = sharedFolder->init(getMachine(), 9397 sf.strName, 9398 sf.strHostPath, 9399 RT_BOOL(sf.fWritable), 9400 RT_BOOL(sf.fAutoMount), 9401 false /* fFailOnError */); 9402 if (FAILED(rc)) return rc; 9403 mHWData->mSharedFolders.push_back(sharedFolder); 9404 } 9405 9406 // Clipboard 9407 mHWData->mClipboardMode = data.clipboardMode; 9408 9409 // drag'n'drop 9410 mHWData->mDragAndDropMode = data.dragAndDropMode; 9411 9412 // guest settings 9413 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize; 9414 9415 // IO settings 9416 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled; 9417 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize; 9418 9419 // Host PCI devices 9420 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin(); 9421 it != data.pciAttachments.end(); 9422 ++it) 9423 { 9424 const settings::HostPCIDeviceAttachment &hpda = *it; 9425 ComObjPtr<PCIDeviceAttachment> pda; 9426 9427 pda.createObject(); 9428 pda->loadSettings(this, hpda); 9429 mHWData->mPCIDeviceAssignments.push_back(pda); 9430 } 9431 9432 /* 9433 * (The following isn't really real hardware, but it lives in HWData 9434 * for reasons of convenience.) 9435 */ 9436 9437 #ifdef VBOX_WITH_GUEST_PROPS 9438 /* Guest properties (optional) */ 9439 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin(); 9440 it != data.llGuestProperties.end(); 9441 ++it) 9442 { 9443 const settings::GuestProperty &prop = *it; 9444 uint32_t fFlags = guestProp::NILFLAG; 9445 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags); 9446 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags }; 9447 mHWData->mGuestProperties[prop.strName] = property; 9448 } 9449 9450 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns; 9451 #endif /* VBOX_WITH_GUEST_PROPS defined */ 9452 9453 rc = loadDebugging(pDbg); 9454 if (FAILED(rc)) 9455 return rc; 9456 9457 mHWData->mAutostart = *pAutostart; 9458 9459 /* default frontend */ 9460 mHWData->mDefaultFrontend = data.strDefaultFrontend; 9461 } 9462 catch(std::bad_alloc &) 9463 { 9464 return E_OUTOFMEMORY; 9465 } 9466 9467 AssertComRC(rc); 9468 return rc; 9469 } 9470 9471 /** 9472 * Called from Machine::loadHardware() to load the debugging settings of the 9473 * machine. 9474 * 9475 * @param pDbg Pointer to the settings. 9476 */ 9477 HRESULT Machine::loadDebugging(const settings::Debugging *pDbg) 9478 { 9479 mHWData->mDebugging = *pDbg; 9480 /* no more processing currently required, this will probably change. */ 9481 return S_OK; 9482 } 9483 9484 /** 9485 * Called from loadMachineDataFromSettings() for the storage controller data, including media. 9486 * 9487 * @param data 9488 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings() 9489 * @param puuidSnapshot 9490 * @return 9491 */ 9492 HRESULT Machine::loadStorageControllers(const settings::Storage &data, 9493 const Guid *puuidRegistry, 9494 const Guid *puuidSnapshot) 9495 { 9496 AssertReturn(!isSessionMachine(), E_FAIL); 9497 9498 HRESULT rc = S_OK; 9499 9500 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin(); 9501 it != data.llStorageControllers.end(); 9502 ++it) 9503 { 9504 const settings::StorageController &ctlData = *it; 9505 9506 ComObjPtr<StorageController> pCtl; 9507 /* Try to find one with the name first. */ 9508 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */); 9509 if (SUCCEEDED(rc)) 9510 return setError(VBOX_E_OBJECT_IN_USE, 9511 tr("Storage controller named '%s' already exists"), 9512 ctlData.strName.c_str()); 9513 9514 pCtl.createObject(); 9515 rc = pCtl->init(this, 9516 ctlData.strName, 9517 ctlData.storageBus, 9518 ctlData.ulInstance, 9519 ctlData.fBootable); 9520 if (FAILED(rc)) return rc; 9521 9522 mStorageControllers->push_back(pCtl); 9523 9524 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType); 9525 if (FAILED(rc)) return rc; 9526 9527 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount); 9528 if (FAILED(rc)) return rc; 9529 9530 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache); 9531 if (FAILED(rc)) return rc; 9532 9533 /* Set IDE emulation settings (only for AHCI controller). */ 9534 if (ctlData.controllerType == StorageControllerType_IntelAhci) 9535 { 9536 if ( (FAILED(rc = pCtl->setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort))) 9537 || (FAILED(rc = pCtl->setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort))) 9538 || (FAILED(rc = pCtl->setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort))) 9539 || (FAILED(rc = pCtl->setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort))) 9540 ) 9541 return rc; 9542 } 9543 9544 /* Load the attached devices now. */ 9545 rc = loadStorageDevices(pCtl, 9546 ctlData, 9547 puuidRegistry, 9548 puuidSnapshot); 9549 if (FAILED(rc)) return rc; 9550 } 9551 9552 return S_OK; 9553 } 9554 9555 /** 9556 * Called from loadStorageControllers for a controller's devices. 9557 * 9558 * @param aStorageController 9559 * @param data 9560 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::loadMachineDataFromSettings() 9561 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine 9562 * @return 9563 */ 9564 HRESULT Machine::loadStorageDevices(StorageController *aStorageController, 9565 const settings::StorageController &data, 9566 const Guid *puuidRegistry, 9567 const Guid *puuidSnapshot) 9568 { 9569 HRESULT rc = S_OK; 9570 9571 /* paranoia: detect duplicate attachments */ 9572 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 9573 it != data.llAttachedDevices.end(); 9574 ++it) 9575 { 9576 const settings::AttachedDevice &ad = *it; 9577 9578 for (settings::AttachedDevicesList::const_iterator it2 = it; 9579 it2 != data.llAttachedDevices.end(); 9580 ++it2) 9581 { 9582 if (it == it2) 9583 continue; 9584 9585 const settings::AttachedDevice &ad2 = *it2; 9586 9587 if ( ad.lPort == ad2.lPort 9588 && ad.lDevice == ad2.lDevice) 9589 { 9590 return setError(E_FAIL, 9591 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"), 9592 aStorageController->getName().c_str(), 9593 ad.lPort, 9594 ad.lDevice, 9595 mUserData->s.strName.c_str()); 9596 } 9597 } 9598 } 9599 9600 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 9601 it != data.llAttachedDevices.end(); 9602 ++it) 9603 { 9604 const settings::AttachedDevice &dev = *it; 9605 ComObjPtr<Medium> medium; 9606 9607 switch (dev.deviceType) 9608 { 9609 case DeviceType_Floppy: 9610 case DeviceType_DVD: 9611 if (dev.strHostDriveSrc.isNotEmpty()) 9612 rc = mParent->host()->findHostDriveByName(dev.deviceType, dev.strHostDriveSrc, false /* fRefresh */, medium); 9613 else 9614 rc = mParent->findRemoveableMedium(dev.deviceType, 9615 dev.uuid, 9616 false /* fRefresh */, 9617 false /* aSetError */, 9618 medium); 9619 if (rc == VBOX_E_OBJECT_NOT_FOUND) 9620 // This is not an error. The host drive or UUID might have vanished, so just go ahead without this removeable medium attachment 9621 rc = S_OK; 9622 break; 9623 9624 case DeviceType_HardDisk: 9625 { 9626 /* find a hard disk by UUID */ 9627 rc = mParent->findHardDiskById(dev.uuid, true /* aDoSetError */, &medium); 9628 if (FAILED(rc)) 9629 { 9630 if (isSnapshotMachine()) 9631 { 9632 // wrap another error message around the "cannot find hard disk" set by findHardDisk 9633 // so the user knows that the bad disk is in a snapshot somewhere 9634 com::ErrorInfo info; 9635 return setError(E_FAIL, 9636 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"), 9637 puuidSnapshot->raw(), 9638 info.getText().raw()); 9639 } 9640 else 9641 return rc; 9642 } 9643 9644 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS); 9645 9646 if (medium->getType() == MediumType_Immutable) 9647 { 9648 if (isSnapshotMachine()) 9649 return setError(E_FAIL, 9650 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} " 9651 "of the virtual machine '%s' ('%s')"), 9652 medium->getLocationFull().c_str(), 9653 dev.uuid.raw(), 9654 puuidSnapshot->raw(), 9655 mUserData->s.strName.c_str(), 9656 mData->m_strConfigFileFull.c_str()); 9657 9658 return setError(E_FAIL, 9659 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"), 9660 medium->getLocationFull().c_str(), 9661 dev.uuid.raw(), 9662 mUserData->s.strName.c_str(), 9663 mData->m_strConfigFileFull.c_str()); 9664 } 9665 9666 if (medium->getType() == MediumType_MultiAttach) 9667 { 9668 if (isSnapshotMachine()) 9669 return setError(E_FAIL, 9670 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} " 9671 "of the virtual machine '%s' ('%s')"), 9672 medium->getLocationFull().c_str(), 9673 dev.uuid.raw(), 9674 puuidSnapshot->raw(), 9675 mUserData->s.strName.c_str(), 9676 mData->m_strConfigFileFull.c_str()); 9677 9678 return setError(E_FAIL, 9679 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"), 9680 medium->getLocationFull().c_str(), 9681 dev.uuid.raw(), 9682 mUserData->s.strName.c_str(), 9683 mData->m_strConfigFileFull.c_str()); 9684 } 9685 9686 if ( !isSnapshotMachine() 9687 && medium->getChildren().size() != 0 9688 ) 9689 return setError(E_FAIL, 9690 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') " 9691 "because it has %d differencing child hard disks"), 9692 medium->getLocationFull().c_str(), 9693 dev.uuid.raw(), 9694 mUserData->s.strName.c_str(), 9695 mData->m_strConfigFileFull.c_str(), 9696 medium->getChildren().size()); 9697 9698 if (findAttachment(mMediaData->mAttachments, 9699 medium)) 9700 return setError(E_FAIL, 9701 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"), 9702 medium->getLocationFull().c_str(), 9703 dev.uuid.raw(), 9704 mUserData->s.strName.c_str(), 9705 mData->m_strConfigFileFull.c_str()); 9706 9707 break; 9708 } 9709 9710 default: 9711 return setError(E_FAIL, 9712 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"), 9713 medium->getLocationFull().c_str(), 9714 mUserData->s.strName.c_str(), 9715 mData->m_strConfigFileFull.c_str()); 9716 } 9717 9718 if (FAILED(rc)) 9719 break; 9720 9721 /* Bandwidth groups are loaded at this point. */ 9722 ComObjPtr<BandwidthGroup> pBwGroup; 9723 9724 if (!dev.strBwGroup.isEmpty()) 9725 { 9726 rc = mBandwidthControl->getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */); 9727 if (FAILED(rc)) 9728 return setError(E_FAIL, 9729 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"), 9730 medium->getLocationFull().c_str(), 9731 dev.strBwGroup.c_str(), 9732 mUserData->s.strName.c_str(), 9733 mData->m_strConfigFileFull.c_str()); 9734 pBwGroup->reference(); 9735 } 9736 9737 const Bstr controllerName = aStorageController->getName(); 9738 ComObjPtr<MediumAttachment> pAttachment; 9739 pAttachment.createObject(); 9740 rc = pAttachment->init(this, 9741 medium, 9742 controllerName, 9743 dev.lPort, 9744 dev.lDevice, 9745 dev.deviceType, 9746 false, 9747 dev.fPassThrough, 9748 dev.fTempEject, 9749 dev.fNonRotational, 9750 dev.fDiscard, 9751 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName()); 9752 if (FAILED(rc)) break; 9753 9754 /* associate the medium with this machine and snapshot */ 9755 if (!medium.isNull()) 9756 { 9757 AutoCaller medCaller(medium); 9758 if (FAILED(medCaller.rc())) return medCaller.rc(); 9759 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS); 9760 9761 if (isSnapshotMachine()) 9762 rc = medium->addBackReference(mData->mUuid, *puuidSnapshot); 9763 else 9764 rc = medium->addBackReference(mData->mUuid); 9765 /* If the medium->addBackReference fails it sets an appropriate 9766 * error message, so no need to do any guesswork here. */ 9767 9768 if (puuidRegistry) 9769 // caller wants registry ID to be set on all attached media (OVF import case) 9770 medium->addRegistry(*puuidRegistry, false /* fRecurse */); 9771 } 9772 9773 if (FAILED(rc)) 9774 break; 9775 9776 /* back up mMediaData to let registeredInit() properly rollback on failure 9777 * (= limited accessibility) */ 9778 setModified(IsModified_Storage); 9779 mMediaData.backup(); 9780 mMediaData->mAttachments.push_back(pAttachment); 9781 } 9782 9783 return rc; 9784 } 9785 9786 /** 9787 * Returns the snapshot with the given UUID or fails of no such snapshot exists. 9788 * 9789 * @param aId snapshot UUID to find (empty UUID refers the first snapshot) 9790 * @param aSnapshot where to return the found snapshot 9791 * @param aSetError true to set extended error info on failure 9792 */ 9793 HRESULT Machine::findSnapshotById(const Guid &aId, 9794 ComObjPtr<Snapshot> &aSnapshot, 9795 bool aSetError /* = false */) 9796 { 9797 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 9798 9799 if (!mData->mFirstSnapshot) 9800 { 9801 if (aSetError) 9802 return setError(E_FAIL, tr("This machine does not have any snapshots")); 9803 return E_FAIL; 9804 } 9805 9806 if (aId.isZero()) 9807 aSnapshot = mData->mFirstSnapshot; 9808 else 9809 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref()); 9810 9811 if (!aSnapshot) 9812 { 9813 if (aSetError) 9814 return setError(E_FAIL, 9815 tr("Could not find a snapshot with UUID {%s}"), 9816 aId.toString().c_str()); 9817 return E_FAIL; 9818 } 9819 9820 return S_OK; 9821 } 9822 9823 /** 9824 * Returns the snapshot with the given name or fails of no such snapshot. 9825 * 9826 * @param aName snapshot name to find 9827 * @param aSnapshot where to return the found snapshot 9828 * @param aSetError true to set extended error info on failure 9829 */ 9830 HRESULT Machine::findSnapshotByName(const Utf8Str &strName, 9831 ComObjPtr<Snapshot> &aSnapshot, 9832 bool aSetError /* = false */) 9833 { 9834 AssertReturn(!strName.isEmpty(), E_INVALIDARG); 9835 9836 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 9837 9838 if (!mData->mFirstSnapshot) 9839 { 9840 if (aSetError) 9841 return setError(VBOX_E_OBJECT_NOT_FOUND, 9842 tr("This machine does not have any snapshots")); 9843 return VBOX_E_OBJECT_NOT_FOUND; 9844 } 9845 9846 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(strName); 9847 9848 if (!aSnapshot) 9849 { 9850 if (aSetError) 9851 return setError(VBOX_E_OBJECT_NOT_FOUND, 9852 tr("Could not find a snapshot named '%s'"), strName.c_str()); 9853 return VBOX_E_OBJECT_NOT_FOUND; 9854 } 9855 9856 return S_OK; 9857 } 9858 9859 /** 9860 * Returns a storage controller object with the given name. 9861 * 9862 * @param aName storage controller name to find 9863 * @param aStorageController where to return the found storage controller 9864 * @param aSetError true to set extended error info on failure 9865 */ 9866 HRESULT Machine::getStorageControllerByName(const Utf8Str &aName, 9867 ComObjPtr<StorageController> &aStorageController, 9868 bool aSetError /* = false */) 9869 { 9870 AssertReturn(!aName.isEmpty(), E_INVALIDARG); 9871 9872 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 9873 it != mStorageControllers->end(); 9874 ++it) 9875 { 9876 if ((*it)->getName() == aName) 9877 { 9878 aStorageController = (*it); 9879 return S_OK; 9880 } 9881 } 9882 9883 if (aSetError) 9884 return setError(VBOX_E_OBJECT_NOT_FOUND, 9885 tr("Could not find a storage controller named '%s'"), 9886 aName.c_str()); 9887 return VBOX_E_OBJECT_NOT_FOUND; 9888 } 9889 9890 /** 9891 * Returns a USB controller object with the given name. 9892 * 9893 * @param aName USB controller name to find 9894 * @param aUSBController where to return the found USB controller 9895 * @param aSetError true to set extended error info on failure 9896 */ 9897 HRESULT Machine::getUSBControllerByName(const Utf8Str &aName, 9898 ComObjPtr<USBController> &aUSBController, 9899 bool aSetError /* = false */) 9900 { 9901 AssertReturn(!aName.isEmpty(), E_INVALIDARG); 9902 9903 for (USBControllerList::const_iterator it = mUSBControllers->begin(); 9904 it != mUSBControllers->end(); 9905 ++it) 9906 { 9907 if ((*it)->getName() == aName) 9908 { 9909 aUSBController = (*it); 9910 return S_OK; 9911 } 9912 } 9913 9914 if (aSetError) 9915 return setError(VBOX_E_OBJECT_NOT_FOUND, 9916 tr("Could not find a storage controller named '%s'"), 9917 aName.c_str()); 9918 return VBOX_E_OBJECT_NOT_FOUND; 9919 } 9920 9921 /** 9922 * Returns the number of USB controller instance of the given type. 9923 * 9924 * @param enmType USB controller type. 9925 */ 9926 ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType) 9927 { 9928 ULONG cCtrls = 0; 9929 9930 for (USBControllerList::const_iterator it = mUSBControllers->begin(); 9931 it != mUSBControllers->end(); 9932 ++it) 9933 { 9934 if ((*it)->getControllerType() == enmType) 9935 cCtrls++; 9936 } 9937 9938 return cCtrls; 9939 } 9940 9941 HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName, 9942 MediaData::AttachmentList &atts) 9943 { 9944 AutoCaller autoCaller(this); 9945 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 9946 9947 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 9948 9949 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin(); 9950 it != mMediaData->mAttachments.end(); 9951 ++it) 9952 { 9953 const ComObjPtr<MediumAttachment> &pAtt = *it; 9954 9955 // should never happen, but deal with NULL pointers in the list. 9956 AssertStmt(!pAtt.isNull(), continue); 9957 9958 // getControllerName() needs caller+read lock 9959 AutoCaller autoAttCaller(pAtt); 9960 if (FAILED(autoAttCaller.rc())) 9961 { 9962 atts.clear(); 9963 return autoAttCaller.rc(); 9964 } 9965 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS); 9966 9967 if (pAtt->getControllerName() == aName) 9968 atts.push_back(pAtt); 9969 } 9970 9971 return S_OK; 9972 } 9973 9974 /** 9975 * Helper for #saveSettings. Cares about renaming the settings directory and 9976 * file if the machine name was changed and about creating a new settings file 9977 * if this is a new machine. 9978 * 9979 * @note Must be never called directly but only from #saveSettings(). 9980 */ 9981 HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings) 9982 { 9983 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 9984 9985 HRESULT rc = S_OK; 9986 9987 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists(); 9988 9989 /// @todo need to handle primary group change, too 9990 9991 /* attempt to rename the settings file if machine name is changed */ 9992 if ( mUserData->s.fNameSync 9993 && mUserData.isBackedUp() 9994 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName 9995 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front()) 9996 ) 9997 { 9998 bool dirRenamed = false; 9999 bool fileRenamed = false; 10000 10001 Utf8Str configFile, newConfigFile; 10002 Utf8Str configFilePrev, newConfigFilePrev; 10003 Utf8Str configDir, newConfigDir; 10004 10005 do 10006 { 10007 int vrc = VINF_SUCCESS; 10008 10009 Utf8Str name = mUserData.backedUpData()->s.strName; 10010 Utf8Str newName = mUserData->s.strName; 10011 Utf8Str group = mUserData.backedUpData()->s.llGroups.front(); 10012 if (group == "/") 10013 group.setNull(); 10014 Utf8Str newGroup = mUserData->s.llGroups.front(); 10015 if (newGroup == "/") 10016 newGroup.setNull(); 10017 10018 configFile = mData->m_strConfigFileFull; 10019 10020 /* first, rename the directory if it matches the group and machine name */ 10021 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s", 10022 group.c_str(), RTPATH_DELIMITER, name.c_str()); 10023 /** @todo hack, make somehow use of ComposeMachineFilename */ 10024 if (mUserData->s.fDirectoryIncludesUUID) 10025 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw()); 10026 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s", 10027 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str()); 10028 /** @todo hack, make somehow use of ComposeMachineFilename */ 10029 if (mUserData->s.fDirectoryIncludesUUID) 10030 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw()); 10031 configDir = configFile; 10032 configDir.stripFilename(); 10033 newConfigDir = configDir; 10034 if ( configDir.length() >= groupPlusName.length() 10035 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(), groupPlusName.c_str())) 10036 { 10037 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length()); 10038 Utf8Str newConfigBaseDir(newConfigDir); 10039 newConfigDir.append(newGroupPlusName); 10040 /* consistency: use \ if appropriate on the platform */ 10041 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false); 10042 /* new dir and old dir cannot be equal here because of 'if' 10043 * above and because name != newName */ 10044 Assert(configDir != newConfigDir); 10045 if (!fSettingsFileIsNew) 10046 { 10047 /* perform real rename only if the machine is not new */ 10048 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0); 10049 if ( vrc == VERR_FILE_NOT_FOUND 10050 || vrc == VERR_PATH_NOT_FOUND) 10051 { 10052 /* create the parent directory, then retry renaming */ 10053 Utf8Str parent(newConfigDir); 10054 parent.stripFilename(); 10055 (void)RTDirCreateFullPath(parent.c_str(), 0700); 10056 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0); 10057 } 10058 if (RT_FAILURE(vrc)) 10059 { 10060 rc = setError(E_FAIL, 10061 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"), 10062 configDir.c_str(), 10063 newConfigDir.c_str(), 10064 vrc); 10065 break; 10066 } 10067 /* delete subdirectories which are no longer needed */ 10068 Utf8Str dir(configDir); 10069 dir.stripFilename(); 10070 while (dir != newConfigBaseDir && dir != ".") 10071 { 10072 vrc = RTDirRemove(dir.c_str()); 10073 if (RT_FAILURE(vrc)) 10074 break; 10075 dir.stripFilename(); 10076 } 10077 dirRenamed = true; 10078 } 10079 } 10080 10081 newConfigFile = Utf8StrFmt("%s%c%s.vbox", 10082 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str()); 10083 10084 /* then try to rename the settings file itself */ 10085 if (newConfigFile != configFile) 10086 { 10087 /* get the path to old settings file in renamed directory */ 10088 configFile = Utf8StrFmt("%s%c%s", 10089 newConfigDir.c_str(), 10090 RTPATH_DELIMITER, 10091 RTPathFilename(configFile.c_str())); 10092 if (!fSettingsFileIsNew) 10093 { 10094 /* perform real rename only if the machine is not new */ 10095 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0); 10096 if (RT_FAILURE(vrc)) 10097 { 10098 rc = setError(E_FAIL, 10099 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"), 10100 configFile.c_str(), 10101 newConfigFile.c_str(), 10102 vrc); 10103 break; 10104 } 10105 fileRenamed = true; 10106 configFilePrev = configFile; 10107 configFilePrev += "-prev"; 10108 newConfigFilePrev = newConfigFile; 10109 newConfigFilePrev += "-prev"; 10110 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0); 10111 } 10112 } 10113 10114 // update m_strConfigFileFull amd mConfigFile 10115 mData->m_strConfigFileFull = newConfigFile; 10116 // compute the relative path too 10117 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile); 10118 10119 // store the old and new so that VirtualBox::saveSettings() can update 10120 // the media registry 10121 if ( mData->mRegistered 10122 && configDir != newConfigDir) 10123 { 10124 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir); 10125 10126 if (pfNeedsGlobalSaveSettings) 10127 *pfNeedsGlobalSaveSettings = true; 10128 } 10129 10130 // in the saved state file path, replace the old directory with the new directory 10131 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str())) 10132 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length()); 10133 10134 // and do the same thing for the saved state file paths of all the online snapshots 10135 if (mData->mFirstSnapshot) 10136 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(), 10137 newConfigDir.c_str()); 10138 } 10139 while (0); 10140 10141 if (FAILED(rc)) 10142 { 10143 /* silently try to rename everything back */ 10144 if (fileRenamed) 10145 { 10146 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0); 10147 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0); 10148 } 10149 if (dirRenamed) 10150 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0); 10151 } 10152 10153 if (FAILED(rc)) return rc; 10154 } 10155 10156 if (fSettingsFileIsNew) 10157 { 10158 /* create a virgin config file */ 10159 int vrc = VINF_SUCCESS; 10160 10161 /* ensure the settings directory exists */ 10162 Utf8Str path(mData->m_strConfigFileFull); 10163 path.stripFilename(); 10164 if (!RTDirExists(path.c_str())) 10165 { 10166 vrc = RTDirCreateFullPath(path.c_str(), 0700); 10167 if (RT_FAILURE(vrc)) 10168 { 10169 return setError(E_FAIL, 10170 tr("Could not create a directory '%s' to save the settings file (%Rrc)"), 10171 path.c_str(), 10172 vrc); 10173 } 10174 } 10175 10176 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */ 10177 path = Utf8Str(mData->m_strConfigFileFull); 10178 RTFILE f = NIL_RTFILE; 10179 vrc = RTFileOpen(&f, path.c_str(), 10180 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE); 10181 if (RT_FAILURE(vrc)) 10182 return setError(E_FAIL, 10183 tr("Could not create the settings file '%s' (%Rrc)"), 10184 path.c_str(), 10185 vrc); 10186 RTFileClose(f); 10187 } 10188 10189 return rc; 10190 } 10191 10192 /** 10193 * Saves and commits machine data, user data and hardware data. 10194 * 10195 * Note that on failure, the data remains uncommitted. 10196 * 10197 * @a aFlags may combine the following flags: 10198 * 10199 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE. 10200 * Used when saving settings after an operation that makes them 100% 10201 * correspond to the settings from the current snapshot. 10202 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if 10203 * #isReallyModified() returns false. This is necessary for cases when we 10204 * change machine data directly, not through the backup()/commit() mechanism. 10205 * - SaveS_Force: settings will be saved without doing a deep compare of the 10206 * settings structures. This is used when this is called because snapshots 10207 * have changed to avoid the overhead of the deep compare. 10208 * 10209 * @note Must be called from under this object's write lock. Locks children for 10210 * writing. 10211 * 10212 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been 10213 * initialized to false and that will be set to true by this function if 10214 * the caller must invoke VirtualBox::saveSettings() because the global 10215 * settings have changed. This will happen if a machine rename has been 10216 * saved and the global machine and media registries will therefore need 10217 * updating. 10218 */ 10219 HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings, 10220 int aFlags /*= 0*/) 10221 { 10222 LogFlowThisFuncEnter(); 10223 10224 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 10225 10226 /* make sure child objects are unable to modify the settings while we are 10227 * saving them */ 10228 ensureNoStateDependencies(); 10229 10230 AssertReturn(!isSnapshotMachine(), 10231 E_FAIL); 10232 10233 HRESULT rc = S_OK; 10234 bool fNeedsWrite = false; 10235 10236 /* First, prepare to save settings. It will care about renaming the 10237 * settings directory and file if the machine name was changed and about 10238 * creating a new settings file if this is a new machine. */ 10239 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings); 10240 if (FAILED(rc)) return rc; 10241 10242 // keep a pointer to the current settings structures 10243 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile; 10244 settings::MachineConfigFile *pNewConfig = NULL; 10245 10246 try 10247 { 10248 // make a fresh one to have everyone write stuff into 10249 pNewConfig = new settings::MachineConfigFile(NULL); 10250 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile); 10251 10252 // now go and copy all the settings data from COM to the settings structures 10253 // (this calles saveSettings() on all the COM objects in the machine) 10254 copyMachineDataToSettings(*pNewConfig); 10255 10256 if (aFlags & SaveS_ResetCurStateModified) 10257 { 10258 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot() 10259 mData->mCurrentStateModified = FALSE; 10260 fNeedsWrite = true; // always, no need to compare 10261 } 10262 else if (aFlags & SaveS_Force) 10263 { 10264 fNeedsWrite = true; // always, no need to compare 10265 } 10266 else 10267 { 10268 if (!mData->mCurrentStateModified) 10269 { 10270 // do a deep compare of the settings that we just saved with the settings 10271 // previously stored in the config file; this invokes MachineConfigFile::operator== 10272 // which does a deep compare of all the settings, which is expensive but less expensive 10273 // than writing out XML in vain 10274 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig); 10275 10276 // could still be modified if any settings changed 10277 mData->mCurrentStateModified = fAnySettingsChanged; 10278 10279 fNeedsWrite = fAnySettingsChanged; 10280 } 10281 else 10282 fNeedsWrite = true; 10283 } 10284 10285 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified; 10286 10287 if (fNeedsWrite) 10288 // now spit it all out! 10289 pNewConfig->write(mData->m_strConfigFileFull); 10290 10291 mData->pMachineConfigFile = pNewConfig; 10292 delete pOldConfig; 10293 commit(); 10294 10295 // after saving settings, we are no longer different from the XML on disk 10296 mData->flModifications = 0; 10297 } 10298 catch (HRESULT err) 10299 { 10300 // we assume that error info is set by the thrower 10301 rc = err; 10302 10303 // restore old config 10304 delete pNewConfig; 10305 mData->pMachineConfigFile = pOldConfig; 10306 } 10307 catch (...) 10308 { 10309 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 10310 } 10311 10312 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway)) 10313 { 10314 /* Fire the data change event, even on failure (since we've already 10315 * committed all data). This is done only for SessionMachines because 10316 * mutable Machine instances are always not registered (i.e. private 10317 * to the client process that creates them) and thus don't need to 10318 * inform callbacks. */ 10319 if (isSessionMachine()) 10320 mParent->onMachineDataChange(mData->mUuid); 10321 } 10322 10323 LogFlowThisFunc(("rc=%08X\n", rc)); 10324 LogFlowThisFuncLeave(); 10325 return rc; 10326 } 10327 10328 /** 10329 * Implementation for saving the machine settings into the given 10330 * settings::MachineConfigFile instance. This copies machine extradata 10331 * from the previous machine config file in the instance data, if any. 10332 * 10333 * This gets called from two locations: 10334 * 10335 * -- Machine::saveSettings(), during the regular XML writing; 10336 * 10337 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets 10338 * exported to OVF and we write the VirtualBox proprietary XML 10339 * into a <vbox:Machine> tag. 10340 * 10341 * This routine fills all the fields in there, including snapshots, *except* 10342 * for the following: 10343 * 10344 * -- fCurrentStateModified. There is some special logic associated with that. 10345 * 10346 * The caller can then call MachineConfigFile::write() or do something else 10347 * with it. 10348 * 10349 * Caller must hold the machine lock! 10350 * 10351 * This throws XML errors and HRESULT, so the caller must have a catch block! 10352 */ 10353 void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config) 10354 { 10355 // deep copy extradata 10356 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems; 10357 10358 config.uuid = mData->mUuid; 10359 10360 // copy name, description, OS type, teleport, UTC etc. 10361 config.machineUserData = mUserData->s; 10362 10363 // Encode the Icon Override data from Machine and store on config userdata. 10364 com::SafeArray<BYTE> iconByte; 10365 COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte)); 10366 ssize_t cbData = iconByte.size(); 10367 if (cbData > 0) 10368 { 10369 ssize_t cchOut = RTBase64EncodedLength(cbData); 10370 Utf8Str strIconData; 10371 strIconData.reserve(cchOut+1); 10372 int vrc = RTBase64Encode(iconByte.raw(), cbData, 10373 strIconData.mutableRaw(), strIconData.capacity(), 10374 NULL); 10375 if (RT_FAILURE(vrc)) 10376 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc); 10377 strIconData.jolt(); 10378 config.machineUserData.ovIcon = strIconData; 10379 } 10380 else 10381 config.machineUserData.ovIcon.setNull(); 10382 10383 if ( mData->mMachineState == MachineState_Saved 10384 || mData->mMachineState == MachineState_Restoring 10385 // when deleting a snapshot we may or may not have a saved state in the current state, 10386 // so let's not assert here please 10387 || ( ( mData->mMachineState == MachineState_DeletingSnapshot 10388 || mData->mMachineState == MachineState_DeletingSnapshotOnline 10389 || mData->mMachineState == MachineState_DeletingSnapshotPaused) 10390 && (!mSSData->strStateFilePath.isEmpty()) 10391 ) 10392 ) 10393 { 10394 Assert(!mSSData->strStateFilePath.isEmpty()); 10395 /* try to make the file name relative to the settings file dir */ 10396 copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile); 10397 } 10398 else 10399 { 10400 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving); 10401 config.strStateFile.setNull(); 10402 } 10403 10404 if (mData->mCurrentSnapshot) 10405 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId(); 10406 else 10407 config.uuidCurrentSnapshot.clear(); 10408 10409 config.timeLastStateChange = mData->mLastStateChange; 10410 config.fAborted = (mData->mMachineState == MachineState_Aborted); 10411 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported); 10412 10413 HRESULT rc = saveHardware(config.hardwareMachine, &config.debugging, &config.autostart); 10414 if (FAILED(rc)) throw rc; 10415 10416 rc = saveStorageControllers(config.storageMachine); 10417 if (FAILED(rc)) throw rc; 10418 10419 // save machine's media registry if this is VirtualBox 4.0 or later 10420 if (config.canHaveOwnMediaRegistry()) 10421 { 10422 // determine machine folder 10423 Utf8Str strMachineFolder = getSettingsFileFull(); 10424 strMachineFolder.stripFilename(); 10425 mParent->saveMediaRegistry(config.mediaRegistry, 10426 getId(), // only media with registry ID == machine UUID 10427 strMachineFolder); 10428 // this throws HRESULT 10429 } 10430 10431 // save snapshots 10432 rc = saveAllSnapshots(config); 10433 if (FAILED(rc)) throw rc; 10434 } 10435 10436 /** 10437 * Saves all snapshots of the machine into the given machine config file. Called 10438 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler(). 10439 * @param config 10440 * @return 10441 */ 10442 HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config) 10443 { 10444 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 10445 10446 HRESULT rc = S_OK; 10447 10448 try 10449 { 10450 config.llFirstSnapshot.clear(); 10451 10452 if (mData->mFirstSnapshot) 10453 { 10454 settings::Snapshot snapNew; 10455 config.llFirstSnapshot.push_back(snapNew); 10456 10457 // get reference to the fresh copy of the snapshot on the list and 10458 // work on that copy directly to avoid excessive copying later 10459 settings::Snapshot &snap = config.llFirstSnapshot.front(); 10460 10461 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/); 10462 if (FAILED(rc)) throw rc; 10463 } 10464 10465 // if (mType == IsSessionMachine) 10466 // mParent->onMachineDataChange(mData->mUuid); @todo is this necessary? 10467 10468 } 10469 catch (HRESULT err) 10470 { 10471 /* we assume that error info is set by the thrower */ 10472 rc = err; 10473 } 10474 catch (...) 10475 { 10476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 10477 } 10478 10479 return rc; 10480 } 10481 10482 /** 10483 * Saves the VM hardware configuration. It is assumed that the 10484 * given node is empty. 10485 * 10486 * @param data Reference to the settings object for the hardware config. 10487 * @param pDbg Pointer to the settings object for the debugging config 10488 * which happens to live in mHWData. 10489 * @param pAutostart Pointer to the settings object for the autostart config 10490 * which happens to live in mHWData. 10491 */ 10492 HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDbg, 10493 settings::Autostart *pAutostart) 10494 { 10495 HRESULT rc = S_OK; 10496 10497 try 10498 { 10499 /* The hardware version attribute (optional). 10500 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */ 10501 if ( mHWData->mHWVersion == "1" 10502 && mSSData->strStateFilePath.isEmpty() 10503 ) 10504 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */ 10505 10506 data.strVersion = mHWData->mHWVersion; 10507 data.uuid = mHWData->mHardwareUUID; 10508 10509 // CPU 10510 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled; 10511 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive; 10512 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled; 10513 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled; 10514 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled; 10515 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled; 10516 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled; 10517 data.fPAE = !!mHWData->mPAEEnabled; 10518 data.enmLongMode = mHWData->mLongMode; 10519 data.fSyntheticCpu = !!mHWData->mSyntheticCpu; 10520 10521 /* Standard and Extended CPUID leafs. */ 10522 data.llCpuIdLeafs.clear(); 10523 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++) 10524 { 10525 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX) 10526 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]); 10527 } 10528 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++) 10529 { 10530 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX) 10531 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]); 10532 } 10533 10534 data.cCPUs = mHWData->mCPUCount; 10535 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled; 10536 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap; 10537 10538 data.llCpus.clear(); 10539 if (data.fCpuHotPlug) 10540 { 10541 for (unsigned idx = 0; idx < data.cCPUs; idx++) 10542 { 10543 if (mHWData->mCPUAttached[idx]) 10544 { 10545 settings::Cpu cpu; 10546 cpu.ulId = idx; 10547 data.llCpus.push_back(cpu); 10548 } 10549 } 10550 } 10551 10552 // memory 10553 data.ulMemorySizeMB = mHWData->mMemorySize; 10554 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled; 10555 10556 // firmware 10557 data.firmwareType = mHWData->mFirmwareType; 10558 10559 // HID 10560 data.pointingHIDType = mHWData->mPointingHIDType; 10561 data.keyboardHIDType = mHWData->mKeyboardHIDType; 10562 10563 // chipset 10564 data.chipsetType = mHWData->mChipsetType; 10565 10566 data.fEmulatedUSBWebcam = !!mHWData->mEmulatedUSBWebcamEnabled; 10567 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled; 10568 10569 // HPET 10570 data.fHPETEnabled = !!mHWData->mHPETEnabled; 10571 10572 // boot order 10573 data.mapBootOrder.clear(); 10574 for (size_t i = 0; 10575 i < RT_ELEMENTS(mHWData->mBootOrder); 10576 ++i) 10577 data.mapBootOrder[i] = mHWData->mBootOrder[i]; 10578 10579 // display 10580 data.graphicsControllerType = mHWData->mGraphicsControllerType; 10581 data.ulVRAMSizeMB = mHWData->mVRAMSize; 10582 data.cMonitors = mHWData->mMonitorCount; 10583 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled; 10584 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled; 10585 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth; 10586 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight; 10587 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate; 10588 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS; 10589 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled; 10590 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++) 10591 { 10592 if (mHWData->maVideoCaptureScreens[i]) 10593 ASMBitSet(&data.u64VideoCaptureScreens, i); 10594 else 10595 ASMBitClear(&data.u64VideoCaptureScreens, i); 10596 } 10597 /* store relative video capture file if possible */ 10598 copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile); 10599 10600 /* VRDEServer settings (optional) */ 10601 rc = mVRDEServer->saveSettings(data.vrdeSettings); 10602 if (FAILED(rc)) throw rc; 10603 10604 /* BIOS (required) */ 10605 rc = mBIOSSettings->saveSettings(data.biosSettings); 10606 if (FAILED(rc)) throw rc; 10607 10608 /* USB Controller (required) */ 10609 for (USBControllerList::const_iterator it = mUSBControllers->begin(); 10610 it != mUSBControllers->end(); 10611 ++it) 10612 { 10613 ComObjPtr<USBController> ctrl = *it; 10614 settings::USBController settingsCtrl; 10615 10616 settingsCtrl.strName = ctrl->getName(); 10617 settingsCtrl.enmType = ctrl->getControllerType(); 10618 10619 data.usbSettings.llUSBControllers.push_back(settingsCtrl); 10620 } 10621 10622 /* USB device filters (required) */ 10623 rc = mUSBDeviceFilters->saveSettings(data.usbSettings); 10624 if (FAILED(rc)) throw rc; 10625 10626 /* Network adapters (required) */ 10627 uint32_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size()); 10628 data.llNetworkAdapters.clear(); 10629 /* Write out only the nominal number of network adapters for this 10630 * chipset type. Since Machine::commit() hasn't been called there 10631 * may be extra NIC settings in the vector. */ 10632 for (ULONG slot = 0; slot < uMaxNICs; ++slot) 10633 { 10634 settings::NetworkAdapter nic; 10635 nic.ulSlot = slot; 10636 /* paranoia check... must not be NULL, but must not crash either. */ 10637 if (mNetworkAdapters[slot]) 10638 { 10639 rc = mNetworkAdapters[slot]->saveSettings(nic); 10640 if (FAILED(rc)) throw rc; 10641 10642 data.llNetworkAdapters.push_back(nic); 10643 } 10644 } 10645 10646 /* Serial ports */ 10647 data.llSerialPorts.clear(); 10648 for (ULONG slot = 0; 10649 slot < RT_ELEMENTS(mSerialPorts); 10650 ++slot) 10651 { 10652 settings::SerialPort s; 10653 s.ulSlot = slot; 10654 rc = mSerialPorts[slot]->saveSettings(s); 10655 if (FAILED(rc)) return rc; 10656 10657 data.llSerialPorts.push_back(s); 10658 } 10659 10660 /* Parallel ports */ 10661 data.llParallelPorts.clear(); 10662 for (ULONG slot = 0; 10663 slot < RT_ELEMENTS(mParallelPorts); 10664 ++slot) 10665 { 10666 settings::ParallelPort p; 10667 p.ulSlot = slot; 10668 rc = mParallelPorts[slot]->saveSettings(p); 10669 if (FAILED(rc)) return rc; 10670 10671 data.llParallelPorts.push_back(p); 10672 } 10673 10674 /* Audio adapter */ 10675 rc = mAudioAdapter->saveSettings(data.audioAdapter); 10676 if (FAILED(rc)) return rc; 10677 10678 /* Shared folders */ 10679 data.llSharedFolders.clear(); 10680 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 10681 it != mHWData->mSharedFolders.end(); 10682 ++it) 10683 { 10684 SharedFolder *pSF = *it; 10685 AutoCaller sfCaller(pSF); 10686 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS); 10687 settings::SharedFolder sf; 10688 sf.strName = pSF->getName(); 10689 sf.strHostPath = pSF->getHostPath(); 10690 sf.fWritable = !!pSF->isWritable(); 10691 sf.fAutoMount = !!pSF->isAutoMounted(); 10692 10693 data.llSharedFolders.push_back(sf); 10694 } 10695 10696 // clipboard 10697 data.clipboardMode = mHWData->mClipboardMode; 10698 10699 // drag'n'drop 10700 data.dragAndDropMode = mHWData->mDragAndDropMode; 10701 10702 /* Guest */ 10703 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize; 10704 10705 // IO settings 10706 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled; 10707 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize; 10708 10709 /* BandwidthControl (required) */ 10710 rc = mBandwidthControl->saveSettings(data.ioSettings); 10711 if (FAILED(rc)) throw rc; 10712 10713 /* Host PCI devices */ 10714 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin(); 10715 it != mHWData->mPCIDeviceAssignments.end(); 10716 ++it) 10717 { 10718 ComObjPtr<PCIDeviceAttachment> pda = *it; 10719 settings::HostPCIDeviceAttachment hpda; 10720 10721 rc = pda->saveSettings(hpda); 10722 if (FAILED(rc)) throw rc; 10723 10724 data.pciAttachments.push_back(hpda); 10725 } 10726 10727 10728 // guest properties 10729 data.llGuestProperties.clear(); 10730 #ifdef VBOX_WITH_GUEST_PROPS 10731 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin(); 10732 it != mHWData->mGuestProperties.end(); 10733 ++it) 10734 { 10735 HWData::GuestProperty property = it->second; 10736 10737 /* Remove transient guest properties at shutdown unless we 10738 * are saving state */ 10739 if ( ( mData->mMachineState == MachineState_PoweredOff 10740 || mData->mMachineState == MachineState_Aborted 10741 || mData->mMachineState == MachineState_Teleported) 10742 && ( property.mFlags & guestProp::TRANSIENT 10743 || property.mFlags & guestProp::TRANSRESET)) 10744 continue; 10745 settings::GuestProperty prop; 10746 prop.strName = it->first; 10747 prop.strValue = property.strValue; 10748 prop.timestamp = property.mTimestamp; 10749 char szFlags[guestProp::MAX_FLAGS_LEN + 1]; 10750 guestProp::writeFlags(property.mFlags, szFlags); 10751 prop.strFlags = szFlags; 10752 10753 data.llGuestProperties.push_back(prop); 10754 } 10755 10756 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns; 10757 /* I presume this doesn't require a backup(). */ 10758 mData->mGuestPropertiesModified = FALSE; 10759 #endif /* VBOX_WITH_GUEST_PROPS defined */ 10760 10761 *pDbg = mHWData->mDebugging; 10762 *pAutostart = mHWData->mAutostart; 10763 10764 data.strDefaultFrontend = mHWData->mDefaultFrontend; 10765 } 10766 catch(std::bad_alloc &) 10767 { 10768 return E_OUTOFMEMORY; 10769 } 10770 10771 AssertComRC(rc); 10772 return rc; 10773 } 10774 10775 /** 10776 * Saves the storage controller configuration. 10777 * 10778 * @param aNode <StorageControllers> node to save the VM hardware configuration to. 10779 */ 10780 HRESULT Machine::saveStorageControllers(settings::Storage &data) 10781 { 10782 data.llStorageControllers.clear(); 10783 10784 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 10785 it != mStorageControllers->end(); 10786 ++it) 10787 { 10788 HRESULT rc; 10789 ComObjPtr<StorageController> pCtl = *it; 10790 10791 settings::StorageController ctl; 10792 ctl.strName = pCtl->getName(); 10793 ctl.controllerType = pCtl->getControllerType(); 10794 ctl.storageBus = pCtl->getStorageBus(); 10795 ctl.ulInstance = pCtl->getInstance(); 10796 ctl.fBootable = pCtl->getBootable(); 10797 10798 /* Save the port count. */ 10799 ULONG portCount; 10800 rc = pCtl->COMGETTER(PortCount)(&portCount); 10801 ComAssertComRCRet(rc, rc); 10802 ctl.ulPortCount = portCount; 10803 10804 /* Save fUseHostIOCache */ 10805 BOOL fUseHostIOCache; 10806 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache); 10807 ComAssertComRCRet(rc, rc); 10808 ctl.fUseHostIOCache = !!fUseHostIOCache; 10809 10810 /* Save IDE emulation settings. */ 10811 if (ctl.controllerType == StorageControllerType_IntelAhci) 10812 { 10813 if ( (FAILED(rc = pCtl->getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort))) 10814 || (FAILED(rc = pCtl->getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort))) 10815 || (FAILED(rc = pCtl->getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort))) 10816 || (FAILED(rc = pCtl->getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort))) 10817 ) 10818 ComAssertComRCRet(rc, rc); 10819 } 10820 10821 /* save the devices now. */ 10822 rc = saveStorageDevices(pCtl, ctl); 10823 ComAssertComRCRet(rc, rc); 10824 10825 data.llStorageControllers.push_back(ctl); 10826 } 10827 10828 return S_OK; 10829 } 10830 10831 /** 10832 * Saves the hard disk configuration. 10833 */ 10834 HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController, 10835 settings::StorageController &data) 10836 { 10837 MediaData::AttachmentList atts; 10838 10839 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()).raw(), atts); 10840 if (FAILED(rc)) return rc; 10841 10842 data.llAttachedDevices.clear(); 10843 for (MediaData::AttachmentList::const_iterator it = atts.begin(); 10844 it != atts.end(); 10845 ++it) 10846 { 10847 settings::AttachedDevice dev; 10848 10849 MediumAttachment *pAttach = *it; 10850 Medium *pMedium = pAttach->getMedium(); 10851 10852 dev.deviceType = pAttach->getType(); 10853 dev.lPort = pAttach->getPort(); 10854 dev.lDevice = pAttach->getDevice(); 10855 if (pMedium) 10856 { 10857 if (pMedium->isHostDrive()) 10858 dev.strHostDriveSrc = pMedium->getLocationFull(); 10859 else 10860 dev.uuid = pMedium->getId(); 10861 dev.fPassThrough = pAttach->getPassthrough(); 10862 dev.fTempEject = pAttach->getTempEject(); 10863 dev.fNonRotational = pAttach->getNonRotational(); 10864 dev.fDiscard = pAttach->getDiscard(); 10865 } 10866 10867 dev.strBwGroup = pAttach->getBandwidthGroup(); 10868 10869 data.llAttachedDevices.push_back(dev); 10870 } 10871 10872 return S_OK; 10873 } 10874 10875 /** 10876 * Saves machine state settings as defined by aFlags 10877 * (SaveSTS_* values). 10878 * 10879 * @param aFlags Combination of SaveSTS_* flags. 10880 * 10881 * @note Locks objects for writing. 10882 */ 10883 HRESULT Machine::saveStateSettings(int aFlags) 10884 { 10885 if (aFlags == 0) 10886 return S_OK; 10887 10888 AutoCaller autoCaller(this); 10889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 10890 10891 /* This object's write lock is also necessary to serialize file access 10892 * (prevent concurrent reads and writes) */ 10893 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 10894 10895 HRESULT rc = S_OK; 10896 10897 Assert(mData->pMachineConfigFile); 10898 10899 try 10900 { 10901 if (aFlags & SaveSTS_CurStateModified) 10902 mData->pMachineConfigFile->fCurrentStateModified = true; 10903 10904 if (aFlags & SaveSTS_StateFilePath) 10905 { 10906 if (!mSSData->strStateFilePath.isEmpty()) 10907 /* try to make the file name relative to the settings file dir */ 10908 copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile); 10909 else 10910 mData->pMachineConfigFile->strStateFile.setNull(); 10911 } 10912 10913 if (aFlags & SaveSTS_StateTimeStamp) 10914 { 10915 Assert( mData->mMachineState != MachineState_Aborted 10916 || mSSData->strStateFilePath.isEmpty()); 10917 10918 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange; 10919 10920 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted); 10921 //@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported); 10922 } 10923 10924 mData->pMachineConfigFile->write(mData->m_strConfigFileFull); 10925 } 10926 catch (...) 10927 { 10928 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 10929 } 10930 10931 return rc; 10932 } 10933 10934 /** 10935 * Ensures that the given medium is added to a media registry. If this machine 10936 * was created with 4.0 or later, then the machine registry is used. Otherwise 10937 * the global VirtualBox media registry is used. 10938 * 10939 * Caller must NOT hold machine lock, media tree or any medium locks! 10940 * 10941 * @param pMedium 10942 */ 10943 void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium) 10944 { 10945 /* Paranoia checks: do not hold machine or media tree locks. */ 10946 AssertReturnVoid(!isWriteLockOnCurrentThread()); 10947 AssertReturnVoid(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 10948 10949 ComObjPtr<Medium> pBase; 10950 { 10951 AutoReadLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 10952 pBase = pMedium->getBase(); 10953 } 10954 10955 /* Paranoia checks: do not hold medium locks. */ 10956 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread()); 10957 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread()); 10958 10959 // decide which medium registry to use now that the medium is attached: 10960 Guid uuid; 10961 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry()) 10962 // machine XML is VirtualBox 4.0 or higher: 10963 uuid = getId(); // machine UUID 10964 else 10965 uuid = mParent->getGlobalRegistryId(); // VirtualBox global registry UUID 10966 10967 if (pMedium->addRegistry(uuid, false /* fRecurse */)) 10968 mParent->markRegistryModified(uuid); 10969 10970 /* For more complex hard disk structures it can happen that the base 10971 * medium isn't yet associated with any medium registry. Do that now. */ 10972 if (pMedium != pBase) 10973 { 10974 if (pBase->addRegistry(uuid, true /* fRecurse */)) 10975 mParent->markRegistryModified(uuid); 10976 } 10977 } 10978 10979 /** 10980 * Creates differencing hard disks for all normal hard disks attached to this 10981 * machine and a new set of attachments to refer to created disks. 10982 * 10983 * Used when taking a snapshot or when deleting the current state. Gets called 10984 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler(). 10985 * 10986 * This method assumes that mMediaData contains the original hard disk attachments 10987 * it needs to create diffs for. On success, these attachments will be replaced 10988 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly 10989 * called to delete created diffs which will also rollback mMediaData and restore 10990 * whatever was backed up before calling this method. 10991 * 10992 * Attachments with non-normal hard disks are left as is. 10993 * 10994 * If @a aOnline is @c false then the original hard disks that require implicit 10995 * diffs will be locked for reading. Otherwise it is assumed that they are 10996 * already locked for writing (when the VM was started). Note that in the latter 10997 * case it is responsibility of the caller to lock the newly created diffs for 10998 * writing if this method succeeds. 10999 * 11000 * @param aProgress Progress object to run (must contain at least as 11001 * many operations left as the number of hard disks 11002 * attached). 11003 * @param aOnline Whether the VM was online prior to this operation. 11004 * 11005 * @note The progress object is not marked as completed, neither on success nor 11006 * on failure. This is a responsibility of the caller. 11007 * 11008 * @note Locks this object and the media tree for writing. 11009 */ 11010 HRESULT Machine::createImplicitDiffs(IProgress *aProgress, 11011 ULONG aWeight, 11012 bool aOnline) 11013 { 11014 LogFlowThisFunc(("aOnline=%d\n", aOnline)); 11015 11016 AutoCaller autoCaller(this); 11017 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11018 11019 AutoMultiWriteLock2 alock(this->lockHandle(), 11020 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 11021 11022 /* must be in a protective state because we release the lock below */ 11023 AssertReturn( mData->mMachineState == MachineState_Saving 11024 || mData->mMachineState == MachineState_LiveSnapshotting 11025 || mData->mMachineState == MachineState_RestoringSnapshot 11026 || mData->mMachineState == MachineState_DeletingSnapshot 11027 , E_FAIL); 11028 11029 HRESULT rc = S_OK; 11030 11031 // use appropriate locked media map (online or offline) 11032 MediumLockListMap lockedMediaOffline; 11033 MediumLockListMap *lockedMediaMap; 11034 if (aOnline) 11035 lockedMediaMap = &mData->mSession.mLockedMedia; 11036 else 11037 lockedMediaMap = &lockedMediaOffline; 11038 11039 try 11040 { 11041 if (!aOnline) 11042 { 11043 /* lock all attached hard disks early to detect "in use" 11044 * situations before creating actual diffs */ 11045 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11046 it != mMediaData->mAttachments.end(); 11047 ++it) 11048 { 11049 MediumAttachment* pAtt = *it; 11050 if (pAtt->getType() == DeviceType_HardDisk) 11051 { 11052 Medium* pMedium = pAtt->getMedium(); 11053 Assert(pMedium); 11054 11055 MediumLockList *pMediumLockList(new MediumLockList()); 11056 alock.release(); 11057 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */, 11058 false /* fMediumLockWrite */, 11059 NULL, 11060 *pMediumLockList); 11061 alock.acquire(); 11062 if (FAILED(rc)) 11063 { 11064 delete pMediumLockList; 11065 throw rc; 11066 } 11067 rc = lockedMediaMap->Insert(pAtt, pMediumLockList); 11068 if (FAILED(rc)) 11069 { 11070 throw setError(rc, 11071 tr("Collecting locking information for all attached media failed")); 11072 } 11073 } 11074 } 11075 11076 /* Now lock all media. If this fails, nothing is locked. */ 11077 alock.release(); 11078 rc = lockedMediaMap->Lock(); 11079 alock.acquire(); 11080 if (FAILED(rc)) 11081 { 11082 throw setError(rc, 11083 tr("Locking of attached media failed")); 11084 } 11085 } 11086 11087 /* remember the current list (note that we don't use backup() since 11088 * mMediaData may be already backed up) */ 11089 MediaData::AttachmentList atts = mMediaData->mAttachments; 11090 11091 /* start from scratch */ 11092 mMediaData->mAttachments.clear(); 11093 11094 /* go through remembered attachments and create diffs for normal hard 11095 * disks and attach them */ 11096 for (MediaData::AttachmentList::const_iterator it = atts.begin(); 11097 it != atts.end(); 11098 ++it) 11099 { 11100 MediumAttachment* pAtt = *it; 11101 11102 DeviceType_T devType = pAtt->getType(); 11103 Medium* pMedium = pAtt->getMedium(); 11104 11105 if ( devType != DeviceType_HardDisk 11106 || pMedium == NULL 11107 || pMedium->getType() != MediumType_Normal) 11108 { 11109 /* copy the attachment as is */ 11110 11111 /** @todo the progress object created in Console::TakeSnaphot 11112 * only expects operations for hard disks. Later other 11113 * device types need to show up in the progress as well. */ 11114 if (devType == DeviceType_HardDisk) 11115 { 11116 if (pMedium == NULL) 11117 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(), 11118 aWeight); // weight 11119 else 11120 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"), 11121 pMedium->getBase()->getName().c_str()).raw(), 11122 aWeight); // weight 11123 } 11124 11125 mMediaData->mAttachments.push_back(pAtt); 11126 continue; 11127 } 11128 11129 /* need a diff */ 11130 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"), 11131 pMedium->getBase()->getName().c_str()).raw(), 11132 aWeight); // weight 11133 11134 Utf8Str strFullSnapshotFolder; 11135 calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder); 11136 11137 ComObjPtr<Medium> diff; 11138 diff.createObject(); 11139 // store the diff in the same registry as the parent 11140 // (this cannot fail here because we can't create implicit diffs for 11141 // unregistered images) 11142 Guid uuidRegistryParent; 11143 bool fInRegistry = pMedium->getFirstRegistryMachineId(uuidRegistryParent); 11144 Assert(fInRegistry); NOREF(fInRegistry); 11145 rc = diff->init(mParent, 11146 pMedium->getPreferredDiffFormat(), 11147 strFullSnapshotFolder.append(RTPATH_SLASH_STR), 11148 uuidRegistryParent); 11149 if (FAILED(rc)) throw rc; 11150 11151 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before 11152 * the push_back? Looks like we're going to release medium with the 11153 * wrong kind of lock (general issue with if we fail anywhere at all) 11154 * and an orphaned VDI in the snapshots folder. */ 11155 11156 /* update the appropriate lock list */ 11157 MediumLockList *pMediumLockList; 11158 rc = lockedMediaMap->Get(pAtt, pMediumLockList); 11159 AssertComRCThrowRC(rc); 11160 if (aOnline) 11161 { 11162 alock.release(); 11163 /* The currently attached medium will be read-only, change 11164 * the lock type to read. */ 11165 rc = pMediumLockList->Update(pMedium, false); 11166 alock.acquire(); 11167 AssertComRCThrowRC(rc); 11168 } 11169 11170 /* release the locks before the potentially lengthy operation */ 11171 alock.release(); 11172 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard, 11173 pMediumLockList, 11174 NULL /* aProgress */, 11175 true /* aWait */); 11176 alock.acquire(); 11177 if (FAILED(rc)) throw rc; 11178 11179 /* actual lock list update is done in Medium::commitMedia */ 11180 11181 rc = diff->addBackReference(mData->mUuid); 11182 AssertComRCThrowRC(rc); 11183 11184 /* add a new attachment */ 11185 ComObjPtr<MediumAttachment> attachment; 11186 attachment.createObject(); 11187 rc = attachment->init(this, 11188 diff, 11189 pAtt->getControllerName(), 11190 pAtt->getPort(), 11191 pAtt->getDevice(), 11192 DeviceType_HardDisk, 11193 true /* aImplicit */, 11194 false /* aPassthrough */, 11195 false /* aTempEject */, 11196 pAtt->getNonRotational(), 11197 pAtt->getDiscard(), 11198 pAtt->getBandwidthGroup()); 11199 if (FAILED(rc)) throw rc; 11200 11201 rc = lockedMediaMap->ReplaceKey(pAtt, attachment); 11202 AssertComRCThrowRC(rc); 11203 mMediaData->mAttachments.push_back(attachment); 11204 } 11205 } 11206 catch (HRESULT aRC) { rc = aRC; } 11207 11208 /* unlock all hard disks we locked when there is no VM */ 11209 if (!aOnline) 11210 { 11211 ErrorInfoKeeper eik; 11212 11213 HRESULT rc1 = lockedMediaMap->Clear(); 11214 AssertComRC(rc1); 11215 } 11216 11217 return rc; 11218 } 11219 11220 /** 11221 * Deletes implicit differencing hard disks created either by 11222 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData. 11223 * 11224 * Note that to delete hard disks created by #AttachDevice() this method is 11225 * called from #fixupMedia() when the changes are rolled back. 11226 * 11227 * @note Locks this object and the media tree for writing. 11228 */ 11229 HRESULT Machine::deleteImplicitDiffs(bool aOnline) 11230 { 11231 LogFlowThisFunc(("aOnline=%d\n", aOnline)); 11232 11233 AutoCaller autoCaller(this); 11234 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 11235 11236 AutoMultiWriteLock2 alock(this->lockHandle(), 11237 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 11238 11239 /* We absolutely must have backed up state. */ 11240 AssertReturn(mMediaData.isBackedUp(), E_FAIL); 11241 11242 /* Check if there are any implicitly created diff images. */ 11243 bool fImplicitDiffs = false; 11244 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11245 it != mMediaData->mAttachments.end(); 11246 ++it) 11247 { 11248 const ComObjPtr<MediumAttachment> &pAtt = *it; 11249 if (pAtt->isImplicit()) 11250 { 11251 fImplicitDiffs = true; 11252 break; 11253 } 11254 } 11255 /* If there is nothing to do, leave early. This saves lots of image locking 11256 * effort. It also avoids a MachineStateChanged event without real reason. 11257 * This is important e.g. when loading a VM config, because there should be 11258 * no events. Otherwise API clients can become thoroughly confused for 11259 * inaccessible VMs (the code for loading VM configs uses this method for 11260 * cleanup if the config makes no sense), as they take such events as an 11261 * indication that the VM is alive, and they would force the VM config to 11262 * be reread, leading to an endless loop. */ 11263 if (!fImplicitDiffs) 11264 return S_OK; 11265 11266 HRESULT rc = S_OK; 11267 MachineState_T oldState = mData->mMachineState; 11268 11269 /* will release the lock before the potentially lengthy operation, 11270 * so protect with the special state (unless already protected) */ 11271 if ( oldState != MachineState_Saving 11272 && oldState != MachineState_LiveSnapshotting 11273 && oldState != MachineState_RestoringSnapshot 11274 && oldState != MachineState_DeletingSnapshot 11275 && oldState != MachineState_DeletingSnapshotOnline 11276 && oldState != MachineState_DeletingSnapshotPaused 11277 ) 11278 setMachineState(MachineState_SettingUp); 11279 11280 // use appropriate locked media map (online or offline) 11281 MediumLockListMap lockedMediaOffline; 11282 MediumLockListMap *lockedMediaMap; 11283 if (aOnline) 11284 lockedMediaMap = &mData->mSession.mLockedMedia; 11285 else 11286 lockedMediaMap = &lockedMediaOffline; 11287 11288 try 11289 { 11290 if (!aOnline) 11291 { 11292 /* lock all attached hard disks early to detect "in use" 11293 * situations before deleting actual diffs */ 11294 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11295 it != mMediaData->mAttachments.end(); 11296 ++it) 11297 { 11298 MediumAttachment* pAtt = *it; 11299 if (pAtt->getType() == DeviceType_HardDisk) 11300 { 11301 Medium* pMedium = pAtt->getMedium(); 11302 Assert(pMedium); 11303 11304 MediumLockList *pMediumLockList(new MediumLockList()); 11305 alock.release(); 11306 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */, 11307 false /* fMediumLockWrite */, 11308 NULL, 11309 *pMediumLockList); 11310 alock.acquire(); 11311 11312 if (FAILED(rc)) 11313 { 11314 delete pMediumLockList; 11315 throw rc; 11316 } 11317 11318 rc = lockedMediaMap->Insert(pAtt, pMediumLockList); 11319 if (FAILED(rc)) 11320 throw rc; 11321 } 11322 } 11323 11324 if (FAILED(rc)) 11325 throw rc; 11326 } // end of offline 11327 11328 /* Lock lists are now up to date and include implicitly created media */ 11329 11330 /* Go through remembered attachments and delete all implicitly created 11331 * diffs and fix up the attachment information */ 11332 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 11333 MediaData::AttachmentList implicitAtts; 11334 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11335 it != mMediaData->mAttachments.end(); 11336 ++it) 11337 { 11338 ComObjPtr<MediumAttachment> pAtt = *it; 11339 ComObjPtr<Medium> pMedium = pAtt->getMedium(); 11340 if (pMedium.isNull()) 11341 continue; 11342 11343 // Implicit attachments go on the list for deletion and back references are removed. 11344 if (pAtt->isImplicit()) 11345 { 11346 /* Deassociate and mark for deletion */ 11347 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName())); 11348 rc = pMedium->removeBackReference(mData->mUuid); 11349 if (FAILED(rc)) 11350 throw rc; 11351 implicitAtts.push_back(pAtt); 11352 continue; 11353 } 11354 11355 /* Was this medium attached before? */ 11356 if (!findAttachment(oldAtts, pMedium)) 11357 { 11358 /* no: de-associate */ 11359 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName())); 11360 rc = pMedium->removeBackReference(mData->mUuid); 11361 if (FAILED(rc)) 11362 throw rc; 11363 continue; 11364 } 11365 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName())); 11366 } 11367 11368 /* If there are implicit attachments to delete, throw away the lock 11369 * map contents (which will unlock all media) since the medium 11370 * attachments will be rolled back. Below we need to completely 11371 * recreate the lock map anyway since it is infinitely complex to 11372 * do this incrementally (would need reconstructing each attachment 11373 * change, which would be extremely hairy). */ 11374 if (implicitAtts.size() != 0) 11375 { 11376 ErrorInfoKeeper eik; 11377 11378 HRESULT rc1 = lockedMediaMap->Clear(); 11379 AssertComRC(rc1); 11380 } 11381 11382 /* rollback hard disk changes */ 11383 mMediaData.rollback(); 11384 11385 MultiResult mrc(S_OK); 11386 11387 // Delete unused implicit diffs. 11388 if (implicitAtts.size() != 0) 11389 { 11390 alock.release(); 11391 11392 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); 11393 it != implicitAtts.end(); 11394 ++it) 11395 { 11396 // Remove medium associated with this attachment. 11397 ComObjPtr<MediumAttachment> pAtt = *it; 11398 Assert(pAtt); 11399 LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName())); 11400 ComObjPtr<Medium> pMedium = pAtt->getMedium(); 11401 Assert(pMedium); 11402 11403 rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/); 11404 // continue on delete failure, just collect error messages 11405 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() )); 11406 mrc = rc; 11407 } 11408 11409 alock.acquire(); 11410 11411 /* if there is a VM recreate media lock map as mentioned above, 11412 * otherwise it is a waste of time and we leave things unlocked */ 11413 if (aOnline) 11414 { 11415 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine; 11416 /* must never be NULL, but better safe than sorry */ 11417 if (!pMachine.isNull()) 11418 { 11419 alock.release(); 11420 rc = mData->mSession.mMachine->lockMedia(); 11421 alock.acquire(); 11422 if (FAILED(rc)) 11423 throw rc; 11424 } 11425 } 11426 } 11427 } 11428 catch (HRESULT aRC) {rc = aRC;} 11429 11430 if (mData->mMachineState == MachineState_SettingUp) 11431 setMachineState(oldState); 11432 11433 /* unlock all hard disks we locked when there is no VM */ 11434 if (!aOnline) 11435 { 11436 ErrorInfoKeeper eik; 11437 11438 HRESULT rc1 = lockedMediaMap->Clear(); 11439 AssertComRC(rc1); 11440 } 11441 11442 return rc; 11443 } 11444 11445 11446 /** 11447 * Looks through the given list of media attachments for one with the given parameters 11448 * and returns it, or NULL if not found. The list is a parameter so that backup lists 11449 * can be searched as well if needed. 11450 * 11451 * @param list 11452 * @param aControllerName 11453 * @param aControllerPort 11454 * @param aDevice 11455 * @return 11456 */ 11457 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 11458 IN_BSTR aControllerName, 11459 LONG aControllerPort, 11460 LONG aDevice) 11461 { 11462 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 11463 it != ll.end(); 11464 ++it) 11465 { 11466 MediumAttachment *pAttach = *it; 11467 if (pAttach->matches(aControllerName, aControllerPort, aDevice)) 11468 return pAttach; 11469 } 11470 11471 return NULL; 11472 } 11473 11474 /** 11475 * Looks through the given list of media attachments for one with the given parameters 11476 * and returns it, or NULL if not found. The list is a parameter so that backup lists 11477 * can be searched as well if needed. 11478 * 11479 * @param list 11480 * @param aControllerName 11481 * @param aControllerPort 11482 * @param aDevice 11483 * @return 11484 */ 11485 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 11486 ComObjPtr<Medium> pMedium) 11487 { 11488 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 11489 it != ll.end(); 11490 ++it) 11491 { 11492 MediumAttachment *pAttach = *it; 11493 ComObjPtr<Medium> pMediumThis = pAttach->getMedium(); 11494 if (pMediumThis == pMedium) 11495 return pAttach; 11496 } 11497 11498 return NULL; 11499 } 11500 11501 /** 11502 * Looks through the given list of media attachments for one with the given parameters 11503 * and returns it, or NULL if not found. The list is a parameter so that backup lists 11504 * can be searched as well if needed. 11505 * 11506 * @param list 11507 * @param aControllerName 11508 * @param aControllerPort 11509 * @param aDevice 11510 * @return 11511 */ 11512 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll, 11513 Guid &id) 11514 { 11515 for (MediaData::AttachmentList::const_iterator it = ll.begin(); 11516 it != ll.end(); 11517 ++it) 11518 { 11519 MediumAttachment *pAttach = *it; 11520 ComObjPtr<Medium> pMediumThis = pAttach->getMedium(); 11521 if (pMediumThis->getId() == id) 11522 return pAttach; 11523 } 11524 11525 return NULL; 11526 } 11527 11528 /** 11529 * Main implementation for Machine::DetachDevice. This also gets called 11530 * from Machine::prepareUnregister() so it has been taken out for simplicity. 11531 * 11532 * @param pAttach Medium attachment to detach. 11533 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here. 11534 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot. 11535 * @return 11536 */ 11537 HRESULT Machine::detachDevice(MediumAttachment *pAttach, 11538 AutoWriteLock &writeLock, 11539 Snapshot *pSnapshot) 11540 { 11541 ComObjPtr<Medium> oldmedium = pAttach->getMedium(); 11542 DeviceType_T mediumType = pAttach->getType(); 11543 11544 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL")); 11545 11546 if (pAttach->isImplicit()) 11547 { 11548 /* attempt to implicitly delete the implicitly created diff */ 11549 11550 /// @todo move the implicit flag from MediumAttachment to Medium 11551 /// and forbid any hard disk operation when it is implicit. Or maybe 11552 /// a special media state for it to make it even more simple. 11553 11554 Assert(mMediaData.isBackedUp()); 11555 11556 /* will release the lock before the potentially lengthy operation, so 11557 * protect with the special state */ 11558 MachineState_T oldState = mData->mMachineState; 11559 setMachineState(MachineState_SettingUp); 11560 11561 writeLock.release(); 11562 11563 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, 11564 true /*aWait*/); 11565 11566 writeLock.acquire(); 11567 11568 setMachineState(oldState); 11569 11570 if (FAILED(rc)) return rc; 11571 } 11572 11573 setModified(IsModified_Storage); 11574 mMediaData.backup(); 11575 mMediaData->mAttachments.remove(pAttach); 11576 11577 if (!oldmedium.isNull()) 11578 { 11579 // if this is from a snapshot, do not defer detachment to commitMedia() 11580 if (pSnapshot) 11581 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId()); 11582 // else if non-hard disk media, do not defer detachment to commitMedia() either 11583 else if (mediumType != DeviceType_HardDisk) 11584 oldmedium->removeBackReference(mData->mUuid); 11585 } 11586 11587 return S_OK; 11588 } 11589 11590 /** 11591 * Goes thru all media of the given list and 11592 * 11593 * 1) calls detachDevice() on each of them for this machine and 11594 * 2) adds all Medium objects found in the process to the given list, 11595 * depending on cleanupMode. 11596 * 11597 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only 11598 * adds hard disks to the list. If it is CleanupMode_Full, this adds all 11599 * media to the list. 11600 * 11601 * This gets called from Machine::Unregister, both for the actual Machine and 11602 * the SnapshotMachine objects that might be found in the snapshots. 11603 * 11604 * Requires caller and locking. The machine lock must be passed in because it 11605 * will be passed on to detachDevice which needs it for temporary unlocking. 11606 * 11607 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice. 11608 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine. 11609 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added; 11610 * otherwise no media get added. 11611 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode. 11612 * @return 11613 */ 11614 HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock, 11615 Snapshot *pSnapshot, 11616 CleanupMode_T cleanupMode, 11617 MediaList &llMedia) 11618 { 11619 Assert(isWriteLockOnCurrentThread()); 11620 11621 HRESULT rc; 11622 11623 // make a temporary list because detachDevice invalidates iterators into 11624 // mMediaData->mAttachments 11625 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments; 11626 11627 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); 11628 it != llAttachments2.end(); 11629 ++it) 11630 { 11631 ComObjPtr<MediumAttachment> &pAttach = *it; 11632 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 11633 11634 if (!pMedium.isNull()) 11635 { 11636 AutoCaller mac(pMedium); 11637 if (FAILED(mac.rc())) return mac.rc(); 11638 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS); 11639 DeviceType_T devType = pMedium->getDeviceType(); 11640 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly 11641 && devType == DeviceType_HardDisk) 11642 || (cleanupMode == CleanupMode_Full) 11643 ) 11644 { 11645 llMedia.push_back(pMedium); 11646 ComObjPtr<Medium> pParent = pMedium->getParent(); 11647 /* 11648 * Search for medias which are not attached to any machine, but 11649 * in the chain to an attached disk. Mediums are only consided 11650 * if they are: 11651 * - have only one child 11652 * - no references to any machines 11653 * - are of normal medium type 11654 */ 11655 while (!pParent.isNull()) 11656 { 11657 AutoCaller mac1(pParent); 11658 if (FAILED(mac1.rc())) return mac1.rc(); 11659 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS); 11660 if (pParent->getChildren().size() == 1) 11661 { 11662 if ( pParent->getMachineBackRefCount() == 0 11663 && pParent->getType() == MediumType_Normal 11664 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end()) 11665 llMedia.push_back(pParent); 11666 } 11667 else 11668 break; 11669 pParent = pParent->getParent(); 11670 } 11671 } 11672 } 11673 11674 // real machine: then we need to use the proper method 11675 rc = detachDevice(pAttach, writeLock, pSnapshot); 11676 11677 if (FAILED(rc)) 11678 return rc; 11679 } 11680 11681 return S_OK; 11682 } 11683 11684 /** 11685 * Perform deferred hard disk detachments. 11686 * 11687 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not 11688 * backed up). 11689 * 11690 * If @a aOnline is @c true then this method will also unlock the old hard disks 11691 * for which the new implicit diffs were created and will lock these new diffs for 11692 * writing. 11693 * 11694 * @param aOnline Whether the VM was online prior to this operation. 11695 * 11696 * @note Locks this object for writing! 11697 */ 11698 void Machine::commitMedia(bool aOnline /*= false*/) 11699 { 11700 AutoCaller autoCaller(this); 11701 AssertComRCReturnVoid(autoCaller.rc()); 11702 11703 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11704 11705 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline)); 11706 11707 HRESULT rc = S_OK; 11708 11709 /* no attach/detach operations -- nothing to do */ 11710 if (!mMediaData.isBackedUp()) 11711 return; 11712 11713 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 11714 bool fMediaNeedsLocking = false; 11715 11716 /* enumerate new attachments */ 11717 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11718 it != mMediaData->mAttachments.end(); 11719 ++it) 11720 { 11721 MediumAttachment *pAttach = *it; 11722 11723 pAttach->commit(); 11724 11725 Medium* pMedium = pAttach->getMedium(); 11726 bool fImplicit = pAttach->isImplicit(); 11727 11728 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n", 11729 (pMedium) ? pMedium->getName().c_str() : "NULL", 11730 fImplicit)); 11731 11732 /** @todo convert all this Machine-based voodoo to MediumAttachment 11733 * based commit logic. */ 11734 if (fImplicit) 11735 { 11736 /* convert implicit attachment to normal */ 11737 pAttach->setImplicit(false); 11738 11739 if ( aOnline 11740 && pMedium 11741 && pAttach->getType() == DeviceType_HardDisk 11742 ) 11743 { 11744 ComObjPtr<Medium> parent = pMedium->getParent(); 11745 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS); 11746 11747 /* update the appropriate lock list */ 11748 MediumLockList *pMediumLockList; 11749 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 11750 AssertComRC(rc); 11751 if (pMediumLockList) 11752 { 11753 /* unlock if there's a need to change the locking */ 11754 if (!fMediaNeedsLocking) 11755 { 11756 rc = mData->mSession.mLockedMedia.Unlock(); 11757 AssertComRC(rc); 11758 fMediaNeedsLocking = true; 11759 } 11760 rc = pMediumLockList->Update(parent, false); 11761 AssertComRC(rc); 11762 rc = pMediumLockList->Append(pMedium, true); 11763 AssertComRC(rc); 11764 } 11765 } 11766 11767 continue; 11768 } 11769 11770 if (pMedium) 11771 { 11772 /* was this medium attached before? */ 11773 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); 11774 oldIt != oldAtts.end(); 11775 ++oldIt) 11776 { 11777 MediumAttachment *pOldAttach = *oldIt; 11778 if (pOldAttach->getMedium() == pMedium) 11779 { 11780 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str())); 11781 11782 /* yes: remove from old to avoid de-association */ 11783 oldAtts.erase(oldIt); 11784 break; 11785 } 11786 } 11787 } 11788 } 11789 11790 /* enumerate remaining old attachments and de-associate from the 11791 * current machine state */ 11792 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 11793 it != oldAtts.end(); 11794 ++it) 11795 { 11796 MediumAttachment *pAttach = *it; 11797 Medium* pMedium = pAttach->getMedium(); 11798 11799 /* Detach only hard disks, since DVD/floppy media is detached 11800 * instantly in MountMedium. */ 11801 if (pAttach->getType() == DeviceType_HardDisk && pMedium) 11802 { 11803 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str())); 11804 11805 /* now de-associate from the current machine state */ 11806 rc = pMedium->removeBackReference(mData->mUuid); 11807 AssertComRC(rc); 11808 11809 if (aOnline) 11810 { 11811 /* unlock since medium is not used anymore */ 11812 MediumLockList *pMediumLockList; 11813 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); 11814 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE)) 11815 { 11816 /* this happens for online snapshots, there the attachment 11817 * is changing, but only to a diff image created under 11818 * the old one, so there is no separate lock list */ 11819 Assert(!pMediumLockList); 11820 } 11821 else 11822 { 11823 AssertComRC(rc); 11824 if (pMediumLockList) 11825 { 11826 rc = mData->mSession.mLockedMedia.Remove(pAttach); 11827 AssertComRC(rc); 11828 } 11829 } 11830 } 11831 } 11832 } 11833 11834 /* take media locks again so that the locking state is consistent */ 11835 if (fMediaNeedsLocking) 11836 { 11837 Assert(aOnline); 11838 rc = mData->mSession.mLockedMedia.Lock(); 11839 AssertComRC(rc); 11840 } 11841 11842 /* commit the hard disk changes */ 11843 mMediaData.commit(); 11844 11845 if (isSessionMachine()) 11846 { 11847 /* 11848 * Update the parent machine to point to the new owner. 11849 * This is necessary because the stored parent will point to the 11850 * session machine otherwise and cause crashes or errors later 11851 * when the session machine gets invalid. 11852 */ 11853 /** @todo Change the MediumAttachment class to behave like any other 11854 * class in this regard by creating peer MediumAttachment 11855 * objects for session machines and share the data with the peer 11856 * machine. 11857 */ 11858 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11859 it != mMediaData->mAttachments.end(); 11860 ++it) 11861 { 11862 (*it)->updateParentMachine(mPeer); 11863 } 11864 11865 /* attach new data to the primary machine and reshare it */ 11866 mPeer->mMediaData.attach(mMediaData); 11867 } 11868 11869 return; 11870 } 11871 11872 /** 11873 * Perform deferred deletion of implicitly created diffs. 11874 * 11875 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not 11876 * backed up). 11877 * 11878 * @note Locks this object for writing! 11879 */ 11880 void Machine::rollbackMedia() 11881 { 11882 AutoCaller autoCaller(this); 11883 AssertComRCReturnVoid(autoCaller.rc()); 11884 11885 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11886 LogFlowThisFunc(("Entering rollbackMedia\n")); 11887 11888 HRESULT rc = S_OK; 11889 11890 /* no attach/detach operations -- nothing to do */ 11891 if (!mMediaData.isBackedUp()) 11892 return; 11893 11894 /* enumerate new attachments */ 11895 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 11896 it != mMediaData->mAttachments.end(); 11897 ++it) 11898 { 11899 MediumAttachment *pAttach = *it; 11900 /* Fix up the backrefs for DVD/floppy media. */ 11901 if (pAttach->getType() != DeviceType_HardDisk) 11902 { 11903 Medium* pMedium = pAttach->getMedium(); 11904 if (pMedium) 11905 { 11906 rc = pMedium->removeBackReference(mData->mUuid); 11907 AssertComRC(rc); 11908 } 11909 } 11910 11911 (*it)->rollback(); 11912 11913 pAttach = *it; 11914 /* Fix up the backrefs for DVD/floppy media. */ 11915 if (pAttach->getType() != DeviceType_HardDisk) 11916 { 11917 Medium* pMedium = pAttach->getMedium(); 11918 if (pMedium) 11919 { 11920 rc = pMedium->addBackReference(mData->mUuid); 11921 AssertComRC(rc); 11922 } 11923 } 11924 } 11925 11926 /** @todo convert all this Machine-based voodoo to MediumAttachment 11927 * based rollback logic. */ 11928 deleteImplicitDiffs(Global::IsOnline(mData->mMachineState)); 11929 11930 return; 11931 } 11932 11933 /** 11934 * Returns true if the settings file is located in the directory named exactly 11935 * as the machine; this means, among other things, that the machine directory 11936 * should be auto-renamed. 11937 * 11938 * @param aSettingsDir if not NULL, the full machine settings file directory 11939 * name will be assigned there. 11940 * 11941 * @note Doesn't lock anything. 11942 * @note Not thread safe (must be called from this object's lock). 11943 */ 11944 bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const 11945 { 11946 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox 11947 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname 11948 if (aSettingsDir) 11949 *aSettingsDir = strMachineDirName; 11950 strMachineDirName.stripPath(); // vmname 11951 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox 11952 strConfigFileOnly.stripPath() // vmname.vbox 11953 .stripExt(); // vmname 11954 /** @todo hack, make somehow use of ComposeMachineFilename */ 11955 if (mUserData->s.fDirectoryIncludesUUID) 11956 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw()); 11957 11958 AssertReturn(!strMachineDirName.isEmpty(), false); 11959 AssertReturn(!strConfigFileOnly.isEmpty(), false); 11960 11961 return strMachineDirName == strConfigFileOnly; 11962 } 11963 11964 /** 11965 * Discards all changes to machine settings. 11966 * 11967 * @param aNotify Whether to notify the direct session about changes or not. 11968 * 11969 * @note Locks objects for writing! 11970 */ 11971 void Machine::rollback(bool aNotify) 11972 { 11973 AutoCaller autoCaller(this); 11974 AssertComRCReturn(autoCaller.rc(), (void)0); 11975 11976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 11977 11978 if (!mStorageControllers.isNull()) 11979 { 11980 if (mStorageControllers.isBackedUp()) 11981 { 11982 /* unitialize all new devices (absent in the backed up list). */ 11983 StorageControllerList::const_iterator it = mStorageControllers->begin(); 11984 StorageControllerList *backedList = mStorageControllers.backedUpData(); 11985 while (it != mStorageControllers->end()) 11986 { 11987 if ( std::find(backedList->begin(), backedList->end(), *it) 11988 == backedList->end() 11989 ) 11990 { 11991 (*it)->uninit(); 11992 } 11993 ++it; 11994 } 11995 11996 /* restore the list */ 11997 mStorageControllers.rollback(); 11998 } 11999 12000 /* rollback any changes to devices after restoring the list */ 12001 if (mData->flModifications & IsModified_Storage) 12002 { 12003 StorageControllerList::const_iterator it = mStorageControllers->begin(); 12004 while (it != mStorageControllers->end()) 12005 { 12006 (*it)->rollback(); 12007 ++it; 12008 } 12009 } 12010 } 12011 12012 if (!mUSBControllers.isNull()) 12013 { 12014 if (mUSBControllers.isBackedUp()) 12015 { 12016 /* unitialize all new devices (absent in the backed up list). */ 12017 USBControllerList::const_iterator it = mUSBControllers->begin(); 12018 USBControllerList *backedList = mUSBControllers.backedUpData(); 12019 while (it != mUSBControllers->end()) 12020 { 12021 if ( std::find(backedList->begin(), backedList->end(), *it) 12022 == backedList->end() 12023 ) 12024 { 12025 (*it)->uninit(); 12026 } 12027 ++it; 12028 } 12029 12030 /* restore the list */ 12031 mUSBControllers.rollback(); 12032 } 12033 12034 /* rollback any changes to devices after restoring the list */ 12035 if (mData->flModifications & IsModified_USB) 12036 { 12037 USBControllerList::const_iterator it = mUSBControllers->begin(); 12038 while (it != mUSBControllers->end()) 12039 { 12040 (*it)->rollback(); 12041 ++it; 12042 } 12043 } 12044 } 12045 12046 mUserData.rollback(); 12047 12048 mHWData.rollback(); 12049 12050 if (mData->flModifications & IsModified_Storage) 12051 rollbackMedia(); 12052 12053 if (mBIOSSettings) 12054 mBIOSSettings->rollback(); 12055 12056 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer)) 12057 mVRDEServer->rollback(); 12058 12059 if (mAudioAdapter) 12060 mAudioAdapter->rollback(); 12061 12062 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB)) 12063 mUSBDeviceFilters->rollback(); 12064 12065 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl)) 12066 mBandwidthControl->rollback(); 12067 12068 if (!mHWData.isNull()) 12069 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType)); 12070 NetworkAdapterVector networkAdapters(mNetworkAdapters.size()); 12071 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)]; 12072 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)]; 12073 12074 if (mData->flModifications & IsModified_NetworkAdapters) 12075 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) 12076 if ( mNetworkAdapters[slot] 12077 && mNetworkAdapters[slot]->isModified()) 12078 { 12079 mNetworkAdapters[slot]->rollback(); 12080 networkAdapters[slot] = mNetworkAdapters[slot]; 12081 } 12082 12083 if (mData->flModifications & IsModified_SerialPorts) 12084 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 12085 if ( mSerialPorts[slot] 12086 && mSerialPorts[slot]->isModified()) 12087 { 12088 mSerialPorts[slot]->rollback(); 12089 serialPorts[slot] = mSerialPorts[slot]; 12090 } 12091 12092 if (mData->flModifications & IsModified_ParallelPorts) 12093 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 12094 if ( mParallelPorts[slot] 12095 && mParallelPorts[slot]->isModified()) 12096 { 12097 mParallelPorts[slot]->rollback(); 12098 parallelPorts[slot] = mParallelPorts[slot]; 12099 } 12100 12101 if (aNotify) 12102 { 12103 /* inform the direct session about changes */ 12104 12105 ComObjPtr<Machine> that = this; 12106 uint32_t flModifications = mData->flModifications; 12107 alock.release(); 12108 12109 if (flModifications & IsModified_SharedFolders) 12110 that->onSharedFolderChange(); 12111 12112 if (flModifications & IsModified_VRDEServer) 12113 that->onVRDEServerChange(/* aRestart */ TRUE); 12114 if (flModifications & IsModified_USB) 12115 that->onUSBControllerChange(); 12116 12117 for (ULONG slot = 0; slot < networkAdapters.size(); slot++) 12118 if (networkAdapters[slot]) 12119 that->onNetworkAdapterChange(networkAdapters[slot], FALSE); 12120 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot++) 12121 if (serialPorts[slot]) 12122 that->onSerialPortChange(serialPorts[slot]); 12123 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot++) 12124 if (parallelPorts[slot]) 12125 that->onParallelPortChange(parallelPorts[slot]); 12126 12127 if (flModifications & IsModified_Storage) 12128 that->onStorageControllerChange(); 12129 12130 #if 0 12131 if (flModifications & IsModified_BandwidthControl) 12132 that->onBandwidthControlChange(); 12133 #endif 12134 } 12135 } 12136 12137 /** 12138 * Commits all the changes to machine settings. 12139 * 12140 * Note that this operation is supposed to never fail. 12141 * 12142 * @note Locks this object and children for writing. 12143 */ 12144 void Machine::commit() 12145 { 12146 AutoCaller autoCaller(this); 12147 AssertComRCReturnVoid(autoCaller.rc()); 12148 12149 AutoCaller peerCaller(mPeer); 12150 AssertComRCReturnVoid(peerCaller.rc()); 12151 12152 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS); 12153 12154 /* 12155 * use safe commit to ensure Snapshot machines (that share mUserData) 12156 * will still refer to a valid memory location 12157 */ 12158 mUserData.commitCopy(); 12159 12160 mHWData.commit(); 12161 12162 if (mMediaData.isBackedUp()) 12163 commitMedia(Global::IsOnline(mData->mMachineState)); 12164 12165 mBIOSSettings->commit(); 12166 mVRDEServer->commit(); 12167 mAudioAdapter->commit(); 12168 mUSBDeviceFilters->commit(); 12169 mBandwidthControl->commit(); 12170 12171 /* Since mNetworkAdapters is a list which might have been changed (resized) 12172 * without using the Backupable<> template we need to handle the copying 12173 * of the list entries manually, including the creation of peers for the 12174 * new objects. */ 12175 bool commitNetworkAdapters = false; 12176 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType); 12177 if (mPeer) 12178 { 12179 /* commit everything, even the ones which will go away */ 12180 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++) 12181 mNetworkAdapters[slot]->commit(); 12182 /* copy over the new entries, creating a peer and uninit the original */ 12183 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size())); 12184 for (size_t slot = 0; slot < newSize; slot++) 12185 { 12186 /* look if this adapter has a peer device */ 12187 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->getPeer(); 12188 if (!peer) 12189 { 12190 /* no peer means the adapter is a newly created one; 12191 * create a peer owning data this data share it with */ 12192 peer.createObject(); 12193 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */); 12194 } 12195 mPeer->mNetworkAdapters[slot] = peer; 12196 } 12197 /* uninit any no longer needed network adapters */ 12198 for (size_t slot = newSize; slot < mNetworkAdapters.size(); slot++) 12199 mNetworkAdapters[slot]->uninit(); 12200 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); slot++) 12201 { 12202 if (mPeer->mNetworkAdapters[slot]) 12203 mPeer->mNetworkAdapters[slot]->uninit(); 12204 } 12205 /* Keep the original network adapter count until this point, so that 12206 * discarding a chipset type change will not lose settings. */ 12207 mNetworkAdapters.resize(newSize); 12208 mPeer->mNetworkAdapters.resize(newSize); 12209 } 12210 else 12211 { 12212 /* we have no peer (our parent is the newly created machine); 12213 * just commit changes to the network adapters */ 12214 commitNetworkAdapters = true; 12215 } 12216 if (commitNetworkAdapters) 12217 { 12218 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++) 12219 mNetworkAdapters[slot]->commit(); 12220 } 12221 12222 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 12223 mSerialPorts[slot]->commit(); 12224 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 12225 mParallelPorts[slot]->commit(); 12226 12227 bool commitStorageControllers = false; 12228 12229 if (mStorageControllers.isBackedUp()) 12230 { 12231 mStorageControllers.commit(); 12232 12233 if (mPeer) 12234 { 12235 /* Commit all changes to new controllers (this will reshare data with 12236 * peers for those who have peers) */ 12237 StorageControllerList *newList = new StorageControllerList(); 12238 StorageControllerList::const_iterator it = mStorageControllers->begin(); 12239 while (it != mStorageControllers->end()) 12240 { 12241 (*it)->commit(); 12242 12243 /* look if this controller has a peer device */ 12244 ComObjPtr<StorageController> peer = (*it)->getPeer(); 12245 if (!peer) 12246 { 12247 /* no peer means the device is a newly created one; 12248 * create a peer owning data this device share it with */ 12249 peer.createObject(); 12250 peer->init(mPeer, *it, true /* aReshare */); 12251 } 12252 else 12253 { 12254 /* remove peer from the old list */ 12255 mPeer->mStorageControllers->remove(peer); 12256 } 12257 /* and add it to the new list */ 12258 newList->push_back(peer); 12259 12260 ++it; 12261 } 12262 12263 /* uninit old peer's controllers that are left */ 12264 it = mPeer->mStorageControllers->begin(); 12265 while (it != mPeer->mStorageControllers->end()) 12266 { 12267 (*it)->uninit(); 12268 ++it; 12269 } 12270 12271 /* attach new list of controllers to our peer */ 12272 mPeer->mStorageControllers.attach(newList); 12273 } 12274 else 12275 { 12276 /* we have no peer (our parent is the newly created machine); 12277 * just commit changes to devices */ 12278 commitStorageControllers = true; 12279 } 12280 } 12281 else 12282 { 12283 /* the list of controllers itself is not changed, 12284 * just commit changes to controllers themselves */ 12285 commitStorageControllers = true; 12286 } 12287 12288 if (commitStorageControllers) 12289 { 12290 StorageControllerList::const_iterator it = mStorageControllers->begin(); 12291 while (it != mStorageControllers->end()) 12292 { 12293 (*it)->commit(); 12294 ++it; 12295 } 12296 } 12297 12298 bool commitUSBControllers = false; 12299 12300 if (mUSBControllers.isBackedUp()) 12301 { 12302 mUSBControllers.commit(); 12303 12304 if (mPeer) 12305 { 12306 /* Commit all changes to new controllers (this will reshare data with 12307 * peers for those who have peers) */ 12308 USBControllerList *newList = new USBControllerList(); 12309 USBControllerList::const_iterator it = mUSBControllers->begin(); 12310 while (it != mUSBControllers->end()) 12311 { 12312 (*it)->commit(); 12313 12314 /* look if this controller has a peer device */ 12315 ComObjPtr<USBController> peer = (*it)->getPeer(); 12316 if (!peer) 12317 { 12318 /* no peer means the device is a newly created one; 12319 * create a peer owning data this device share it with */ 12320 peer.createObject(); 12321 peer->init(mPeer, *it, true /* aReshare */); 12322 } 12323 else 12324 { 12325 /* remove peer from the old list */ 12326 mPeer->mUSBControllers->remove(peer); 12327 } 12328 /* and add it to the new list */ 12329 newList->push_back(peer); 12330 12331 ++it; 12332 } 12333 12334 /* uninit old peer's controllers that are left */ 12335 it = mPeer->mUSBControllers->begin(); 12336 while (it != mPeer->mUSBControllers->end()) 12337 { 12338 (*it)->uninit(); 12339 ++it; 12340 } 12341 12342 /* attach new list of controllers to our peer */ 12343 mPeer->mUSBControllers.attach(newList); 12344 } 12345 else 12346 { 12347 /* we have no peer (our parent is the newly created machine); 12348 * just commit changes to devices */ 12349 commitUSBControllers = true; 12350 } 12351 } 12352 else 12353 { 12354 /* the list of controllers itself is not changed, 12355 * just commit changes to controllers themselves */ 12356 commitUSBControllers = true; 12357 } 12358 12359 if (commitUSBControllers) 12360 { 12361 USBControllerList::const_iterator it = mUSBControllers->begin(); 12362 while (it != mUSBControllers->end()) 12363 { 12364 (*it)->commit(); 12365 ++it; 12366 } 12367 } 12368 12369 if (isSessionMachine()) 12370 { 12371 /* attach new data to the primary machine and reshare it */ 12372 mPeer->mUserData.attach(mUserData); 12373 mPeer->mHWData.attach(mHWData); 12374 /* mMediaData is reshared by fixupMedia */ 12375 // mPeer->mMediaData.attach(mMediaData); 12376 Assert(mPeer->mMediaData.data() == mMediaData.data()); 12377 } 12378 } 12379 12380 /** 12381 * Copies all the hardware data from the given machine. 12382 * 12383 * Currently, only called when the VM is being restored from a snapshot. In 12384 * particular, this implies that the VM is not running during this method's 12385 * call. 12386 * 12387 * @note This method must be called from under this object's lock. 12388 * 12389 * @note This method doesn't call #commit(), so all data remains backed up and 12390 * unsaved. 12391 */ 12392 void Machine::copyFrom(Machine *aThat) 12393 { 12394 AssertReturnVoid(!isSnapshotMachine()); 12395 AssertReturnVoid(aThat->isSnapshotMachine()); 12396 12397 AssertReturnVoid(!Global::IsOnline(mData->mMachineState)); 12398 12399 mHWData.assignCopy(aThat->mHWData); 12400 12401 // create copies of all shared folders (mHWData after attaching a copy 12402 // contains just references to original objects) 12403 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin(); 12404 it != mHWData->mSharedFolders.end(); 12405 ++it) 12406 { 12407 ComObjPtr<SharedFolder> folder; 12408 folder.createObject(); 12409 HRESULT rc = folder->initCopy(getMachine(), *it); 12410 AssertComRC(rc); 12411 *it = folder; 12412 } 12413 12414 mBIOSSettings->copyFrom(aThat->mBIOSSettings); 12415 mVRDEServer->copyFrom(aThat->mVRDEServer); 12416 mAudioAdapter->copyFrom(aThat->mAudioAdapter); 12417 mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters); 12418 mBandwidthControl->copyFrom(aThat->mBandwidthControl); 12419 12420 /* create private copies of all controllers */ 12421 mStorageControllers.backup(); 12422 mStorageControllers->clear(); 12423 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin(); 12424 it != aThat->mStorageControllers->end(); 12425 ++it) 12426 { 12427 ComObjPtr<StorageController> ctrl; 12428 ctrl.createObject(); 12429 ctrl->initCopy(this, *it); 12430 mStorageControllers->push_back(ctrl); 12431 } 12432 12433 /* create private copies of all USB controllers */ 12434 mUSBControllers.backup(); 12435 mUSBControllers->clear(); 12436 for (USBControllerList::iterator it = aThat->mUSBControllers->begin(); 12437 it != aThat->mUSBControllers->end(); 12438 ++it) 12439 { 12440 ComObjPtr<USBController> ctrl; 12441 ctrl.createObject(); 12442 ctrl->initCopy(this, *it); 12443 mUSBControllers->push_back(ctrl); 12444 } 12445 12446 mNetworkAdapters.resize(aThat->mNetworkAdapters.size()); 12447 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) 12448 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]); 12449 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 12450 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]); 12451 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 12452 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]); 12453 } 12454 12455 /** 12456 * Returns whether the given storage controller is hotplug capable. 12457 * 12458 * @returns true if the controller supports hotplugging 12459 * false otherwise. 12460 * @param enmCtrlType The controller type to check for. 12461 */ 12462 bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType) 12463 { 12464 switch (enmCtrlType) 12465 { 12466 case StorageControllerType_IntelAhci: 12467 return true; 12468 case StorageControllerType_LsiLogic: 12469 case StorageControllerType_LsiLogicSas: 12470 case StorageControllerType_BusLogic: 12471 case StorageControllerType_PIIX3: 12472 case StorageControllerType_PIIX4: 12473 case StorageControllerType_ICH6: 12474 case StorageControllerType_I82078: 12475 default: 12476 return false; 12477 } 12478 } 12479 12480 #ifdef VBOX_WITH_RESOURCE_USAGE_API 12481 12482 void Machine::getDiskList(MediaList &list) 12483 { 12484 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 12485 it != mMediaData->mAttachments.end(); 12486 ++it) 12487 { 12488 MediumAttachment* pAttach = *it; 12489 /* just in case */ 12490 AssertStmt(pAttach, continue); 12491 12492 AutoCaller localAutoCallerA(pAttach); 12493 if (FAILED(localAutoCallerA.rc())) continue; 12494 12495 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS); 12496 12497 if (pAttach->getType() == DeviceType_HardDisk) 12498 list.push_back(pAttach->getMedium()); 12499 } 12500 } 12501 12502 void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid) 12503 { 12504 AssertReturnVoid(isWriteLockOnCurrentThread()); 12505 AssertPtrReturnVoid(aCollector); 12506 12507 pm::CollectorHAL *hal = aCollector->getHAL(); 12508 /* Create sub metrics */ 12509 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User", 12510 "Percentage of processor time spent in user mode by the VM process."); 12511 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel", 12512 "Percentage of processor time spent in kernel mode by the VM process."); 12513 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used", 12514 "Size of resident portion of VM process in memory."); 12515 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used", 12516 "Actual size of all VM disks combined."); 12517 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx", 12518 "Network receive rate."); 12519 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx", 12520 "Network transmit rate."); 12521 /* Create and register base metrics */ 12522 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid, 12523 cpuLoadUser, cpuLoadKernel); 12524 aCollector->registerBaseMetric(cpuLoad); 12525 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid, 12526 ramUsageUsed); 12527 aCollector->registerBaseMetric(ramUsage); 12528 MediaList disks; 12529 getDiskList(disks); 12530 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks, 12531 diskUsageUsed); 12532 aCollector->registerBaseMetric(diskUsage); 12533 12534 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0)); 12535 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 12536 new pm::AggregateAvg())); 12537 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 12538 new pm::AggregateMin())); 12539 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 12540 new pm::AggregateMax())); 12541 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0)); 12542 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 12543 new pm::AggregateAvg())); 12544 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 12545 new pm::AggregateMin())); 12546 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 12547 new pm::AggregateMax())); 12548 12549 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0)); 12550 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 12551 new pm::AggregateAvg())); 12552 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 12553 new pm::AggregateMin())); 12554 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 12555 new pm::AggregateMax())); 12556 12557 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0)); 12558 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 12559 new pm::AggregateAvg())); 12560 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 12561 new pm::AggregateMin())); 12562 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 12563 new pm::AggregateMax())); 12564 12565 12566 /* Guest metrics collector */ 12567 mCollectorGuest = new pm::CollectorGuest(aMachine, pid); 12568 aCollector->registerGuest(mCollectorGuest); 12569 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", 12570 this, __PRETTY_FUNCTION__, mCollectorGuest)); 12571 12572 /* Create sub metrics */ 12573 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User", 12574 "Percentage of processor time spent in user mode as seen by the guest."); 12575 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel", 12576 "Percentage of processor time spent in kernel mode as seen by the guest."); 12577 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle", 12578 "Percentage of processor time spent idling as seen by the guest."); 12579 12580 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */ 12581 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM."); 12582 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM."); 12583 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM."); 12584 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM."); 12585 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory."); 12586 12587 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file."); 12588 12589 /* Create and register base metrics */ 12590 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine, 12591 machineNetRx, machineNetTx); 12592 aCollector->registerBaseMetric(machineNetRate); 12593 12594 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine, 12595 guestLoadUser, guestLoadKernel, guestLoadIdle); 12596 aCollector->registerBaseMetric(guestCpuLoad); 12597 12598 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine, 12599 guestMemTotal, guestMemFree, 12600 guestMemBalloon, guestMemShared, 12601 guestMemCache, guestPagedTotal); 12602 aCollector->registerBaseMetric(guestCpuMem); 12603 12604 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0)); 12605 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg())); 12606 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin())); 12607 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax())); 12608 12609 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0)); 12610 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg())); 12611 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin())); 12612 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax())); 12613 12614 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0)); 12615 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg())); 12616 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin())); 12617 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax())); 12618 12619 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0)); 12620 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg())); 12621 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin())); 12622 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax())); 12623 12624 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0)); 12625 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg())); 12626 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin())); 12627 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax())); 12628 12629 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0)); 12630 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg())); 12631 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin())); 12632 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax())); 12633 12634 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0)); 12635 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg())); 12636 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin())); 12637 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax())); 12638 12639 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0)); 12640 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg())); 12641 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin())); 12642 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax())); 12643 12644 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0)); 12645 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg())); 12646 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin())); 12647 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax())); 12648 12649 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0)); 12650 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg())); 12651 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin())); 12652 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax())); 12653 12654 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0)); 12655 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg())); 12656 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin())); 12657 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax())); 12658 } 12659 12660 void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine) 12661 { 12662 AssertReturnVoid(isWriteLockOnCurrentThread()); 12663 12664 if (aCollector) 12665 { 12666 aCollector->unregisterMetricsFor(aMachine); 12667 aCollector->unregisterBaseMetricsFor(aMachine); 12668 } 12669 } 12670 12671 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 12672 12673 12674 //////////////////////////////////////////////////////////////////////////////// 12675 12676 DEFINE_EMPTY_CTOR_DTOR(SessionMachine) 12677 12678 HRESULT SessionMachine::FinalConstruct() 12679 { 12680 LogFlowThisFunc(("\n")); 12681 52 if (mClientToken != NULLHANDLE) 53 ::DosCloseMutexSem(mClientToken); 54 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 55 if (mClientToken >= 0) 56 ::semctl(mClientToken, 0, IPC_RMID); 57 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 58 mClientTokenId = "0"; 59 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 60 #else 61 # error "Port me!" 62 #endif 63 mClientToken = CTTOKENARG; 64 } 65 66 Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine) : 67 mMachine(pMachine) 68 { 12682 69 #if defined(RT_OS_WINDOWS) 12683 mIPCSem = NULL; 12684 #elif defined(RT_OS_OS2) 12685 mIPCSem = NULLHANDLE; 12686 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12687 mIPCSem = -1; 12688 #else 12689 # error "Port me!" 12690 #endif 12691 12692 return BaseFinalConstruct(); 12693 } 12694 12695 void SessionMachine::FinalRelease() 12696 { 12697 LogFlowThisFunc(("\n")); 12698 12699 uninit(Uninit::Unexpected); 12700 12701 BaseFinalRelease(); 12702 } 12703 12704 /** 12705 * @note Must be called only by Machine::openSession() from its own write lock. 12706 */ 12707 HRESULT SessionMachine::init(Machine *aMachine) 12708 { 12709 LogFlowThisFuncEnter(); 12710 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str())); 12711 12712 AssertReturn(aMachine, E_INVALIDARG); 12713 12714 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL); 12715 12716 /* Enclose the state transition NotReady->InInit->Ready */ 12717 AutoInitSpan autoInitSpan(this); 12718 AssertReturn(autoInitSpan.isOk(), E_FAIL); 12719 12720 /* create the interprocess semaphore */ 12721 #if defined(RT_OS_WINDOWS) 12722 mIPCSemName = aMachine->mData->m_strConfigFileFull; 12723 for (size_t i = 0; i < mIPCSemName.length(); i++) 12724 if (mIPCSemName.raw()[i] == '\\') 12725 mIPCSemName.raw()[i] = '/'; 12726 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw()); 12727 ComAssertMsgRet(mIPCSem, 12728 ("Cannot create IPC mutex '%ls', err=%d", 12729 mIPCSemName.raw(), ::GetLastError()), 12730 E_FAIL); 70 Bstr tokenId = pMachine->mData->m_strConfigFileFull; 71 for (size_t i = 0; i < tokenId.length(); i++) 72 if (tokenId.raw()[i] == '\\') 73 tokenId.raw()[i] = '/'; 74 mClientToken = ::CreateMutex(NULL, FALSE, tokenId.raw()); 75 mClientTokenId = tokenId; 76 AssertMsg(mClientToken, 77 ("Cannot create token '%s', err=%d", 78 mClientTokenId.c_str(), ::GetLastError())); 12731 79 #elif defined(RT_OS_OS2) 12732 80 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}", 12733 aMachine->mData->mUuid.raw()); 12734 mIPCSemName = ipcSem; 12735 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE); 12736 ComAssertMsgRet(arc == NO_ERROR, 12737 ("Cannot create IPC mutex '%s', arc=%ld", 12738 ipcSem.c_str(), arc), 12739 E_FAIL); 81 pMachine->mData->mUuid.raw()); 82 mClientTokenId = ipcSem; 83 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE); 84 AssertMsg(arc == NO_ERROR, 85 ("Cannot create token '%s', arc=%ld", 86 ipcSem.c_str(), arc)); 12740 87 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12741 88 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN … … 12747 94 # endif 12748 95 key_t key; 12749 m IPCSem= -1;12750 m IPCKey= "0";96 mClientToken = -1; 97 mClientTokenId = "0"; 12751 98 for (uint32_t i = 0; i < 1 << 24; i++) 12752 99 { … … 12755 102 if (sem >= 0 || (errno != EEXIST && errno != EACCES)) 12756 103 { 12757 m IPCSem= sem;104 mClientToken = sem; 12758 105 if (sem >= 0) 12759 m IPCKey= BstrFmt("%u", key);106 mClientTokenId = BstrFmt("%u", key); 12760 107 break; 12761 108 } 12762 109 } 12763 110 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 12764 Utf8Str semName = aMachine->mData->m_strConfigFileFull;111 Utf8Str semName = pMachine->mData->m_strConfigFileFull; 12765 112 char *pszSemName = NULL; 12766 113 RTStrUtf8ToCurrentCP(&pszSemName, semName); … … 12768 115 RTStrFree(pszSemName); 12769 116 12770 m IPCSem= ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);117 mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT); 12771 118 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 12772 119 12773 120 int errnoSave = errno; 12774 if (mIPCSem < 0 && errnoSave == ENOSYS) 12775 { 12776 setError(E_FAIL, 12777 tr("Cannot create IPC semaphore. Most likely your host kernel lacks " 12778 "support for SysV IPC. Check the host kernel configuration for " 12779 "CONFIG_SYSVIPC=y")); 12780 return E_FAIL; 121 if (mClientToken < 0 && errnoSave == ENOSYS) 122 { 123 mMachine->setError(E_FAIL, 124 tr("Cannot create IPC semaphore. Most likely your host kernel lacks " 125 "support for SysV IPC. Check the host kernel configuration for " 126 "CONFIG_SYSVIPC=y")); 127 mClientToken = CTTOKENARG; 128 return; 12781 129 } 12782 130 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing 12783 * the IPC semaphores*/12784 if (m IPCSem< 0 && errnoSave == ENOSPC)131 * the token */ 132 if (mClientToken < 0 && errnoSave == ENOSPC) 12785 133 { 12786 134 #ifdef RT_OS_LINUX 12787 setError(E_FAIL,12788 tr("Cannot create IPC semaphore because the system limit for the "12789 "maximum number of semaphore sets (SEMMNI), or the system wide "12790 "maximum number of semaphores (SEMMNS) would be exceeded. The "12791 "current set of SysV IPC semaphores can be determined from "12792 "the file /proc/sysvipc/sem"));12793 #else 12794 setError(E_FAIL,12795 tr("Cannot create IPC semaphore because the system-imposed limit "12796 "on the maximum number of allowed semaphores or semaphore "12797 "identifiers system-wide would be exceeded"));12798 #endif 12799 return E_FAIL;12800 }12801 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),12802 E_FAIL);135 mMachine->setError(E_FAIL, 136 tr("Cannot create IPC semaphore because the system limit for the " 137 "maximum number of semaphore sets (SEMMNI), or the system wide " 138 "maximum number of semaphores (SEMMNS) would be exceeded. The " 139 "current set of SysV IPC semaphores can be determined from " 140 "the file /proc/sysvipc/sem")); 141 #else 142 mMachine->setError(E_FAIL, 143 tr("Cannot create IPC semaphore because the system-imposed limit " 144 "on the maximum number of allowed semaphores or semaphore " 145 "identifiers system-wide would be exceeded")); 146 #endif 147 mClientToken = CTTOKENARG; 148 return; 149 } 150 AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave)); 12803 151 /* set the initial value to 1 */ 12804 int rv = ::semctl(mIPCSem, 0, SETVAL, 1); 12805 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno), 12806 E_FAIL); 152 int rv = ::semctl(mClientToken, 0, SETVAL, 1); 153 errnoSave = errno; 154 if (rv != 0) 155 { 156 ::semctl(mClientToken, 0, IPC_RMID); 157 mClientToken = CTTOKENARG; 158 AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave)); 159 } 12807 160 #else 12808 161 # error "Port me!" 12809 162 #endif 12810 12811 /* memorize the peer Machine */ 12812 unconst(mPeer) = aMachine; 12813 /* share the parent pointer */ 12814 unconst(mParent) = aMachine->mParent; 12815 12816 /* take the pointers to data to share */ 12817 mData.share(aMachine->mData); 12818 mSSData.share(aMachine->mSSData); 12819 12820 mUserData.share(aMachine->mUserData); 12821 mHWData.share(aMachine->mHWData); 12822 mMediaData.share(aMachine->mMediaData); 12823 12824 mStorageControllers.allocate(); 12825 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin(); 12826 it != aMachine->mStorageControllers->end(); 12827 ++it) 12828 { 12829 ComObjPtr<StorageController> ctl; 12830 ctl.createObject(); 12831 ctl->init(this, *it); 12832 mStorageControllers->push_back(ctl); 12833 } 12834 12835 mUSBControllers.allocate(); 12836 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin(); 12837 it != aMachine->mUSBControllers->end(); 12838 ++it) 12839 { 12840 ComObjPtr<USBController> ctl; 12841 ctl.createObject(); 12842 ctl->init(this, *it); 12843 mUSBControllers->push_back(ctl); 12844 } 12845 12846 unconst(mBIOSSettings).createObject(); 12847 mBIOSSettings->init(this, aMachine->mBIOSSettings); 12848 /* create another VRDEServer object that will be mutable */ 12849 unconst(mVRDEServer).createObject(); 12850 mVRDEServer->init(this, aMachine->mVRDEServer); 12851 /* create another audio adapter object that will be mutable */ 12852 unconst(mAudioAdapter).createObject(); 12853 mAudioAdapter->init(this, aMachine->mAudioAdapter); 12854 /* create a list of serial ports that will be mutable */ 12855 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 12856 { 12857 unconst(mSerialPorts[slot]).createObject(); 12858 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]); 12859 } 12860 /* create a list of parallel ports that will be mutable */ 12861 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 12862 { 12863 unconst(mParallelPorts[slot]).createObject(); 12864 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]); 12865 } 12866 12867 /* create another USB device filters object that will be mutable */ 12868 unconst(mUSBDeviceFilters).createObject(); 12869 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters); 12870 12871 /* create a list of network adapters that will be mutable */ 12872 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size()); 12873 for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) 12874 { 12875 unconst(mNetworkAdapters[slot]).createObject(); 12876 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]); 12877 } 12878 12879 /* create another bandwidth control object that will be mutable */ 12880 unconst(mBandwidthControl).createObject(); 12881 mBandwidthControl->init(this, aMachine->mBandwidthControl); 12882 12883 /* default is to delete saved state on Saved -> PoweredOff transition */ 12884 mRemoveSavedState = true; 12885 12886 /* Confirm a successful initialization when it's the case */ 12887 autoInitSpan.setSucceeded(); 12888 12889 LogFlowThisFuncLeave(); 12890 return S_OK; 12891 } 12892 12893 /** 12894 * Uninitializes this session object. If the reason is other than 12895 * Uninit::Unexpected, then this method MUST be called from #checkForDeath(). 12896 * 12897 * @param aReason uninitialization reason 12898 * 12899 * @note Locks mParent + this object for writing. 12900 */ 12901 void SessionMachine::uninit(Uninit::Reason aReason) 12902 { 12903 LogFlowThisFuncEnter(); 12904 LogFlowThisFunc(("reason=%d\n", aReason)); 12905 12906 /* 12907 * Strongly reference ourselves to prevent this object deletion after 12908 * mData->mSession.mMachine.setNull() below (which can release the last 12909 * reference and call the destructor). Important: this must be done before 12910 * accessing any members (and before AutoUninitSpan that does it as well). 12911 * This self reference will be released as the very last step on return. 12912 */ 12913 ComObjPtr<SessionMachine> selfRef = this; 12914 12915 /* Enclose the state transition Ready->InUninit->NotReady */ 12916 AutoUninitSpan autoUninitSpan(this); 12917 if (autoUninitSpan.uninitDone()) 12918 { 12919 LogFlowThisFunc(("Already uninitialized\n")); 12920 LogFlowThisFuncLeave(); 12921 return; 12922 } 12923 12924 if (autoUninitSpan.initFailed()) 12925 { 12926 /* We've been called by init() because it's failed. It's not really 12927 * necessary (nor it's safe) to perform the regular uninit sequence 12928 * below, the following is enough. 12929 */ 12930 LogFlowThisFunc(("Initialization failed.\n")); 163 } 164 165 bool Machine::ClientToken::isReady() 166 { 167 return mClientToken != CTTOKENARG; 168 } 169 170 void Machine::ClientToken::getId(Utf8Str &strId) 171 { 172 strId = mClientTokenId; 173 } 174 175 CTTOKENTYPE Machine::ClientToken::getToken() 176 { 177 return mClientToken; 178 } 179 180 bool Machine::ClientToken::release() 181 { 182 bool terminated = false; 183 12931 184 #if defined(RT_OS_WINDOWS) 12932 if (mIPCSem) 12933 ::CloseHandle(mIPCSem); 12934 mIPCSem = NULL; 185 AssertMsg(mClientToken, ("semaphore must be created")); 186 187 /* release the token */ 188 ::ReleaseMutex(mClientToken); 189 terminated = true; 12935 190 #elif defined(RT_OS_OS2) 12936 if (mIPCSem != NULLHANDLE) 12937 ::DosCloseMutexSem(mIPCSem); 12938 mIPCSem = NULLHANDLE; 191 AssertMsg(mClientToken, ("semaphore must be created")); 192 193 /* release the token */ 194 ::DosReleaseMutexSem(mClientToken); 195 terminated = true; 12939 196 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12940 if (mIPCSem >= 0) 12941 ::semctl(mIPCSem, 0, IPC_RMID); 12942 mIPCSem = -1; 12943 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 12944 mIPCKey = "0"; 12945 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 197 AssertMsg(mClientToken >= 0, ("semaphore must be created")); 198 int val = ::semctl(mClientToken, 0, GETVAL); 199 if (val > 0) 200 { 201 /* the semaphore is signaled, meaning the session is terminated */ 202 terminated = true; 203 } 12946 204 #else 12947 205 # error "Port me!" 12948 206 #endif 12949 uninitDataAndChildObjects();12950 mData.free();12951 unconst(mParent) = NULL;12952 unconst(mPeer) = NULL;12953 LogFlowThisFuncLeave();12954 return;12955 }12956 12957 MachineState_T lastState;12958 {12959 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);12960 lastState = mData->mMachineState;12961 }12962 NOREF(lastState);12963 12964 #ifdef VBOX_WITH_USB12965 // release all captured USB devices, but do this before requesting the locks below12966 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))12967 {12968 /* Console::captureUSBDevices() is called in the VM process only after12969 * setting the machine state to Starting or Restoring.12970 * Console::detachAllUSBDevices() will be called upon successful12971 * termination. So, we need to release USB devices only if there was12972 * an abnormal termination of a running VM.12973 *12974 * This is identical to SessionMachine::DetachAllUSBDevices except12975 * for the aAbnormal argument. */12976 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);12977 AssertComRC(rc);12978 NOREF(rc);12979 12980 USBProxyService *service = mParent->host()->usbProxyService();12981 if (service)12982 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);12983 }12984 #endif /* VBOX_WITH_USB */12985 12986 // we need to lock this object in uninit() because the lock is shared12987 // with mPeer (as well as data we modify below). mParent->addProcessToReap()12988 // and others need mParent lock, and USB needs host lock.12989 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);12990 12991 #ifdef VBOX_WITH_RESOURCE_USAGE_API12992 /*12993 * It is safe to call Machine::unregisterMetrics() here because12994 * PerformanceCollector::samplerCallback no longer accesses guest methods12995 * holding the lock.12996 */12997 unregisterMetrics(mParent->performanceCollector(), mPeer);12998 /* The guest must be unregistered after its metrics (@bugref{5949}). */12999 LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",13000 this, __PRETTY_FUNCTION__, mCollectorGuest));13001 if (mCollectorGuest)13002 {13003 mParent->performanceCollector()->unregisterGuest(mCollectorGuest);13004 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()13005 mCollectorGuest = NULL;13006 }13007 #endif13008 13009 if (aReason == Uninit::Abnormal)13010 {13011 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",13012 Global::IsOnlineOrTransient(lastState)));13013 13014 /* reset the state to Aborted */13015 if (mData->mMachineState != MachineState_Aborted)13016 setMachineState(MachineState_Aborted);13017 }13018 13019 // any machine settings modified?13020 if (mData->flModifications)13021 {13022 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));13023 rollback(false /* aNotify */);13024 }13025 13026 Assert( mConsoleTaskData.strStateFilePath.isEmpty()13027 || !mConsoleTaskData.mSnapshot);13028 if (!mConsoleTaskData.strStateFilePath.isEmpty())13029 {13030 LogWarningThisFunc(("canceling failed save state request!\n"));13031 endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));13032 }13033 else if (!mConsoleTaskData.mSnapshot.isNull())13034 {13035 LogWarningThisFunc(("canceling untaken snapshot!\n"));13036 13037 /* delete all differencing hard disks created (this will also attach13038 * their parents back by rolling back mMediaData) */13039 rollbackMedia();13040 13041 // delete the saved state file (it might have been already created)13042 // AFTER killing the snapshot so that releaseSavedStateFile() won't13043 // think it's still in use13044 Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();13045 mConsoleTaskData.mSnapshot->uninit();13046 releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );13047 }13048 13049 if (!mData->mSession.mType.isEmpty())13050 {13051 /* mType is not null when this machine's process has been started by13052 * Machine::LaunchVMProcess(), therefore it is our child. We13053 * need to queue the PID to reap the process (and avoid zombies on13054 * Linux). */13055 Assert(mData->mSession.mPID != NIL_RTPROCESS);13056 mParent->addProcessToReap(mData->mSession.mPID);13057 }13058 13059 mData->mSession.mPID = NIL_RTPROCESS;13060 13061 if (aReason == Uninit::Unexpected)13062 {13063 /* Uninitialization didn't come from #checkForDeath(), so tell the13064 * client watcher thread to update the set of machines that have open13065 * sessions. */13066 mParent->updateClientWatcher();13067 }13068 13069 /* uninitialize all remote controls */13070 if (mData->mSession.mRemoteControls.size())13071 {13072 LogFlowThisFunc(("Closing remote sessions (%d):\n",13073 mData->mSession.mRemoteControls.size()));13074 13075 Data::Session::RemoteControlList::iterator it =13076 mData->mSession.mRemoteControls.begin();13077 while (it != mData->mSession.mRemoteControls.end())13078 {13079 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));13080 HRESULT rc = (*it)->Uninitialize();13081 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));13082 if (FAILED(rc))13083 LogWarningThisFunc(("Forgot to close the remote session?\n"));13084 ++it;13085 }13086 mData->mSession.mRemoteControls.clear();13087 }13088 13089 /*13090 * An expected uninitialization can come only from #checkForDeath().13091 * Otherwise it means that something's gone really wrong (for example,13092 * the Session implementation has released the VirtualBox reference13093 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,13094 * etc). However, it's also possible, that the client releases the IPC13095 * semaphore correctly (i.e. before it releases the VirtualBox reference),13096 * but the VirtualBox release event comes first to the server process.13097 * This case is practically possible, so we should not assert on an13098 * unexpected uninit, just log a warning.13099 */13100 13101 if ((aReason == Uninit::Unexpected))13102 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));13103 13104 if (aReason != Uninit::Normal)13105 {13106 mData->mSession.mDirectControl.setNull();13107 }13108 else13109 {13110 /* this must be null here (see #OnSessionEnd()) */13111 Assert(mData->mSession.mDirectControl.isNull());13112 Assert(mData->mSession.mState == SessionState_Unlocking);13113 Assert(!mData->mSession.mProgress.isNull());13114 }13115 if (mData->mSession.mProgress)13116 {13117 if (aReason == Uninit::Normal)13118 mData->mSession.mProgress->notifyComplete(S_OK);13119 else13120 mData->mSession.mProgress->notifyComplete(E_FAIL,13121 COM_IIDOF(ISession),13122 getComponentName(),13123 tr("The VM session was aborted"));13124 mData->mSession.mProgress.setNull();13125 }13126 13127 /* remove the association between the peer machine and this session machine */13128 Assert( (SessionMachine*)mData->mSession.mMachine == this13129 || aReason == Uninit::Unexpected);13130 13131 /* reset the rest of session data */13132 mData->mSession.mMachine.setNull();13133 mData->mSession.mState = SessionState_Unlocked;13134 mData->mSession.mType.setNull();13135 13136 /* close the interprocess semaphore before leaving the exclusive lock */13137 #if defined(RT_OS_WINDOWS)13138 if (mIPCSem)13139 ::CloseHandle(mIPCSem);13140 mIPCSem = NULL;13141 #elif defined(RT_OS_OS2)13142 if (mIPCSem != NULLHANDLE)13143 ::DosCloseMutexSem(mIPCSem);13144 mIPCSem = NULLHANDLE;13145 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)13146 if (mIPCSem >= 0)13147 ::semctl(mIPCSem, 0, IPC_RMID);13148 mIPCSem = -1;13149 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN13150 mIPCKey = "0";13151 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */13152 #else13153 # error "Port me!"13154 #endif13155 13156 /* fire an event */13157 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);13158 13159 uninitDataAndChildObjects();13160 13161 /* free the essential data structure last */13162 mData.free();13163 13164 /* release the exclusive lock before setting the below two to NULL */13165 multilock.release();13166 13167 unconst(mParent) = NULL;13168 unconst(mPeer) = NULL;13169 13170 LogFlowThisFuncLeave();13171 }13172 13173 // util::Lockable interface13174 ////////////////////////////////////////////////////////////////////////////////13175 13176 /**13177 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle13178 * with the primary Machine instance (mPeer).13179 */13180 RWLockHandle *SessionMachine::lockHandle() const13181 {13182 AssertReturn(mPeer != NULL, NULL);13183 return mPeer->lockHandle();13184 }13185 13186 // IInternalMachineControl methods13187 ////////////////////////////////////////////////////////////////////////////////13188 13189 /**13190 * Passes collected guest statistics to performance collector object13191 */13192 STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser,13193 ULONG aCpuKernel, ULONG aCpuIdle,13194 ULONG aMemTotal, ULONG aMemFree,13195 ULONG aMemBalloon, ULONG aMemShared,13196 ULONG aMemCache, ULONG aPageTotal,13197 ULONG aAllocVMM, ULONG aFreeVMM,13198 ULONG aBalloonedVMM, ULONG aSharedVMM,13199 ULONG aVmNetRx, ULONG aVmNetTx)13200 {13201 #ifdef VBOX_WITH_RESOURCE_USAGE_API13202 if (mCollectorGuest)13203 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,13204 aMemTotal, aMemFree, aMemBalloon, aMemShared,13205 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,13206 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);13207 13208 return S_OK;13209 #else13210 NOREF(aValidStats);13211 NOREF(aCpuUser);13212 NOREF(aCpuKernel);13213 NOREF(aCpuIdle);13214 NOREF(aMemTotal);13215 NOREF(aMemFree);13216 NOREF(aMemBalloon);13217 NOREF(aMemShared);13218 NOREF(aMemCache);13219 NOREF(aPageTotal);13220 NOREF(aAllocVMM);13221 NOREF(aFreeVMM);13222 NOREF(aBalloonedVMM);13223 NOREF(aSharedVMM);13224 NOREF(aVmNetRx);13225 NOREF(aVmNetTx);13226 return E_NOTIMPL;13227 #endif13228 }13229 13230 /**13231 * @note Locks this object for writing.13232 */13233 STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)13234 {13235 AutoCaller autoCaller(this);13236 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13237 13238 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13239 13240 mRemoveSavedState = aRemove;13241 13242 return S_OK;13243 }13244 13245 /**13246 * @note Locks the same as #setMachineState() does.13247 */13248 STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)13249 {13250 return setMachineState(aMachineState);13251 }13252 13253 /**13254 * @note Locks this object for reading.13255 */13256 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)13257 {13258 AutoCaller autoCaller(this);13259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13260 13261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);13262 13263 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)13264 mIPCSemName.cloneTo(aId);13265 return S_OK;13266 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)13267 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN13268 mIPCKey.cloneTo(aId);13269 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */13270 mData->m_strConfigFileFull.cloneTo(aId);13271 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */13272 return S_OK;13273 #else13274 # error "Port me!"13275 #endif13276 }13277 13278 /**13279 * @note Locks this object for writing.13280 */13281 STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)13282 {13283 LogFlowThisFunc(("aProgress=%p\n", aProgress));13284 AutoCaller autoCaller(this);13285 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13286 13287 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13288 13289 if (mData->mSession.mState != SessionState_Locked)13290 return VBOX_E_INVALID_OBJECT_STATE;13291 13292 if (!mData->mSession.mProgress.isNull())13293 mData->mSession.mProgress->setOtherProgressObject(aProgress);13294 13295 LogFlowThisFunc(("returns S_OK.\n"));13296 return S_OK;13297 }13298 13299 /**13300 * @note Locks this object for writing.13301 */13302 STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)13303 {13304 AutoCaller autoCaller(this);13305 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13306 13307 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13308 13309 if (mData->mSession.mState != SessionState_Locked)13310 return VBOX_E_INVALID_OBJECT_STATE;13311 13312 /* Finalize the LaunchVMProcess progress object. */13313 if (mData->mSession.mProgress)13314 {13315 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);13316 mData->mSession.mProgress.setNull();13317 }13318 13319 if (SUCCEEDED((HRESULT)iResult))13320 {13321 #ifdef VBOX_WITH_RESOURCE_USAGE_API13322 /* The VM has been powered up successfully, so it makes sense13323 * now to offer the performance metrics for a running machine13324 * object. Doing it earlier wouldn't be safe. */13325 registerMetrics(mParent->performanceCollector(), mPeer,13326 mData->mSession.mPID);13327 #endif /* VBOX_WITH_RESOURCE_USAGE_API */13328 }13329 13330 return S_OK;13331 }13332 13333 /**13334 * @note Locks this object for writing.13335 */13336 STDMETHODIMP SessionMachine::BeginPoweringDown(IProgress **aProgress)13337 {13338 LogFlowThisFuncEnter();13339 13340 CheckComArgOutPointerValid(aProgress);13341 13342 AutoCaller autoCaller(this);13343 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13344 13345 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13346 13347 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,13348 E_FAIL);13349 13350 /* create a progress object to track operation completion */13351 ComObjPtr<Progress> pProgress;13352 pProgress.createObject();13353 pProgress->init(getVirtualBox(),13354 static_cast<IMachine *>(this) /* aInitiator */,13355 Bstr(tr("Stopping the virtual machine")).raw(),13356 FALSE /* aCancelable */);13357 13358 /* fill in the console task data */13359 mConsoleTaskData.mLastState = mData->mMachineState;13360 mConsoleTaskData.mProgress = pProgress;13361 13362 /* set the state to Stopping (this is expected by Console::PowerDown()) */13363 setMachineState(MachineState_Stopping);13364 13365 pProgress.queryInterfaceTo(aProgress);13366 13367 return S_OK;13368 }13369 13370 /**13371 * @note Locks this object for writing.13372 */13373 STDMETHODIMP SessionMachine::EndPoweringDown(LONG iResult, IN_BSTR aErrMsg)13374 {13375 LogFlowThisFuncEnter();13376 13377 AutoCaller autoCaller(this);13378 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13379 13380 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13381 13382 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_PoweredOff)13383 || (FAILED(iResult) && mData->mMachineState == MachineState_Stopping))13384 && mConsoleTaskData.mLastState != MachineState_Null,13385 E_FAIL);13386 13387 /*13388 * On failure, set the state to the state we had when BeginPoweringDown()13389 * was called (this is expected by Console::PowerDown() and the associated13390 * task). On success the VM process already changed the state to13391 * MachineState_PoweredOff, so no need to do anything.13392 */13393 if (FAILED(iResult))13394 setMachineState(mConsoleTaskData.mLastState);13395 13396 /* notify the progress object about operation completion */13397 Assert(mConsoleTaskData.mProgress);13398 if (SUCCEEDED(iResult))13399 mConsoleTaskData.mProgress->notifyComplete(S_OK);13400 else13401 {13402 Utf8Str strErrMsg(aErrMsg);13403 if (strErrMsg.length())13404 mConsoleTaskData.mProgress->notifyComplete(iResult,13405 COM_IIDOF(ISession),13406 getComponentName(),13407 strErrMsg.c_str());13408 else13409 mConsoleTaskData.mProgress->notifyComplete(iResult);13410 }13411 13412 /* clear out the temporary saved state data */13413 mConsoleTaskData.mLastState = MachineState_Null;13414 mConsoleTaskData.mProgress.setNull();13415 13416 LogFlowThisFuncLeave();13417 return S_OK;13418 }13419 13420 13421 /**13422 * Goes through the USB filters of the given machine to see if the given13423 * device matches any filter or not.13424 *13425 * @note Locks the same as USBController::hasMatchingFilter() does.13426 */13427 STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,13428 BOOL *aMatched,13429 ULONG *aMaskedIfs)13430 {13431 LogFlowThisFunc(("\n"));13432 13433 CheckComArgNotNull(aUSBDevice);13434 CheckComArgOutPointerValid(aMatched);13435 13436 AutoCaller autoCaller(this);13437 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13438 13439 #ifdef VBOX_WITH_USB13440 *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs);13441 #else13442 NOREF(aUSBDevice);13443 NOREF(aMaskedIfs);13444 *aMatched = FALSE;13445 #endif13446 13447 return S_OK;13448 }13449 13450 /**13451 * @note Locks the same as Host::captureUSBDevice() does.13452 */13453 STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)13454 {13455 LogFlowThisFunc(("\n"));13456 13457 AutoCaller autoCaller(this);13458 AssertComRCReturnRC(autoCaller.rc());13459 13460 #ifdef VBOX_WITH_USB13461 /* if captureDeviceForVM() fails, it must have set extended error info */13462 clearError();13463 MultiResult rc = mParent->host()->checkUSBProxyService();13464 if (FAILED(rc)) return rc;13465 13466 USBProxyService *service = mParent->host()->usbProxyService();13467 AssertReturn(service, E_FAIL);13468 return service->captureDeviceForVM(this, Guid(aId).ref());13469 #else13470 NOREF(aId);13471 return E_NOTIMPL;13472 #endif13473 }13474 13475 /**13476 * @note Locks the same as Host::detachUSBDevice() does.13477 */13478 STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)13479 {13480 LogFlowThisFunc(("\n"));13481 13482 AutoCaller autoCaller(this);13483 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13484 13485 #ifdef VBOX_WITH_USB13486 USBProxyService *service = mParent->host()->usbProxyService();13487 AssertReturn(service, E_FAIL);13488 return service->detachDeviceFromVM(this, Guid(aId).ref(), !!aDone);13489 #else13490 NOREF(aId);13491 NOREF(aDone);13492 return E_NOTIMPL;13493 #endif13494 }13495 13496 /**13497 * Inserts all machine filters to the USB proxy service and then calls13498 * Host::autoCaptureUSBDevices().13499 *13500 * Called by Console from the VM process upon VM startup.13501 *13502 * @note Locks what called methods lock.13503 */13504 STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()13505 {13506 LogFlowThisFunc(("\n"));13507 13508 AutoCaller autoCaller(this);13509 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13510 13511 #ifdef VBOX_WITH_USB13512 HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */);13513 AssertComRC(rc);13514 NOREF(rc);13515 13516 USBProxyService *service = mParent->host()->usbProxyService();13517 AssertReturn(service, E_FAIL);13518 return service->autoCaptureDevicesForVM(this);13519 #else13520 return S_OK;13521 #endif13522 }13523 13524 /**13525 * Removes all machine filters from the USB proxy service and then calls13526 * Host::detachAllUSBDevices().13527 *13528 * Called by Console from the VM process upon normal VM termination or by13529 * SessionMachine::uninit() upon abnormal VM termination (from under the13530 * Machine/SessionMachine lock).13531 *13532 * @note Locks what called methods lock.13533 */13534 STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)13535 {13536 LogFlowThisFunc(("\n"));13537 13538 AutoCaller autoCaller(this);13539 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13540 13541 #ifdef VBOX_WITH_USB13542 HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */);13543 AssertComRC(rc);13544 NOREF(rc);13545 13546 USBProxyService *service = mParent->host()->usbProxyService();13547 AssertReturn(service, E_FAIL);13548 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);13549 #else13550 NOREF(aDone);13551 return S_OK;13552 #endif13553 }13554 13555 /**13556 * @note Locks this object for writing.13557 */13558 STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,13559 IProgress **aProgress)13560 {13561 LogFlowThisFuncEnter();13562 13563 AssertReturn(aSession, E_INVALIDARG);13564 AssertReturn(aProgress, E_INVALIDARG);13565 13566 AutoCaller autoCaller(this);13567 13568 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));13569 /*13570 * We don't assert below because it might happen that a non-direct session13571 * informs us it is closed right after we've been uninitialized -- it's ok.13572 */13573 if (FAILED(autoCaller.rc())) return autoCaller.rc();13574 13575 /* get IInternalSessionControl interface */13576 ComPtr<IInternalSessionControl> control(aSession);13577 13578 ComAssertRet(!control.isNull(), E_INVALIDARG);13579 13580 /* Creating a Progress object requires the VirtualBox lock, and13581 * thus locking it here is required by the lock order rules. */13582 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);13583 13584 if (control == mData->mSession.mDirectControl)13585 {13586 ComAssertRet(aProgress, E_POINTER);13587 13588 /* The direct session is being normally closed by the client process13589 * ----------------------------------------------------------------- */13590 13591 /* go to the closing state (essential for all open*Session() calls and13592 * for #checkForDeath()) */13593 Assert(mData->mSession.mState == SessionState_Locked);13594 mData->mSession.mState = SessionState_Unlocking;13595 13596 /* set direct control to NULL to release the remote instance */13597 mData->mSession.mDirectControl.setNull();13598 LogFlowThisFunc(("Direct control is set to NULL\n"));13599 13600 if (mData->mSession.mProgress)13601 {13602 /* finalize the progress, someone might wait if a frontend13603 * closes the session before powering on the VM. */13604 mData->mSession.mProgress->notifyComplete(E_FAIL,13605 COM_IIDOF(ISession),13606 getComponentName(),13607 tr("The VM session was closed before any attempt to power it on"));13608 mData->mSession.mProgress.setNull();13609 }13610 13611 /* Create the progress object the client will use to wait until13612 * #checkForDeath() is called to uninitialize this session object after13613 * it releases the IPC semaphore.13614 * Note! Because we're "reusing" mProgress here, this must be a proxy13615 * object just like for LaunchVMProcess. */13616 Assert(mData->mSession.mProgress.isNull());13617 ComObjPtr<ProgressProxy> progress;13618 progress.createObject();13619 ComPtr<IUnknown> pPeer(mPeer);13620 progress->init(mParent, pPeer,13621 Bstr(tr("Closing session")).raw(),13622 FALSE /* aCancelable */);13623 progress.queryInterfaceTo(aProgress);13624 mData->mSession.mProgress = progress;13625 }13626 else13627 {13628 /* the remote session is being normally closed */13629 Data::Session::RemoteControlList::iterator it =13630 mData->mSession.mRemoteControls.begin();13631 while (it != mData->mSession.mRemoteControls.end())13632 {13633 if (control == *it)13634 break;13635 ++it;13636 }13637 BOOL found = it != mData->mSession.mRemoteControls.end();13638 ComAssertMsgRet(found, ("The session is not found in the session list!"),13639 E_INVALIDARG);13640 // This MUST be erase(it), not remove(*it) as the latter triggers a13641 // very nasty use after free due to the place where the value "lives".13642 mData->mSession.mRemoteControls.erase(it);13643 }13644 13645 /* signal the client watcher thread, because the client is going away */13646 mParent->updateClientWatcher();13647 13648 LogFlowThisFuncLeave();13649 return S_OK;13650 }13651 13652 /**13653 * @note Locks this object for writing.13654 */13655 STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStateFilePath)13656 {13657 LogFlowThisFuncEnter();13658 13659 CheckComArgOutPointerValid(aProgress);13660 CheckComArgOutPointerValid(aStateFilePath);13661 13662 AutoCaller autoCaller(this);13663 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13664 13665 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13666 13667 AssertReturn( mData->mMachineState == MachineState_Paused13668 && mConsoleTaskData.mLastState == MachineState_Null13669 && mConsoleTaskData.strStateFilePath.isEmpty(),13670 E_FAIL);13671 13672 /* create a progress object to track operation completion */13673 ComObjPtr<Progress> pProgress;13674 pProgress.createObject();13675 pProgress->init(getVirtualBox(),13676 static_cast<IMachine *>(this) /* aInitiator */,13677 Bstr(tr("Saving the execution state of the virtual machine")).raw(),13678 FALSE /* aCancelable */);13679 13680 Utf8Str strStateFilePath;13681 /* stateFilePath is null when the machine is not running */13682 if (mData->mMachineState == MachineState_Paused)13683 composeSavedStateFilename(strStateFilePath);13684 13685 /* fill in the console task data */13686 mConsoleTaskData.mLastState = mData->mMachineState;13687 mConsoleTaskData.strStateFilePath = strStateFilePath;13688 mConsoleTaskData.mProgress = pProgress;13689 13690 /* set the state to Saving (this is expected by Console::SaveState()) */13691 setMachineState(MachineState_Saving);13692 13693 strStateFilePath.cloneTo(aStateFilePath);13694 pProgress.queryInterfaceTo(aProgress);13695 13696 return S_OK;13697 }13698 13699 /**13700 * @note Locks mParent + this object for writing.13701 */13702 STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)13703 {13704 LogFlowThisFunc(("\n"));13705 13706 AutoCaller autoCaller(this);13707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13708 13709 /* endSavingState() need mParent lock */13710 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);13711 13712 AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)13713 || (FAILED(iResult) && mData->mMachineState == MachineState_Saving))13714 && mConsoleTaskData.mLastState != MachineState_Null13715 && !mConsoleTaskData.strStateFilePath.isEmpty(),13716 E_FAIL);13717 13718 /*13719 * On failure, set the state to the state we had when BeginSavingState()13720 * was called (this is expected by Console::SaveState() and the associated13721 * task). On success the VM process already changed the state to13722 * MachineState_Saved, so no need to do anything.13723 */13724 if (FAILED(iResult))13725 setMachineState(mConsoleTaskData.mLastState);13726 13727 return endSavingState(iResult, aErrMsg);13728 }13729 13730 /**13731 * @note Locks this object for writing.13732 */13733 STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)13734 {13735 LogFlowThisFunc(("\n"));13736 13737 CheckComArgStrNotEmptyOrNull(aSavedStateFile);13738 13739 AutoCaller autoCaller(this);13740 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13741 13742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13743 13744 AssertReturn( mData->mMachineState == MachineState_PoweredOff13745 || mData->mMachineState == MachineState_Teleported13746 || mData->mMachineState == MachineState_Aborted13747 , E_FAIL); /** @todo setError. */13748 13749 Utf8Str stateFilePathFull = aSavedStateFile;13750 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);13751 if (RT_FAILURE(vrc))13752 return setError(VBOX_E_FILE_ERROR,13753 tr("Invalid saved state file path '%ls' (%Rrc)"),13754 aSavedStateFile,13755 vrc);13756 13757 mSSData->strStateFilePath = stateFilePathFull;13758 13759 /* The below setMachineState() will detect the state transition and will13760 * update the settings file */13761 13762 return setMachineState(MachineState_Saved);13763 }13764 13765 STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),13766 ComSafeArrayOut(BSTR, aValues),13767 ComSafeArrayOut(LONG64, aTimestamps),13768 ComSafeArrayOut(BSTR, aFlags))13769 {13770 LogFlowThisFunc(("\n"));13771 13772 #ifdef VBOX_WITH_GUEST_PROPS13773 using namespace guestProp;13774 13775 AutoCaller autoCaller(this);13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13777 13778 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);13779 13780 CheckComArgOutSafeArrayPointerValid(aNames);13781 CheckComArgOutSafeArrayPointerValid(aValues);13782 CheckComArgOutSafeArrayPointerValid(aTimestamps);13783 CheckComArgOutSafeArrayPointerValid(aFlags);13784 13785 size_t cEntries = mHWData->mGuestProperties.size();13786 com::SafeArray<BSTR> names(cEntries);13787 com::SafeArray<BSTR> values(cEntries);13788 com::SafeArray<LONG64> timestamps(cEntries);13789 com::SafeArray<BSTR> flags(cEntries);13790 unsigned i = 0;13791 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();13792 it != mHWData->mGuestProperties.end();13793 ++it)13794 {13795 char szFlags[MAX_FLAGS_LEN + 1];13796 it->first.cloneTo(&names[i]);13797 it->second.strValue.cloneTo(&values[i]);13798 timestamps[i] = it->second.mTimestamp;13799 /* If it is NULL, keep it NULL. */13800 if (it->second.mFlags)13801 {13802 writeFlags(it->second.mFlags, szFlags);13803 Bstr(szFlags).cloneTo(&flags[i]);13804 }13805 else13806 flags[i] = NULL;13807 ++i;13808 }13809 names.detachTo(ComSafeArrayOutArg(aNames));13810 values.detachTo(ComSafeArrayOutArg(aValues));13811 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));13812 flags.detachTo(ComSafeArrayOutArg(aFlags));13813 return S_OK;13814 #else13815 ReturnComNotImplemented();13816 #endif13817 }13818 13819 STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,13820 IN_BSTR aValue,13821 LONG64 aTimestamp,13822 IN_BSTR aFlags)13823 {13824 LogFlowThisFunc(("\n"));13825 13826 #ifdef VBOX_WITH_GUEST_PROPS13827 using namespace guestProp;13828 13829 CheckComArgStrNotEmptyOrNull(aName);13830 CheckComArgNotNull(aValue);13831 CheckComArgNotNull(aFlags);13832 13833 try13834 {13835 /*13836 * Convert input up front.13837 */13838 Utf8Str utf8Name(aName);13839 uint32_t fFlags = NILFLAG;13840 if (aFlags)13841 {13842 Utf8Str utf8Flags(aFlags);13843 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);13844 AssertRCReturn(vrc, E_INVALIDARG);13845 }13846 13847 /*13848 * Now grab the object lock, validate the state and do the update.13849 */13850 AutoCaller autoCaller(this);13851 if (FAILED(autoCaller.rc())) return autoCaller.rc();13852 13853 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);13854 13855 switch (mData->mMachineState)13856 {13857 case MachineState_Paused:13858 case MachineState_Running:13859 case MachineState_Teleporting:13860 case MachineState_TeleportingPausedVM:13861 case MachineState_LiveSnapshotting:13862 case MachineState_DeletingSnapshotOnline:13863 case MachineState_DeletingSnapshotPaused:13864 case MachineState_Saving:13865 case MachineState_Stopping:13866 break;13867 13868 default:13869 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),13870 VBOX_E_INVALID_VM_STATE);13871 }13872 13873 setModified(IsModified_MachineData);13874 mHWData.backup();13875 13876 bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0';13877 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name);13878 if (it != mHWData->mGuestProperties.end())13879 {13880 if (!fDelete)13881 {13882 it->second.strValue = aValue;13883 it->second.mTimestamp = aTimestamp;13884 it->second.mFlags = fFlags;13885 }13886 else13887 mHWData->mGuestProperties.erase(it);13888 13889 mData->mGuestPropertiesModified = TRUE;13890 }13891 else if (!fDelete)13892 {13893 HWData::GuestProperty prop;13894 prop.strValue = aValue;13895 prop.mTimestamp = aTimestamp;13896 prop.mFlags = fFlags;13897 13898 mHWData->mGuestProperties[utf8Name] = prop;13899 mData->mGuestPropertiesModified = TRUE;13900 }13901 13902 /*13903 * Send a callback notification if appropriate13904 */13905 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()13906 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),13907 RTSTR_MAX,13908 utf8Name.c_str(),13909 RTSTR_MAX, NULL)13910 )13911 {13912 alock.release();13913 13914 mParent->onGuestPropertyChange(mData->mUuid,13915 aName,13916 aValue,13917 aFlags);13918 }13919 }13920 catch (...)13921 {13922 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);13923 }13924 return S_OK;13925 #else13926 ReturnComNotImplemented();13927 #endif13928 }13929 13930 STDMETHODIMP SessionMachine::LockMedia()13931 {13932 AutoCaller autoCaller(this);13933 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13934 13935 AutoMultiWriteLock2 alock(this->lockHandle(),13936 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);13937 13938 AssertReturn( mData->mMachineState == MachineState_Starting13939 || mData->mMachineState == MachineState_Restoring13940 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);13941 13942 clearError();13943 alock.release();13944 return lockMedia();13945 }13946 13947 STDMETHODIMP SessionMachine::UnlockMedia()13948 {13949 unlockMedia();13950 return S_OK;13951 }13952 13953 STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,13954 IMediumAttachment **aNewAttachment)13955 {13956 CheckComArgNotNull(aAttachment);13957 CheckComArgOutPointerValid(aNewAttachment);13958 13959 AutoCaller autoCaller(this);13960 if (FAILED(autoCaller.rc())) return autoCaller.rc();13961 13962 // request the host lock first, since might be calling Host methods for getting host drives;13963 // next, protect the media tree all the while we're in here, as well as our member variables13964 AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),13965 this->lockHandle(),13966 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);13967 13968 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);13969 13970 Bstr ctrlName;13971 LONG lPort;13972 LONG lDevice;13973 bool fTempEject;13974 {13975 AutoCaller autoAttachCaller(this);13976 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();13977 13978 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);13979 13980 /* Need to query the details first, as the IMediumAttachment reference13981 * might be to the original settings, which we are going to change. */13982 ctrlName = pAttach->getControllerName();13983 lPort = pAttach->getPort();13984 lDevice = pAttach->getDevice();13985 fTempEject = pAttach->getTempEject();13986 }13987 13988 if (!fTempEject)13989 {13990 /* Remember previously mounted medium. The medium before taking the13991 * backup is not necessarily the same thing. */13992 ComObjPtr<Medium> oldmedium;13993 oldmedium = pAttach->getMedium();13994 13995 setModified(IsModified_Storage);13996 mMediaData.backup();13997 13998 // The backup operation makes the pAttach reference point to the13999 // old settings. Re-get the correct reference.14000 pAttach = findAttachment(mMediaData->mAttachments,14001 ctrlName.raw(),14002 lPort,14003 lDevice);14004 14005 {14006 AutoCaller autoAttachCaller(this);14007 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();14008 14009 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);14010 if (!oldmedium.isNull())14011 oldmedium->removeBackReference(mData->mUuid);14012 14013 pAttach->updateMedium(NULL);14014 pAttach->updateEjected();14015 }14016 14017 setModified(IsModified_Storage);14018 }14019 else14020 {14021 {14022 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);14023 pAttach->updateEjected();14024 }14025 }14026 14027 pAttach.queryInterfaceTo(aNewAttachment);14028 14029 return S_OK;14030 }14031 14032 // public methods only for internal purposes14033 /////////////////////////////////////////////////////////////////////////////14034 14035 /**14036 * Called from the client watcher thread to check for expected or unexpected14037 * death of the client process that has a direct session to this machine.14038 *14039 * On Win32 and on OS/2, this method is called only when we've got the14040 * mutex (i.e. the client has either died or terminated normally) so it always14041 * returns @c true (the client is terminated, the session machine is14042 * uninitialized).14043 *14044 * On other platforms, the method returns @c true if the client process has14045 * terminated normally or abnormally and the session machine was uninitialized,14046 * and @c false if the client process is still alive.14047 *14048 * @note Locks this object for writing.14049 */14050 bool SessionMachine::checkForDeath()14051 {14052 Uninit::Reason reason;14053 bool terminated = false;14054 14055 /* Enclose autoCaller with a block because calling uninit() from under it14056 * will deadlock. */14057 {14058 AutoCaller autoCaller(this);14059 if (!autoCaller.isOk())14060 {14061 /* return true if not ready, to cause the client watcher to exclude14062 * the corresponding session from watching */14063 LogFlowThisFunc(("Already uninitialized!\n"));14064 return true;14065 }14066 14067 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);14068 14069 /* Determine the reason of death: if the session state is Closing here,14070 * everything is fine. Otherwise it means that the client did not call14071 * OnSessionEnd() before it released the IPC semaphore. This may happen14072 * either because the client process has abnormally terminated, or14073 * because it simply forgot to call ISession::Close() before exiting. We14074 * threat the latter also as an abnormal termination (see14075 * Session::uninit() for details). */14076 reason = mData->mSession.mState == SessionState_Unlocking ?14077 Uninit::Normal :14078 Uninit::Abnormal;14079 14080 #if defined(RT_OS_WINDOWS)14081 14082 AssertMsg(mIPCSem, ("semaphore must be created"));14083 14084 /* release the IPC mutex */14085 ::ReleaseMutex(mIPCSem);14086 14087 terminated = true;14088 14089 #elif defined(RT_OS_OS2)14090 14091 AssertMsg(mIPCSem, ("semaphore must be created"));14092 14093 /* release the IPC mutex */14094 ::DosReleaseMutexSem(mIPCSem);14095 14096 terminated = true;14097 14098 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)14099 14100 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));14101 14102 int val = ::semctl(mIPCSem, 0, GETVAL);14103 if (val > 0)14104 {14105 /* the semaphore is signaled, meaning the session is terminated */14106 terminated = true;14107 }14108 14109 #else14110 # error "Port me!"14111 #endif14112 14113 } /* AutoCaller block */14114 14115 if (terminated)14116 uninit(reason);14117 14118 207 return terminated; 14119 208 } 14120 209 14121 /** 14122 * @note Locks this object for reading. 14123 */ 14124 HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter) 14125 { 14126 LogFlowThisFunc(("\n")); 14127 14128 AutoCaller autoCaller(this); 14129 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14130 14131 ComPtr<IInternalSessionControl> directControl; 14132 { 14133 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14134 directControl = mData->mSession.mDirectControl; 14135 } 14136 14137 /* ignore notifications sent after #OnSessionEnd() is called */ 14138 if (!directControl) 14139 return S_OK; 14140 14141 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter); 14142 } 14143 14144 /** 14145 * @note Locks this object for reading. 14146 */ 14147 HRESULT SessionMachine::onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName, 14148 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort) 14149 { 14150 LogFlowThisFunc(("\n")); 14151 14152 AutoCaller autoCaller(this); 14153 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14154 14155 ComPtr<IInternalSessionControl> directControl; 14156 { 14157 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14158 directControl = mData->mSession.mDirectControl; 14159 } 14160 14161 /* ignore notifications sent after #OnSessionEnd() is called */ 14162 if (!directControl) 14163 return S_OK; 14164 /* 14165 * instead acting like callback we ask IVirtualBox deliver corresponding event 14166 */ 14167 14168 mParent->onNatRedirectChange(getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp, (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort); 14169 return S_OK; 14170 } 14171 14172 /** 14173 * @note Locks this object for reading. 14174 */ 14175 HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort) 14176 { 14177 LogFlowThisFunc(("\n")); 14178 14179 AutoCaller autoCaller(this); 14180 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14181 14182 ComPtr<IInternalSessionControl> directControl; 14183 { 14184 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14185 directControl = mData->mSession.mDirectControl; 14186 } 14187 14188 /* ignore notifications sent after #OnSessionEnd() is called */ 14189 if (!directControl) 14190 return S_OK; 14191 14192 return directControl->OnSerialPortChange(serialPort); 14193 } 14194 14195 /** 14196 * @note Locks this object for reading. 14197 */ 14198 HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort) 14199 { 14200 LogFlowThisFunc(("\n")); 14201 14202 AutoCaller autoCaller(this); 14203 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14204 14205 ComPtr<IInternalSessionControl> directControl; 14206 { 14207 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14208 directControl = mData->mSession.mDirectControl; 14209 } 14210 14211 /* ignore notifications sent after #OnSessionEnd() is called */ 14212 if (!directControl) 14213 return S_OK; 14214 14215 return directControl->OnParallelPortChange(parallelPort); 14216 } 14217 14218 /** 14219 * @note Locks this object for reading. 14220 */ 14221 HRESULT SessionMachine::onStorageControllerChange() 14222 { 14223 LogFlowThisFunc(("\n")); 14224 14225 AutoCaller autoCaller(this); 14226 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14227 14228 ComPtr<IInternalSessionControl> directControl; 14229 { 14230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14231 directControl = mData->mSession.mDirectControl; 14232 } 14233 14234 /* ignore notifications sent after #OnSessionEnd() is called */ 14235 if (!directControl) 14236 return S_OK; 14237 14238 return directControl->OnStorageControllerChange(); 14239 } 14240 14241 /** 14242 * @note Locks this object for reading. 14243 */ 14244 HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce) 14245 { 14246 LogFlowThisFunc(("\n")); 14247 14248 AutoCaller autoCaller(this); 14249 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14250 14251 ComPtr<IInternalSessionControl> directControl; 14252 { 14253 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14254 directControl = mData->mSession.mDirectControl; 14255 } 14256 14257 /* ignore notifications sent after #OnSessionEnd() is called */ 14258 if (!directControl) 14259 return S_OK; 14260 14261 return directControl->OnMediumChange(aAttachment, aForce); 14262 } 14263 14264 /** 14265 * @note Locks this object for reading. 14266 */ 14267 HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove) 14268 { 14269 LogFlowThisFunc(("\n")); 14270 14271 AutoCaller autoCaller(this); 14272 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14273 14274 ComPtr<IInternalSessionControl> directControl; 14275 { 14276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14277 directControl = mData->mSession.mDirectControl; 14278 } 14279 14280 /* ignore notifications sent after #OnSessionEnd() is called */ 14281 if (!directControl) 14282 return S_OK; 14283 14284 return directControl->OnCPUChange(aCPU, aRemove); 14285 } 14286 14287 HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap) 14288 { 14289 LogFlowThisFunc(("\n")); 14290 14291 AutoCaller autoCaller(this); 14292 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14293 14294 ComPtr<IInternalSessionControl> directControl; 14295 { 14296 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14297 directControl = mData->mSession.mDirectControl; 14298 } 14299 14300 /* ignore notifications sent after #OnSessionEnd() is called */ 14301 if (!directControl) 14302 return S_OK; 14303 14304 return directControl->OnCPUExecutionCapChange(aExecutionCap); 14305 } 14306 14307 /** 14308 * @note Locks this object for reading. 14309 */ 14310 HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart) 14311 { 14312 LogFlowThisFunc(("\n")); 14313 14314 AutoCaller autoCaller(this); 14315 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14316 14317 ComPtr<IInternalSessionControl> directControl; 14318 { 14319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14320 directControl = mData->mSession.mDirectControl; 14321 } 14322 14323 /* ignore notifications sent after #OnSessionEnd() is called */ 14324 if (!directControl) 14325 return S_OK; 14326 14327 return directControl->OnVRDEServerChange(aRestart); 14328 } 14329 14330 /** 14331 * @note Locks this object for reading. 14332 */ 14333 HRESULT SessionMachine::onVideoCaptureChange() 14334 { 14335 LogFlowThisFunc(("\n")); 14336 14337 AutoCaller autoCaller(this); 14338 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14339 14340 ComPtr<IInternalSessionControl> directControl; 14341 { 14342 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14343 directControl = mData->mSession.mDirectControl; 14344 } 14345 14346 /* ignore notifications sent after #OnSessionEnd() is called */ 14347 if (!directControl) 14348 return S_OK; 14349 14350 return directControl->OnVideoCaptureChange(); 14351 } 14352 14353 /** 14354 * @note Locks this object for reading. 14355 */ 14356 HRESULT SessionMachine::onUSBControllerChange() 14357 { 14358 LogFlowThisFunc(("\n")); 14359 14360 AutoCaller autoCaller(this); 14361 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14362 14363 ComPtr<IInternalSessionControl> directControl; 14364 { 14365 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14366 directControl = mData->mSession.mDirectControl; 14367 } 14368 14369 /* ignore notifications sent after #OnSessionEnd() is called */ 14370 if (!directControl) 14371 return S_OK; 14372 14373 return directControl->OnUSBControllerChange(); 14374 } 14375 14376 /** 14377 * @note Locks this object for reading. 14378 */ 14379 HRESULT SessionMachine::onSharedFolderChange() 14380 { 14381 LogFlowThisFunc(("\n")); 14382 14383 AutoCaller autoCaller(this); 14384 AssertComRCReturnRC(autoCaller.rc()); 14385 14386 ComPtr<IInternalSessionControl> directControl; 14387 { 14388 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14389 directControl = mData->mSession.mDirectControl; 14390 } 14391 14392 /* ignore notifications sent after #OnSessionEnd() is called */ 14393 if (!directControl) 14394 return S_OK; 14395 14396 return directControl->OnSharedFolderChange(FALSE /* aGlobal */); 14397 } 14398 14399 /** 14400 * @note Locks this object for reading. 14401 */ 14402 HRESULT SessionMachine::onClipboardModeChange(ClipboardMode_T aClipboardMode) 14403 { 14404 LogFlowThisFunc(("\n")); 14405 14406 AutoCaller autoCaller(this); 14407 AssertComRCReturnRC(autoCaller.rc()); 14408 14409 ComPtr<IInternalSessionControl> directControl; 14410 { 14411 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14412 directControl = mData->mSession.mDirectControl; 14413 } 14414 14415 /* ignore notifications sent after #OnSessionEnd() is called */ 14416 if (!directControl) 14417 return S_OK; 14418 14419 return directControl->OnClipboardModeChange(aClipboardMode); 14420 } 14421 14422 /** 14423 * @note Locks this object for reading. 14424 */ 14425 HRESULT SessionMachine::onDragAndDropModeChange(DragAndDropMode_T aDragAndDropMode) 14426 { 14427 LogFlowThisFunc(("\n")); 14428 14429 AutoCaller autoCaller(this); 14430 AssertComRCReturnRC(autoCaller.rc()); 14431 14432 ComPtr<IInternalSessionControl> directControl; 14433 { 14434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14435 directControl = mData->mSession.mDirectControl; 14436 } 14437 14438 /* ignore notifications sent after #OnSessionEnd() is called */ 14439 if (!directControl) 14440 return S_OK; 14441 14442 return directControl->OnDragAndDropModeChange(aDragAndDropMode); 14443 } 14444 14445 /** 14446 * @note Locks this object for reading. 14447 */ 14448 HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) 14449 { 14450 LogFlowThisFunc(("\n")); 14451 14452 AutoCaller autoCaller(this); 14453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14454 14455 ComPtr<IInternalSessionControl> directControl; 14456 { 14457 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14458 directControl = mData->mSession.mDirectControl; 14459 } 14460 14461 /* ignore notifications sent after #OnSessionEnd() is called */ 14462 if (!directControl) 14463 return S_OK; 14464 14465 return directControl->OnBandwidthGroupChange(aBandwidthGroup); 14466 } 14467 14468 /** 14469 * @note Locks this object for reading. 14470 */ 14471 HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent) 14472 { 14473 LogFlowThisFunc(("\n")); 14474 14475 AutoCaller autoCaller(this); 14476 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14477 14478 ComPtr<IInternalSessionControl> directControl; 14479 { 14480 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14481 directControl = mData->mSession.mDirectControl; 14482 } 14483 14484 /* ignore notifications sent after #OnSessionEnd() is called */ 14485 if (!directControl) 14486 return S_OK; 14487 14488 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent); 14489 } 14490 14491 /** 14492 * Returns @c true if this machine's USB controller reports it has a matching 14493 * filter for the given USB device and @c false otherwise. 14494 * 14495 * @note locks this object for reading. 14496 */ 14497 bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs) 14498 { 14499 AutoCaller autoCaller(this); 14500 /* silently return if not ready -- this method may be called after the 14501 * direct machine session has been called */ 14502 if (!autoCaller.isOk()) 14503 return false; 14504 14505 #ifdef VBOX_WITH_USB 14506 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14507 14508 switch (mData->mMachineState) 14509 { 14510 case MachineState_Starting: 14511 case MachineState_Restoring: 14512 case MachineState_TeleportingIn: 14513 case MachineState_Paused: 14514 case MachineState_Running: 14515 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of 14516 * elsewhere... */ 14517 alock.release(); 14518 return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs); 14519 default: break; 14520 } 14521 #else 14522 NOREF(aDevice); 14523 NOREF(aMaskedIfs); 14524 #endif 14525 return false; 14526 } 14527 14528 /** 14529 * @note The calls shall hold no locks. Will temporarily lock this object for reading. 14530 */ 14531 HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice, 14532 IVirtualBoxErrorInfo *aError, 14533 ULONG aMaskedIfs) 14534 { 14535 LogFlowThisFunc(("\n")); 14536 14537 AutoCaller autoCaller(this); 14538 14539 /* This notification may happen after the machine object has been 14540 * uninitialized (the session was closed), so don't assert. */ 14541 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 14542 14543 ComPtr<IInternalSessionControl> directControl; 14544 { 14545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14546 directControl = mData->mSession.mDirectControl; 14547 } 14548 14549 /* fail on notifications sent after #OnSessionEnd() is called, it is 14550 * expected by the caller */ 14551 if (!directControl) 14552 return E_FAIL; 14553 14554 /* No locks should be held at this point. */ 14555 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf()))); 14556 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf()))); 14557 14558 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs); 14559 } 14560 14561 /** 14562 * @note The calls shall hold no locks. Will temporarily lock this object for reading. 14563 */ 14564 HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId, 14565 IVirtualBoxErrorInfo *aError) 14566 { 14567 LogFlowThisFunc(("\n")); 14568 14569 AutoCaller autoCaller(this); 14570 14571 /* This notification may happen after the machine object has been 14572 * uninitialized (the session was closed), so don't assert. */ 14573 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 14574 14575 ComPtr<IInternalSessionControl> directControl; 14576 { 14577 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 14578 directControl = mData->mSession.mDirectControl; 14579 } 14580 14581 /* fail on notifications sent after #OnSessionEnd() is called, it is 14582 * expected by the caller */ 14583 if (!directControl) 14584 return E_FAIL; 14585 14586 /* No locks should be held at this point. */ 14587 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf()))); 14588 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf()))); 14589 14590 return directControl->OnUSBDeviceDetach(aId, aError); 14591 } 14592 14593 // protected methods 14594 ///////////////////////////////////////////////////////////////////////////// 14595 14596 /** 14597 * Helper method to finalize saving the state. 14598 * 14599 * @note Must be called from under this object's lock. 14600 * 14601 * @param aRc S_OK if the snapshot has been taken successfully 14602 * @param aErrMsg human readable error message for failure 14603 * 14604 * @note Locks mParent + this objects for writing. 14605 */ 14606 HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg) 14607 { 14608 LogFlowThisFuncEnter(); 14609 14610 AutoCaller autoCaller(this); 14611 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14612 14613 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 14614 14615 HRESULT rc = S_OK; 14616 14617 if (SUCCEEDED(aRc)) 14618 { 14619 mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath; 14620 14621 /* save all VM settings */ 14622 rc = saveSettings(NULL); 14623 // no need to check whether VirtualBox.xml needs saving also since 14624 // we can't have a name change pending at this point 14625 } 14626 else 14627 { 14628 // delete the saved state file (it might have been already created); 14629 // we need not check whether this is shared with a snapshot here because 14630 // we certainly created this saved state file here anew 14631 RTFileDelete(mConsoleTaskData.strStateFilePath.c_str()); 14632 } 14633 14634 /* notify the progress object about operation completion */ 14635 Assert(mConsoleTaskData.mProgress); 14636 if (SUCCEEDED(aRc)) 14637 mConsoleTaskData.mProgress->notifyComplete(S_OK); 14638 else 14639 { 14640 if (aErrMsg.length()) 14641 mConsoleTaskData.mProgress->notifyComplete(aRc, 14642 COM_IIDOF(ISession), 14643 getComponentName(), 14644 aErrMsg.c_str()); 14645 else 14646 mConsoleTaskData.mProgress->notifyComplete(aRc); 14647 } 14648 14649 /* clear out the temporary saved state data */ 14650 mConsoleTaskData.mLastState = MachineState_Null; 14651 mConsoleTaskData.strStateFilePath.setNull(); 14652 mConsoleTaskData.mProgress.setNull(); 14653 14654 LogFlowThisFuncLeave(); 14655 return rc; 14656 } 14657 14658 /** 14659 * Deletes the given file if it is no longer in use by either the current machine state 14660 * (if the machine is "saved") or any of the machine's snapshots. 14661 * 14662 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine 14663 * but is different for each SnapshotMachine. When calling this, the order of calling this 14664 * function on the one hand and changing that variable OR the snapshots tree on the other hand 14665 * is therefore critical. I know, it's all rather messy. 14666 * 14667 * @param strStateFile 14668 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use. 14669 */ 14670 void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile, 14671 Snapshot *pSnapshotToIgnore) 14672 { 14673 // it is safe to delete this saved state file if it is not currently in use by the machine ... 14674 if ( (strStateFile.isNotEmpty()) 14675 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state 14676 ) 14677 // ... and it must also not be shared with other snapshots 14678 if ( !mData->mFirstSnapshot 14679 || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore) 14680 // this checks the SnapshotMachine's state file paths 14681 ) 14682 RTFileDelete(strStateFile.c_str()); 14683 } 14684 14685 /** 14686 * Locks the attached media. 14687 * 14688 * All attached hard disks are locked for writing and DVD/floppy are locked for 14689 * reading. Parents of attached hard disks (if any) are locked for reading. 14690 * 14691 * This method also performs accessibility check of all media it locks: if some 14692 * media is inaccessible, the method will return a failure and a bunch of 14693 * extended error info objects per each inaccessible medium. 14694 * 14695 * Note that this method is atomic: if it returns a success, all media are 14696 * locked as described above; on failure no media is locked at all (all 14697 * succeeded individual locks will be undone). 14698 * 14699 * The caller is responsible for doing the necessary state sanity checks. 14700 * 14701 * The locks made by this method must be undone by calling #unlockMedia() when 14702 * no more needed. 14703 */ 14704 HRESULT SessionMachine::lockMedia() 14705 { 14706 AutoCaller autoCaller(this); 14707 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14708 14709 AutoMultiWriteLock2 alock(this->lockHandle(), 14710 &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 14711 14712 /* bail out if trying to lock things with already set up locking */ 14713 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL); 14714 14715 MultiResult mrc(S_OK); 14716 14717 /* Collect locking information for all medium objects attached to the VM. */ 14718 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 14719 it != mMediaData->mAttachments.end(); 14720 ++it) 14721 { 14722 MediumAttachment* pAtt = *it; 14723 DeviceType_T devType = pAtt->getType(); 14724 Medium *pMedium = pAtt->getMedium(); 14725 14726 MediumLockList *pMediumLockList(new MediumLockList()); 14727 // There can be attachments without a medium (floppy/dvd), and thus 14728 // it's impossible to create a medium lock list. It still makes sense 14729 // to have the empty medium lock list in the map in case a medium is 14730 // attached later. 14731 if (pMedium != NULL) 14732 { 14733 MediumType_T mediumType = pMedium->getType(); 14734 bool fIsReadOnlyLock = mediumType == MediumType_Readonly 14735 || mediumType == MediumType_Shareable; 14736 bool fIsVitalImage = (devType == DeviceType_HardDisk); 14737 14738 alock.release(); 14739 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */, 14740 !fIsReadOnlyLock /* fMediumLockWrite */, 14741 NULL, 14742 *pMediumLockList); 14743 alock.acquire(); 14744 if (FAILED(mrc)) 14745 { 14746 delete pMediumLockList; 14747 mData->mSession.mLockedMedia.Clear(); 14748 break; 14749 } 14750 } 14751 14752 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList); 14753 if (FAILED(rc)) 14754 { 14755 mData->mSession.mLockedMedia.Clear(); 14756 mrc = setError(rc, 14757 tr("Collecting locking information for all attached media failed")); 14758 break; 14759 } 14760 } 14761 14762 if (SUCCEEDED(mrc)) 14763 { 14764 /* Now lock all media. If this fails, nothing is locked. */ 14765 alock.release(); 14766 HRESULT rc = mData->mSession.mLockedMedia.Lock(); 14767 alock.acquire(); 14768 if (FAILED(rc)) 14769 { 14770 mrc = setError(rc, 14771 tr("Locking of attached media failed")); 14772 } 14773 } 14774 14775 return mrc; 14776 } 14777 14778 /** 14779 * Undoes the locks made by by #lockMedia(). 14780 */ 14781 void SessionMachine::unlockMedia() 14782 { 14783 AutoCaller autoCaller(this); 14784 AssertComRCReturnVoid(autoCaller.rc()); 14785 14786 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 14787 14788 /* we may be holding important error info on the current thread; 14789 * preserve it */ 14790 ErrorInfoKeeper eik; 14791 14792 HRESULT rc = mData->mSession.mLockedMedia.Clear(); 14793 AssertComRC(rc); 14794 } 14795 14796 /** 14797 * Helper to change the machine state (reimplementation). 14798 * 14799 * @note Locks this object for writing. 14800 * @note This method must not call saveSettings or SaveSettings, otherwise 14801 * it can cause crashes in random places due to unexpectedly committing 14802 * the current settings. The caller is responsible for that. The call 14803 * to saveStateSettings is fine, because this method does not commit. 14804 */ 14805 HRESULT SessionMachine::setMachineState(MachineState_T aMachineState) 14806 { 14807 LogFlowThisFuncEnter(); 14808 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) )); 14809 14810 AutoCaller autoCaller(this); 14811 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 14812 14813 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 14814 14815 MachineState_T oldMachineState = mData->mMachineState; 14816 14817 AssertMsgReturn(oldMachineState != aMachineState, 14818 ("oldMachineState=%s, aMachineState=%s\n", 14819 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)), 14820 E_FAIL); 14821 14822 HRESULT rc = S_OK; 14823 14824 int stsFlags = 0; 14825 bool deleteSavedState = false; 14826 14827 /* detect some state transitions */ 14828 14829 if ( ( oldMachineState == MachineState_Saved 14830 && aMachineState == MachineState_Restoring) 14831 || ( ( oldMachineState == MachineState_PoweredOff 14832 || oldMachineState == MachineState_Teleported 14833 || oldMachineState == MachineState_Aborted 14834 ) 14835 && ( aMachineState == MachineState_TeleportingIn 14836 || aMachineState == MachineState_Starting 14837 ) 14838 ) 14839 ) 14840 { 14841 /* The EMT thread is about to start */ 14842 14843 /* Nothing to do here for now... */ 14844 14845 /// @todo NEWMEDIA don't let mDVDDrive and other children 14846 /// change anything when in the Starting/Restoring state 14847 } 14848 else if ( ( oldMachineState == MachineState_Running 14849 || oldMachineState == MachineState_Paused 14850 || oldMachineState == MachineState_Teleporting 14851 || oldMachineState == MachineState_LiveSnapshotting 14852 || oldMachineState == MachineState_Stuck 14853 || oldMachineState == MachineState_Starting 14854 || oldMachineState == MachineState_Stopping 14855 || oldMachineState == MachineState_Saving 14856 || oldMachineState == MachineState_Restoring 14857 || oldMachineState == MachineState_TeleportingPausedVM 14858 || oldMachineState == MachineState_TeleportingIn 14859 ) 14860 && ( aMachineState == MachineState_PoweredOff 14861 || aMachineState == MachineState_Saved 14862 || aMachineState == MachineState_Teleported 14863 || aMachineState == MachineState_Aborted 14864 ) 14865 /* ignore PoweredOff->Saving->PoweredOff transition when taking a 14866 * snapshot */ 14867 && ( mConsoleTaskData.mSnapshot.isNull() 14868 || mConsoleTaskData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */ 14869 ) 14870 ) 14871 { 14872 /* The EMT thread has just stopped, unlock attached media. Note that as 14873 * opposed to locking that is done from Console, we do unlocking here 14874 * because the VM process may have aborted before having a chance to 14875 * properly unlock all media it locked. */ 14876 14877 unlockMedia(); 14878 } 14879 14880 if (oldMachineState == MachineState_Restoring) 14881 { 14882 if (aMachineState != MachineState_Saved) 14883 { 14884 /* 14885 * delete the saved state file once the machine has finished 14886 * restoring from it (note that Console sets the state from 14887 * Restoring to Saved if the VM couldn't restore successfully, 14888 * to give the user an ability to fix an error and retry -- 14889 * we keep the saved state file in this case) 14890 */ 14891 deleteSavedState = true; 14892 } 14893 } 14894 else if ( oldMachineState == MachineState_Saved 14895 && ( aMachineState == MachineState_PoweredOff 14896 || aMachineState == MachineState_Aborted 14897 || aMachineState == MachineState_Teleported 14898 ) 14899 ) 14900 { 14901 /* 14902 * delete the saved state after Console::ForgetSavedState() is called 14903 * or if the VM process (owning a direct VM session) crashed while the 14904 * VM was Saved 14905 */ 14906 14907 /// @todo (dmik) 14908 // Not sure that deleting the saved state file just because of the 14909 // client death before it attempted to restore the VM is a good 14910 // thing. But when it crashes we need to go to the Aborted state 14911 // which cannot have the saved state file associated... The only 14912 // way to fix this is to make the Aborted condition not a VM state 14913 // but a bool flag: i.e., when a crash occurs, set it to true and 14914 // change the state to PoweredOff or Saved depending on the 14915 // saved state presence. 14916 14917 deleteSavedState = true; 14918 mData->mCurrentStateModified = TRUE; 14919 stsFlags |= SaveSTS_CurStateModified; 14920 } 14921 14922 if ( aMachineState == MachineState_Starting 14923 || aMachineState == MachineState_Restoring 14924 || aMachineState == MachineState_TeleportingIn 14925 ) 14926 { 14927 /* set the current state modified flag to indicate that the current 14928 * state is no more identical to the state in the 14929 * current snapshot */ 14930 if (!mData->mCurrentSnapshot.isNull()) 14931 { 14932 mData->mCurrentStateModified = TRUE; 14933 stsFlags |= SaveSTS_CurStateModified; 14934 } 14935 } 14936 14937 if (deleteSavedState) 14938 { 14939 if (mRemoveSavedState) 14940 { 14941 Assert(!mSSData->strStateFilePath.isEmpty()); 14942 14943 // it is safe to delete the saved state file if ... 14944 if ( !mData->mFirstSnapshot // ... we have no snapshots or 14945 || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */) 14946 // ... none of the snapshots share the saved state file 14947 ) 14948 RTFileDelete(mSSData->strStateFilePath.c_str()); 14949 } 14950 14951 mSSData->strStateFilePath.setNull(); 14952 stsFlags |= SaveSTS_StateFilePath; 14953 } 14954 14955 /* redirect to the underlying peer machine */ 14956 mPeer->setMachineState(aMachineState); 14957 14958 if ( aMachineState == MachineState_PoweredOff 14959 || aMachineState == MachineState_Teleported 14960 || aMachineState == MachineState_Aborted 14961 || aMachineState == MachineState_Saved) 14962 { 14963 /* the machine has stopped execution 14964 * (or the saved state file was adopted) */ 14965 stsFlags |= SaveSTS_StateTimeStamp; 14966 } 14967 14968 if ( ( oldMachineState == MachineState_PoweredOff 14969 || oldMachineState == MachineState_Aborted 14970 || oldMachineState == MachineState_Teleported 14971 ) 14972 && aMachineState == MachineState_Saved) 14973 { 14974 /* the saved state file was adopted */ 14975 Assert(!mSSData->strStateFilePath.isEmpty()); 14976 stsFlags |= SaveSTS_StateFilePath; 14977 } 14978 14979 #ifdef VBOX_WITH_GUEST_PROPS 14980 if ( aMachineState == MachineState_PoweredOff 14981 || aMachineState == MachineState_Aborted 14982 || aMachineState == MachineState_Teleported) 14983 { 14984 /* Make sure any transient guest properties get removed from the 14985 * property store on shutdown. */ 14986 14987 HWData::GuestPropertyMap::const_iterator it; 14988 BOOL fNeedsSaving = mData->mGuestPropertiesModified; 14989 if (!fNeedsSaving) 14990 for (it = mHWData->mGuestProperties.begin(); 14991 it != mHWData->mGuestProperties.end(); ++it) 14992 if ( (it->second.mFlags & guestProp::TRANSIENT) 14993 || (it->second.mFlags & guestProp::TRANSRESET)) 14994 { 14995 fNeedsSaving = true; 14996 break; 14997 } 14998 if (fNeedsSaving) 14999 { 15000 mData->mCurrentStateModified = TRUE; 15001 stsFlags |= SaveSTS_CurStateModified; 15002 } 15003 } 15004 #endif 15005 15006 rc = saveStateSettings(stsFlags); 15007 15008 if ( ( oldMachineState != MachineState_PoweredOff 15009 && oldMachineState != MachineState_Aborted 15010 && oldMachineState != MachineState_Teleported 15011 ) 15012 && ( aMachineState == MachineState_PoweredOff 15013 || aMachineState == MachineState_Aborted 15014 || aMachineState == MachineState_Teleported 15015 ) 15016 ) 15017 { 15018 /* we've been shut down for any reason */ 15019 /* no special action so far */ 15020 } 15021 15022 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) )); 15023 LogFlowThisFuncLeave(); 15024 return rc; 15025 } 15026 15027 /** 15028 * Sends the current machine state value to the VM process. 15029 * 15030 * @note Locks this object for reading, then calls a client process. 15031 */ 15032 HRESULT SessionMachine::updateMachineStateOnClient() 15033 { 15034 AutoCaller autoCaller(this); 15035 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 15036 15037 ComPtr<IInternalSessionControl> directControl; 15038 { 15039 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 15040 AssertReturn(!!mData, E_FAIL); 15041 directControl = mData->mSession.mDirectControl; 15042 15043 /* directControl may be already set to NULL here in #OnSessionEnd() 15044 * called too early by the direct session process while there is still 15045 * some operation (like deleting the snapshot) in progress. The client 15046 * process in this case is waiting inside Session::close() for the 15047 * "end session" process object to complete, while #uninit() called by 15048 * #checkForDeath() on the Watcher thread is waiting for the pending 15049 * operation to complete. For now, we accept this inconsistent behavior 15050 * and simply do nothing here. */ 15051 15052 if (mData->mSession.mState == SessionState_Unlocking) 15053 return S_OK; 15054 15055 AssertReturn(!directControl.isNull(), E_FAIL); 15056 } 15057 15058 return directControl->UpdateMachineState(mData->mMachineState); 15059 } 210 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/src-server/ClientWatcher.cpp
r47555 r47561 1 /* $Id$ */2 1 /** @file 3 * Implementation of IVirtualBox in VBoxSVC. 2 * 3 * VirtualBox API client crash watcher 4 4 */ 5 5 … … 17 17 18 18 #include <iprt/asm.h> 19 #include <iprt/base64.h> 20 #include <iprt/buildconfig.h> 21 #include <iprt/cpp/utils.h> 22 #include <iprt/dir.h> 23 #include <iprt/env.h> 24 #include <iprt/file.h> 25 #include <iprt/path.h> 19 #include <iprt/assert.h> 20 #include <iprt/log.h> 21 #include <iprt/semaphore.h> 26 22 #include <iprt/process.h> 27 #include <iprt/rand.h> 28 #include <iprt/sha.h> 29 #include <iprt/string.h> 30 #include <iprt/stream.h> 31 #include <iprt/thread.h> 32 #include <iprt/uuid.h> 33 #include <iprt/cpp/xml.h> 34 35 #include <VBox/com/com.h> 36 #include <VBox/com/array.h> 37 #include "VBox/com/EventQueue.h" 38 39 #include <VBox/err.h> 40 #include <VBox/param.h> 41 #include <VBox/settings.h> 42 #include <VBox/version.h> 43 44 #include <package-generated.h> 45 46 #include <algorithm> 47 #include <set> 23 24 #include <VBox/com/defs.h> 25 48 26 #include <vector> 49 #include <memory> // for auto_ptr 50 27 28 #include "VirtualBoxBase.h" 29 #include "AutoCaller.h" 30 #include "ClientWatcher.h" 31 #include "ClientToken.h" 51 32 #include "VirtualBoxImpl.h" 52 53 #include "Global.h"54 33 #include "MachineImpl.h" 55 #include "MediumImpl.h"56 #include "SharedFolderImpl.h"57 #include "ProgressImpl.h"58 #include "ProgressProxyImpl.h"59 #include "HostImpl.h"60 #include "USBControllerImpl.h"61 #include "SystemPropertiesImpl.h"62 #include "GuestOSTypeImpl.h"63 #include "NetworkServiceRunner.h"64 #include "DHCPServerImpl.h"65 #include "NATNetworkImpl.h"66 #ifdef VBOX_WITH_RESOURCE_USAGE_API67 # include "PerformanceImpl.h"68 #endif /* VBOX_WITH_RESOURCE_USAGE_API */69 #include "EventImpl.h"70 #include "VBoxEvents.h"71 #ifdef VBOX_WITH_EXTPACK72 # include "ExtPackManagerImpl.h"73 #endif74 #include "AutostartDb.h"75 76 #include "AutoCaller.h"77 #include "Logging.h"78 #include "objectslist.h"79 80 #ifdef RT_OS_WINDOWS81 # include "win/svchlp.h"82 # include "win/VBoxComEvents.h"83 #endif84 85 ////////////////////////////////////////////////////////////////////////////////86 //87 // Definitions88 //89 ////////////////////////////////////////////////////////////////////////////////90 91 #define VBOX_GLOBAL_SETTINGS_FILE "VirtualBox.xml"92 93 ////////////////////////////////////////////////////////////////////////////////94 //95 // Global variables96 //97 ////////////////////////////////////////////////////////////////////////////////98 99 // static100 Bstr VirtualBox::sVersion;101 102 // static103 Bstr VirtualBox::sVersionNormalized;104 105 // static106 ULONG VirtualBox::sRevision;107 108 // static109 Bstr VirtualBox::sPackageType;110 111 // static112 Bstr VirtualBox::sAPIVersion;113 34 114 35 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER 115 /** Table for adaptive timeouts in the client watcher. The counter starts at 116 * the maximum value and decreases to 0. */ 117 static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 }; 118 #endif 119 120 //////////////////////////////////////////////////////////////////////////////// 121 // 122 // CallbackEvent class 123 // 124 //////////////////////////////////////////////////////////////////////////////// 125 126 /** 127 * Abstract callback event class to asynchronously call VirtualBox callbacks 128 * on a dedicated event thread. Subclasses reimplement #handleCallback() 129 * to call appropriate IVirtualBoxCallback methods depending on the event 130 * to be dispatched. 131 * 132 * @note The VirtualBox instance passed to the constructor is strongly 133 * referenced, so that the VirtualBox singleton won't be released until the 134 * event gets handled by the event thread. 135 */ 136 class VirtualBox::CallbackEvent : public Event 36 /** Table for adaptive timeouts. After an update the counter starts at the 37 * maximum value and decreases to 0, i.e. first the short timeouts are used 38 * and then the longer ones. This minimizes the detection latency in the 39 * cases where a change is expected, for crashes. */ 40 static const RTMSINTERVAL s_aUpdateTimeoutSteps[] = { 500, 200, 100, 50, 20, 10, 5 }; 41 #endif 42 43 44 45 VirtualBox::ClientWatcher::ClientWatcher() : 46 mLock(LOCKCLASS_OBJECTSTATE) 137 47 { 138 public: 139 140 CallbackEvent(VirtualBox *aVirtualBox, VBoxEventType_T aWhat) 141 : mVirtualBox(aVirtualBox), mWhat(aWhat) 48 AssertReleaseFailed(); 49 } 50 51 VirtualBox::ClientWatcher::~ClientWatcher() 52 { 53 if (mThread != NIL_RTTHREAD) 142 54 { 143 Assert(aVirtualBox); 55 /* signal the client watcher thread, should be exiting now */ 56 update(); 57 /* wait for termination */ 58 RTThreadWait(mThread, RT_INDEFINITE_WAIT, NULL); 59 mThread = NIL_RTTHREAD; 144 60 } 145 146 void *handler(); 147 148 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) = 0; 149 150 private: 151 152 /** 153 * Note that this is a weak ref -- the CallbackEvent handler thread 154 * is bound to the lifetime of the VirtualBox instance, so it's safe. 155 */ 156 VirtualBox *mVirtualBox; 157 protected: 158 VBoxEventType_T mWhat; 159 }; 160 161 //////////////////////////////////////////////////////////////////////////////// 162 // 163 // VirtualBox private member data definition 164 // 165 //////////////////////////////////////////////////////////////////////////////// 166 61 mProcesses.clear(); 167 62 #if defined(RT_OS_WINDOWS) 168 #define UPDATEREQARG NULL 169 #define UPDATEREQTYPE HANDLE 170 #elif defined(RT_OS_OS2) 171 #define UPDATEREQARG NIL_RTSEMEVENT 172 #define UPDATEREQTYPE RTSEMEVENT 173 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 174 #define UPDATEREQARG 175 #define UPDATEREQTYPE RTSEMEVENT 176 #else 177 # error "Port me!" 178 #endif 179 180 typedef ObjectsList<Machine> MachinesOList; 181 typedef ObjectsList<Medium> MediaOList; 182 typedef ObjectsList<GuestOSType> GuestOSTypesOList; 183 typedef ObjectsList<SharedFolder> SharedFoldersOList; 184 typedef ObjectsList<DHCPServer> DHCPServersOList; 185 typedef ObjectsList<NATNetwork> NATNetworksOList; 186 187 typedef std::map<Guid, ComPtr<IProgress> > ProgressMap; 188 typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap; 189 190 /** 191 * Main VirtualBox data structure. 192 * @note |const| members are persistent during lifetime so can be accessed 193 * without locking. 194 */ 195 struct VirtualBox::Data 196 { 197 Data() 198 : pMainConfigFile(NULL), 199 uuidMediaRegistry("48024e5c-fdd9-470f-93af-ec29f7ea518c"), 200 uRegistryNeedsSaving(0), 201 lockMachines(LOCKCLASS_LISTOFMACHINES), 202 allMachines(lockMachines), 203 lockGuestOSTypes(LOCKCLASS_LISTOFOTHEROBJECTS), 204 allGuestOSTypes(lockGuestOSTypes), 205 lockMedia(LOCKCLASS_LISTOFMEDIA), 206 allHardDisks(lockMedia), 207 allDVDImages(lockMedia), 208 allFloppyImages(lockMedia), 209 lockSharedFolders(LOCKCLASS_LISTOFOTHEROBJECTS), 210 allSharedFolders(lockSharedFolders), 211 lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS), 212 allDHCPServers(lockDHCPServers), 213 lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS), 214 allNATNetworks(lockNATNetworks), 215 mtxProgressOperations(LOCKCLASS_PROGRESSLIST), 216 updateReq(UPDATEREQARG), 217 threadClientWatcher(NIL_RTTHREAD), 218 threadAsyncEvent(NIL_RTTHREAD), 219 pAsyncEventQ(NULL), 220 pAutostartDb(NULL), 221 fSettingsCipherKeySet(false) 63 if (mUpdateReq != NULL) 222 64 { 65 ::CloseHandle(mUpdateReq); 66 mUpdateReq = NULL; 223 67 } 224 225 ~Data()68 #elif defined(RT_OS_OS2) || defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 69 if (mUpdateReq != NIL_RTSEMEVENT) 226 70 { 227 if (pMainConfigFile) 228 { 229 delete pMainConfigFile; 230 pMainConfigFile = NULL; 231 } 232 }; 233 234 // const data members not requiring locking 235 const Utf8Str strHomeDir; 236 237 // VirtualBox main settings file 238 const Utf8Str strSettingsFilePath; 239 settings::MainConfigFile *pMainConfigFile; 240 241 // constant pseudo-machine ID for global media registry 242 const Guid uuidMediaRegistry; 243 244 // counter if global media registry needs saving, updated using atomic 245 // operations, without requiring any locks 246 uint64_t uRegistryNeedsSaving; 247 248 // const objects not requiring locking 249 const ComObjPtr<Host> pHost; 250 const ComObjPtr<SystemProperties> pSystemProperties; 251 #ifdef VBOX_WITH_RESOURCE_USAGE_API 252 const ComObjPtr<PerformanceCollector> pPerformanceCollector; 253 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 254 255 // Each of the following lists use a particular lock handle that protects the 256 // list as a whole. As opposed to version 3.1 and earlier, these lists no 257 // longer need the main VirtualBox object lock, but only the respective list 258 // lock. In each case, the locking order is defined that the list must be 259 // requested before object locks of members of the lists (see the order definitions 260 // in AutoLock.h; e.g. LOCKCLASS_LISTOFMACHINES before LOCKCLASS_MACHINEOBJECT). 261 RWLockHandle lockMachines; 262 MachinesOList allMachines; 263 264 RWLockHandle lockGuestOSTypes; 265 GuestOSTypesOList allGuestOSTypes; 266 267 // All the media lists are protected by the following locking handle: 268 RWLockHandle lockMedia; 269 MediaOList allHardDisks, // base images only! 270 allDVDImages, 271 allFloppyImages; 272 // the hard disks map is an additional map sorted by UUID for quick lookup 273 // and contains ALL hard disks (base and differencing); it is protected by 274 // the same lock as the other media lists above 275 HardDiskMap mapHardDisks; 276 277 // list of pending machine renames (also protected by media tree lock; 278 // see VirtualBox::rememberMachineNameChangeForMedia()) 279 struct PendingMachineRename 280 { 281 Utf8Str strConfigDirOld; 282 Utf8Str strConfigDirNew; 283 }; 284 typedef std::list<PendingMachineRename> PendingMachineRenamesList; 285 PendingMachineRenamesList llPendingMachineRenames; 286 287 RWLockHandle lockSharedFolders; 288 SharedFoldersOList allSharedFolders; 289 290 RWLockHandle lockDHCPServers; 291 DHCPServersOList allDHCPServers; 292 293 RWLockHandle lockNATNetworks; 294 NATNetworksOList allNATNetworks; 295 296 RWLockHandle mtxProgressOperations; 297 ProgressMap mapProgressOperations; 298 299 // the following are data for the client watcher thread 300 const UPDATEREQTYPE updateReq; 301 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER 302 uint8_t updateAdaptCtr; 303 #endif 304 const RTTHREAD threadClientWatcher; 305 typedef std::list<RTPROCESS> ProcessList; 306 ProcessList llProcesses; 307 308 // the following are data for the async event thread 309 const RTTHREAD threadAsyncEvent; 310 EventQueue * const pAsyncEventQ; 311 const ComObjPtr<EventSource> pEventSource; 312 313 #ifdef VBOX_WITH_EXTPACK 314 /** The extension pack manager object lives here. */ 315 const ComObjPtr<ExtPackManager> ptrExtPackManager; 316 #endif 317 318 /** The global autostart database for the user. */ 319 AutostartDb * const pAutostartDb; 320 321 /** Settings secret */ 322 bool fSettingsCipherKeySet; 323 uint8_t SettingsCipherKey[RTSHA512_HASH_SIZE]; 324 }; 325 326 327 // constructor / destructor 328 ///////////////////////////////////////////////////////////////////////////// 329 330 VirtualBox::VirtualBox() 331 {} 332 333 VirtualBox::~VirtualBox() 334 {} 335 336 HRESULT VirtualBox::FinalConstruct() 337 { 338 LogFlowThisFunc(("\n")); 339 340 HRESULT rc = init(); 341 342 BaseFinalConstruct(); 343 344 return rc; 345 } 346 347 void VirtualBox::FinalRelease() 348 { 349 LogFlowThisFunc(("\n")); 350 351 uninit(); 352 353 BaseFinalRelease(); 354 } 355 356 // public initializer/uninitializer for internal purposes only 357 ///////////////////////////////////////////////////////////////////////////// 358 359 /** 360 * Initializes the VirtualBox object. 361 * 362 * @return COM result code 363 */ 364 HRESULT VirtualBox::init() 365 { 366 /* Enclose the state transition NotReady->InInit->Ready */ 367 AutoInitSpan autoInitSpan(this); 368 AssertReturn(autoInitSpan.isOk(), E_FAIL); 369 370 /* Locking this object for writing during init sounds a bit paradoxical, 371 * but in the current locking mess this avoids that some code gets a 372 * read lock and later calls code which wants the same write lock. */ 373 AutoWriteLock lock(this COMMA_LOCKVAL_SRC_POS); 374 375 // allocate our instance data 376 m = new Data; 377 378 LogFlow(("===========================================================\n")); 379 LogFlowThisFuncEnter(); 380 381 if (sVersion.isEmpty()) 382 sVersion = RTBldCfgVersion(); 383 if (sVersionNormalized.isEmpty()) 384 { 385 Utf8Str tmp(RTBldCfgVersion()); 386 if (tmp.endsWith(VBOX_BUILD_PUBLISHER)) 387 tmp = tmp.substr(0, tmp.length() - strlen(VBOX_BUILD_PUBLISHER)); 388 sVersionNormalized = tmp; 389 } 390 sRevision = RTBldCfgRevision(); 391 if (sPackageType.isEmpty()) 392 sPackageType = VBOX_PACKAGE_STRING; 393 if (sAPIVersion.isEmpty()) 394 sAPIVersion = VBOX_API_VERSION_STRING; 395 LogFlowThisFunc(("Version: %ls, Package: %ls, API Version: %ls\n", sVersion.raw(), sPackageType.raw(), sAPIVersion.raw())); 396 397 /* Get the VirtualBox home directory. */ 398 { 399 char szHomeDir[RTPATH_MAX]; 400 int vrc = com::GetVBoxUserHomeDirectory(szHomeDir, sizeof(szHomeDir)); 401 if (RT_FAILURE(vrc)) 402 return setError(E_FAIL, 403 tr("Could not create the VirtualBox home directory '%s' (%Rrc)"), 404 szHomeDir, vrc); 405 406 unconst(m->strHomeDir) = szHomeDir; 407 } 408 409 /* compose the VirtualBox.xml file name */ 410 unconst(m->strSettingsFilePath) = Utf8StrFmt("%s%c%s", 411 m->strHomeDir.c_str(), 412 RTPATH_DELIMITER, 413 VBOX_GLOBAL_SETTINGS_FILE); 414 HRESULT rc = S_OK; 415 bool fCreate = false; 416 try 417 { 418 // load and parse VirtualBox.xml; this will throw on XML or logic errors 419 try 420 { 421 m->pMainConfigFile = new settings::MainConfigFile(&m->strSettingsFilePath); 422 } 423 catch (xml::EIPRTFailure &e) 424 { 425 // this is thrown by the XML backend if the RTOpen() call fails; 426 // only if the main settings file does not exist, create it, 427 // if there's something more serious, then do fail! 428 if (e.rc() == VERR_FILE_NOT_FOUND) 429 fCreate = true; 430 else 431 throw; 432 } 433 434 if (fCreate) 435 m->pMainConfigFile = new settings::MainConfigFile(NULL); 436 437 #ifdef VBOX_WITH_RESOURCE_USAGE_API 438 /* create the performance collector object BEFORE host */ 439 unconst(m->pPerformanceCollector).createObject(); 440 rc = m->pPerformanceCollector->init(); 441 ComAssertComRCThrowRC(rc); 442 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 443 444 /* create the host object early, machines will need it */ 445 unconst(m->pHost).createObject(); 446 rc = m->pHost->init(this); 447 ComAssertComRCThrowRC(rc); 448 449 rc = m->pHost->loadSettings(m->pMainConfigFile->host); 450 if (FAILED(rc)) throw rc; 451 452 /* 453 * Create autostart database object early, because the system properties 454 * might need it. 455 */ 456 unconst(m->pAutostartDb) = new AutostartDb; 457 458 /* create the system properties object, someone may need it too */ 459 unconst(m->pSystemProperties).createObject(); 460 rc = m->pSystemProperties->init(this); 461 ComAssertComRCThrowRC(rc); 462 463 rc = m->pSystemProperties->loadSettings(m->pMainConfigFile->systemProperties); 464 if (FAILED(rc)) throw rc; 465 466 /* guest OS type objects, needed by machines */ 467 for (size_t i = 0; i < Global::cOSTypes; ++i) 468 { 469 ComObjPtr<GuestOSType> guestOSTypeObj; 470 rc = guestOSTypeObj.createObject(); 471 if (SUCCEEDED(rc)) 472 { 473 rc = guestOSTypeObj->init(Global::sOSTypes[i]); 474 if (SUCCEEDED(rc)) 475 m->allGuestOSTypes.addChild(guestOSTypeObj); 476 } 477 ComAssertComRCThrowRC(rc); 478 } 479 480 /* all registered media, needed by machines */ 481 if (FAILED(rc = initMedia(m->uuidMediaRegistry, 482 m->pMainConfigFile->mediaRegistry, 483 Utf8Str::Empty))) // const Utf8Str &machineFolder 484 throw rc; 485 486 /* machines */ 487 if (FAILED(rc = initMachines())) 488 throw rc; 489 490 #ifdef DEBUG 491 LogFlowThisFunc(("Dumping media backreferences\n")); 492 dumpAllBackRefs(); 493 #endif 494 495 /* net services - dhcp services */ 496 for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin(); 497 it != m->pMainConfigFile->llDhcpServers.end(); 498 ++it) 499 { 500 const settings::DHCPServer &data = *it; 501 502 ComObjPtr<DHCPServer> pDhcpServer; 503 if (SUCCEEDED(rc = pDhcpServer.createObject())) 504 rc = pDhcpServer->init(this, data); 505 if (FAILED(rc)) throw rc; 506 507 rc = registerDHCPServer(pDhcpServer, false /* aSaveRegistry */); 508 if (FAILED(rc)) throw rc; 509 } 510 511 /* net services - nat networks */ 512 for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin(); 513 it != m->pMainConfigFile->llNATNetworks.end(); 514 ++it) 515 { 516 const settings::NATNetwork &net = *it; 517 518 ComObjPtr<NATNetwork> pNATNetwork; 519 if (SUCCEEDED(rc = pNATNetwork.createObject())) 520 rc = pNATNetwork->init(this, net); 521 if (FAILED(rc)) throw rc; 522 523 rc = registerNATNetwork(pNATNetwork, false /* aSaveRegistry */); 524 if (FAILED(rc)) throw rc; 525 } 526 527 /* events */ 528 if (SUCCEEDED(rc = unconst(m->pEventSource).createObject())) 529 rc = m->pEventSource->init(static_cast<IVirtualBox*>(this)); 530 if (FAILED(rc)) throw rc; 531 532 #ifdef VBOX_WITH_EXTPACK 533 /* extension manager */ 534 rc = unconst(m->ptrExtPackManager).createObject(); 535 if (SUCCEEDED(rc)) 536 rc = m->ptrExtPackManager->initExtPackManager(this, VBOXEXTPACKCTX_PER_USER_DAEMON); 537 if (FAILED(rc)) 538 throw rc; 539 #endif 540 } 541 catch (HRESULT err) 542 { 543 /* we assume that error info is set by the thrower */ 544 rc = err; 545 } 546 catch (...) 547 { 548 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 549 } 550 551 if (SUCCEEDED(rc)) 552 { 553 /* start the client watcher thread */ 554 #if defined(RT_OS_WINDOWS) 555 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL); 556 #elif defined(RT_OS_OS2) 557 RTSemEventCreate(&unconst(m->updateReq)); 558 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 559 RTSemEventCreate(&unconst(m->updateReq)); 560 ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0); 561 #else 562 # error "Port me!" 563 #endif 564 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher), 565 ClientWatcher, 566 (void *)this, 567 0, 568 RTTHREADTYPE_MAIN_WORKER, 569 RTTHREADFLAGS_WAITABLE, 570 "Watcher"); 571 ComAssertRC(vrc); 572 if (RT_FAILURE(vrc)) 573 rc = E_FAIL; 574 } 575 576 if (SUCCEEDED(rc)) 577 { 578 try 579 { 580 /* start the async event handler thread */ 581 int vrc = RTThreadCreate(&unconst(m->threadAsyncEvent), 582 AsyncEventHandler, 583 &unconst(m->pAsyncEventQ), 584 0, 585 RTTHREADTYPE_MAIN_WORKER, 586 RTTHREADFLAGS_WAITABLE, 587 "EventHandler"); 588 ComAssertRCThrow(vrc, E_FAIL); 589 590 /* wait until the thread sets m->pAsyncEventQ */ 591 RTThreadUserWait(m->threadAsyncEvent, RT_INDEFINITE_WAIT); 592 ComAssertThrow(m->pAsyncEventQ, E_FAIL); 593 } 594 catch (HRESULT aRC) 595 { 596 rc = aRC; 597 } 598 } 599 600 /* Confirm a successful initialization when it's the case */ 601 if (SUCCEEDED(rc)) 602 autoInitSpan.setSucceeded(); 603 604 #ifdef VBOX_WITH_EXTPACK 605 /* Let the extension packs have a go at things. */ 606 if (SUCCEEDED(rc)) 607 { 608 lock.release(); 609 m->ptrExtPackManager->callAllVirtualBoxReadyHooks(); 610 } 611 #endif 612 613 LogFlowThisFunc(("rc=%08X\n", rc)); 614 LogFlowThisFuncLeave(); 615 LogFlow(("===========================================================\n")); 616 return rc; 617 } 618 619 HRESULT VirtualBox::initMachines() 620 { 621 for (settings::MachinesRegistry::const_iterator it = m->pMainConfigFile->llMachines.begin(); 622 it != m->pMainConfigFile->llMachines.end(); 623 ++it) 624 { 625 HRESULT rc = S_OK; 626 const settings::MachineRegistryEntry &xmlMachine = *it; 627 Guid uuid = xmlMachine.uuid; 628 629 ComObjPtr<Machine> pMachine; 630 if (SUCCEEDED(rc = pMachine.createObject())) 631 { 632 rc = pMachine->initFromSettings(this, 633 xmlMachine.strSettingsFile, 634 &uuid); 635 if (SUCCEEDED(rc)) 636 rc = registerMachine(pMachine); 637 if (FAILED(rc)) 638 return rc; 639 } 640 } 641 642 return S_OK; 643 } 644 645 /** 646 * Loads a media registry from XML and adds the media contained therein to 647 * the global lists of known media. 648 * 649 * This now (4.0) gets called from two locations: 650 * 651 * -- VirtualBox::init(), to load the global media registry from VirtualBox.xml; 652 * 653 * -- Machine::loadMachineDataFromSettings(), to load the per-machine registry 654 * from machine XML, for machines created with VirtualBox 4.0 or later. 655 * 656 * In both cases, the media found are added to the global lists so the 657 * global arrays of media (including the GUI's virtual media manager) 658 * continue to work as before. 659 * 660 * @param uuidMachineRegistry The UUID of the media registry. This is either the 661 * transient UUID created at VirtualBox startup for the global registry or 662 * a machine ID. 663 * @param mediaRegistry The XML settings structure to load, either from VirtualBox.xml 664 * or a machine XML. 665 * @return 666 */ 667 HRESULT VirtualBox::initMedia(const Guid &uuidRegistry, 668 const settings::MediaRegistry mediaRegistry, 669 const Utf8Str &strMachineFolder) 670 { 671 LogFlow(("VirtualBox::initMedia ENTERING, uuidRegistry=%s, strMachineFolder=%s\n", 672 uuidRegistry.toString().c_str(), 673 strMachineFolder.c_str())); 674 675 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 676 677 HRESULT rc = S_OK; 678 settings::MediaList::const_iterator it; 679 for (it = mediaRegistry.llHardDisks.begin(); 680 it != mediaRegistry.llHardDisks.end(); 681 ++it) 682 { 683 const settings::Medium &xmlHD = *it; 684 685 ComObjPtr<Medium> pHardDisk; 686 if (SUCCEEDED(rc = pHardDisk.createObject())) 687 rc = pHardDisk->init(this, 688 NULL, // parent 689 DeviceType_HardDisk, 690 uuidRegistry, 691 xmlHD, // XML data; this recurses to processes the children 692 strMachineFolder); 693 if (FAILED(rc)) return rc; 694 695 rc = registerMedium(pHardDisk, &pHardDisk, DeviceType_HardDisk); 696 if (FAILED(rc)) return rc; 697 } 698 699 for (it = mediaRegistry.llDvdImages.begin(); 700 it != mediaRegistry.llDvdImages.end(); 701 ++it) 702 { 703 const settings::Medium &xmlDvd = *it; 704 705 ComObjPtr<Medium> pImage; 706 if (SUCCEEDED(pImage.createObject())) 707 rc = pImage->init(this, 708 NULL, 709 DeviceType_DVD, 710 uuidRegistry, 711 xmlDvd, 712 strMachineFolder); 713 if (FAILED(rc)) return rc; 714 715 rc = registerMedium(pImage, &pImage, DeviceType_DVD); 716 if (FAILED(rc)) return rc; 717 } 718 719 for (it = mediaRegistry.llFloppyImages.begin(); 720 it != mediaRegistry.llFloppyImages.end(); 721 ++it) 722 { 723 const settings::Medium &xmlFloppy = *it; 724 725 ComObjPtr<Medium> pImage; 726 if (SUCCEEDED(pImage.createObject())) 727 rc = pImage->init(this, 728 NULL, 729 DeviceType_Floppy, 730 uuidRegistry, 731 xmlFloppy, 732 strMachineFolder); 733 if (FAILED(rc)) return rc; 734 735 rc = registerMedium(pImage, &pImage, DeviceType_Floppy); 736 if (FAILED(rc)) return rc; 737 } 738 739 LogFlow(("VirtualBox::initMedia LEAVING\n")); 740 741 return S_OK; 742 } 743 744 void VirtualBox::uninit() 745 { 746 Assert(!m->uRegistryNeedsSaving); 747 if (m->uRegistryNeedsSaving) 748 saveSettings(); 749 750 /* Enclose the state transition Ready->InUninit->NotReady */ 751 AutoUninitSpan autoUninitSpan(this); 752 if (autoUninitSpan.uninitDone()) 753 return; 754 755 LogFlow(("===========================================================\n")); 756 LogFlowThisFuncEnter(); 757 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed())); 758 759 /* tell all our child objects we've been uninitialized */ 760 761 LogFlowThisFunc(("Uninitializing machines (%d)...\n", m->allMachines.size())); 762 if (m->pHost) 763 { 764 /* It is necessary to hold the VirtualBox and Host locks here because 765 we may have to uninitialize SessionMachines. */ 766 AutoMultiWriteLock2 multilock(this, m->pHost COMMA_LOCKVAL_SRC_POS); 767 m->allMachines.uninitAll(); 768 } 769 else 770 m->allMachines.uninitAll(); 771 m->allFloppyImages.uninitAll(); 772 m->allDVDImages.uninitAll(); 773 m->allHardDisks.uninitAll(); 774 m->allDHCPServers.uninitAll(); 775 776 m->mapProgressOperations.clear(); 777 778 m->allGuestOSTypes.uninitAll(); 779 780 /* Note that we release singleton children after we've all other children. 781 * In some cases this is important because these other children may use 782 * some resources of the singletons which would prevent them from 783 * uninitializing (as for example, mSystemProperties which owns 784 * MediumFormat objects which Medium objects refer to) */ 785 if (m->pSystemProperties) 786 { 787 m->pSystemProperties->uninit(); 788 unconst(m->pSystemProperties).setNull(); 789 } 790 791 if (m->pHost) 792 { 793 m->pHost->uninit(); 794 unconst(m->pHost).setNull(); 795 } 796 797 #ifdef VBOX_WITH_RESOURCE_USAGE_API 798 if (m->pPerformanceCollector) 799 { 800 m->pPerformanceCollector->uninit(); 801 unconst(m->pPerformanceCollector).setNull(); 802 } 803 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 804 805 LogFlowThisFunc(("Terminating the async event handler...\n")); 806 if (m->threadAsyncEvent != NIL_RTTHREAD) 807 { 808 /* signal to exit the event loop */ 809 if (RT_SUCCESS(m->pAsyncEventQ->interruptEventQueueProcessing())) 810 { 811 /* 812 * Wait for thread termination (only after we've successfully 813 * interrupted the event queue processing!) 814 */ 815 int vrc = RTThreadWait(m->threadAsyncEvent, 60000, NULL); 816 if (RT_FAILURE(vrc)) 817 LogWarningFunc(("RTThreadWait(%RTthrd) -> %Rrc\n", 818 m->threadAsyncEvent, vrc)); 819 } 820 else 821 { 822 AssertMsgFailed(("interruptEventQueueProcessing() failed\n")); 823 RTThreadWait(m->threadAsyncEvent, 0, NULL); 824 } 825 826 unconst(m->threadAsyncEvent) = NIL_RTTHREAD; 827 unconst(m->pAsyncEventQ) = NULL; 828 } 829 830 LogFlowThisFunc(("Releasing event source...\n")); 831 if (m->pEventSource) 832 { 833 // we don't perform uninit() as it's possible that some pending event refers to this source 834 unconst(m->pEventSource).setNull(); 835 } 836 837 LogFlowThisFunc(("Terminating the client watcher...\n")); 838 if (m->threadClientWatcher != NIL_RTTHREAD) 839 { 840 /* signal the client watcher thread */ 841 updateClientWatcher(); 842 /* wait for the termination */ 843 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL); 844 unconst(m->threadClientWatcher) = NIL_RTTHREAD; 845 } 846 m->llProcesses.clear(); 847 #if defined(RT_OS_WINDOWS) 848 if (m->updateReq != NULL) 849 { 850 ::CloseHandle(m->updateReq); 851 unconst(m->updateReq) = NULL; 852 } 853 #elif defined(RT_OS_OS2) 854 if (m->updateReq != NIL_RTSEMEVENT) 855 { 856 RTSemEventDestroy(m->updateReq); 857 unconst(m->updateReq) = NIL_RTSEMEVENT; 858 } 859 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 860 if (m->updateReq != NIL_RTSEMEVENT) 861 { 862 RTSemEventDestroy(m->updateReq); 863 unconst(m->updateReq) = NIL_RTSEMEVENT; 71 RTSemEventDestroy(mUpdateReq); 72 mUpdateReq = NIL_RTSEMEVENT; 864 73 } 865 74 #else 866 75 # error "Port me!" 867 76 #endif 868 869 delete m->pAutostartDb;870 871 // clean up our instance data872 delete m;873 874 /* Unload hard disk plugin backends. */875 VDShutdown();876 877 LogFlowThisFuncLeave();878 LogFlow(("===========================================================\n"));879 77 } 880 78 881 // IVirtualBox properties 882 ///////////////////////////////////////////////////////////////////////////// 883 884 STDMETHODIMP VirtualBox::COMGETTER(Version)(BSTR *aVersion) 79 VirtualBox::ClientWatcher::ClientWatcher(const ComObjPtr<VirtualBox> &pVirtualBox) : 80 mVirtualBox(pVirtualBox), 81 mThread(NIL_RTTHREAD), 82 mUpdateReq(CWUPDATEREQARG), 83 mLock(LOCKCLASS_OBJECTSTATE) 885 84 { 886 CheckComArgNotNull(aVersion); 887 888 AutoCaller autoCaller(this); 889 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 890 891 sVersion.cloneTo(aVersion); 892 return S_OK; 85 #if defined(RT_OS_WINDOWS) 86 mUpdateReq = ::CreateEvent(NULL, FALSE, FALSE, NULL); 87 #elif defined(RT_OS_OS2) 88 RTSemEventCreate(&mUpdateReq); 89 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 90 RTSemEventCreate(&mUpdateReq); 91 /* start with high timeouts, nothing to do */ 92 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, 0); 93 #else 94 # error "Port me!" 95 #endif 96 97 int vrc = RTThreadCreate(&mThread, 98 worker, 99 (void *)this, 100 0, 101 RTTHREADTYPE_MAIN_WORKER, 102 RTTHREADFLAGS_WAITABLE, 103 "Watcher"); 104 AssertRC(vrc); 893 105 } 894 106 895 STDMETHODIMP VirtualBox::COMGETTER(VersionNormalized)(BSTR *aVersionNormalized)107 bool VirtualBox::ClientWatcher::isReady() 896 108 { 897 CheckComArgNotNull(aVersionNormalized); 898 899 AutoCaller autoCaller(this); 900 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 901 902 sVersionNormalized.cloneTo(aVersionNormalized); 903 return S_OK; 109 return mThread != NIL_RTTHREAD; 904 110 } 905 111 906 STDMETHODIMP VirtualBox::COMGETTER(Revision)(ULONG *aRevision) 112 /** 113 * Sends a signal to the thread to rescan the clients/VMs having open sessions. 114 */ 115 void VirtualBox::ClientWatcher::update() 907 116 { 908 CheckComArgNotNull(aRevision); 909 910 AutoCaller autoCaller(this); 911 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 912 913 *aRevision = sRevision; 914 return S_OK; 117 AssertReturnVoid(mThread != NIL_RTTHREAD); 118 119 /* sent an update request */ 120 #if defined(RT_OS_WINDOWS) 121 ::SetEvent(mUpdateReq); 122 #elif defined(RT_OS_OS2) 123 RTSemEventSignal(mUpdateReq); 124 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 125 /* use short timeouts, as we expect changes */ 126 ASMAtomicUoWriteU8(&mUpdateAdaptCtr, RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1); 127 RTSemEventSignal(mUpdateReq); 128 #else 129 # error "Port me!" 130 #endif 915 131 } 916 132 917 STDMETHODIMP VirtualBox::COMGETTER(PackageType)(BSTR *aPackageType) 133 /** 134 * Adds a process to the list of processes to be reaped. This call should be 135 * followed by a call to update() to cause the necessary actions immediately, 136 * in case the process crashes straight away. 137 */ 138 void VirtualBox::ClientWatcher::addProcess(RTPROCESS pid) 918 139 { 919 CheckComArgNotNull(aPackageType); 920 921 AutoCaller autoCaller(this); 922 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 923 924 sPackageType.cloneTo(aPackageType); 925 return S_OK; 140 AssertReturnVoid(mThread != NIL_RTTHREAD); 141 /* @todo r=klaus, do the reaping on all platforms! */ 142 #ifndef RT_OS_WINDOWS 143 AutoWriteLock alock(mLock COMMA_LOCKVAL_SRC_POS); 144 mProcesses.push_back(pid); 145 #endif 926 146 } 927 147 928 STDMETHODIMP VirtualBox::COMGETTER(APIVersion)(BSTR *aAPIVersion)929 {930 CheckComArgNotNull(aAPIVersion);931 932 AutoCaller autoCaller(this);933 if (FAILED(autoCaller.rc())) return autoCaller.rc();934 935 sAPIVersion.cloneTo(aAPIVersion);936 return S_OK;937 }938 939 STDMETHODIMP VirtualBox::COMGETTER(HomeFolder)(BSTR *aHomeFolder)940 {941 CheckComArgNotNull(aHomeFolder);942 943 AutoCaller autoCaller(this);944 if (FAILED(autoCaller.rc())) return autoCaller.rc();945 946 /* mHomeDir is const and doesn't need a lock */947 m->strHomeDir.cloneTo(aHomeFolder);948 return S_OK;949 }950 951 STDMETHODIMP VirtualBox::COMGETTER(SettingsFilePath)(BSTR *aSettingsFilePath)952 {953 CheckComArgNotNull(aSettingsFilePath);954 955 AutoCaller autoCaller(this);956 if (FAILED(autoCaller.rc())) return autoCaller.rc();957 958 /* mCfgFile.mName is const and doesn't need a lock */959 m->strSettingsFilePath.cloneTo(aSettingsFilePath);960 return S_OK;961 }962 963 STDMETHODIMP VirtualBox::COMGETTER(Host)(IHost **aHost)964 {965 CheckComArgOutPointerValid(aHost);966 967 AutoCaller autoCaller(this);968 if (FAILED(autoCaller.rc())) return autoCaller.rc();969 970 /* mHost is const, no need to lock */971 m->pHost.queryInterfaceTo(aHost);972 return S_OK;973 }974 975 STDMETHODIMP976 VirtualBox::COMGETTER(SystemProperties)(ISystemProperties **aSystemProperties)977 {978 CheckComArgOutPointerValid(aSystemProperties);979 980 AutoCaller autoCaller(this);981 if (FAILED(autoCaller.rc())) return autoCaller.rc();982 983 /* mSystemProperties is const, no need to lock */984 m->pSystemProperties.queryInterfaceTo(aSystemProperties);985 return S_OK;986 }987 988 STDMETHODIMP989 VirtualBox::COMGETTER(Machines)(ComSafeArrayOut(IMachine *, aMachines))990 {991 CheckComArgOutSafeArrayPointerValid(aMachines);992 993 AutoCaller autoCaller(this);994 if (FAILED(autoCaller.rc())) return autoCaller.rc();995 996 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);997 SafeIfaceArray<IMachine> machines(m->allMachines.getList());998 machines.detachTo(ComSafeArrayOutArg(aMachines));999 1000 return S_OK;1001 }1002 1003 STDMETHODIMP1004 VirtualBox::COMGETTER(MachineGroups)(ComSafeArrayOut(BSTR, aMachineGroups))1005 {1006 CheckComArgOutSafeArrayPointerValid(aMachineGroups);1007 1008 AutoCaller autoCaller(this);1009 if (FAILED(autoCaller.rc())) return autoCaller.rc();1010 1011 std::list<Bstr> allGroups;1012 1013 /* get copy of all machine references, to avoid holding the list lock */1014 MachinesOList::MyList allMachines;1015 {1016 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);1017 allMachines = m->allMachines.getList();1018 }1019 for (MachinesOList::MyList::const_iterator it = allMachines.begin();1020 it != allMachines.end();1021 ++it)1022 {1023 const ComObjPtr<Machine> &pMachine = *it;1024 AutoCaller autoMachineCaller(pMachine);1025 if (FAILED(autoMachineCaller.rc()))1026 continue;1027 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);1028 1029 if (pMachine->isAccessible())1030 {1031 const StringsList &thisGroups = pMachine->getGroups();1032 for (StringsList::const_iterator it2 = thisGroups.begin();1033 it2 != thisGroups.end();1034 ++it2)1035 allGroups.push_back(*it2);1036 }1037 }1038 1039 /* throw out any duplicates */1040 allGroups.sort();1041 allGroups.unique();1042 com::SafeArray<BSTR> machineGroups(allGroups.size());1043 size_t i = 0;1044 for (std::list<Bstr>::const_iterator it = allGroups.begin();1045 it != allGroups.end();1046 ++it, i++)1047 {1048 const Bstr &tmp = *it;1049 tmp.cloneTo(&machineGroups[i]);1050 }1051 machineGroups.detachTo(ComSafeArrayOutArg(aMachineGroups));1052 1053 return S_OK;1054 }1055 1056 STDMETHODIMP VirtualBox::COMGETTER(HardDisks)(ComSafeArrayOut(IMedium *, aHardDisks))1057 {1058 CheckComArgOutSafeArrayPointerValid(aHardDisks);1059 1060 AutoCaller autoCaller(this);1061 if (FAILED(autoCaller.rc())) return autoCaller.rc();1062 1063 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS);1064 SafeIfaceArray<IMedium> hardDisks(m->allHardDisks.getList());1065 hardDisks.detachTo(ComSafeArrayOutArg(aHardDisks));1066 1067 return S_OK;1068 }1069 1070 STDMETHODIMP VirtualBox::COMGETTER(DVDImages)(ComSafeArrayOut(IMedium *, aDVDImages))1071 {1072 CheckComArgOutSafeArrayPointerValid(aDVDImages);1073 1074 AutoCaller autoCaller(this);1075 if (FAILED(autoCaller.rc())) return autoCaller.rc();1076 1077 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);1078 SafeIfaceArray<IMedium> images(m->allDVDImages.getList());1079 images.detachTo(ComSafeArrayOutArg(aDVDImages));1080 1081 return S_OK;1082 }1083 1084 STDMETHODIMP VirtualBox::COMGETTER(FloppyImages)(ComSafeArrayOut(IMedium *, aFloppyImages))1085 {1086 CheckComArgOutSafeArrayPointerValid(aFloppyImages);1087 1088 AutoCaller autoCaller(this);1089 if (FAILED(autoCaller.rc())) return autoCaller.rc();1090 1091 AutoReadLock al(m->allFloppyImages.getLockHandle() COMMA_LOCKVAL_SRC_POS);1092 SafeIfaceArray<IMedium> images(m->allFloppyImages.getList());1093 images.detachTo(ComSafeArrayOutArg(aFloppyImages));1094 1095 return S_OK;1096 }1097 1098 STDMETHODIMP VirtualBox::COMGETTER(ProgressOperations)(ComSafeArrayOut(IProgress *, aOperations))1099 {1100 CheckComArgOutPointerValid(aOperations);1101 1102 AutoCaller autoCaller(this);1103 if (FAILED(autoCaller.rc())) return autoCaller.rc();1104 1105 /* protect mProgressOperations */1106 AutoReadLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS);1107 SafeIfaceArray<IProgress> progress(m->mapProgressOperations);1108 progress.detachTo(ComSafeArrayOutArg(aOperations));1109 1110 return S_OK;1111 }1112 1113 STDMETHODIMP VirtualBox::COMGETTER(GuestOSTypes)(ComSafeArrayOut(IGuestOSType *, aGuestOSTypes))1114 {1115 CheckComArgOutSafeArrayPointerValid(aGuestOSTypes);1116 1117 AutoCaller autoCaller(this);1118 if (FAILED(autoCaller.rc())) return autoCaller.rc();1119 1120 AutoReadLock al(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS);1121 SafeIfaceArray<IGuestOSType> ostypes(m->allGuestOSTypes.getList());1122 ostypes.detachTo(ComSafeArrayOutArg(aGuestOSTypes));1123 1124 return S_OK;1125 }1126 1127 STDMETHODIMP VirtualBox::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders))1128 {1129 #ifndef RT_OS_WINDOWS1130 NOREF(aSharedFoldersSize);1131 #endif /* RT_OS_WINDOWS */1132 1133 CheckComArgOutSafeArrayPointerValid(aSharedFolders);1134 1135 AutoCaller autoCaller(this);1136 if (FAILED(autoCaller.rc())) return autoCaller.rc();1137 1138 return setError(E_NOTIMPL, "Not yet implemented");1139 }1140 1141 STDMETHODIMP1142 VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformanceCollector)1143 {1144 #ifdef VBOX_WITH_RESOURCE_USAGE_API1145 CheckComArgOutPointerValid(aPerformanceCollector);1146 1147 AutoCaller autoCaller(this);1148 if (FAILED(autoCaller.rc())) return autoCaller.rc();1149 1150 /* mPerformanceCollector is const, no need to lock */1151 m->pPerformanceCollector.queryInterfaceTo(aPerformanceCollector);1152 1153 return S_OK;1154 #else /* !VBOX_WITH_RESOURCE_USAGE_API */1155 ReturnComNotImplemented();1156 #endif /* !VBOX_WITH_RESOURCE_USAGE_API */1157 }1158 1159 STDMETHODIMP1160 VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers))1161 {1162 CheckComArgOutSafeArrayPointerValid(aDHCPServers);1163 1164 AutoCaller autoCaller(this);1165 if (FAILED(autoCaller.rc())) return autoCaller.rc();1166 1167 AutoReadLock al(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);1168 SafeIfaceArray<IDHCPServer> svrs(m->allDHCPServers.getList());1169 svrs.detachTo(ComSafeArrayOutArg(aDHCPServers));1170 1171 return S_OK;1172 }1173 1174 1175 STDMETHODIMP1176 VirtualBox::COMGETTER(NATNetworks)(ComSafeArrayOut(INATNetwork *, aNATNetworks))1177 {1178 #ifdef VBOX_WITH_NAT_SERVICE1179 CheckComArgOutSafeArrayPointerValid(aNATNetworks);1180 1181 AutoCaller autoCaller(this);1182 if (FAILED(autoCaller.rc())) return autoCaller.rc();1183 1184 AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);1185 SafeIfaceArray<INATNetwork> nets(m->allNATNetworks.getList());1186 nets.detachTo(ComSafeArrayOutArg(aNATNetworks));1187 1188 return S_OK;1189 #else1190 NOREF(aNATNetworks);1191 # ifndef RT_OS_WINDOWS1192 NOREF(aNATNetworksSize);1193 # endif1194 return E_NOTIMPL;1195 #endif1196 }1197 1198 1199 STDMETHODIMP1200 VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource)1201 {1202 CheckComArgOutPointerValid(aEventSource);1203 1204 AutoCaller autoCaller(this);1205 if (FAILED(autoCaller.rc())) return autoCaller.rc();1206 1207 /* event source is const, no need to lock */1208 m->pEventSource.queryInterfaceTo(aEventSource);1209 1210 return S_OK;1211 }1212 1213 STDMETHODIMP1214 VirtualBox::COMGETTER(ExtensionPackManager)(IExtPackManager **aExtPackManager)1215 {1216 CheckComArgOutPointerValid(aExtPackManager);1217 1218 AutoCaller autoCaller(this);1219 HRESULT hrc = autoCaller.rc();1220 if (SUCCEEDED(hrc))1221 {1222 #ifdef VBOX_WITH_EXTPACK1223 /* The extension pack manager is const, no need to lock. */1224 hrc = m->ptrExtPackManager.queryInterfaceTo(aExtPackManager);1225 #else1226 hrc = E_NOTIMPL;1227 #endif1228 }1229 1230 return hrc;1231 }1232 1233 STDMETHODIMP VirtualBox::COMGETTER(InternalNetworks)(ComSafeArrayOut(BSTR, aInternalNetworks))1234 {1235 CheckComArgOutSafeArrayPointerValid(aInternalNetworks);1236 1237 AutoCaller autoCaller(this);1238 if (FAILED(autoCaller.rc())) return autoCaller.rc();1239 1240 std::list<Bstr> allInternalNetworks;1241 1242 /* get copy of all machine references, to avoid holding the list lock */1243 MachinesOList::MyList allMachines;1244 {1245 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);1246 allMachines = m->allMachines.getList();1247 }1248 for (MachinesOList::MyList::const_iterator it = allMachines.begin();1249 it != allMachines.end();1250 ++it)1251 {1252 const ComObjPtr<Machine> &pMachine = *it;1253 AutoCaller autoMachineCaller(pMachine);1254 if (FAILED(autoMachineCaller.rc()))1255 continue;1256 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);1257 1258 if (pMachine->isAccessible())1259 {1260 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());1261 for (ULONG i = 0; i < cNetworkAdapters; i++)1262 {1263 ComPtr<INetworkAdapter> pNet;1264 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());1265 if (FAILED(rc) || pNet.isNull())1266 continue;1267 Bstr strInternalNetwork;1268 rc = pNet->COMGETTER(InternalNetwork)(strInternalNetwork.asOutParam());1269 if (FAILED(rc) || strInternalNetwork.isEmpty())1270 continue;1271 1272 allInternalNetworks.push_back(strInternalNetwork);1273 }1274 }1275 }1276 1277 /* throw out any duplicates */1278 allInternalNetworks.sort();1279 allInternalNetworks.unique();1280 com::SafeArray<BSTR> internalNetworks(allInternalNetworks.size());1281 size_t i = 0;1282 for (std::list<Bstr>::const_iterator it = allInternalNetworks.begin();1283 it != allInternalNetworks.end();1284 ++it, i++)1285 {1286 const Bstr &tmp = *it;1287 tmp.cloneTo(&internalNetworks[i]);1288 }1289 internalNetworks.detachTo(ComSafeArrayOutArg(aInternalNetworks));1290 1291 return S_OK;1292 }1293 1294 STDMETHODIMP VirtualBox::COMGETTER(GenericNetworkDrivers)(ComSafeArrayOut(BSTR, aGenericNetworkDrivers))1295 {1296 CheckComArgOutSafeArrayPointerValid(aGenericNetworkDrivers);1297 1298 AutoCaller autoCaller(this);1299 if (FAILED(autoCaller.rc())) return autoCaller.rc();1300 1301 std::list<Bstr> allGenericNetworkDrivers;1302 1303 /* get copy of all machine references, to avoid holding the list lock */1304 MachinesOList::MyList allMachines;1305 {1306 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);1307 allMachines = m->allMachines.getList();1308 }1309 for (MachinesOList::MyList::const_iterator it = allMachines.begin();1310 it != allMachines.end();1311 ++it)1312 {1313 const ComObjPtr<Machine> &pMachine = *it;1314 AutoCaller autoMachineCaller(pMachine);1315 if (FAILED(autoMachineCaller.rc()))1316 continue;1317 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS);1318 1319 if (pMachine->isAccessible())1320 {1321 uint32_t cNetworkAdapters = Global::getMaxNetworkAdapters(pMachine->getChipsetType());1322 for (ULONG i = 0; i < cNetworkAdapters; i++)1323 {1324 ComPtr<INetworkAdapter> pNet;1325 HRESULT rc = pMachine->GetNetworkAdapter(i, pNet.asOutParam());1326 if (FAILED(rc) || pNet.isNull())1327 continue;1328 Bstr strGenericNetworkDriver;1329 rc = pNet->COMGETTER(GenericDriver)(strGenericNetworkDriver.asOutParam());1330 if (FAILED(rc) || strGenericNetworkDriver.isEmpty())1331 continue;1332 1333 allGenericNetworkDrivers.push_back(strGenericNetworkDriver);1334 }1335 }1336 }1337 1338 /* throw out any duplicates */1339 allGenericNetworkDrivers.sort();1340 allGenericNetworkDrivers.unique();1341 com::SafeArray<BSTR> genericNetworks(allGenericNetworkDrivers.size());1342 size_t i = 0;1343 for (std::list<Bstr>::const_iterator it = allGenericNetworkDrivers.begin();1344 it != allGenericNetworkDrivers.end();1345 ++it, i++)1346 {1347 const Bstr &tmp = *it;1348 tmp.cloneTo(&genericNetworks[i]);1349 }1350 genericNetworks.detachTo(ComSafeArrayOutArg(aGenericNetworkDrivers));1351 1352 return S_OK;1353 }1354 1355 STDMETHODIMP1356 VirtualBox::CheckFirmwarePresent(FirmwareType_T aFirmwareType,1357 IN_BSTR aVersion,1358 BSTR *aUrl,1359 BSTR *aFile,1360 BOOL *aResult)1361 {1362 CheckComArgNotNull(aResult);1363 1364 AutoCaller autoCaller(this);1365 if (FAILED(autoCaller.rc())) return autoCaller.rc();1366 1367 NOREF(aVersion);1368 1369 static const struct1370 {1371 FirmwareType_T type;1372 const char* fileName;1373 const char* url;1374 }1375 firmwareDesc[] =1376 {1377 {1378 /* compiled-in firmware */1379 FirmwareType_BIOS, NULL, NULL1380 },1381 {1382 FirmwareType_EFI32, "VBoxEFI32.fd", "http://virtualbox.org/firmware/VBoxEFI32.fd"1383 },1384 {1385 FirmwareType_EFI64, "VBoxEFI64.fd", "http://virtualbox.org/firmware/VBoxEFI64.fd"1386 },1387 {1388 FirmwareType_EFIDUAL, "VBoxEFIDual.fd", "http://virtualbox.org/firmware/VBoxEFIDual.fd"1389 }1390 };1391 1392 for (size_t i = 0; i < sizeof(firmwareDesc) / sizeof(firmwareDesc[0]); i++)1393 {1394 if (aFirmwareType != firmwareDesc[i].type)1395 continue;1396 1397 /* compiled-in firmware */1398 if (firmwareDesc[i].fileName == NULL)1399 {1400 *aResult = TRUE;1401 break;1402 }1403 1404 Utf8Str shortName, fullName;1405 1406 shortName = Utf8StrFmt("Firmware%c%s",1407 RTPATH_DELIMITER,1408 firmwareDesc[i].fileName);1409 int rc = calculateFullPath(shortName, fullName);1410 AssertRCReturn(rc, rc);1411 if (RTFileExists(fullName.c_str()))1412 {1413 *aResult = TRUE;1414 if (aFile)1415 Utf8Str(fullName).cloneTo(aFile);1416 break;1417 }1418 1419 char pszVBoxPath[RTPATH_MAX];1420 rc = RTPathExecDir(pszVBoxPath, RTPATH_MAX);1421 AssertRCReturn(rc, rc);1422 fullName = Utf8StrFmt("%s%c%s",1423 pszVBoxPath,1424 RTPATH_DELIMITER,1425 firmwareDesc[i].fileName);1426 if (RTFileExists(fullName.c_str()))1427 {1428 *aResult = TRUE;1429 if (aFile)1430 Utf8Str(fullName).cloneTo(aFile);1431 break;1432 }1433 1434 /** @todo: account for version in the URL */1435 if (aUrl != NULL)1436 {1437 Utf8Str strUrl(firmwareDesc[i].url);1438 strUrl.cloneTo(aUrl);1439 }1440 *aResult = FALSE;1441 1442 /* Assume single record per firmware type */1443 break;1444 }1445 1446 return S_OK;1447 }1448 // IVirtualBox methods1449 /////////////////////////////////////////////////////////////////////////////1450 1451 /* Helper for VirtualBox::ComposeMachineFilename */1452 static void sanitiseMachineFilename(Utf8Str &aName);1453 1454 STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName,1455 IN_BSTR aGroup,1456 IN_BSTR aCreateFlags,1457 IN_BSTR aBaseFolder,1458 BSTR *aFilename)1459 {1460 LogFlowThisFuncEnter();1461 LogFlowThisFunc(("aName=\"%ls\",aBaseFolder=\"%ls\"\n", aName, aBaseFolder));1462 1463 CheckComArgStrNotEmptyOrNull(aName);1464 CheckComArgOutPointerValid(aFilename);1465 1466 AutoCaller autoCaller(this);1467 if (FAILED(autoCaller.rc())) return autoCaller.rc();1468 1469 Utf8Str strCreateFlags(aCreateFlags);1470 Guid id;1471 bool fDirectoryIncludesUUID = false;1472 if (!strCreateFlags.isEmpty())1473 {1474 const char *pcszNext = strCreateFlags.c_str();1475 while (*pcszNext != '\0')1476 {1477 Utf8Str strFlag;1478 const char *pcszComma = RTStrStr(pcszNext, ",");1479 if (!pcszComma)1480 strFlag = pcszNext;1481 else1482 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext);1483 1484 const char *pcszEqual = RTStrStr(strFlag.c_str(), "=");1485 /* skip over everything which doesn't contain '=' */1486 if (pcszEqual && pcszEqual != strFlag.c_str())1487 {1488 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str());1489 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1));1490 1491 if (strKey == "UUID")1492 id = strValue.c_str();1493 else if (strKey == "directoryIncludesUUID")1494 fDirectoryIncludesUUID = (strValue == "1");1495 }1496 1497 if (!pcszComma)1498 pcszNext += strFlag.length();1499 else1500 pcszNext += strFlag.length() + 1;1501 }1502 }1503 1504 if (id.isZero())1505 fDirectoryIncludesUUID = false;1506 else if (!id.isValid())1507 {1508 /* do something else */1509 return setError(E_INVALIDARG,1510 tr("'%ls' is not a valid Guid"),1511 id.toStringCurly().c_str());1512 }1513 1514 Utf8Str strGroup(aGroup);1515 if (strGroup.isEmpty())1516 strGroup = "/";1517 HRESULT rc = validateMachineGroup(strGroup, true);1518 if (FAILED(rc))1519 return rc;1520 1521 /* Compose the settings file name using the following scheme:1522 *1523 * <base_folder><group>/<machine_name>/<machine_name>.xml1524 *1525 * If a non-null and non-empty base folder is specified, the default1526 * machine folder will be used as a base folder.1527 * We sanitise the machine name to a safe white list of characters before1528 * using it.1529 */1530 Utf8Str strBase = aBaseFolder;1531 Utf8Str strName = aName;1532 Utf8Str strDirName(strName);1533 if (fDirectoryIncludesUUID)1534 strDirName += Utf8StrFmt(" (%RTuuid)", id.raw());1535 sanitiseMachineFilename(strName);1536 sanitiseMachineFilename(strDirName);1537 1538 if (strBase.isEmpty())1539 /* we use the non-full folder value below to keep the path relative */1540 getDefaultMachineFolder(strBase);1541 1542 calculateFullPath(strBase, strBase);1543 1544 /* eliminate toplevel group to avoid // in the result */1545 if (strGroup == "/")1546 strGroup.setNull();1547 Bstr bstrSettingsFile = BstrFmt("%s%s%c%s%c%s.vbox",1548 strBase.c_str(),1549 strGroup.c_str(),1550 RTPATH_DELIMITER,1551 strDirName.c_str(),1552 RTPATH_DELIMITER,1553 strName.c_str());1554 1555 bstrSettingsFile.detachTo(aFilename);1556 1557 return S_OK;1558 }1559 1560 148 /** 1561 * Remove characters from a machine file name which can be problematic on 1562 * particular systems. 1563 * @param strName The file name to sanitise. 149 * Thread worker function that watches the termination of all client processes 150 * that have open sessions using IMachine::LockMachine() 1564 151 */ 1565 void sanitiseMachineFilename(Utf8Str &strName) 1566 { 1567 /** Set of characters which should be safe for use in filenames: some basic 1568 * ASCII, Unicode from Latin-1 alphabetic to the end of Hangul. We try to 1569 * skip anything that could count as a control character in Windows or 1570 * *nix, or be otherwise difficult for shells to handle (I would have 1571 * preferred to remove the space and brackets too). We also remove all 1572 * characters which need UTF-16 surrogate pairs for Windows's benefit. */ 1573 RTUNICP aCpSet[] = 1574 { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_', 1575 0xa0, 0xd7af, '\0' }; 1576 char *pszName = strName.mutableRaw(); 1577 int cReplacements = RTStrPurgeComplementSet(pszName, aCpSet, '_'); 1578 Assert(cReplacements >= 0); 1579 NOREF(cReplacements); 1580 /* No leading dot or dash. */ 1581 if (pszName[0] == '.' || pszName[0] == '-') 1582 pszName[0] = '_'; 1583 /* No trailing dot. */ 1584 if (pszName[strName.length() - 1] == '.') 1585 pszName[strName.length() - 1] = '_'; 1586 /* Mangle leading and trailing spaces. */ 1587 for (size_t i = 0; pszName[i] == ' '; ++i) 1588 pszName[i] = '_'; 1589 for (size_t i = strName.length() - 1; i && pszName[i] == ' '; --i) 1590 pszName[i] = '_'; 1591 } 1592 1593 #ifdef DEBUG 1594 /** Simple unit test/operation examples for sanitiseMachineFilename(). */ 1595 static unsigned testSanitiseMachineFilename(void (*pfnPrintf)(const char *, ...)) 1596 { 1597 unsigned cErrors = 0; 1598 1599 /** Expected results of sanitising given file names. */ 1600 static struct 1601 { 1602 /** The test file name to be sanitised (Utf-8). */ 1603 const char *pcszIn; 1604 /** The expected sanitised output (Utf-8). */ 1605 const char *pcszOutExpected; 1606 } aTest[] = 1607 { 1608 { "OS/2 2.1", "OS_2 2.1" }, 1609 { "-!My VM!-", "__My VM_-" }, 1610 { "\xF0\x90\x8C\xB0", "____" }, 1611 { " My VM ", "__My VM__" }, 1612 { ".My VM.", "_My VM_" }, 1613 { "My VM", "My VM" } 1614 }; 1615 for (unsigned i = 0; i < RT_ELEMENTS(aTest); ++i) 1616 { 1617 Utf8Str str(aTest[i].pcszIn); 1618 sanitiseMachineFilename(str); 1619 if (str.compare(aTest[i].pcszOutExpected)) 1620 { 1621 ++cErrors; 1622 pfnPrintf("%s: line %d, expected %s, actual %s\n", 1623 __PRETTY_FUNCTION__, i, aTest[i].pcszOutExpected, 1624 str.c_str()); 1625 } 1626 } 1627 return cErrors; 1628 } 1629 1630 /** @todo Proper testcase. */ 1631 /** @todo Do we have a better method of doing init functions? */ 1632 namespace 1633 { 1634 class TestSanitiseMachineFilename 1635 { 1636 public: 1637 TestSanitiseMachineFilename(void) 1638 { 1639 Assert(!testSanitiseMachineFilename(RTAssertMsg2)); 1640 } 1641 }; 1642 TestSanitiseMachineFilename s_TestSanitiseMachineFilename; 1643 } 1644 #endif 1645 1646 /** @note Locks mSystemProperties object for reading. */ 1647 STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile, 1648 IN_BSTR aName, 1649 ComSafeArrayIn(IN_BSTR, aGroups), 1650 IN_BSTR aOsTypeId, 1651 IN_BSTR aCreateFlags, 1652 IMachine **aMachine) 1653 { 1654 LogFlowThisFuncEnter(); 1655 LogFlowThisFunc(("aSettingsFile=\"%ls\", aName=\"%ls\", aOsTypeId =\"%ls\", aCreateFlags=\"%ls\"\n", aSettingsFile, aName, aOsTypeId, aCreateFlags)); 1656 1657 CheckComArgStrNotEmptyOrNull(aName); 1658 /** @todo tighten checks on aId? */ 1659 CheckComArgOutPointerValid(aMachine); 1660 1661 AutoCaller autoCaller(this); 1662 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1663 1664 StringsList llGroups; 1665 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups); 1666 if (FAILED(rc)) 1667 return rc; 1668 1669 Utf8Str strCreateFlags(aCreateFlags); 1670 Guid id; 1671 bool fForceOverwrite = false; 1672 bool fDirectoryIncludesUUID = false; 1673 if (!strCreateFlags.isEmpty()) 1674 { 1675 const char *pcszNext = strCreateFlags.c_str(); 1676 while (*pcszNext != '\0') 1677 { 1678 Utf8Str strFlag; 1679 const char *pcszComma = RTStrStr(pcszNext, ","); 1680 if (!pcszComma) 1681 strFlag = pcszNext; 1682 else 1683 strFlag = Utf8Str(pcszNext, pcszComma - pcszNext); 1684 1685 const char *pcszEqual = RTStrStr(strFlag.c_str(), "="); 1686 /* skip over everything which doesn't contain '=' */ 1687 if (pcszEqual && pcszEqual != strFlag.c_str()) 1688 { 1689 Utf8Str strKey(strFlag.c_str(), pcszEqual - strFlag.c_str()); 1690 Utf8Str strValue(strFlag.c_str() + (pcszEqual - strFlag.c_str() + 1)); 1691 1692 if (strKey == "UUID") 1693 id = strValue.c_str(); 1694 else if (strKey == "forceOverwrite") 1695 fForceOverwrite = (strValue == "1"); 1696 else if (strKey == "directoryIncludesUUID") 1697 fDirectoryIncludesUUID = (strValue == "1"); 1698 } 1699 1700 if (!pcszComma) 1701 pcszNext += strFlag.length(); 1702 else 1703 pcszNext += strFlag.length() + 1; 1704 } 1705 } 1706 /* Create UUID if none was specified. */ 1707 if (id.isZero()) 1708 id.create(); 1709 else if (!id.isValid()) 1710 { 1711 /* do something else */ 1712 return setError(E_INVALIDARG, 1713 tr("'%ls' is not a valid Guid"), 1714 id.toStringCurly().c_str()); 1715 } 1716 1717 /* NULL settings file means compose automatically */ 1718 Bstr bstrSettingsFile(aSettingsFile); 1719 if (bstrSettingsFile.isEmpty()) 1720 { 1721 Utf8Str strNewCreateFlags(Utf8StrFmt("UUID=%RTuuid", id.raw())); 1722 if (fDirectoryIncludesUUID) 1723 strNewCreateFlags += ",directoryIncludesUUID=1"; 1724 1725 rc = ComposeMachineFilename(aName, 1726 Bstr(llGroups.front()).raw(), 1727 Bstr(strNewCreateFlags).raw(), 1728 NULL /* aBaseFolder */, 1729 bstrSettingsFile.asOutParam()); 1730 if (FAILED(rc)) return rc; 1731 } 1732 1733 /* create a new object */ 1734 ComObjPtr<Machine> machine; 1735 rc = machine.createObject(); 1736 if (FAILED(rc)) return rc; 1737 1738 GuestOSType *osType = NULL; 1739 rc = findGuestOSType(Bstr(aOsTypeId), osType); 1740 if (FAILED(rc)) return rc; 1741 1742 /* initialize the machine object */ 1743 rc = machine->init(this, 1744 Utf8Str(bstrSettingsFile), 1745 Utf8Str(aName), 1746 llGroups, 1747 osType, 1748 id, 1749 fForceOverwrite, 1750 fDirectoryIncludesUUID); 1751 if (SUCCEEDED(rc)) 1752 { 1753 /* set the return value */ 1754 rc = machine.queryInterfaceTo(aMachine); 1755 AssertComRC(rc); 1756 1757 #ifdef VBOX_WITH_EXTPACK 1758 /* call the extension pack hooks */ 1759 m->ptrExtPackManager->callAllVmCreatedHooks(machine); 1760 #endif 1761 } 1762 1763 LogFlowThisFuncLeave(); 1764 1765 return rc; 1766 } 1767 1768 STDMETHODIMP VirtualBox::OpenMachine(IN_BSTR aSettingsFile, 1769 IMachine **aMachine) 1770 { 1771 CheckComArgStrNotEmptyOrNull(aSettingsFile); 1772 CheckComArgOutPointerValid(aMachine); 1773 1774 AutoCaller autoCaller(this); 1775 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1776 1777 HRESULT rc = E_FAIL; 1778 1779 /* create a new object */ 1780 ComObjPtr<Machine> machine; 1781 rc = machine.createObject(); 1782 if (SUCCEEDED(rc)) 1783 { 1784 /* initialize the machine object */ 1785 rc = machine->initFromSettings(this, 1786 aSettingsFile, 1787 NULL); /* const Guid *aId */ 1788 if (SUCCEEDED(rc)) 1789 { 1790 /* set the return value */ 1791 rc = machine.queryInterfaceTo(aMachine); 1792 ComAssertComRC(rc); 1793 } 1794 } 1795 1796 return rc; 1797 } 1798 1799 /** @note Locks objects! */ 1800 STDMETHODIMP VirtualBox::RegisterMachine(IMachine *aMachine) 1801 { 1802 CheckComArgNotNull(aMachine); 1803 1804 AutoCaller autoCaller(this); 1805 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1806 1807 HRESULT rc; 1808 1809 Bstr name; 1810 rc = aMachine->COMGETTER(Name)(name.asOutParam()); 1811 if (FAILED(rc)) return rc; 1812 1813 /* We can safely cast child to Machine * here because only Machine 1814 * implementations of IMachine can be among our children. */ 1815 Machine *pMachine = static_cast<Machine*>(aMachine); 1816 1817 AutoCaller machCaller(pMachine); 1818 ComAssertComRCRetRC(machCaller.rc()); 1819 1820 rc = registerMachine(pMachine); 1821 /* fire an event */ 1822 if (SUCCEEDED(rc)) 1823 onMachineRegistered(pMachine->getId(), TRUE); 1824 1825 return rc; 1826 } 1827 1828 /** @note Locks this object for reading, then some machine objects for reading. */ 1829 STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine) 1830 { 1831 LogFlowThisFuncEnter(); 1832 LogFlowThisFunc(("aName=\"%ls\", aMachine={%p}\n", aNameOrId, aMachine)); 1833 1834 CheckComArgStrNotEmptyOrNull(aNameOrId); 1835 CheckComArgOutPointerValid(aMachine); 1836 1837 AutoCaller autoCaller(this); 1838 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1839 1840 /* start with not found */ 1841 HRESULT rc = S_OK; 1842 ComObjPtr<Machine> pMachineFound; 1843 1844 Guid id(aNameOrId); 1845 if (id.isValid() && !id.isZero()) 1846 1847 rc = findMachine(id, 1848 true /* fPermitInaccessible */, 1849 true /* setError */, 1850 &pMachineFound); 1851 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error 1852 else 1853 { 1854 Utf8Str strName(aNameOrId); 1855 rc = findMachineByName(aNameOrId, 1856 true /* setError */, 1857 &pMachineFound); 1858 // returns VBOX_E_OBJECT_NOT_FOUND if not found and sets error 1859 } 1860 1861 /* this will set (*machine) to NULL if machineObj is null */ 1862 pMachineFound.queryInterfaceTo(aMachine); 1863 1864 LogFlowThisFunc(("aName=\"%ls\", aMachine=%p, rc=%08X\n", aNameOrId, *aMachine, rc)); 1865 LogFlowThisFuncLeave(); 1866 1867 return rc; 1868 } 1869 1870 STDMETHODIMP VirtualBox::GetMachinesByGroups(ComSafeArrayIn(IN_BSTR, aGroups), ComSafeArrayOut(IMachine *, aMachines)) 1871 { 1872 CheckComArgSafeArrayNotNull(aGroups); 1873 CheckComArgOutSafeArrayPointerValid(aMachines); 1874 1875 AutoCaller autoCaller(this); 1876 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1877 1878 StringsList llGroups; 1879 HRESULT rc = convertMachineGroups(ComSafeArrayInArg(aGroups), &llGroups); 1880 if (FAILED(rc)) 1881 return rc; 1882 /* we want to rely on sorted groups during compare, to save time */ 1883 llGroups.sort(); 1884 1885 /* get copy of all machine references, to avoid holding the list lock */ 1886 MachinesOList::MyList allMachines; 1887 { 1888 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 1889 allMachines = m->allMachines.getList(); 1890 } 1891 1892 com::SafeIfaceArray<IMachine> saMachines; 1893 for (MachinesOList::MyList::const_iterator it = allMachines.begin(); 1894 it != allMachines.end(); 1895 ++it) 1896 { 1897 const ComObjPtr<Machine> &pMachine = *it; 1898 AutoCaller autoMachineCaller(pMachine); 1899 if (FAILED(autoMachineCaller.rc())) 1900 continue; 1901 AutoReadLock mlock(pMachine COMMA_LOCKVAL_SRC_POS); 1902 1903 if (pMachine->isAccessible()) 1904 { 1905 const StringsList &thisGroups = pMachine->getGroups(); 1906 for (StringsList::const_iterator it2 = thisGroups.begin(); 1907 it2 != thisGroups.end(); 1908 ++it2) 1909 { 1910 const Utf8Str &group = *it2; 1911 bool fAppended = false; 1912 for (StringsList::const_iterator it3 = llGroups.begin(); 1913 it3 != llGroups.end(); 1914 ++it3) 1915 { 1916 int order = it3->compare(group); 1917 if (order == 0) 1918 { 1919 saMachines.push_back(pMachine); 1920 fAppended = true; 1921 break; 1922 } 1923 else if (order > 0) 1924 break; 1925 else 1926 continue; 1927 } 1928 /* avoid duplicates and save time */ 1929 if (fAppended) 1930 break; 1931 } 1932 } 1933 } 1934 1935 saMachines.detachTo(ComSafeArrayOutArg(aMachines)); 1936 1937 return S_OK; 1938 } 1939 1940 STDMETHODIMP VirtualBox::GetMachineStates(ComSafeArrayIn(IMachine *, aMachines), ComSafeArrayOut(MachineState_T, aStates)) 1941 { 1942 CheckComArgSafeArrayNotNull(aMachines); 1943 CheckComArgOutSafeArrayPointerValid(aStates); 1944 1945 com::SafeIfaceArray<IMachine> saMachines(ComSafeArrayInArg(aMachines)); 1946 com::SafeArray<MachineState_T> saStates(saMachines.size()); 1947 for (size_t i = 0; i < saMachines.size(); i++) 1948 { 1949 ComPtr<IMachine> pMachine = saMachines[i]; 1950 MachineState_T state = MachineState_Null; 1951 if (!pMachine.isNull()) 1952 { 1953 HRESULT rc = pMachine->COMGETTER(State)(&state); 1954 if (rc == E_ACCESSDENIED) 1955 rc = S_OK; 1956 AssertComRC(rc); 1957 } 1958 saStates[i] = state; 1959 } 1960 saStates.detachTo(ComSafeArrayOutArg(aStates)); 1961 1962 return S_OK; 1963 } 1964 1965 STDMETHODIMP VirtualBox::CreateHardDisk(IN_BSTR aFormat, 1966 IN_BSTR aLocation, 1967 IMedium **aHardDisk) 1968 { 1969 CheckComArgOutPointerValid(aHardDisk); 1970 1971 AutoCaller autoCaller(this); 1972 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1973 1974 /* we don't access non-const data members so no need to lock */ 1975 1976 Utf8Str format(aFormat); 1977 if (format.isEmpty()) 1978 getDefaultHardDiskFormat(format); 1979 1980 ComObjPtr<Medium> hardDisk; 1981 hardDisk.createObject(); 1982 HRESULT rc = hardDisk->init(this, 1983 format, 1984 aLocation, 1985 Guid::Empty /* media registry: none yet */); 1986 1987 if (SUCCEEDED(rc)) 1988 hardDisk.queryInterfaceTo(aHardDisk); 1989 1990 return rc; 1991 } 1992 1993 STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation, 1994 DeviceType_T deviceType, 1995 AccessMode_T accessMode, 1996 BOOL fForceNewUuid, 1997 IMedium **aMedium) 1998 { 1999 HRESULT rc = S_OK; 2000 CheckComArgStrNotEmptyOrNull(aLocation); 2001 CheckComArgOutPointerValid(aMedium); 2002 2003 AutoCaller autoCaller(this); 2004 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2005 2006 Guid id(aLocation); 2007 ComObjPtr<Medium> pMedium; 2008 2009 // have to get write lock as the whole find/update sequence must be done 2010 // in one critical section, otherwise there are races which can lead to 2011 // multiple Medium objects with the same content 2012 AutoWriteLock treeLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 2013 2014 // check if the device type is correct, and see if a medium for the 2015 // given path has already initialized; if so, return that 2016 switch (deviceType) 2017 { 2018 case DeviceType_HardDisk: 2019 if (id.isValid() && !id.isZero()) 2020 rc = findHardDiskById(id, false /* setError */, &pMedium); 2021 else 2022 rc = findHardDiskByLocation(aLocation, 2023 false, /* aSetError */ 2024 &pMedium); 2025 break; 2026 2027 case DeviceType_Floppy: 2028 case DeviceType_DVD: 2029 if (id.isValid() && !id.isZero()) 2030 rc = findDVDOrFloppyImage(deviceType, &id, Utf8Str::Empty, 2031 false /* setError */, &pMedium); 2032 else 2033 rc = findDVDOrFloppyImage(deviceType, NULL, aLocation, 2034 false /* setError */, &pMedium); 2035 2036 // enforce read-only for DVDs even if caller specified ReadWrite 2037 if (deviceType == DeviceType_DVD) 2038 accessMode = AccessMode_ReadOnly; 2039 break; 2040 2041 default: 2042 return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy %d", deviceType); 2043 } 2044 2045 if (pMedium.isNull()) 2046 { 2047 pMedium.createObject(); 2048 treeLock.release(); 2049 rc = pMedium->init(this, 2050 aLocation, 2051 (accessMode == AccessMode_ReadWrite) ? Medium::OpenReadWrite : Medium::OpenReadOnly, 2052 !!fForceNewUuid, 2053 deviceType); 2054 treeLock.acquire(); 2055 2056 if (SUCCEEDED(rc)) 2057 { 2058 rc = registerMedium(pMedium, &pMedium, deviceType); 2059 2060 treeLock.release(); 2061 2062 /* Note that it's important to call uninit() on failure to register 2063 * because the differencing hard disk would have been already associated 2064 * with the parent and this association needs to be broken. */ 2065 2066 if (FAILED(rc)) 2067 { 2068 pMedium->uninit(); 2069 rc = VBOX_E_OBJECT_NOT_FOUND; 2070 } 2071 } 2072 else 2073 rc = VBOX_E_OBJECT_NOT_FOUND; 2074 } 2075 2076 if (SUCCEEDED(rc)) 2077 pMedium.queryInterfaceTo(aMedium); 2078 2079 return rc; 2080 } 2081 2082 2083 /** @note Locks this object for reading. */ 2084 STDMETHODIMP VirtualBox::GetGuestOSType(IN_BSTR aId, IGuestOSType **aType) 2085 { 2086 CheckComArgNotNull(aType); 2087 2088 AutoCaller autoCaller(this); 2089 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2090 2091 *aType = NULL; 2092 2093 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS); 2094 for (GuestOSTypesOList::iterator it = m->allGuestOSTypes.begin(); 2095 it != m->allGuestOSTypes.end(); 2096 ++it) 2097 { 2098 const Bstr &typeId = (*it)->id(); 2099 AssertMsg(!typeId.isEmpty(), ("ID must not be NULL")); 2100 if (typeId.compare(aId, Bstr::CaseInsensitive) == 0) 2101 { 2102 (*it).queryInterfaceTo(aType); 2103 break; 2104 } 2105 } 2106 2107 return (*aType) ? S_OK : 2108 setError(E_INVALIDARG, 2109 tr("'%ls' is not a valid Guest OS type"), 2110 aId); 2111 } 2112 2113 STDMETHODIMP VirtualBox::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, 2114 BOOL /* aWritable */, BOOL /* aAutoMount */) 2115 { 2116 CheckComArgStrNotEmptyOrNull(aName); 2117 CheckComArgStrNotEmptyOrNull(aHostPath); 2118 2119 AutoCaller autoCaller(this); 2120 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2121 2122 return setError(E_NOTIMPL, "Not yet implemented"); 2123 } 2124 2125 STDMETHODIMP VirtualBox::RemoveSharedFolder(IN_BSTR aName) 2126 { 2127 CheckComArgStrNotEmptyOrNull(aName); 2128 2129 AutoCaller autoCaller(this); 2130 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2131 2132 return setError(E_NOTIMPL, "Not yet implemented"); 2133 } 2134 2135 /** 2136 * @note Locks this object for reading. 2137 */ 2138 STDMETHODIMP VirtualBox::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys)) 2139 { 2140 using namespace settings; 2141 2142 CheckComArgOutSafeArrayPointerValid(aKeys); 2143 2144 AutoCaller autoCaller(this); 2145 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2146 2147 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2148 2149 com::SafeArray<BSTR> saKeys(m->pMainConfigFile->mapExtraDataItems.size()); 2150 int i = 0; 2151 for (StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.begin(); 2152 it != m->pMainConfigFile->mapExtraDataItems.end(); 2153 ++it, ++i) 2154 { 2155 const Utf8Str &strName = it->first; // the key 2156 strName.cloneTo(&saKeys[i]); 2157 } 2158 saKeys.detachTo(ComSafeArrayOutArg(aKeys)); 2159 2160 return S_OK; 2161 } 2162 2163 /** 2164 * @note Locks this object for reading. 2165 */ 2166 STDMETHODIMP VirtualBox::GetExtraData(IN_BSTR aKey, 2167 BSTR *aValue) 2168 { 2169 CheckComArgStrNotEmptyOrNull(aKey); 2170 CheckComArgNotNull(aValue); 2171 2172 AutoCaller autoCaller(this); 2173 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2174 2175 /* start with nothing found */ 2176 Utf8Str strKey(aKey); 2177 Bstr bstrResult; 2178 2179 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey); 2180 if (it != m->pMainConfigFile->mapExtraDataItems.end()) 2181 // found: 2182 bstrResult = it->second; // source is a Utf8Str 2183 2184 /* return the result to caller (may be empty) */ 2185 bstrResult.cloneTo(aValue); 2186 2187 return S_OK; 2188 } 2189 2190 /** 2191 * @note Locks this object for writing. 2192 */ 2193 STDMETHODIMP VirtualBox::SetExtraData(IN_BSTR aKey, 2194 IN_BSTR aValue) 2195 { 2196 CheckComArgStrNotEmptyOrNull(aKey); 2197 2198 AutoCaller autoCaller(this); 2199 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2200 2201 Utf8Str strKey(aKey); 2202 Utf8Str strValue(aValue); 2203 Utf8Str strOldValue; // empty 2204 2205 // locking note: we only hold the read lock briefly to look up the old value, 2206 // then release it and call the onExtraCanChange callbacks. There is a small 2207 // chance of a race insofar as the callback might be called twice if two callers 2208 // change the same key at the same time, but that's a much better solution 2209 // than the deadlock we had here before. The actual changing of the extradata 2210 // is then performed under the write lock and race-free. 2211 2212 // look up the old value first; if nothing has changed then we need not do anything 2213 { 2214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up 2215 settings::StringsMap::const_iterator it = m->pMainConfigFile->mapExtraDataItems.find(strKey); 2216 if (it != m->pMainConfigFile->mapExtraDataItems.end()) 2217 strOldValue = it->second; 2218 } 2219 2220 bool fChanged; 2221 if ((fChanged = (strOldValue != strValue))) 2222 { 2223 // ask for permission from all listeners outside the locks; 2224 // onExtraDataCanChange() only briefly requests the VirtualBox 2225 // lock to copy the list of callbacks to invoke 2226 Bstr error; 2227 Bstr bstrValue(aValue); 2228 2229 if (!onExtraDataCanChange(Guid::Empty, aKey, bstrValue.raw(), error)) 2230 { 2231 const char *sep = error.isEmpty() ? "" : ": "; 2232 CBSTR err = error.raw(); 2233 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n", 2234 sep, err)); 2235 return setError(E_ACCESSDENIED, 2236 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"), 2237 aKey, 2238 bstrValue.raw(), 2239 sep, 2240 err); 2241 } 2242 2243 // data is changing and change not vetoed: then write it out under the lock 2244 2245 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2246 2247 if (strValue.isEmpty()) 2248 m->pMainConfigFile->mapExtraDataItems.erase(strKey); 2249 else 2250 m->pMainConfigFile->mapExtraDataItems[strKey] = strValue; 2251 // creates a new key if needed 2252 2253 /* save settings on success */ 2254 HRESULT rc = saveSettings(); 2255 if (FAILED(rc)) return rc; 2256 } 2257 2258 // fire notification outside the lock 2259 if (fChanged) 2260 onExtraDataChange(Guid::Empty, aKey, aValue); 2261 2262 return S_OK; 2263 } 2264 2265 /** 2266 * 2267 */ 2268 STDMETHODIMP VirtualBox::SetSettingsSecret(IN_BSTR aValue) 2269 { 2270 storeSettingsKey(aValue); 2271 decryptSettings(); 2272 return S_OK; 2273 } 2274 2275 int VirtualBox::decryptMediumSettings(Medium *pMedium) 2276 { 2277 Bstr bstrCipher; 2278 HRESULT hrc = pMedium->GetProperty(Bstr("InitiatorSecretEncrypted").raw(), 2279 bstrCipher.asOutParam()); 2280 if (SUCCEEDED(hrc)) 2281 { 2282 Utf8Str strPlaintext; 2283 int rc = decryptSetting(&strPlaintext, bstrCipher); 2284 if (RT_SUCCESS(rc)) 2285 pMedium->setPropertyDirect("InitiatorSecret", strPlaintext); 2286 else 2287 return rc; 2288 } 2289 return VINF_SUCCESS; 2290 } 2291 2292 /** 2293 * Decrypt all encrypted settings. 2294 * 2295 * So far we only have encrypted iSCSI initiator secrets so we just go through 2296 * all hard disk mediums and determine the plain 'InitiatorSecret' from 2297 * 'InitiatorSecretEncrypted. The latter is stored as Base64 because medium 2298 * properties need to be null-terminated strings. 2299 */ 2300 int VirtualBox::decryptSettings() 2301 { 2302 bool fFailure = false; 2303 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS); 2304 for (MediaList::const_iterator mt = m->allHardDisks.begin(); 2305 mt != m->allHardDisks.end(); 2306 ++mt) 2307 { 2308 ComObjPtr<Medium> pMedium = *mt; 2309 AutoCaller medCaller(pMedium); 2310 if (FAILED(medCaller.rc())) 2311 continue; 2312 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS); 2313 int vrc = decryptMediumSettings(pMedium); 2314 if (RT_FAILURE(vrc)) 2315 fFailure = true; 2316 } 2317 return fFailure ? VERR_INVALID_PARAMETER : VINF_SUCCESS; 2318 } 2319 2320 /** 2321 * Encode. 2322 * 2323 * @param aPlaintext plaintext to be encrypted 2324 * @param aCiphertext resulting ciphertext (base64-encoded) 2325 */ 2326 int VirtualBox::encryptSetting(const Utf8Str &aPlaintext, Utf8Str *aCiphertext) 2327 { 2328 uint8_t abCiphertext[32]; 2329 char szCipherBase64[128]; 2330 size_t cchCipherBase64; 2331 int rc = encryptSettingBytes((uint8_t*)aPlaintext.c_str(), abCiphertext, 2332 aPlaintext.length()+1, sizeof(abCiphertext)); 2333 if (RT_SUCCESS(rc)) 2334 { 2335 rc = RTBase64Encode(abCiphertext, sizeof(abCiphertext), 2336 szCipherBase64, sizeof(szCipherBase64), 2337 &cchCipherBase64); 2338 if (RT_SUCCESS(rc)) 2339 *aCiphertext = szCipherBase64; 2340 } 2341 return rc; 2342 } 2343 2344 /** 2345 * Decode. 2346 * 2347 * @param aPlaintext resulting plaintext 2348 * @param aCiphertext ciphertext (base64-encoded) to decrypt 2349 */ 2350 int VirtualBox::decryptSetting(Utf8Str *aPlaintext, const Utf8Str &aCiphertext) 2351 { 2352 uint8_t abPlaintext[64]; 2353 uint8_t abCiphertext[64]; 2354 size_t cbCiphertext; 2355 int rc = RTBase64Decode(aCiphertext.c_str(), 2356 abCiphertext, sizeof(abCiphertext), 2357 &cbCiphertext, NULL); 2358 if (RT_SUCCESS(rc)) 2359 { 2360 rc = decryptSettingBytes(abPlaintext, abCiphertext, cbCiphertext); 2361 if (RT_SUCCESS(rc)) 2362 { 2363 for (unsigned i = 0; i < cbCiphertext; i++) 2364 { 2365 /* sanity check: null-terminated string? */ 2366 if (abPlaintext[i] == '\0') 2367 { 2368 /* sanity check: valid UTF8 string? */ 2369 if (RTStrIsValidEncoding((const char*)abPlaintext)) 2370 { 2371 *aPlaintext = Utf8Str((const char*)abPlaintext); 2372 return VINF_SUCCESS; 2373 } 2374 } 2375 } 2376 rc = VERR_INVALID_MAGIC; 2377 } 2378 } 2379 return rc; 2380 } 2381 2382 /** 2383 * Encrypt secret bytes. Use the m->SettingsCipherKey as key. 2384 * 2385 * @param aPlaintext clear text to be encrypted 2386 * @param aCiphertext resulting encrypted text 2387 * @param aPlaintextSize size of the plaintext 2388 * @param aCiphertextSize size of the ciphertext 2389 */ 2390 int VirtualBox::encryptSettingBytes(const uint8_t *aPlaintext, uint8_t *aCiphertext, 2391 size_t aPlaintextSize, size_t aCiphertextSize) const 2392 { 2393 unsigned i, j; 2394 uint8_t aBytes[64]; 2395 2396 if (!m->fSettingsCipherKeySet) 2397 return VERR_INVALID_STATE; 2398 2399 if (aCiphertextSize > sizeof(aBytes)) 2400 return VERR_BUFFER_OVERFLOW; 2401 2402 if (aCiphertextSize < 32) 2403 return VERR_INVALID_PARAMETER; 2404 2405 AssertCompile(sizeof(m->SettingsCipherKey) >= 32); 2406 2407 /* store the first 8 bytes of the cipherkey for verification */ 2408 for (i = 0, j = 0; i < 8; i++, j++) 2409 aCiphertext[i] = m->SettingsCipherKey[j]; 2410 2411 for (unsigned k = 0; k < aPlaintextSize && i < aCiphertextSize; i++, k++) 2412 { 2413 aCiphertext[i] = (aPlaintext[k] ^ m->SettingsCipherKey[j]); 2414 if (++j >= sizeof(m->SettingsCipherKey)) 2415 j = 0; 2416 } 2417 2418 /* fill with random data to have a minimal length (salt) */ 2419 if (i < aCiphertextSize) 2420 { 2421 RTRandBytes(aBytes, aCiphertextSize - i); 2422 for (int k = 0; i < aCiphertextSize; i++, k++) 2423 { 2424 aCiphertext[i] = aBytes[k] ^ m->SettingsCipherKey[j]; 2425 if (++j >= sizeof(m->SettingsCipherKey)) 2426 j = 0; 2427 } 2428 } 2429 2430 return VINF_SUCCESS; 2431 } 2432 2433 /** 2434 * Decrypt secret bytes. Use the m->SettingsCipherKey as key. 2435 * 2436 * @param aPlaintext resulting plaintext 2437 * @param aCiphertext ciphertext to be decrypted 2438 * @param aCiphertextSize size of the ciphertext == size of the plaintext 2439 */ 2440 int VirtualBox::decryptSettingBytes(uint8_t *aPlaintext, 2441 const uint8_t *aCiphertext, size_t aCiphertextSize) const 2442 { 2443 unsigned i, j; 2444 2445 if (!m->fSettingsCipherKeySet) 2446 return VERR_INVALID_STATE; 2447 2448 if (aCiphertextSize < 32) 2449 return VERR_INVALID_PARAMETER; 2450 2451 /* key verification */ 2452 for (i = 0, j = 0; i < 8; i++, j++) 2453 if (aCiphertext[i] != m->SettingsCipherKey[j]) 2454 return VERR_INVALID_MAGIC; 2455 2456 /* poison */ 2457 memset(aPlaintext, 0xff, aCiphertextSize); 2458 for (int k = 0; i < aCiphertextSize; i++, k++) 2459 { 2460 aPlaintext[k] = aCiphertext[i] ^ m->SettingsCipherKey[j]; 2461 if (++j >= sizeof(m->SettingsCipherKey)) 2462 j = 0; 2463 } 2464 2465 return VINF_SUCCESS; 2466 } 2467 2468 /** 2469 * Store a settings key. 2470 * 2471 * @param aKey the key to store 2472 */ 2473 void VirtualBox::storeSettingsKey(const Utf8Str &aKey) 2474 { 2475 RTSha512(aKey.c_str(), aKey.length(), m->SettingsCipherKey); 2476 m->fSettingsCipherKeySet = true; 2477 } 2478 2479 // public methods only for internal purposes 2480 ///////////////////////////////////////////////////////////////////////////// 2481 2482 #ifdef DEBUG 2483 void VirtualBox::dumpAllBackRefs() 2484 { 2485 { 2486 AutoReadLock al(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS); 2487 for (MediaList::const_iterator mt = m->allHardDisks.begin(); 2488 mt != m->allHardDisks.end(); 2489 ++mt) 2490 { 2491 ComObjPtr<Medium> pMedium = *mt; 2492 pMedium->dumpBackRefs(); 2493 } 2494 } 2495 { 2496 AutoReadLock al(m->allDVDImages.getLockHandle() COMMA_LOCKVAL_SRC_POS); 2497 for (MediaList::const_iterator mt = m->allDVDImages.begin(); 2498 mt != m->allDVDImages.end(); 2499 ++mt) 2500 { 2501 ComObjPtr<Medium> pMedium = *mt; 2502 pMedium->dumpBackRefs(); 2503 } 2504 } 2505 } 2506 #endif 2507 2508 /** 2509 * Posts an event to the event queue that is processed asynchronously 2510 * on a dedicated thread. 2511 * 2512 * Posting events to the dedicated event queue is useful to perform secondary 2513 * actions outside any object locks -- for example, to iterate over a list 2514 * of callbacks and inform them about some change caused by some object's 2515 * method call. 2516 * 2517 * @param event event to post; must have been allocated using |new|, will 2518 * be deleted automatically by the event thread after processing 2519 * 2520 * @note Doesn't lock any object. 2521 */ 2522 HRESULT VirtualBox::postEvent(Event *event) 2523 { 2524 AssertReturn(event, E_FAIL); 2525 2526 HRESULT rc; 2527 AutoCaller autoCaller(this); 2528 if (SUCCEEDED((rc = autoCaller.rc()))) 2529 { 2530 if (autoCaller.state() != Ready) 2531 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the event is discarded!\n", 2532 autoCaller.state())); 2533 // return S_OK 2534 else if ( (m->pAsyncEventQ) 2535 && (m->pAsyncEventQ->postEvent(event)) 2536 ) 2537 return S_OK; 2538 else 2539 rc = E_FAIL; 2540 } 2541 2542 // in any event of failure, we must clean up here, or we'll leak; 2543 // the caller has allocated the object using new() 2544 delete event; 2545 return rc; 2546 } 2547 2548 /** 2549 * Adds a progress to the global collection of pending operations. 2550 * Usually gets called upon progress object initialization. 2551 * 2552 * @param aProgress Operation to add to the collection. 2553 * 2554 * @note Doesn't lock objects. 2555 */ 2556 HRESULT VirtualBox::addProgress(IProgress *aProgress) 2557 { 2558 CheckComArgNotNull(aProgress); 2559 2560 AutoCaller autoCaller(this); 2561 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2562 2563 Bstr id; 2564 HRESULT rc = aProgress->COMGETTER(Id)(id.asOutParam()); 2565 AssertComRCReturnRC(rc); 2566 2567 /* protect mProgressOperations */ 2568 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS); 2569 2570 m->mapProgressOperations.insert(ProgressMap::value_type(Guid(id), aProgress)); 2571 return S_OK; 2572 } 2573 2574 /** 2575 * Removes the progress from the global collection of pending operations. 2576 * Usually gets called upon progress completion. 2577 * 2578 * @param aId UUID of the progress operation to remove 2579 * 2580 * @note Doesn't lock objects. 2581 */ 2582 HRESULT VirtualBox::removeProgress(IN_GUID aId) 2583 { 2584 AutoCaller autoCaller(this); 2585 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2586 2587 ComPtr<IProgress> progress; 2588 2589 /* protect mProgressOperations */ 2590 AutoWriteLock safeLock(m->mtxProgressOperations COMMA_LOCKVAL_SRC_POS); 2591 2592 size_t cnt = m->mapProgressOperations.erase(aId); 2593 Assert(cnt == 1); 2594 NOREF(cnt); 2595 2596 return S_OK; 2597 } 2598 2599 #ifdef RT_OS_WINDOWS 2600 2601 struct StartSVCHelperClientData 2602 { 2603 ComObjPtr<VirtualBox> that; 2604 ComObjPtr<Progress> progress; 2605 bool privileged; 2606 VirtualBox::SVCHelperClientFunc func; 2607 void *user; 2608 }; 2609 2610 /** 2611 * Helper method that starts a worker thread that: 2612 * - creates a pipe communication channel using SVCHlpClient; 2613 * - starts an SVC Helper process that will inherit this channel; 2614 * - executes the supplied function by passing it the created SVCHlpClient 2615 * and opened instance to communicate to the Helper process and the given 2616 * Progress object. 2617 * 2618 * The user function is supposed to communicate to the helper process 2619 * using the \a aClient argument to do the requested job and optionally expose 2620 * the progress through the \a aProgress object. The user function should never 2621 * call notifyComplete() on it: this will be done automatically using the 2622 * result code returned by the function. 2623 * 2624 * Before the user function is started, the communication channel passed to 2625 * the \a aClient argument is fully set up, the function should start using 2626 * its write() and read() methods directly. 2627 * 2628 * The \a aVrc parameter of the user function may be used to return an error 2629 * code if it is related to communication errors (for example, returned by 2630 * the SVCHlpClient members when they fail). In this case, the correct error 2631 * message using this value will be reported to the caller. Note that the 2632 * value of \a aVrc is inspected only if the user function itself returns 2633 * success. 2634 * 2635 * If a failure happens anywhere before the user function would be normally 2636 * called, it will be called anyway in special "cleanup only" mode indicated 2637 * by \a aClient, \a aProgress and \aVrc arguments set to NULL. In this mode, 2638 * all the function is supposed to do is to cleanup its aUser argument if 2639 * necessary (it's assumed that the ownership of this argument is passed to 2640 * the user function once #startSVCHelperClient() returns a success, thus 2641 * making it responsible for the cleanup). 2642 * 2643 * After the user function returns, the thread will send the SVCHlpMsg::Null 2644 * message to indicate a process termination. 2645 * 2646 * @param aPrivileged |true| to start the SVC Helper process as a privileged 2647 * user that can perform administrative tasks 2648 * @param aFunc user function to run 2649 * @param aUser argument to the user function 2650 * @param aProgress progress object that will track operation completion 2651 * 2652 * @note aPrivileged is currently ignored (due to some unsolved problems in 2653 * Vista) and the process will be started as a normal (unprivileged) 2654 * process. 2655 * 2656 * @note Doesn't lock anything. 2657 */ 2658 HRESULT VirtualBox::startSVCHelperClient(bool aPrivileged, 2659 SVCHelperClientFunc aFunc, 2660 void *aUser, Progress *aProgress) 2661 { 2662 AssertReturn(aFunc, E_POINTER); 2663 AssertReturn(aProgress, E_POINTER); 2664 2665 AutoCaller autoCaller(this); 2666 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2667 2668 /* create the SVCHelperClientThread() argument */ 2669 std::auto_ptr <StartSVCHelperClientData> 2670 d(new StartSVCHelperClientData()); 2671 AssertReturn(d.get(), E_OUTOFMEMORY); 2672 2673 d->that = this; 2674 d->progress = aProgress; 2675 d->privileged = aPrivileged; 2676 d->func = aFunc; 2677 d->user = aUser; 2678 2679 RTTHREAD tid = NIL_RTTHREAD; 2680 int vrc = RTThreadCreate(&tid, SVCHelperClientThread, 2681 static_cast <void *>(d.get()), 2682 0, RTTHREADTYPE_MAIN_WORKER, 2683 RTTHREADFLAGS_WAITABLE, "SVCHelper"); 2684 if (RT_FAILURE(vrc)) 2685 return setError(E_FAIL, "Could not create SVCHelper thread (%Rrc)", vrc); 2686 2687 /* d is now owned by SVCHelperClientThread(), so release it */ 2688 d.release(); 2689 2690 return S_OK; 2691 } 2692 2693 /** 2694 * Worker thread for startSVCHelperClient(). 2695 */ 2696 /* static */ 2697 DECLCALLBACK(int) 2698 VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser) 152 /*static*/ 153 DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD /* thread */, void *pvUser) 2699 154 { 2700 155 LogFlowFuncEnter(); 2701 156 2702 std::auto_ptr<StartSVCHelperClientData> 2703 d(static_cast<StartSVCHelperClientData*>(aUser)); 2704 2705 HRESULT rc = S_OK; 2706 bool userFuncCalled = false; 157 VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser; 158 Assert(that); 159 160 typedef std::vector<ComObjPtr<Machine> > MachineVector; 161 typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector; 162 163 SessionMachineVector machines; 164 MachineVector spawnedMachines; 165 166 size_t cnt = 0; 167 size_t cntSpawned = 0; 168 169 VirtualBoxBase::initializeComForThread(); 170 171 #if defined(RT_OS_WINDOWS) 172 173 /// @todo (dmik) processes reaping! 174 175 HANDLE handles[MAXIMUM_WAIT_OBJECTS]; 176 handles[0] = that->mUpdateReq; 2707 177 2708 178 do 2709 179 { 2710 AssertBreakStmt(d.get(), rc = E_POINTER); 2711 AssertReturn(!d->progress.isNull(), E_POINTER); 2712 2713 /* protect VirtualBox from uninitialization */ 2714 AutoCaller autoCaller(d->that); 2715 if (!autoCaller.isOk()) 2716 { 2717 /* it's too late */ 2718 rc = autoCaller.rc(); 2719 break; 2720 } 2721 2722 int vrc = VINF_SUCCESS; 2723 2724 Guid id; 2725 id.create(); 2726 SVCHlpClient client; 2727 vrc = client.create(Utf8StrFmt("VirtualBox\\SVCHelper\\{%RTuuid}", 2728 id.raw()).c_str()); 2729 if (RT_FAILURE(vrc)) 2730 { 2731 rc = d->that->setError(E_FAIL, 2732 tr("Could not create the communication channel (%Rrc)"), vrc); 2733 break; 2734 } 2735 2736 /* get the path to the executable */ 2737 char exePathBuf[RTPATH_MAX]; 2738 char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX); 2739 if (!exePath) 2740 { 2741 rc = d->that->setError(E_FAIL, tr("Cannot get executable name")); 2742 break; 2743 } 2744 2745 Utf8Str argsStr = Utf8StrFmt("/Helper %s", client.name().c_str()); 2746 2747 LogFlowFunc(("Starting '\"%s\" %s'...\n", exePath, argsStr.c_str())); 2748 2749 RTPROCESS pid = NIL_RTPROCESS; 2750 2751 if (d->privileged) 2752 { 2753 /* Attempt to start a privileged process using the Run As dialog */ 2754 2755 Bstr file = exePath; 2756 Bstr parameters = argsStr; 2757 2758 SHELLEXECUTEINFO shExecInfo; 2759 2760 shExecInfo.cbSize = sizeof(SHELLEXECUTEINFO); 2761 2762 shExecInfo.fMask = NULL; 2763 shExecInfo.hwnd = NULL; 2764 shExecInfo.lpVerb = L"runas"; 2765 shExecInfo.lpFile = file.raw(); 2766 shExecInfo.lpParameters = parameters.raw(); 2767 shExecInfo.lpDirectory = NULL; 2768 shExecInfo.nShow = SW_NORMAL; 2769 shExecInfo.hInstApp = NULL; 2770 2771 if (!ShellExecuteEx(&shExecInfo)) 2772 { 2773 int vrc2 = RTErrConvertFromWin32(GetLastError()); 2774 /* hide excessive details in case of a frequent error 2775 * (pressing the Cancel button to close the Run As dialog) */ 2776 if (vrc2 == VERR_CANCELLED) 2777 rc = d->that->setError(E_FAIL, 2778 tr("Operation canceled by the user")); 2779 else 2780 rc = d->that->setError(E_FAIL, 2781 tr("Could not launch a privileged process '%s' (%Rrc)"), 2782 exePath, vrc2); 2783 break; 2784 } 2785 } 2786 else 2787 { 2788 const char *args[] = { exePath, "/Helper", client.name().c_str(), 0 }; 2789 vrc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &pid); 2790 if (RT_FAILURE(vrc)) 2791 { 2792 rc = d->that->setError(E_FAIL, 2793 tr("Could not launch a process '%s' (%Rrc)"), exePath, vrc); 2794 break; 2795 } 2796 } 2797 2798 /* wait for the client to connect */ 2799 vrc = client.connect(); 2800 if (RT_SUCCESS(vrc)) 2801 { 2802 /* start the user supplied function */ 2803 rc = d->func(&client, d->progress, d->user, &vrc); 2804 userFuncCalled = true; 2805 } 2806 2807 /* send the termination signal to the process anyway */ 2808 { 2809 int vrc2 = client.write(SVCHlpMsg::Null); 2810 if (RT_SUCCESS(vrc)) 2811 vrc = vrc2; 2812 } 2813 2814 if (SUCCEEDED(rc) && RT_FAILURE(vrc)) 2815 { 2816 rc = d->that->setError(E_FAIL, 2817 tr("Could not operate the communication channel (%Rrc)"), vrc); 2818 break; 2819 } 2820 } 2821 while (0); 2822 2823 if (FAILED(rc) && !userFuncCalled) 2824 { 2825 /* call the user function in the "cleanup only" mode 2826 * to let it free resources passed to in aUser */ 2827 d->func(NULL, NULL, d->user, NULL); 2828 } 2829 2830 d->progress->notifyComplete(rc); 2831 2832 LogFlowFuncLeave(); 2833 return 0; 2834 } 2835 2836 #endif /* RT_OS_WINDOWS */ 2837 2838 /** 2839 * Sends a signal to the client watcher thread to rescan the set of machines 2840 * that have open sessions. 2841 * 2842 * @note Doesn't lock anything. 2843 */ 2844 void VirtualBox::updateClientWatcher() 2845 { 2846 AutoCaller autoCaller(this); 2847 AssertComRCReturnVoid(autoCaller.rc()); 2848 2849 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD); 2850 2851 /* sent an update request */ 2852 #if defined(RT_OS_WINDOWS) 2853 ::SetEvent(m->updateReq); 2854 #elif defined(RT_OS_OS2) 2855 RTSemEventSignal(m->updateReq); 2856 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 2857 ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1); 2858 RTSemEventSignal(m->updateReq); 2859 #else 2860 # error "Port me!" 2861 #endif 2862 } 2863 2864 /** 2865 * Adds the given child process ID to the list of processes to be reaped. 2866 * This call should be followed by #updateClientWatcher() to take the effect. 2867 */ 2868 void VirtualBox::addProcessToReap(RTPROCESS pid) 2869 { 2870 AutoCaller autoCaller(this); 2871 AssertComRCReturnVoid(autoCaller.rc()); 2872 2873 /// @todo (dmik) Win32? 2874 #ifndef RT_OS_WINDOWS 2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2876 m->llProcesses.push_back(pid); 2877 #endif 2878 } 2879 2880 /** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */ 2881 struct MachineEvent : public VirtualBox::CallbackEvent 2882 { 2883 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, BOOL aBool) 2884 : CallbackEvent(aVB, aWhat), id(aId.toUtf16()) 2885 , mBool(aBool) 2886 { } 2887 2888 MachineEvent(VirtualBox *aVB, VBoxEventType_T aWhat, const Guid &aId, MachineState_T aState) 2889 : CallbackEvent(aVB, aWhat), id(aId.toUtf16()) 2890 , mState(aState) 2891 {} 2892 2893 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) 2894 { 2895 switch (mWhat) 2896 { 2897 case VBoxEventType_OnMachineDataChanged: 2898 aEvDesc.init(aSource, mWhat, id.raw(), mBool); 2899 break; 2900 2901 case VBoxEventType_OnMachineStateChanged: 2902 aEvDesc.init(aSource, mWhat, id.raw(), mState); 2903 break; 2904 2905 case VBoxEventType_OnMachineRegistered: 2906 aEvDesc.init(aSource, mWhat, id.raw(), mBool); 2907 break; 2908 2909 default: 2910 AssertFailedReturn(S_OK); 2911 } 2912 return S_OK; 2913 } 2914 2915 Bstr id; 2916 MachineState_T mState; 2917 BOOL mBool; 2918 }; 2919 2920 /** 2921 * @note Doesn't lock any object. 2922 */ 2923 void VirtualBox::onMachineStateChange(const Guid &aId, MachineState_T aState) 2924 { 2925 postEvent(new MachineEvent(this, VBoxEventType_OnMachineStateChanged, aId, aState)); 2926 } 2927 2928 /** 2929 * @note Doesn't lock any object. 2930 */ 2931 void VirtualBox::onMachineDataChange(const Guid &aId, BOOL aTemporary) 2932 { 2933 postEvent(new MachineEvent(this, VBoxEventType_OnMachineDataChanged, aId, aTemporary)); 2934 } 2935 2936 /** 2937 * @note Locks this object for reading. 2938 */ 2939 BOOL VirtualBox::onExtraDataCanChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue, 2940 Bstr &aError) 2941 { 2942 LogFlowThisFunc(("machine={%s} aKey={%ls} aValue={%ls}\n", 2943 aId.toString().c_str(), aKey, aValue)); 2944 2945 AutoCaller autoCaller(this); 2946 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 2947 2948 BOOL allowChange = TRUE; 2949 Bstr id = aId.toUtf16(); 2950 2951 VBoxEventDesc evDesc; 2952 evDesc.init(m->pEventSource, VBoxEventType_OnExtraDataCanChange, id.raw(), aKey, aValue); 2953 BOOL fDelivered = evDesc.fire(3000); /* Wait up to 3 secs for delivery */ 2954 //Assert(fDelivered); 2955 if (fDelivered) 2956 { 2957 ComPtr<IEvent> aEvent; 2958 evDesc.getEvent(aEvent.asOutParam()); 2959 ComPtr<IExtraDataCanChangeEvent> aCanChangeEvent = aEvent; 2960 Assert(aCanChangeEvent); 2961 BOOL fVetoed = FALSE; 2962 aCanChangeEvent->IsVetoed(&fVetoed); 2963 allowChange = !fVetoed; 2964 2965 if (!allowChange) 2966 { 2967 SafeArray<BSTR> aVetos; 2968 aCanChangeEvent->GetVetos(ComSafeArrayAsOutParam(aVetos)); 2969 if (aVetos.size() > 0) 2970 aError = aVetos[0]; 2971 } 2972 } 2973 else 2974 allowChange = TRUE; 2975 2976 LogFlowThisFunc(("allowChange=%RTbool\n", allowChange)); 2977 return allowChange; 2978 } 2979 2980 /** Event for onExtraDataChange() */ 2981 struct ExtraDataEvent : public VirtualBox::CallbackEvent 2982 { 2983 ExtraDataEvent(VirtualBox *aVB, const Guid &aMachineId, 2984 IN_BSTR aKey, IN_BSTR aVal) 2985 : CallbackEvent(aVB, VBoxEventType_OnExtraDataChanged) 2986 , machineId(aMachineId.toUtf16()), key(aKey), val(aVal) 2987 {} 2988 2989 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) 2990 { 2991 return aEvDesc.init(aSource, VBoxEventType_OnExtraDataChanged, machineId.raw(), key.raw(), val.raw()); 2992 } 2993 2994 Bstr machineId, key, val; 2995 }; 2996 2997 /** 2998 * @note Doesn't lock any object. 2999 */ 3000 void VirtualBox::onExtraDataChange(const Guid &aId, IN_BSTR aKey, IN_BSTR aValue) 3001 { 3002 postEvent(new ExtraDataEvent(this, aId, aKey, aValue)); 3003 } 3004 3005 /** 3006 * @note Doesn't lock any object. 3007 */ 3008 void VirtualBox::onMachineRegistered(const Guid &aId, BOOL aRegistered) 3009 { 3010 postEvent(new MachineEvent(this, VBoxEventType_OnMachineRegistered, aId, aRegistered)); 3011 } 3012 3013 /** Event for onSessionStateChange() */ 3014 struct SessionEvent : public VirtualBox::CallbackEvent 3015 { 3016 SessionEvent(VirtualBox *aVB, const Guid &aMachineId, SessionState_T aState) 3017 : CallbackEvent(aVB, VBoxEventType_OnSessionStateChanged) 3018 , machineId(aMachineId.toUtf16()), sessionState(aState) 3019 {} 3020 3021 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) 3022 { 3023 return aEvDesc.init(aSource, VBoxEventType_OnSessionStateChanged, machineId.raw(), sessionState); 3024 } 3025 Bstr machineId; 3026 SessionState_T sessionState; 3027 }; 3028 3029 /** 3030 * @note Doesn't lock any object. 3031 */ 3032 void VirtualBox::onSessionStateChange(const Guid &aId, SessionState_T aState) 3033 { 3034 postEvent(new SessionEvent(this, aId, aState)); 3035 } 3036 3037 /** Event for onSnapshotTaken(), onSnapshotDeleted() and onSnapshotChange() */ 3038 struct SnapshotEvent : public VirtualBox::CallbackEvent 3039 { 3040 SnapshotEvent(VirtualBox *aVB, const Guid &aMachineId, const Guid &aSnapshotId, 3041 VBoxEventType_T aWhat) 3042 : CallbackEvent(aVB, aWhat) 3043 , machineId(aMachineId), snapshotId(aSnapshotId) 3044 {} 3045 3046 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) 3047 { 3048 return aEvDesc.init(aSource, mWhat, machineId.toUtf16().raw(), 3049 snapshotId.toUtf16().raw()); 3050 } 3051 3052 Guid machineId; 3053 Guid snapshotId; 3054 }; 3055 3056 /** 3057 * @note Doesn't lock any object. 3058 */ 3059 void VirtualBox::onSnapshotTaken(const Guid &aMachineId, const Guid &aSnapshotId) 3060 { 3061 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, 3062 VBoxEventType_OnSnapshotTaken)); 3063 } 3064 3065 /** 3066 * @note Doesn't lock any object. 3067 */ 3068 void VirtualBox::onSnapshotDeleted(const Guid &aMachineId, const Guid &aSnapshotId) 3069 { 3070 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, 3071 VBoxEventType_OnSnapshotDeleted)); 3072 } 3073 3074 /** 3075 * @note Doesn't lock any object. 3076 */ 3077 void VirtualBox::onSnapshotChange(const Guid &aMachineId, const Guid &aSnapshotId) 3078 { 3079 postEvent(new SnapshotEvent(this, aMachineId, aSnapshotId, 3080 VBoxEventType_OnSnapshotChanged)); 3081 } 3082 3083 /** Event for onGuestPropertyChange() */ 3084 struct GuestPropertyEvent : public VirtualBox::CallbackEvent 3085 { 3086 GuestPropertyEvent(VirtualBox *aVBox, const Guid &aMachineId, 3087 IN_BSTR aName, IN_BSTR aValue, IN_BSTR aFlags) 3088 : CallbackEvent(aVBox, VBoxEventType_OnGuestPropertyChanged), 3089 machineId(aMachineId), 3090 name(aName), 3091 value(aValue), 3092 flags(aFlags) 3093 {} 3094 3095 virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) 3096 { 3097 return aEvDesc.init(aSource, VBoxEventType_OnGuestPropertyChanged, 3098 machineId.toUtf16().raw(), name.raw(), value.raw(), flags.raw()); 3099 } 3100 3101 Guid machineId; 3102 Bstr name, value, flags; 3103 }; 3104 3105 /** 3106 * @note Doesn't lock any object. 3107 */ 3108 void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName, 3109 IN_BSTR aValue, IN_BSTR aFlags) 3110 { 3111 postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags)); 3112 } 3113 3114 /** Event for onMachineUninit(), this is not a CallbackEvent */ 3115 class MachineUninitEvent : public Event 3116 { 3117 public: 3118 3119 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine) 3120 : mVirtualBox(aVirtualBox), mMachine(aMachine) 3121 { 3122 Assert(aVirtualBox); 3123 Assert(aMachine); 3124 } 3125 3126 void *handler() 3127 { 3128 #ifdef VBOX_WITH_RESOURCE_USAGE_API 3129 /* Handle unregistering metrics here, as it is not vital to get 3130 * it done immediately. It reduces the number of locks needed and 3131 * the lock contention in SessionMachine::uninit. */ 3132 { 3133 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS); 3134 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine); 3135 } 3136 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 3137 3138 return NULL; 3139 } 3140 3141 private: 3142 3143 /** 3144 * Note that this is a weak ref -- the CallbackEvent handler thread 3145 * is bound to the lifetime of the VirtualBox instance, so it's safe. 3146 */ 3147 VirtualBox *mVirtualBox; 3148 3149 /** Reference to the machine object. */ 3150 ComObjPtr<Machine> mMachine; 3151 }; 3152 3153 /** 3154 * Trigger internal event. This isn't meant to be signalled to clients. 3155 * @note Doesn't lock any object. 3156 */ 3157 void VirtualBox::onMachineUninit(Machine *aMachine) 3158 { 3159 postEvent(new MachineUninitEvent(this, aMachine)); 3160 } 3161 3162 /** 3163 * @note Doesn't lock any object. 3164 */ 3165 void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName, 3166 NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort, 3167 IN_BSTR aGuestIp, uint16_t aGuestPort) 3168 { 3169 fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp, 3170 aHostPort, aGuestIp, aGuestPort); 3171 } 3172 3173 void VirtualBox::onNATNetworkChange(IN_BSTR aName) 3174 { 3175 fireNATNetworkChangedEvent(m->pEventSource, aName); 3176 } 3177 3178 void VirtualBox::onNATNetworkStartStop(IN_BSTR aName, BOOL fStart) 3179 { 3180 fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart); 3181 } 3182 void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled, 3183 IN_BSTR aNetwork, IN_BSTR aGateway, 3184 BOOL aAdvertiseDefaultIpv6RouteEnabled, 3185 BOOL fNeedDhcpServer) 3186 { 3187 fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, 3188 aNetwork, aGateway, 3189 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer); 3190 } 3191 3192 void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6, 3193 IN_BSTR aRuleName, NATProtocol_T proto, 3194 IN_BSTR aHostIp, LONG aHostPort, 3195 IN_BSTR aGuestIp, LONG aGuestPort) 3196 { 3197 fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, 3198 fIpv6, aRuleName, proto, 3199 aHostIp, aHostPort, 3200 aGuestIp, aGuestPort); 3201 } 3202 3203 /** 3204 * @note Locks this object for reading. 3205 */ 3206 ComObjPtr<GuestOSType> VirtualBox::getUnknownOSType() 3207 { 3208 ComObjPtr<GuestOSType> type; 3209 AutoCaller autoCaller(this); 3210 AssertComRCReturn(autoCaller.rc(), type); 3211 3212 /* unknown type must always be the first */ 3213 ComAssertRet(m->allGuestOSTypes.size() > 0, type); 3214 3215 return m->allGuestOSTypes.front(); 3216 } 3217 3218 /** 3219 * Returns the list of opened machines (machines having direct sessions opened 3220 * by client processes) and optionally the list of direct session controls. 3221 * 3222 * @param aMachines Where to put opened machines (will be empty if none). 3223 * @param aControls Where to put direct session controls (optional). 3224 * 3225 * @note The returned lists contain smart pointers. So, clear it as soon as 3226 * it becomes no more necessary to release instances. 3227 * 3228 * @note It can be possible that a session machine from the list has been 3229 * already uninitialized, so do a usual AutoCaller/AutoReadLock sequence 3230 * when accessing unprotected data directly. 3231 * 3232 * @note Locks objects for reading. 3233 */ 3234 void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines, 3235 InternalControlList *aControls /*= NULL*/) 3236 { 3237 AutoCaller autoCaller(this); 3238 AssertComRCReturnVoid(autoCaller.rc()); 3239 3240 aMachines.clear(); 3241 if (aControls) 3242 aControls->clear(); 3243 3244 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3245 3246 for (MachinesOList::iterator it = m->allMachines.begin(); 3247 it != m->allMachines.end(); 3248 ++it) 3249 { 3250 ComObjPtr<SessionMachine> sm; 3251 ComPtr<IInternalSessionControl> ctl; 3252 if ((*it)->isSessionOpen(sm, &ctl)) 3253 { 3254 aMachines.push_back(sm); 3255 if (aControls) 3256 aControls->push_back(ctl); 3257 } 3258 } 3259 } 3260 3261 /** 3262 * Searches for a machine object with the given ID in the collection 3263 * of registered machines. 3264 * 3265 * @param aId Machine UUID to look for. 3266 * @param aPermitInaccessible If true, inaccessible machines will be found; 3267 * if false, this will fail if the given machine is inaccessible. 3268 * @param aSetError If true, set errorinfo if the machine is not found. 3269 * @param aMachine Returned machine, if found. 3270 * @return 3271 */ 3272 HRESULT VirtualBox::findMachine(const Guid &aId, 3273 bool fPermitInaccessible, 3274 bool aSetError, 3275 ComObjPtr<Machine> *aMachine /* = NULL */) 3276 { 3277 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND; 3278 3279 AutoCaller autoCaller(this); 3280 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 3281 3282 { 3283 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3284 3285 for (MachinesOList::iterator it = m->allMachines.begin(); 3286 it != m->allMachines.end(); 3287 ++it) 3288 { 3289 ComObjPtr<Machine> pMachine = *it; 3290 3291 if (!fPermitInaccessible) 3292 { 3293 // skip inaccessible machines 3294 AutoCaller machCaller(pMachine); 3295 if (FAILED(machCaller.rc())) 3296 continue; 3297 } 3298 3299 if (pMachine->getId() == aId) 3300 { 3301 rc = S_OK; 3302 if (aMachine) 3303 *aMachine = pMachine; 3304 break; 3305 } 3306 } 3307 } 3308 3309 if (aSetError && FAILED(rc)) 3310 rc = setError(rc, 3311 tr("Could not find a registered machine with UUID {%RTuuid}"), 3312 aId.raw()); 3313 3314 return rc; 3315 } 3316 3317 /** 3318 * Searches for a machine object with the given name or location in the 3319 * collection of registered machines. 3320 * 3321 * @param aName Machine name or location to look for. 3322 * @param aSetError If true, set errorinfo if the machine is not found. 3323 * @param aMachine Returned machine, if found. 3324 * @return 3325 */ 3326 HRESULT VirtualBox::findMachineByName(const Utf8Str &aName, bool aSetError, 3327 ComObjPtr<Machine> *aMachine /* = NULL */) 3328 { 3329 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND; 3330 3331 AutoReadLock al(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3332 for (MachinesOList::iterator it = m->allMachines.begin(); 3333 it != m->allMachines.end(); 3334 ++it) 3335 { 3336 ComObjPtr<Machine> &pMachine = *it; 3337 AutoCaller machCaller(pMachine); 3338 if (machCaller.rc()) 3339 continue; // we can't ask inaccessible machines for their names 3340 3341 AutoReadLock machLock(pMachine COMMA_LOCKVAL_SRC_POS); 3342 if (pMachine->getName() == aName) 3343 { 3344 rc = S_OK; 3345 if (aMachine) 3346 *aMachine = pMachine; 3347 break; 3348 } 3349 if (!RTPathCompare(pMachine->getSettingsFileFull().c_str(), aName.c_str())) 3350 { 3351 rc = S_OK; 3352 if (aMachine) 3353 *aMachine = pMachine; 3354 break; 3355 } 3356 } 3357 3358 if (aSetError && FAILED(rc)) 3359 rc = setError(rc, 3360 tr("Could not find a registered machine named '%s'"), aName.c_str()); 3361 3362 return rc; 3363 } 3364 3365 static HRESULT validateMachineGroupHelper(const Utf8Str &aGroup, bool fPrimary, VirtualBox *pVirtualBox) 3366 { 3367 /* empty strings are invalid */ 3368 if (aGroup.isEmpty()) 3369 return E_INVALIDARG; 3370 /* the toplevel group is valid */ 3371 if (aGroup == "/") 3372 return S_OK; 3373 /* any other strings of length 1 are invalid */ 3374 if (aGroup.length() == 1) 3375 return E_INVALIDARG; 3376 /* must start with a slash */ 3377 if (aGroup.c_str()[0] != '/') 3378 return E_INVALIDARG; 3379 /* must not end with a slash */ 3380 if (aGroup.c_str()[aGroup.length() - 1] == '/') 3381 return E_INVALIDARG; 3382 /* check the group components */ 3383 const char *pStr = aGroup.c_str() + 1; /* first char is /, skip it */ 3384 while (pStr) 3385 { 3386 char *pSlash = RTStrStr(pStr, "/"); 3387 if (pSlash) 3388 { 3389 /* no empty components (or // sequences in other words) */ 3390 if (pSlash == pStr) 3391 return E_INVALIDARG; 3392 /* check if the machine name rules are violated, because that means 3393 * the group components are too close to the limits. */ 3394 Utf8Str tmp((const char *)pStr, (size_t)(pSlash - pStr)); 3395 Utf8Str tmp2(tmp); 3396 sanitiseMachineFilename(tmp); 3397 if (tmp != tmp2) 3398 return E_INVALIDARG; 3399 if (fPrimary) 3400 { 3401 HRESULT rc = pVirtualBox->findMachineByName(tmp, 3402 false /* aSetError */); 3403 if (SUCCEEDED(rc)) 3404 return VBOX_E_VM_ERROR; 3405 } 3406 pStr = pSlash + 1; 3407 } 3408 else 3409 { 3410 /* check if the machine name rules are violated, because that means 3411 * the group components is too close to the limits. */ 3412 Utf8Str tmp(pStr); 3413 Utf8Str tmp2(tmp); 3414 sanitiseMachineFilename(tmp); 3415 if (tmp != tmp2) 3416 return E_INVALIDARG; 3417 pStr = NULL; 3418 } 3419 } 3420 return S_OK; 3421 } 3422 3423 /** 3424 * Validates a machine group. 3425 * 3426 * @param aMachineGroup Machine group. 3427 * @param fPrimary Set if this is the primary group. 3428 * 3429 * @return S_OK or E_INVALIDARG 3430 */ 3431 HRESULT VirtualBox::validateMachineGroup(const Utf8Str &aGroup, bool fPrimary) 3432 { 3433 HRESULT rc = validateMachineGroupHelper(aGroup, fPrimary, this); 3434 if (FAILED(rc)) 3435 { 3436 if (rc == VBOX_E_VM_ERROR) 3437 rc = setError(E_INVALIDARG, 3438 tr("Machine group '%s' conflicts with a virtual machine name"), 3439 aGroup.c_str()); 3440 else 3441 rc = setError(rc, 3442 tr("Invalid machine group '%s'"), 3443 aGroup.c_str()); 3444 } 3445 return rc; 3446 } 3447 3448 /** 3449 * Takes a list of machine groups, and sanitizes/validates it. 3450 * 3451 * @param aMachineGroups Safearray with the machine groups. 3452 * @param pllMachineGroups Pointer to list of strings for the result. 3453 * 3454 * @return S_OK or E_INVALIDARG 3455 */ 3456 HRESULT VirtualBox::convertMachineGroups(ComSafeArrayIn(IN_BSTR, aMachineGroups), StringsList *pllMachineGroups) 3457 { 3458 pllMachineGroups->clear(); 3459 if (aMachineGroups) 3460 { 3461 com::SafeArray<IN_BSTR> machineGroups(ComSafeArrayInArg(aMachineGroups)); 3462 for (size_t i = 0; i < machineGroups.size(); i++) 3463 { 3464 Utf8Str group(machineGroups[i]); 3465 if (group.length() == 0) 3466 group = "/"; 3467 3468 HRESULT rc = validateMachineGroup(group, i == 0); 3469 if (FAILED(rc)) 3470 return rc; 3471 3472 /* no duplicates please */ 3473 if ( find(pllMachineGroups->begin(), pllMachineGroups->end(), group) 3474 == pllMachineGroups->end()) 3475 pllMachineGroups->push_back(group); 3476 } 3477 if (pllMachineGroups->size() == 0) 3478 pllMachineGroups->push_back("/"); 3479 } 3480 else 3481 pllMachineGroups->push_back("/"); 3482 3483 return S_OK; 3484 } 3485 3486 /** 3487 * Searches for a Medium object with the given ID in the list of registered 3488 * hard disks. 3489 * 3490 * @param aId ID of the hard disk. Must not be empty. 3491 * @param aSetError If @c true , the appropriate error info is set in case 3492 * when the hard disk is not found. 3493 * @param aHardDisk Where to store the found hard disk object (can be NULL). 3494 * 3495 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found. 3496 * 3497 * @note Locks the media tree for reading. 3498 */ 3499 HRESULT VirtualBox::findHardDiskById(const Guid &id, 3500 bool aSetError, 3501 ComObjPtr<Medium> *aHardDisk /*= NULL*/) 3502 { 3503 AssertReturn(!id.isZero(), E_INVALIDARG); 3504 3505 // we use the hard disks map, but it is protected by the 3506 // hard disk _list_ lock handle 3507 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3508 3509 HardDiskMap::const_iterator it = m->mapHardDisks.find(id); 3510 if (it != m->mapHardDisks.end()) 3511 { 3512 if (aHardDisk) 3513 *aHardDisk = (*it).second; 3514 return S_OK; 3515 } 3516 3517 if (aSetError) 3518 return setError(VBOX_E_OBJECT_NOT_FOUND, 3519 tr("Could not find an open hard disk with UUID {%RTuuid}"), 3520 id.raw()); 3521 3522 return VBOX_E_OBJECT_NOT_FOUND; 3523 } 3524 3525 /** 3526 * Searches for a Medium object with the given ID or location in the list of 3527 * registered hard disks. If both ID and location are specified, the first 3528 * object that matches either of them (not necessarily both) is returned. 3529 * 3530 * @param aLocation Full location specification. Must not be empty. 3531 * @param aSetError If @c true , the appropriate error info is set in case 3532 * when the hard disk is not found. 3533 * @param aHardDisk Where to store the found hard disk object (can be NULL). 3534 * 3535 * @return S_OK, E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found. 3536 * 3537 * @note Locks the media tree for reading. 3538 */ 3539 HRESULT VirtualBox::findHardDiskByLocation(const Utf8Str &strLocation, 3540 bool aSetError, 3541 ComObjPtr<Medium> *aHardDisk /*= NULL*/) 3542 { 3543 AssertReturn(!strLocation.isEmpty(), E_INVALIDARG); 3544 3545 // we use the hard disks map, but it is protected by the 3546 // hard disk _list_ lock handle 3547 AutoReadLock alock(m->allHardDisks.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3548 3549 for (HardDiskMap::const_iterator it = m->mapHardDisks.begin(); 3550 it != m->mapHardDisks.end(); 3551 ++it) 3552 { 3553 const ComObjPtr<Medium> &pHD = (*it).second; 3554 3555 AutoCaller autoCaller(pHD); 3556 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3557 AutoWriteLock mlock(pHD COMMA_LOCKVAL_SRC_POS); 3558 3559 Utf8Str strLocationFull = pHD->getLocationFull(); 3560 3561 if (0 == RTPathCompare(strLocationFull.c_str(), strLocation.c_str())) 3562 { 3563 if (aHardDisk) 3564 *aHardDisk = pHD; 3565 return S_OK; 3566 } 3567 } 3568 3569 if (aSetError) 3570 return setError(VBOX_E_OBJECT_NOT_FOUND, 3571 tr("Could not find an open hard disk with location '%s'"), 3572 strLocation.c_str()); 3573 3574 return VBOX_E_OBJECT_NOT_FOUND; 3575 } 3576 3577 /** 3578 * Searches for a Medium object with the given ID or location in the list of 3579 * registered DVD or floppy images, depending on the @a mediumType argument. 3580 * If both ID and file path are specified, the first object that matches either 3581 * of them (not necessarily both) is returned. 3582 * 3583 * @param mediumType Must be either DeviceType_DVD or DeviceType_Floppy. 3584 * @param aId ID of the image file (unused when NULL). 3585 * @param aLocation Full path to the image file (unused when NULL). 3586 * @param aSetError If @c true, the appropriate error info is set in case when 3587 * the image is not found. 3588 * @param aImage Where to store the found image object (can be NULL). 3589 * 3590 * @return S_OK when found or E_INVALIDARG or VBOX_E_OBJECT_NOT_FOUND when not found. 3591 * 3592 * @note Locks the media tree for reading. 3593 */ 3594 HRESULT VirtualBox::findDVDOrFloppyImage(DeviceType_T mediumType, 3595 const Guid *aId, 3596 const Utf8Str &aLocation, 3597 bool aSetError, 3598 ComObjPtr<Medium> *aImage /* = NULL */) 3599 { 3600 AssertReturn(aId || !aLocation.isEmpty(), E_INVALIDARG); 3601 3602 Utf8Str location; 3603 if (!aLocation.isEmpty()) 3604 { 3605 int vrc = calculateFullPath(aLocation, location); 3606 if (RT_FAILURE(vrc)) 3607 return setError(VBOX_E_FILE_ERROR, 3608 tr("Invalid image file location '%s' (%Rrc)"), 3609 aLocation.c_str(), 3610 vrc); 3611 } 3612 3613 MediaOList *pMediaList; 3614 3615 switch (mediumType) 3616 { 3617 case DeviceType_DVD: 3618 pMediaList = &m->allDVDImages; 3619 break; 3620 3621 case DeviceType_Floppy: 3622 pMediaList = &m->allFloppyImages; 3623 break; 3624 3625 default: 3626 return E_INVALIDARG; 3627 } 3628 3629 AutoReadLock alock(pMediaList->getLockHandle() COMMA_LOCKVAL_SRC_POS); 3630 3631 bool found = false; 3632 3633 for (MediaList::const_iterator it = pMediaList->begin(); 3634 it != pMediaList->end(); 3635 ++it) 3636 { 3637 // no AutoCaller, registered image life time is bound to this 3638 Medium *pMedium = *it; 3639 AutoReadLock imageLock(pMedium COMMA_LOCKVAL_SRC_POS); 3640 const Utf8Str &strLocationFull = pMedium->getLocationFull(); 3641 3642 found = ( aId 3643 && pMedium->getId() == *aId) 3644 || ( !aLocation.isEmpty() 3645 && RTPathCompare(location.c_str(), 3646 strLocationFull.c_str()) == 0); 3647 if (found) 3648 { 3649 if (pMedium->getDeviceType() != mediumType) 3650 { 3651 if (mediumType == DeviceType_DVD) 3652 return setError(E_INVALIDARG, 3653 "Cannot mount DVD medium '%s' as floppy", strLocationFull.c_str()); 3654 else 3655 return setError(E_INVALIDARG, 3656 "Cannot mount floppy medium '%s' as DVD", strLocationFull.c_str()); 3657 } 3658 3659 if (aImage) 3660 *aImage = pMedium; 3661 break; 3662 } 3663 } 3664 3665 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND; 3666 3667 if (aSetError && !found) 3668 { 3669 if (aId) 3670 setError(rc, 3671 tr("Could not find an image file with UUID {%RTuuid} in the media registry ('%s')"), 3672 aId->raw(), 3673 m->strSettingsFilePath.c_str()); 3674 else 3675 setError(rc, 3676 tr("Could not find an image file with location '%s' in the media registry ('%s')"), 3677 aLocation.c_str(), 3678 m->strSettingsFilePath.c_str()); 3679 } 3680 3681 return rc; 3682 } 3683 3684 /** 3685 * Searches for an IMedium object that represents the given UUID. 3686 * 3687 * If the UUID is empty (indicating an empty drive), this sets pMedium 3688 * to NULL and returns S_OK. 3689 * 3690 * If the UUID refers to a host drive of the given device type, this 3691 * sets pMedium to the object from the list in IHost and returns S_OK. 3692 * 3693 * If the UUID is an image file, this sets pMedium to the object that 3694 * findDVDOrFloppyImage() returned. 3695 * 3696 * If none of the above apply, this returns VBOX_E_OBJECT_NOT_FOUND. 3697 * 3698 * @param mediumType Must be DeviceType_DVD or DeviceType_Floppy. 3699 * @param uuid UUID to search for; must refer to a host drive or an image file or be null. 3700 * @param fRefresh Whether to refresh the list of host drives in IHost (see Host::getDrives()) 3701 * @param pMedium out: IMedium object found. 3702 * @return 3703 */ 3704 HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType, 3705 const Guid &uuid, 3706 bool fRefresh, 3707 bool aSetError, 3708 ComObjPtr<Medium> &pMedium) 3709 { 3710 if (uuid.isZero()) 3711 { 3712 // that's easy 3713 pMedium.setNull(); 3714 return S_OK; 3715 } 3716 else if (!uuid.isValid()) 3717 { 3718 /* handling of case invalid GUID */ 3719 return setError(VBOX_E_OBJECT_NOT_FOUND, 3720 tr("Guid '%ls' is invalid"), 3721 uuid.toString().c_str()); 3722 } 3723 3724 // first search for host drive with that UUID 3725 HRESULT rc = m->pHost->findHostDriveById(mediumType, 3726 uuid, 3727 fRefresh, 3728 pMedium); 3729 if (rc == VBOX_E_OBJECT_NOT_FOUND) 3730 // then search for an image with that UUID 3731 rc = findDVDOrFloppyImage(mediumType, &uuid, Utf8Str::Empty, aSetError, &pMedium); 3732 3733 return rc; 3734 } 3735 3736 HRESULT VirtualBox::findGuestOSType(const Bstr &bstrOSType, 3737 GuestOSType*& pGuestOSType) 3738 { 3739 /* Look for a GuestOSType object */ 3740 AssertMsg(m->allGuestOSTypes.size() != 0, 3741 ("Guest OS types array must be filled")); 3742 3743 if (bstrOSType.isEmpty()) 3744 { 3745 pGuestOSType = NULL; 3746 return S_OK; 3747 } 3748 3749 AutoReadLock alock(m->allGuestOSTypes.getLockHandle() COMMA_LOCKVAL_SRC_POS); 3750 for (GuestOSTypesOList::const_iterator it = m->allGuestOSTypes.begin(); 3751 it != m->allGuestOSTypes.end(); 3752 ++it) 3753 { 3754 if ((*it)->id() == bstrOSType) 3755 { 3756 pGuestOSType = *it; 3757 return S_OK; 3758 } 3759 } 3760 3761 return setError(VBOX_E_OBJECT_NOT_FOUND, 3762 tr("Guest OS type '%ls' is invalid"), 3763 bstrOSType.raw()); 3764 } 3765 3766 /** 3767 * Returns the constant pseudo-machine UUID that is used to identify the 3768 * global media registry. 3769 * 3770 * Starting with VirtualBox 4.0 each medium remembers in its instance data 3771 * in which media registry it is saved (if any): this can either be a machine 3772 * UUID, if it's in a per-machine media registry, or this global ID. 3773 * 3774 * This UUID is only used to identify the VirtualBox object while VirtualBox 3775 * is running. It is a compile-time constant and not saved anywhere. 3776 * 3777 * @return 3778 */ 3779 const Guid& VirtualBox::getGlobalRegistryId() const 3780 { 3781 return m->uuidMediaRegistry; 3782 } 3783 3784 const ComObjPtr<Host>& VirtualBox::host() const 3785 { 3786 return m->pHost; 3787 } 3788 3789 SystemProperties* VirtualBox::getSystemProperties() const 3790 { 3791 return m->pSystemProperties; 3792 } 3793 3794 #ifdef VBOX_WITH_EXTPACK 3795 /** 3796 * Getter that SystemProperties and others can use to talk to the extension 3797 * pack manager. 3798 */ 3799 ExtPackManager* VirtualBox::getExtPackManager() const 3800 { 3801 return m->ptrExtPackManager; 3802 } 3803 #endif 3804 3805 /** 3806 * Getter that machines can talk to the autostart database. 3807 */ 3808 AutostartDb* VirtualBox::getAutostartDb() const 3809 { 3810 return m->pAutostartDb; 3811 } 3812 3813 #ifdef VBOX_WITH_RESOURCE_USAGE_API 3814 const ComObjPtr<PerformanceCollector>& VirtualBox::performanceCollector() const 3815 { 3816 return m->pPerformanceCollector; 3817 } 3818 #endif /* VBOX_WITH_RESOURCE_USAGE_API */ 3819 3820 /** 3821 * Returns the default machine folder from the system properties 3822 * with proper locking. 3823 * @return 3824 */ 3825 void VirtualBox::getDefaultMachineFolder(Utf8Str &str) const 3826 { 3827 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS); 3828 str = m->pSystemProperties->m->strDefaultMachineFolder; 3829 } 3830 3831 /** 3832 * Returns the default hard disk format from the system properties 3833 * with proper locking. 3834 * @return 3835 */ 3836 void VirtualBox::getDefaultHardDiskFormat(Utf8Str &str) const 3837 { 3838 AutoReadLock propsLock(m->pSystemProperties COMMA_LOCKVAL_SRC_POS); 3839 str = m->pSystemProperties->m->strDefaultHardDiskFormat; 3840 } 3841 3842 const Utf8Str& VirtualBox::homeDir() const 3843 { 3844 return m->strHomeDir; 3845 } 3846 3847 /** 3848 * Calculates the absolute path of the given path taking the VirtualBox home 3849 * directory as the current directory. 3850 * 3851 * @param aPath Path to calculate the absolute path for. 3852 * @param aResult Where to put the result (used only on success, can be the 3853 * same Utf8Str instance as passed in @a aPath). 3854 * @return IPRT result. 3855 * 3856 * @note Doesn't lock any object. 3857 */ 3858 int VirtualBox::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult) 3859 { 3860 AutoCaller autoCaller(this); 3861 AssertComRCReturn(autoCaller.rc(), VERR_GENERAL_FAILURE); 3862 3863 /* no need to lock since mHomeDir is const */ 3864 3865 char folder[RTPATH_MAX]; 3866 int vrc = RTPathAbsEx(m->strHomeDir.c_str(), 3867 strPath.c_str(), 3868 folder, 3869 sizeof(folder)); 3870 if (RT_SUCCESS(vrc)) 3871 aResult = folder; 3872 3873 return vrc; 3874 } 3875 3876 /** 3877 * Copies strSource to strTarget, making it relative to the VirtualBox config folder 3878 * if it is a subdirectory thereof, or simply copying it otherwise. 3879 * 3880 * @param strSource Path to evalue and copy. 3881 * @param strTarget Buffer to receive target path. 3882 */ 3883 void VirtualBox::copyPathRelativeToConfig(const Utf8Str &strSource, 3884 Utf8Str &strTarget) 3885 { 3886 AutoCaller autoCaller(this); 3887 AssertComRCReturnVoid(autoCaller.rc()); 3888 3889 // no need to lock since mHomeDir is const 3890 3891 // use strTarget as a temporary buffer to hold the machine settings dir 3892 strTarget = m->strHomeDir; 3893 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str())) 3894 // is relative: then append what's left 3895 strTarget.append(strSource.c_str() + strTarget.length()); // include '/' 3896 else 3897 // is not relative: then overwrite 3898 strTarget = strSource; 3899 } 3900 3901 // private methods 3902 ///////////////////////////////////////////////////////////////////////////// 3903 3904 /** 3905 * Checks if there is a hard disk, DVD or floppy image with the given ID or 3906 * location already registered. 3907 * 3908 * On return, sets @a aConflict to the string describing the conflicting medium, 3909 * or sets it to @c Null if no conflicting media is found. Returns S_OK in 3910 * either case. A failure is unexpected. 3911 * 3912 * @param aId UUID to check. 3913 * @param aLocation Location to check. 3914 * @param aConflict Where to return parameters of the conflicting medium. 3915 * @param ppMedium Medium reference in case this is simply a duplicate. 3916 * 3917 * @note Locks the media tree and media objects for reading. 3918 */ 3919 HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId, 3920 const Utf8Str &aLocation, 3921 Utf8Str &aConflict, 3922 ComObjPtr<Medium> *ppMedium) 3923 { 3924 AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL); 3925 AssertReturn(ppMedium, E_INVALIDARG); 3926 3927 aConflict.setNull(); 3928 ppMedium->setNull(); 3929 3930 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3931 3932 HRESULT rc = S_OK; 3933 3934 ComObjPtr<Medium> pMediumFound; 3935 const char *pcszType = NULL; 3936 3937 if (aId.isValid() && !aId.isZero()) 3938 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound); 3939 if (FAILED(rc) && !aLocation.isEmpty()) 3940 rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound); 3941 if (SUCCEEDED(rc)) 3942 pcszType = tr("hard disk"); 3943 3944 if (!pcszType) 3945 { 3946 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, aLocation, false /* aSetError */, &pMediumFound); 3947 if (SUCCEEDED(rc)) 3948 pcszType = tr("CD/DVD image"); 3949 } 3950 3951 if (!pcszType) 3952 { 3953 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, aLocation, false /* aSetError */, &pMediumFound); 3954 if (SUCCEEDED(rc)) 3955 pcszType = tr("floppy image"); 3956 } 3957 3958 if (pcszType && pMediumFound) 3959 { 3960 /* Note: no AutoCaller since bound to this */ 3961 AutoReadLock mlock(pMediumFound COMMA_LOCKVAL_SRC_POS); 3962 3963 Utf8Str strLocFound = pMediumFound->getLocationFull(); 3964 Guid idFound = pMediumFound->getId(); 3965 3966 if ( (RTPathCompare(strLocFound.c_str(), aLocation.c_str()) == 0) 3967 && (idFound == aId) 3968 ) 3969 *ppMedium = pMediumFound; 3970 3971 aConflict = Utf8StrFmt(tr("%s '%s' with UUID {%RTuuid}"), 3972 pcszType, 3973 strLocFound.c_str(), 3974 idFound.raw()); 3975 } 3976 3977 return S_OK; 3978 } 3979 3980 /** 3981 * Checks whether the given UUID is already in use by one medium for the 3982 * given device type. 3983 * 3984 * @returns true if the UUID is already in use 3985 * fale otherwise 3986 * @param aId The UUID to check. 3987 * @param deviceType The device type the UUID is going to be checked for 3988 * conflicts. 3989 */ 3990 bool VirtualBox::isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType) 3991 { 3992 /* A zero UUID is invalid here, always claim that it is already used. */ 3993 AssertReturn(!aId.isZero(), true); 3994 3995 AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3996 3997 HRESULT rc = S_OK; 3998 bool fInUse = false; 3999 4000 ComObjPtr<Medium> pMediumFound; 4001 4002 switch (deviceType) 4003 { 4004 case DeviceType_HardDisk: 4005 rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound); 4006 break; 4007 case DeviceType_DVD: 4008 rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound); 4009 break; 4010 case DeviceType_Floppy: 4011 rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound); 4012 break; 4013 default: 4014 AssertMsgFailed(("Invalid device type %d\n", deviceType)); 4015 } 4016 4017 if (SUCCEEDED(rc) && pMediumFound) 4018 fInUse = true; 4019 4020 return fInUse; 4021 } 4022 4023 /** 4024 * Called from Machine::prepareSaveSettings() when it has detected 4025 * that a machine has been renamed. Such renames will require 4026 * updating the global media registry during the 4027 * VirtualBox::saveSettings() that follows later. 4028 * 4029 * When a machine is renamed, there may well be media (in particular, 4030 * diff images for snapshots) in the global registry that will need 4031 * to have their paths updated. Before 3.2, Machine::saveSettings 4032 * used to call VirtualBox::saveSettings implicitly, which was both 4033 * unintuitive and caused locking order problems. Now, we remember 4034 * such pending name changes with this method so that 4035 * VirtualBox::saveSettings() can process them properly. 4036 */ 4037 void VirtualBox::rememberMachineNameChangeForMedia(const Utf8Str &strOldConfigDir, 4038 const Utf8Str &strNewConfigDir) 4039 { 4040 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4041 4042 Data::PendingMachineRename pmr; 4043 pmr.strConfigDirOld = strOldConfigDir; 4044 pmr.strConfigDirNew = strNewConfigDir; 4045 m->llPendingMachineRenames.push_back(pmr); 4046 } 4047 4048 struct SaveMediaRegistriesDesc 4049 { 4050 MediaList llMedia; 4051 ComObjPtr<VirtualBox> pVirtualBox; 4052 }; 4053 4054 static int fntSaveMediaRegistries(RTTHREAD ThreadSelf, void *pvUser) 4055 { 4056 NOREF(ThreadSelf); 4057 SaveMediaRegistriesDesc *pDesc = (SaveMediaRegistriesDesc *)pvUser; 4058 if (!pDesc) 4059 { 4060 LogRelFunc(("Thread for saving media registries lacks parameters\n")); 4061 return VERR_INVALID_PARAMETER; 4062 } 4063 4064 for (MediaList::const_iterator it = pDesc->llMedia.begin(); 4065 it != pDesc->llMedia.end(); 4066 ++it) 4067 { 4068 Medium *pMedium = *it; 4069 pMedium->markRegistriesModified(); 4070 } 4071 4072 pDesc->pVirtualBox->saveModifiedRegistries(); 4073 4074 pDesc->llMedia.clear(); 4075 pDesc->pVirtualBox.setNull(); 4076 delete pDesc; 4077 4078 return VINF_SUCCESS; 4079 } 4080 4081 /** 4082 * Goes through all known media (hard disks, floppies and DVDs) and saves 4083 * those into the given settings::MediaRegistry structures whose registry 4084 * ID match the given UUID. 4085 * 4086 * Before actually writing to the structures, all media paths (not just the 4087 * ones for the given registry) are updated if machines have been renamed 4088 * since the last call. 4089 * 4090 * This gets called from two contexts: 4091 * 4092 * -- VirtualBox::saveSettings() with the UUID of the global registry 4093 * (VirtualBox::Data.uuidRegistry); this will save those media 4094 * which had been loaded from the global registry or have been 4095 * attached to a "legacy" machine which can't save its own registry; 4096 * 4097 * -- Machine::saveSettings() with the UUID of a machine, if a medium 4098 * has been attached to a machine created with VirtualBox 4.0 or later. 4099 * 4100 * Media which have only been temporarily opened without having been 4101 * attached to a machine have a NULL registry UUID and therefore don't 4102 * get saved. 4103 * 4104 * This locks the media tree. Throws HRESULT on errors! 4105 * 4106 * @param mediaRegistry Settings structure to fill. 4107 * @param uuidRegistry The UUID of the media registry; either a machine UUID (if machine registry) or the UUID of the global registry. 4108 * @param strMachineFolder The machine folder for relative paths, if machine registry, or an empty string otherwise. 4109 */ 4110 void VirtualBox::saveMediaRegistry(settings::MediaRegistry &mediaRegistry, 4111 const Guid &uuidRegistry, 4112 const Utf8Str &strMachineFolder) 4113 { 4114 // lock all media for the following; use a write lock because we're 4115 // modifying the PendingMachineRenamesList, which is protected by this 4116 AutoWriteLock mediaLock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4117 4118 // if a machine was renamed, then we'll need to refresh media paths 4119 if (m->llPendingMachineRenames.size()) 4120 { 4121 // make a single list from the three media lists so we don't need three loops 4122 MediaList llAllMedia; 4123 // with hard disks, we must use the map, not the list, because the list only has base images 4124 for (HardDiskMap::iterator it = m->mapHardDisks.begin(); it != m->mapHardDisks.end(); ++it) 4125 llAllMedia.push_back(it->second); 4126 for (MediaList::iterator it = m->allDVDImages.begin(); it != m->allDVDImages.end(); ++it) 4127 llAllMedia.push_back(*it); 4128 for (MediaList::iterator it = m->allFloppyImages.begin(); it != m->allFloppyImages.end(); ++it) 4129 llAllMedia.push_back(*it); 4130 4131 SaveMediaRegistriesDesc *pDesc = new SaveMediaRegistriesDesc(); 4132 for (MediaList::iterator it = llAllMedia.begin(); 4133 it != llAllMedia.end(); 4134 ++it) 4135 { 4136 Medium *pMedium = *it; 4137 for (Data::PendingMachineRenamesList::iterator it2 = m->llPendingMachineRenames.begin(); 4138 it2 != m->llPendingMachineRenames.end(); 4139 ++it2) 4140 { 4141 const Data::PendingMachineRename &pmr = *it2; 4142 HRESULT rc = pMedium->updatePath(pmr.strConfigDirOld, 4143 pmr.strConfigDirNew); 4144 if (SUCCEEDED(rc)) 4145 { 4146 // Remember which medium objects has been changed, 4147 // to trigger saving their registries later. 4148 pDesc->llMedia.push_back(pMedium); 4149 } else if (rc == VBOX_E_FILE_ERROR) 4150 /* nothing */; 4151 else 4152 AssertComRC(rc); 4153 } 4154 } 4155 // done, don't do it again until we have more machine renames 4156 m->llPendingMachineRenames.clear(); 4157 4158 if (pDesc->llMedia.size()) 4159 { 4160 // Handle the media registry saving in a separate thread, to 4161 // avoid giant locking problems and passing up the list many 4162 // levels up to whoever triggered saveSettings, as there are 4163 // lots of places which would need to handle saving more settings. 4164 pDesc->pVirtualBox = this; 4165 int vrc = RTThreadCreate(NULL, 4166 fntSaveMediaRegistries, 4167 (void *)pDesc, 4168 0, // cbStack (default) 4169 RTTHREADTYPE_MAIN_WORKER, 4170 0, // flags 4171 "SaveMediaReg"); 4172 ComAssertRC(vrc); 4173 // failure means that settings aren't saved, but there isn't 4174 // much we can do besides avoiding memory leaks 4175 if (RT_FAILURE(vrc)) 4176 { 4177 LogRelFunc(("Failed to create thread for saving media registries (%Rrc)\n", vrc)); 4178 delete pDesc; 4179 } 4180 } 4181 else 4182 delete pDesc; 4183 } 4184 4185 struct { 4186 MediaOList &llSource; 4187 settings::MediaList &llTarget; 4188 } s[] = 4189 { 4190 // hard disks 4191 { m->allHardDisks, mediaRegistry.llHardDisks }, 4192 // CD/DVD images 4193 { m->allDVDImages, mediaRegistry.llDvdImages }, 4194 // floppy images 4195 { m->allFloppyImages, mediaRegistry.llFloppyImages } 4196 }; 4197 4198 HRESULT rc; 4199 4200 for (size_t i = 0; i < RT_ELEMENTS(s); ++i) 4201 { 4202 MediaOList &llSource = s[i].llSource; 4203 settings::MediaList &llTarget = s[i].llTarget; 4204 llTarget.clear(); 4205 for (MediaList::const_iterator it = llSource.begin(); 4206 it != llSource.end(); 4207 ++it) 4208 { 4209 Medium *pMedium = *it; 4210 AutoCaller autoCaller(pMedium); 4211 if (FAILED(autoCaller.rc())) throw autoCaller.rc(); 4212 AutoReadLock mlock(pMedium COMMA_LOCKVAL_SRC_POS); 4213 4214 if (pMedium->isInRegistry(uuidRegistry)) 4215 { 4216 settings::Medium med; 4217 rc = pMedium->saveSettings(med, strMachineFolder); // this recurses into child hard disks 4218 if (FAILED(rc)) throw rc; 4219 llTarget.push_back(med); 4220 } 4221 } 4222 } 4223 } 4224 4225 /** 4226 * Helper function which actually writes out VirtualBox.xml, the main configuration file. 4227 * Gets called from the public VirtualBox::SaveSettings() as well as from various other 4228 * places internally when settings need saving. 4229 * 4230 * @note Caller must have locked the VirtualBox object for writing and must not hold any 4231 * other locks since this locks all kinds of member objects and trees temporarily, 4232 * which could cause conflicts. 4233 */ 4234 HRESULT VirtualBox::saveSettings() 4235 { 4236 AutoCaller autoCaller(this); 4237 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4238 4239 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 4240 AssertReturn(!m->strSettingsFilePath.isEmpty(), E_FAIL); 4241 4242 HRESULT rc = S_OK; 4243 4244 try 4245 { 4246 // machines 4247 m->pMainConfigFile->llMachines.clear(); 4248 { 4249 AutoReadLock machinesLock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 4250 for (MachinesOList::iterator it = m->allMachines.begin(); 4251 it != m->allMachines.end(); 4252 ++it) 4253 { 4254 Machine *pMachine = *it; 4255 // save actual machine registry entry 4256 settings::MachineRegistryEntry mre; 4257 rc = pMachine->saveRegistryEntry(mre); 4258 m->pMainConfigFile->llMachines.push_back(mre); 4259 } 4260 } 4261 4262 saveMediaRegistry(m->pMainConfigFile->mediaRegistry, 4263 m->uuidMediaRegistry, // global media registry ID 4264 Utf8Str::Empty); // strMachineFolder 4265 4266 m->pMainConfigFile->llDhcpServers.clear(); 4267 { 4268 AutoReadLock dhcpLock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS); 4269 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin(); 4270 it != m->allDHCPServers.end(); 4271 ++it) 4272 { 4273 settings::DHCPServer d; 4274 rc = (*it)->saveSettings(d); 4275 if (FAILED(rc)) throw rc; 4276 m->pMainConfigFile->llDhcpServers.push_back(d); 4277 } 4278 } 4279 4280 #ifdef VBOX_WITH_NAT_SERVICE 4281 /* Saving NAT Network configuration */ 4282 m->pMainConfigFile->llNATNetworks.clear(); 4283 { 4284 AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS); 4285 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin(); 4286 it != m->allNATNetworks.end(); 4287 ++it) 4288 { 4289 settings::NATNetwork n; 4290 rc = (*it)->saveSettings(n); 4291 if (FAILED(rc)) throw rc; 4292 m->pMainConfigFile->llNATNetworks.push_back(n); 4293 } 4294 } 4295 #endif 4296 4297 // leave extra data alone, it's still in the config file 4298 4299 // host data (USB filters) 4300 rc = m->pHost->saveSettings(m->pMainConfigFile->host); 4301 if (FAILED(rc)) throw rc; 4302 4303 rc = m->pSystemProperties->saveSettings(m->pMainConfigFile->systemProperties); 4304 if (FAILED(rc)) throw rc; 4305 4306 // and write out the XML, still under the lock 4307 m->pMainConfigFile->write(m->strSettingsFilePath); 4308 } 4309 catch (HRESULT err) 4310 { 4311 /* we assume that error info is set by the thrower */ 4312 rc = err; 4313 } 4314 catch (...) 4315 { 4316 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS); 4317 } 4318 4319 return rc; 4320 } 4321 4322 /** 4323 * Helper to register the machine. 4324 * 4325 * When called during VirtualBox startup, adds the given machine to the 4326 * collection of registered machines. Otherwise tries to mark the machine 4327 * as registered, and, if succeeded, adds it to the collection and 4328 * saves global settings. 4329 * 4330 * @note The caller must have added itself as a caller of the @a aMachine 4331 * object if calls this method not on VirtualBox startup. 4332 * 4333 * @param aMachine machine to register 4334 * 4335 * @note Locks objects! 4336 */ 4337 HRESULT VirtualBox::registerMachine(Machine *aMachine) 4338 { 4339 ComAssertRet(aMachine, E_INVALIDARG); 4340 4341 AutoCaller autoCaller(this); 4342 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4343 4344 HRESULT rc = S_OK; 4345 4346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4347 4348 { 4349 ComObjPtr<Machine> pMachine; 4350 rc = findMachine(aMachine->getId(), 4351 true /* fPermitInaccessible */, 4352 false /* aDoSetError */, 4353 &pMachine); 4354 if (SUCCEEDED(rc)) 4355 { 4356 /* sanity */ 4357 AutoLimitedCaller machCaller(pMachine); 4358 AssertComRC(machCaller.rc()); 4359 4360 return setError(E_INVALIDARG, 4361 tr("Registered machine with UUID {%RTuuid} ('%s') already exists"), 4362 aMachine->getId().raw(), 4363 pMachine->getSettingsFileFull().c_str()); 4364 } 4365 4366 ComAssertRet(rc == VBOX_E_OBJECT_NOT_FOUND, rc); 4367 rc = S_OK; 4368 } 4369 4370 if (autoCaller.state() != InInit) 4371 { 4372 rc = aMachine->prepareRegister(); 4373 if (FAILED(rc)) return rc; 4374 } 4375 4376 /* add to the collection of registered machines */ 4377 m->allMachines.addChild(aMachine); 4378 4379 if (autoCaller.state() != InInit) 4380 rc = saveSettings(); 4381 4382 return rc; 4383 } 4384 4385 /** 4386 * Remembers the given medium object by storing it in either the global 4387 * medium registry or a machine one. 4388 * 4389 * @note Caller must hold the media tree lock for writing; in addition, this 4390 * locks @a pMedium for reading 4391 * 4392 * @param pMedium Medium object to remember. 4393 * @param ppMedium Actually stored medium object. Can be different if due 4394 * to an unavoidable race there was a duplicate Medium object 4395 * created. 4396 * @param argType Either DeviceType_HardDisk, DeviceType_DVD or DeviceType_Floppy. 4397 * @return 4398 */ 4399 HRESULT VirtualBox::registerMedium(const ComObjPtr<Medium> &pMedium, 4400 ComObjPtr<Medium> *ppMedium, 4401 DeviceType_T argType) 4402 { 4403 AssertReturn(pMedium != NULL, E_INVALIDARG); 4404 AssertReturn(ppMedium != NULL, E_INVALIDARG); 4405 4406 AutoCaller autoCaller(this); 4407 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4408 4409 AutoCaller mediumCaller(pMedium); 4410 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc()); 4411 4412 const char *pszDevType = NULL; 4413 ObjectsList<Medium> *pall = NULL; 4414 switch (argType) 4415 { 4416 case DeviceType_HardDisk: 4417 pall = &m->allHardDisks; 4418 pszDevType = tr("hard disk"); 4419 break; 4420 case DeviceType_DVD: 4421 pszDevType = tr("DVD image"); 4422 pall = &m->allDVDImages; 4423 break; 4424 case DeviceType_Floppy: 4425 pszDevType = tr("floppy image"); 4426 pall = &m->allFloppyImages; 4427 break; 4428 default: 4429 AssertMsgFailedReturn(("invalid device type %d", argType), E_INVALIDARG); 4430 } 4431 4432 // caller must hold the media tree write lock 4433 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 4434 4435 Guid id; 4436 Utf8Str strLocationFull; 4437 ComObjPtr<Medium> pParent; 4438 { 4439 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 4440 id = pMedium->getId(); 4441 strLocationFull = pMedium->getLocationFull(); 4442 pParent = pMedium->getParent(); 4443 } 4444 4445 HRESULT rc; 4446 4447 Utf8Str strConflict; 4448 ComObjPtr<Medium> pDupMedium; 4449 rc = checkMediaForConflicts(id, 4450 strLocationFull, 4451 strConflict, 4452 &pDupMedium); 4453 if (FAILED(rc)) return rc; 4454 4455 if (pDupMedium.isNull()) 4456 { 4457 if (strConflict.length()) 4458 return setError(E_INVALIDARG, 4459 tr("Cannot register the %s '%s' {%RTuuid} because a %s already exists"), 4460 pszDevType, 4461 strLocationFull.c_str(), 4462 id.raw(), 4463 strConflict.c_str(), 4464 m->strSettingsFilePath.c_str()); 4465 4466 // add to the collection if it is a base medium 4467 if (pParent.isNull()) 4468 pall->getList().push_back(pMedium); 4469 4470 // store all hard disks (even differencing images) in the map 4471 if (argType == DeviceType_HardDisk) 4472 m->mapHardDisks[id] = pMedium; 4473 4474 *ppMedium = pMedium; 4475 } 4476 else 4477 { 4478 // pMedium may be the last reference to the Medium object, and the 4479 // caller may have specified the same ComObjPtr as the output parameter. 4480 // In this case the assignment will uninit the object, and we must not 4481 // have a caller pending. 4482 mediumCaller.release(); 4483 *ppMedium = pDupMedium; 4484 } 4485 4486 return rc; 4487 } 4488 4489 /** 4490 * Removes the given medium from the respective registry. 4491 * 4492 * @param pMedium Hard disk object to remove. 4493 * 4494 * @note Caller must hold the media tree lock for writing; in addition, this locks @a pMedium for reading 4495 */ 4496 HRESULT VirtualBox::unregisterMedium(Medium *pMedium) 4497 { 4498 AssertReturn(pMedium != NULL, E_INVALIDARG); 4499 4500 AutoCaller autoCaller(this); 4501 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4502 4503 AutoCaller mediumCaller(pMedium); 4504 AssertComRCReturn(mediumCaller.rc(), mediumCaller.rc()); 4505 4506 // caller must hold the media tree write lock 4507 Assert(getMediaTreeLockHandle().isWriteLockOnCurrentThread()); 4508 4509 Guid id; 4510 ComObjPtr<Medium> pParent; 4511 DeviceType_T devType; 4512 { 4513 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 4514 id = pMedium->getId(); 4515 pParent = pMedium->getParent(); 4516 devType = pMedium->getDeviceType(); 4517 } 4518 4519 ObjectsList<Medium> *pall = NULL; 4520 switch (devType) 4521 { 4522 case DeviceType_HardDisk: 4523 pall = &m->allHardDisks; 4524 break; 4525 case DeviceType_DVD: 4526 pall = &m->allDVDImages; 4527 break; 4528 case DeviceType_Floppy: 4529 pall = &m->allFloppyImages; 4530 break; 4531 default: 4532 AssertMsgFailedReturn(("invalid device type %d", devType), E_INVALIDARG); 4533 } 4534 4535 // remove from the collection if it is a base medium 4536 if (pParent.isNull()) 4537 pall->getList().remove(pMedium); 4538 4539 // remove all hard disks (even differencing images) from map 4540 if (devType == DeviceType_HardDisk) 4541 { 4542 size_t cnt = m->mapHardDisks.erase(id); 4543 Assert(cnt == 1); 4544 NOREF(cnt); 4545 } 4546 4547 return S_OK; 4548 } 4549 4550 /** 4551 * Little helper called from unregisterMachineMedia() to recursively add media to the given list, 4552 * with children appearing before their parents. 4553 * @param llMedia 4554 * @param pMedium 4555 */ 4556 void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMedium) 4557 { 4558 // recurse first, then add ourselves; this way children end up on the 4559 // list before their parents 4560 4561 const MediaList &llChildren = pMedium->getChildren(); 4562 for (MediaList::const_iterator it = llChildren.begin(); 4563 it != llChildren.end(); 4564 ++it) 4565 { 4566 Medium *pChild = *it; 4567 pushMediumToListWithChildren(llMedia, pChild); 4568 } 4569 4570 Log(("Pushing medium %RTuuid\n", pMedium->getId().raw())); 4571 llMedia.push_back(pMedium); 4572 } 4573 4574 /** 4575 * Unregisters all Medium objects which belong to the given machine registry. 4576 * Gets called from Machine::uninit() just before the machine object dies 4577 * and must only be called with a machine UUID as the registry ID. 4578 * 4579 * Locks the media tree. 4580 * 4581 * @param uuidMachine Medium registry ID (always a machine UUID) 4582 * @return 4583 */ 4584 HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine) 4585 { 4586 Assert(!uuidMachine.isZero() && uuidMachine.isValid()); 4587 4588 LogFlowFuncEnter(); 4589 4590 AutoCaller autoCaller(this); 4591 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4592 4593 MediaList llMedia2Close; 4594 4595 { 4596 AutoWriteLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4597 4598 for (MediaOList::iterator it = m->allHardDisks.getList().begin(); 4599 it != m->allHardDisks.getList().end(); 4600 ++it) 4601 { 4602 ComObjPtr<Medium> pMedium = *it; 4603 AutoCaller medCaller(pMedium); 4604 if (FAILED(medCaller.rc())) return medCaller.rc(); 4605 AutoReadLock medlock(pMedium COMMA_LOCKVAL_SRC_POS); 4606 4607 if (pMedium->isInRegistry(uuidMachine)) 4608 // recursively with children first 4609 pushMediumToListWithChildren(llMedia2Close, pMedium); 4610 } 4611 } 4612 4613 for (MediaList::iterator it = llMedia2Close.begin(); 4614 it != llMedia2Close.end(); 4615 ++it) 4616 { 4617 ComObjPtr<Medium> pMedium = *it; 4618 Log(("Closing medium %RTuuid\n", pMedium->getId().raw())); 4619 AutoCaller mac(pMedium); 4620 pMedium->close(mac); 4621 } 4622 4623 LogFlowFuncLeave(); 4624 4625 return S_OK; 4626 } 4627 4628 /** 4629 * Removes the given machine object from the internal list of registered machines. 4630 * Called from Machine::Unregister(). 4631 * @param pMachine 4632 * @param id UUID of the machine. Must be passed by caller because machine may be dead by this time. 4633 * @return 4634 */ 4635 HRESULT VirtualBox::unregisterMachine(Machine *pMachine, 4636 const Guid &id) 4637 { 4638 // remove from the collection of registered machines 4639 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4640 m->allMachines.removeChild(pMachine); 4641 // save the global registry 4642 HRESULT rc = saveSettings(); 4643 alock.release(); 4644 4645 /* 4646 * Now go over all known media and checks if they were registered in the 4647 * media registry of the given machine. Each such medium is then moved to 4648 * a different media registry to make sure it doesn't get lost since its 4649 * media registry is about to go away. 4650 * 4651 * This fixes the following use case: Image A.vdi of machine A is also used 4652 * by machine B, but registered in the media registry of machine A. If machine 4653 * A is deleted, A.vdi must be moved to the registry of B, or else B will 4654 * become inaccessible. 4655 */ 4656 { 4657 AutoReadLock tlock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 4658 // iterate over the list of *base* images 4659 for (MediaOList::iterator it = m->allHardDisks.getList().begin(); 4660 it != m->allHardDisks.getList().end(); 4661 ++it) 4662 { 4663 ComObjPtr<Medium> &pMedium = *it; 4664 AutoCaller medCaller(pMedium); 4665 if (FAILED(medCaller.rc())) return medCaller.rc(); 4666 AutoWriteLock mlock(pMedium COMMA_LOCKVAL_SRC_POS); 4667 4668 if (pMedium->removeRegistry(id, true /* fRecurse */)) 4669 { 4670 // machine ID was found in base medium's registry list: 4671 // move this base image and all its children to another registry then 4672 // 1) first, find a better registry to add things to 4673 const Guid *puuidBetter = pMedium->getAnyMachineBackref(); 4674 if (puuidBetter) 4675 { 4676 // 2) better registry found: then use that 4677 pMedium->addRegistry(*puuidBetter, true /* fRecurse */); 4678 // 3) and make sure the registry is saved below 4679 mlock.release(); 4680 tlock.release(); 4681 markRegistryModified(*puuidBetter); 4682 tlock.acquire(); 4683 mlock.release(); 4684 } 4685 } 4686 } 4687 } 4688 4689 saveModifiedRegistries(); 4690 4691 /* fire an event */ 4692 onMachineRegistered(id, FALSE); 4693 4694 return rc; 4695 } 4696 4697 /** 4698 * Marks the registry for @a uuid as modified, so that it's saved in a later 4699 * call to saveModifiedRegistries(). 4700 * 4701 * @param uuid 4702 */ 4703 void VirtualBox::markRegistryModified(const Guid &uuid) 4704 { 4705 if (uuid == getGlobalRegistryId()) 4706 ASMAtomicIncU64(&m->uRegistryNeedsSaving); 4707 else 4708 { 4709 ComObjPtr<Machine> pMachine; 4710 HRESULT rc = findMachine(uuid, 4711 false /* fPermitInaccessible */, 4712 false /* aSetError */, 4713 &pMachine); 4714 if (SUCCEEDED(rc)) 4715 { 4716 AutoCaller machineCaller(pMachine); 4717 if (SUCCEEDED(machineCaller.rc())) 4718 ASMAtomicIncU64(&pMachine->uRegistryNeedsSaving); 4719 } 4720 } 4721 } 4722 4723 /** 4724 * Saves all settings files according to the modified flags in the Machine 4725 * objects and in the VirtualBox object. 4726 * 4727 * This locks machines and the VirtualBox object as necessary, so better not 4728 * hold any locks before calling this. 4729 * 4730 * @return 4731 */ 4732 void VirtualBox::saveModifiedRegistries() 4733 { 4734 HRESULT rc = S_OK; 4735 bool fNeedsGlobalSettings = false; 4736 uint64_t uOld; 4737 4738 { 4739 AutoReadLock alock(m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 4740 for (MachinesOList::iterator it = m->allMachines.begin(); 4741 it != m->allMachines.end(); 4742 ++it) 4743 { 4744 const ComObjPtr<Machine> &pMachine = *it; 4745 4746 for (;;) 4747 { 4748 uOld = ASMAtomicReadU64(&pMachine->uRegistryNeedsSaving); 4749 if (!uOld) 4750 break; 4751 if (ASMAtomicCmpXchgU64(&pMachine->uRegistryNeedsSaving, 0, uOld)) 4752 break; 4753 ASMNopPause(); 4754 } 4755 if (uOld) 4756 { 4757 AutoCaller autoCaller(pMachine); 4758 if (FAILED(autoCaller.rc())) 4759 continue; 4760 /* object is already dead, no point in saving settings */ 4761 if (autoCaller.state() != Ready) 4762 continue; 4763 AutoWriteLock mlock(pMachine COMMA_LOCKVAL_SRC_POS); 4764 rc = pMachine->saveSettings(&fNeedsGlobalSettings, 4765 Machine::SaveS_Force); // caller said save, so stop arguing 4766 } 4767 } 4768 } 4769 4770 for (;;) 4771 { 4772 uOld = ASMAtomicReadU64(&m->uRegistryNeedsSaving); 4773 if (!uOld) 4774 break; 4775 if (ASMAtomicCmpXchgU64(&m->uRegistryNeedsSaving, 0, uOld)) 4776 break; 4777 ASMNopPause(); 4778 } 4779 if (uOld || fNeedsGlobalSettings) 4780 { 4781 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4782 rc = saveSettings(); 4783 } 4784 NOREF(rc); /* XXX */ 4785 } 4786 4787 4788 /* static */ 4789 const Bstr &VirtualBox::getVersionNormalized() 4790 { 4791 return sVersionNormalized; 4792 } 4793 4794 /** 4795 * Checks if the path to the specified file exists, according to the path 4796 * information present in the file name. Optionally the path is created. 4797 * 4798 * Note that the given file name must contain the full path otherwise the 4799 * extracted relative path will be created based on the current working 4800 * directory which is normally unknown. 4801 * 4802 * @param aFileName Full file name which path is checked/created. 4803 * @param aCreate Flag if the path should be created if it doesn't exist. 4804 * 4805 * @return Extended error information on failure to check/create the path. 4806 */ 4807 /* static */ 4808 HRESULT VirtualBox::ensureFilePathExists(const Utf8Str &strFileName, bool fCreate) 4809 { 4810 Utf8Str strDir(strFileName); 4811 strDir.stripFilename(); 4812 if (!RTDirExists(strDir.c_str())) 4813 { 4814 if (fCreate) 4815 { 4816 int vrc = RTDirCreateFullPath(strDir.c_str(), 0700); 4817 if (RT_FAILURE(vrc)) 4818 return setErrorStatic(VBOX_E_IPRT_ERROR, 4819 Utf8StrFmt(tr("Could not create the directory '%s' (%Rrc)"), 4820 strDir.c_str(), 4821 vrc)); 4822 } 4823 else 4824 return setErrorStatic(VBOX_E_IPRT_ERROR, 4825 Utf8StrFmt(tr("Directory '%s' does not exist"), 4826 strDir.c_str())); 4827 } 4828 4829 return S_OK; 4830 } 4831 4832 const Utf8Str& VirtualBox::settingsFilePath() 4833 { 4834 return m->strSettingsFilePath; 4835 } 4836 4837 /** 4838 * Returns the lock handle which protects the media trees (hard disks, 4839 * DVDs, floppies). As opposed to version 3.1 and earlier, these lists 4840 * are no longer protected by the VirtualBox lock, but by this more 4841 * specialized lock. Mind the locking order: always request this lock 4842 * after the VirtualBox object lock but before the locks of the media 4843 * objects contained in these lists. See AutoLock.h. 4844 */ 4845 RWLockHandle& VirtualBox::getMediaTreeLockHandle() 4846 { 4847 return m->lockMedia; 4848 } 4849 4850 /** 4851 * Thread function that watches the termination of all client processes 4852 * that have opened sessions using IMachine::LockMachine() 4853 */ 4854 // static 4855 DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser) 4856 { 4857 LogFlowFuncEnter(); 4858 4859 VirtualBox *that = (VirtualBox*)pvUser; 4860 Assert(that); 4861 4862 typedef std::vector< ComObjPtr<Machine> > MachineVector; 4863 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector; 4864 4865 SessionMachineVector machines; 4866 MachineVector spawnedMachines; 4867 4868 size_t cnt = 0; 4869 size_t cntSpawned = 0; 4870 4871 VirtualBoxBase::initializeComForThread(); 4872 4873 #if defined(RT_OS_WINDOWS) 4874 4875 /// @todo (dmik) processes reaping! 4876 4877 HANDLE handles[MAXIMUM_WAIT_OBJECTS]; 4878 handles[0] = that->m->updateReq; 4879 4880 do 4881 { 4882 AutoCaller autoCaller(that); 180 AutoCaller autoCaller(that->mVirtualBox); 4883 181 /* VirtualBox has been early uninitialized, terminate */ 4884 182 if (!autoCaller.isOk()) … … 4934 232 CloseHandle(handles[i]); 4935 233 234 // get reference to the machines list in VirtualBox 235 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); 236 4936 237 // lock the machines list for reading 4937 AutoReadLock thatLock( that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);238 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 4938 239 4939 240 /* obtain a new set of opened machines */ … … 4941 242 machines.clear(); 4942 243 4943 for (MachinesOList::iterator it = that->m->allMachines.begin();4944 it != that->m->allMachines.end();244 for (MachinesOList::iterator it = allMachines.begin(); 245 it != allMachines.end(); 4945 246 ++it) 4946 247 { … … 4950 251 4951 252 ComObjPtr<SessionMachine> sm; 4952 HANDLE ipcSem; 4953 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem)) 4954 { 4955 machines.push_back(sm); 4956 handles[1 + cnt] = ipcSem; 4957 ++cnt; 253 if ((*it)->isSessionOpenOrClosing(sm)) 254 { 255 AutoCaller smCaller(sm); 256 if (smCaller.isOk()) 257 { 258 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS); 259 Machine::ClientToken *ct = sm->getClientToken(); 260 if (ct) 261 { 262 HANDLE ipcSem = ct->getToken(); 263 machines.push_back(sm); 264 handles[1 + cnt] = ipcSem; 265 ++cnt; 266 } 267 } 4958 268 } 4959 269 } … … 4965 275 spawnedMachines.clear(); 4966 276 4967 for (MachinesOList::iterator it = that->m->allMachines.begin();4968 it != that->m->allMachines.end();277 for (MachinesOList::iterator it = allMachines.begin(); 278 it != allMachines.end(); 4969 279 ++it) 4970 280 { … … 4973 283 ("MAXIMUM_WAIT_OBJECTS reached")); 4974 284 4975 RTPROCESS pid; 4976 if ((*it)->isSessionSpawning(&pid)) 4977 { 4978 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid); 4979 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n", 4980 pid, GetLastError())); 4981 if (rc == 0) 285 if ((*it)->isSessionSpawning()) 286 { 287 ULONG pid; 288 HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid); 289 if (SUCCEEDED(hrc)) 4982 290 { 4983 spawnedMachines.push_back(*it); 4984 handles[1 + cnt + cntSpawned] = ph; 4985 ++cntSpawned; 291 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid); 292 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n", 293 pid, GetLastError())); 294 if (ph != NULL) 295 { 296 spawnedMachines.push_back(*it); 297 handles[1 + cnt + cntSpawned] = ph; 298 ++cntSpawned; 299 } 4986 300 } 4987 301 } … … 4998 312 4999 313 /* close old process handles */ 5000 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ 314 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i) 5001 315 CloseHandle(handles[i]); 5002 316 … … 5018 332 do 5019 333 { 5020 AutoCaller autoCaller(that );334 AutoCaller autoCaller(that->mVirtualBox); 5021 335 /* VirtualBox has been early uninitialized, terminate */ 5022 336 if (!autoCaller.isOk()) … … 5028 342 autoCaller.release(); 5029 343 5030 int vrc = RTSemEventWait(that->m ->updateReq, 500);344 int vrc = RTSemEventWait(that->mUpdateReq, 500); 5031 345 5032 346 /* Restore the caller before using VirtualBox. If it fails, this … … 5081 395 * termination; find which mutex is in the Owner Died 5082 396 * state */ 5083 for (size_t i = 0; i < cnt; ++ 397 for (size_t i = 0; i < cnt; ++i) 5084 398 { 5085 399 PID pid; TID tid; … … 5115 429 if (cntSpawned > 0) 5116 430 { 5117 for (size_t i = 0; i < cntSpawned; ++ 431 for (size_t i = 0; i < cntSpawned; ++i) 5118 432 updateSpawned |= (spawnedMachines[i])-> 5119 433 checkForSpawnFailure(); … … 5123 437 if (update || updateSpawned) 5124 438 { 5125 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS); 439 // get reference to the machines list in VirtualBox 440 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); 441 442 // lock the machines list for reading 443 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 5126 444 5127 445 if (update) … … 5135 453 machines.clear(); 5136 454 5137 for (MachinesOList::iterator it = that->m->allMachines.begin();5138 it != that->m->allMachines.end(); ++it)455 for (MachinesOList::iterator it = allMachines.begin(); 456 it != allMachines.end(); ++it) 5139 457 { 5140 458 /// @todo handle situations with more than 64 objects … … 5144 462 5145 463 ComObjPtr<SessionMachine> sm; 5146 HMTX ipcSem; 5147 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem)) 464 if ((*it)->isSessionOpenOrClosing(sm)) 5148 465 { 5149 machines.push_back(sm); 5150 handles[cnt].hsemCur = (HSEM)ipcSem; 5151 handles[cnt].ulUser = cnt; 5152 ++ cnt; 466 AutoCaller smCaller(sm); 467 if (smCaller.isOk()) 468 { 469 AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS); 470 ClientToken *ct = sm->getClientToken(); 471 if (ct) 472 { 473 HMTX ipcSem = ct->getToken(); 474 machines.push_back(sm); 475 handles[cnt].hsemCur = (HSEM)ipcSem; 476 handles[cnt].ulUser = cnt; 477 ++cnt; 478 } 479 } 5153 480 } 5154 481 } … … 5173 500 spawnedMachines.clear(); 5174 501 5175 for (MachinesOList::iterator it = that->m->allMachines.begin();5176 it != that->m->allMachines.end(); ++it)502 for (MachinesOList::iterator it = allMachines.begin(); 503 it != allMachines.end(); ++it) 5177 504 { 5178 505 if ((*it)->isSessionSpawning()) … … 5204 531 do 5205 532 { 5206 AutoCaller autoCaller(that );533 AutoCaller autoCaller(that->mVirtualBox); 5207 534 if (!autoCaller.isOk()) 5208 535 break; … … 5223 550 do 5224 551 { 5225 uOld = ASMAtomicUoReadU8(&that->m ->updateAdaptCtr);552 uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr); 5226 553 uNew = uOld ? uOld - 1 : uOld; 5227 } while (!ASMAtomicCmpXchgU8(&that->m ->updateAdaptCtr, uNew, uOld));5228 Assert(uOld <= RT_ELEMENTS(s_ updateAdaptTimeouts) - 1);5229 cMillies = s_ updateAdaptTimeouts[uOld];5230 } 5231 5232 int rc = RTSemEventWait(that->m ->updateReq, cMillies);554 } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld)); 555 Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1); 556 cMillies = s_aUpdateTimeoutSteps[uOld]; 557 } 558 559 int rc = RTSemEventWait(that->mUpdateReq, cMillies); 5233 560 5234 561 /* … … 5244 571 /* RT_SUCCESS(rc) means an update event is signaled */ 5245 572 573 // get reference to the machines list in VirtualBox 574 VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); 575 5246 576 // lock the machines list for reading 5247 AutoReadLock thatLock( that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);577 AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); 5248 578 5249 579 if (RT_SUCCESS(rc) || update) … … 5252 582 machines.clear(); 5253 583 5254 for (MachinesOList::iterator it = that->m->allMachines.begin();5255 it != that->m->allMachines.end();584 for (MachinesOList::iterator it = allMachines.begin(); 585 it != allMachines.end(); 5256 586 ++it) 5257 587 { … … 5270 600 spawnedMachines.clear(); 5271 601 5272 for (MachinesOList::iterator it = that->m->allMachines.begin();5273 it != that->m->allMachines.end();602 for (MachinesOList::iterator it = allMachines.begin(); 603 it != allMachines.end(); 5274 604 ++it) 5275 605 { … … 5286 616 5287 617 update = false; 5288 for (size_t i = 0; i < cnt; ++ 618 for (size_t i = 0; i < cnt; ++i) 5289 619 update |= (machines[i])->checkForDeath(); 5290 620 5291 621 updateSpawned = false; 5292 for (size_t i = 0; i < cntSpawned; ++ 622 for (size_t i = 0; i < cntSpawned; ++i) 5293 623 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure(); 5294 624 5295 625 /* reap child processes */ 5296 626 { 5297 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);5298 if (that->m ->llProcesses.size())627 AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS); 628 if (that->mProcesses.size()) 5299 629 { 5300 630 LogFlowFunc(("UPDATE: child process count = %d\n", 5301 that->m ->llProcesses.size()));5302 VirtualBox:: Data::ProcessList::iterator it = that->m->llProcesses.begin();5303 while (it != that->m ->llProcesses.end())631 that->mProcesses.size())); 632 VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin(); 633 while (it != that->mProcesses.end()) 5304 634 { 5305 635 RTPROCESS pid = *it; … … 5311 641 pid, pid, status.iStatus, 5312 642 status.enmReason)); 5313 it = that->m ->llProcesses.erase(it);643 it = that->mProcesses.erase(it); 5314 644 } 5315 645 else … … 5320 650 { 5321 651 /* remove the process if it is not already running */ 5322 it = that->m ->llProcesses.erase(it);652 it = that->mProcesses.erase(it); 5323 653 } 5324 654 else 5325 ++ 655 ++it; 5326 656 } 5327 657 } … … 5342 672 5343 673 VirtualBoxBase::uninitializeComForThread(); 674 5344 675 LogFlowFuncLeave(); 5345 676 return 0; 5346 677 } 5347 5348 /**5349 * Thread function that handles custom events posted using #postEvent().5350 */5351 // static5352 DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser)5353 {5354 LogFlowFuncEnter();5355 5356 AssertReturn(pvUser, VERR_INVALID_POINTER);5357 5358 HRESULT hr = com::Initialize();5359 if (FAILED(hr))5360 return VERR_COM_UNEXPECTED;5361 5362 int rc = VINF_SUCCESS;5363 5364 try5365 {5366 /* Create an event queue for the current thread. */5367 EventQueue *pEventQueue = new EventQueue();5368 AssertPtr(pEventQueue);5369 5370 /* Return the queue to the one who created this thread. */5371 *(static_cast <EventQueue **>(pvUser)) = pEventQueue;5372 5373 /* signal that we're ready. */5374 RTThreadUserSignal(thread);5375 5376 /*5377 * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes5378 * we must not stop processing events and delete the pEventQueue object. This must5379 * be done ONLY when we stop this loop via interruptEventQueueProcessing().5380 * See @bugref{5724}.5381 */5382 for (;;)5383 {5384 rc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT);5385 if (rc == VERR_INTERRUPTED)5386 {5387 LogFlow(("Event queue processing ended with rc=%Rrc\n", rc));5388 rc = VINF_SUCCESS; /* Set success when exiting. */5389 break;5390 }5391 }5392 5393 delete pEventQueue;5394 }5395 catch (std::bad_alloc &ba)5396 {5397 rc = VERR_NO_MEMORY;5398 NOREF(ba);5399 }5400 5401 com::Shutdown();5402 5403 LogFlowFuncLeaveRC(rc);5404 return rc;5405 }5406 5407 5408 ////////////////////////////////////////////////////////////////////////////////5409 5410 /**5411 * Takes the current list of registered callbacks of the managed VirtualBox5412 * instance, and calls #handleCallback() for every callback item from the5413 * list, passing the item as an argument.5414 *5415 * @note Locks the managed VirtualBox object for reading but leaves the lock5416 * before iterating over callbacks and calling their methods.5417 */5418 void *VirtualBox::CallbackEvent::handler()5419 {5420 if (!mVirtualBox)5421 return NULL;5422 5423 AutoCaller autoCaller(mVirtualBox);5424 if (!autoCaller.isOk())5425 {5426 LogWarningFunc(("VirtualBox has been uninitialized (state=%d), the callback event is discarded!\n",5427 autoCaller.state()));5428 /* We don't need mVirtualBox any more, so release it */5429 mVirtualBox = NULL;5430 return NULL;5431 }5432 5433 {5434 VBoxEventDesc evDesc;5435 prepareEventDesc(mVirtualBox->m->pEventSource, evDesc);5436 5437 evDesc.fire(/* don't wait for delivery */0);5438 }5439 5440 mVirtualBox = NULL; /* Not needed any longer. Still make sense to do this? */5441 return NULL;5442 }5443 5444 //STDMETHODIMP VirtualBox::CreateDHCPServerForInterface(/*IHostNetworkInterface * aIinterface,*/ IDHCPServer ** aServer)5445 //{5446 // return E_NOTIMPL;5447 //}5448 5449 STDMETHODIMP VirtualBox::CreateDHCPServer(IN_BSTR aName, IDHCPServer ** aServer)5450 {5451 CheckComArgStrNotEmptyOrNull(aName);5452 CheckComArgNotNull(aServer);5453 5454 AutoCaller autoCaller(this);5455 if (FAILED(autoCaller.rc())) return autoCaller.rc();5456 5457 ComObjPtr<DHCPServer> dhcpServer;5458 dhcpServer.createObject();5459 HRESULT rc = dhcpServer->init(this, aName);5460 if (FAILED(rc)) return rc;5461 5462 rc = registerDHCPServer(dhcpServer, true);5463 if (FAILED(rc)) return rc;5464 5465 dhcpServer.queryInterfaceTo(aServer);5466 5467 return rc;5468 }5469 5470 STDMETHODIMP VirtualBox::FindDHCPServerByNetworkName(IN_BSTR aName, IDHCPServer ** aServer)5471 {5472 CheckComArgStrNotEmptyOrNull(aName);5473 CheckComArgNotNull(aServer);5474 5475 AutoCaller autoCaller(this);5476 if (FAILED(autoCaller.rc())) return autoCaller.rc();5477 5478 HRESULT rc;5479 Bstr bstr;5480 ComPtr<DHCPServer> found;5481 5482 AutoReadLock alock(m->allDHCPServers.getLockHandle() COMMA_LOCKVAL_SRC_POS);5483 5484 for (DHCPServersOList::const_iterator it = m->allDHCPServers.begin();5485 it != m->allDHCPServers.end();5486 ++it)5487 {5488 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());5489 if (FAILED(rc)) return rc;5490 5491 if (bstr == aName)5492 {5493 found = *it;5494 break;5495 }5496 }5497 5498 if (!found)5499 return E_INVALIDARG;5500 5501 return found.queryInterfaceTo(aServer);5502 }5503 5504 STDMETHODIMP VirtualBox::RemoveDHCPServer(IDHCPServer * aServer)5505 {5506 CheckComArgNotNull(aServer);5507 5508 AutoCaller autoCaller(this);5509 if (FAILED(autoCaller.rc())) return autoCaller.rc();5510 5511 HRESULT rc = unregisterDHCPServer(static_cast<DHCPServer *>(aServer), true);5512 5513 return rc;5514 }5515 5516 /**5517 * Remembers the given DHCP server in the settings.5518 *5519 * @param aDHCPServer DHCP server object to remember.5520 * @param aSaveSettings @c true to save settings to disk (default).5521 *5522 * When @a aSaveSettings is @c true, this operation may fail because of the5523 * failed #saveSettings() method it calls. In this case, the dhcp server object5524 * will not be remembered. It is therefore the responsibility of the caller to5525 * call this method as the last step of some action that requires registration5526 * in order to make sure that only fully functional dhcp server objects get5527 * registered.5528 *5529 * @note Locks this object for writing and @a aDHCPServer for reading.5530 */5531 HRESULT VirtualBox::registerDHCPServer(DHCPServer *aDHCPServer,5532 bool aSaveSettings /*= true*/)5533 {5534 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);5535 5536 AutoCaller autoCaller(this);5537 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());5538 5539 AutoCaller dhcpServerCaller(aDHCPServer);5540 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());5541 5542 Bstr name;5543 HRESULT rc;5544 rc = aDHCPServer->COMGETTER(NetworkName)(name.asOutParam());5545 if (FAILED(rc)) return rc;5546 5547 ComPtr<IDHCPServer> existing;5548 rc = FindDHCPServerByNetworkName(name.raw(), existing.asOutParam());5549 if (SUCCEEDED(rc))5550 return E_INVALIDARG;5551 5552 rc = S_OK;5553 5554 m->allDHCPServers.addChild(aDHCPServer);5555 5556 if (aSaveSettings)5557 {5558 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);5559 rc = saveSettings();5560 vboxLock.release();5561 5562 if (FAILED(rc))5563 unregisterDHCPServer(aDHCPServer, false /* aSaveSettings */);5564 }5565 5566 return rc;5567 }5568 5569 /**5570 * Removes the given DHCP server from the settings.5571 *5572 * @param aDHCPServer DHCP server object to remove.5573 * @param aSaveSettings @c true to save settings to disk (default).5574 *5575 * When @a aSaveSettings is @c true, this operation may fail because of the5576 * failed #saveSettings() method it calls. In this case, the DHCP server5577 * will NOT be removed from the settingsi when this method returns.5578 *5579 * @note Locks this object for writing.5580 */5581 HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer,5582 bool aSaveSettings /*= true*/)5583 {5584 AssertReturn(aDHCPServer != NULL, E_INVALIDARG);5585 5586 AutoCaller autoCaller(this);5587 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());5588 5589 AutoCaller dhcpServerCaller(aDHCPServer);5590 AssertComRCReturn(dhcpServerCaller.rc(), dhcpServerCaller.rc());5591 5592 m->allDHCPServers.removeChild(aDHCPServer);5593 5594 HRESULT rc = S_OK;5595 5596 if (aSaveSettings)5597 {5598 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);5599 rc = saveSettings();5600 vboxLock.release();5601 5602 if (FAILED(rc))5603 registerDHCPServer(aDHCPServer, false /* aSaveSettings */);5604 }5605 5606 return rc;5607 }5608 5609 5610 /**5611 * NAT Network5612 */5613 5614 STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork)5615 {5616 #ifdef VBOX_WITH_NAT_SERVICE5617 CheckComArgStrNotEmptyOrNull(aName);5618 CheckComArgNotNull(aNatNetwork);5619 5620 AutoCaller autoCaller(this);5621 if (FAILED(autoCaller.rc())) return autoCaller.rc();5622 5623 ComObjPtr<NATNetwork> natNetwork;5624 natNetwork.createObject();5625 HRESULT rc = natNetwork->init(this, aName);5626 if (FAILED(rc)) return rc;5627 5628 rc = registerNATNetwork(natNetwork, true);5629 if (FAILED(rc)) return rc;5630 5631 natNetwork.queryInterfaceTo(aNatNetwork);5632 5633 fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE);5634 return rc;5635 #else5636 NOREF(aName);5637 NOREF(aNatNetwork);5638 return E_NOTIMPL;5639 #endif5640 }5641 5642 STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork)5643 {5644 #ifdef VBOX_WITH_NAT_SERVICE5645 CheckComArgStrNotEmptyOrNull(aName);5646 CheckComArgNotNull(aNetwork);5647 5648 AutoCaller autoCaller(this);5649 if (FAILED(autoCaller.rc())) return autoCaller.rc();5650 5651 HRESULT rc;5652 Bstr bstr;5653 ComPtr<NATNetwork> found;5654 5655 AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS);5656 5657 for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin();5658 it != m->allNATNetworks.end();5659 ++it)5660 {5661 rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam());5662 if (FAILED(rc)) return rc;5663 5664 if (bstr == aName)5665 {5666 found = *it;5667 break;5668 }5669 }5670 5671 if (!found)5672 return E_INVALIDARG;5673 5674 return found.queryInterfaceTo(aNetwork);5675 #else5676 NOREF(aName);5677 NOREF(aNetwork);5678 return E_NOTIMPL;5679 #endif5680 }5681 5682 STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork)5683 {5684 #ifdef VBOX_WITH_NAT_SERVICE5685 CheckComArgNotNull(aNetwork);5686 5687 AutoCaller autoCaller(this);5688 if (FAILED(autoCaller.rc())) return autoCaller.rc();5689 Bstr name;5690 HRESULT rc;5691 NATNetwork *network = static_cast<NATNetwork *>(aNetwork);5692 rc = network->COMGETTER(NetworkName)(name.asOutParam());5693 rc = unregisterNATNetwork(network, true);5694 fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE);5695 return rc;5696 #else5697 NOREF(aNetwork);5698 return E_NOTIMPL;5699 #endif5700 5701 }5702 /**5703 * Remembers the given NAT network in the settings.5704 *5705 * @param aNATNetwork NAT Network object to remember.5706 * @param aSaveSettings @c true to save settings to disk (default).5707 *5708 *5709 * @note Locks this object for writing and @a aNATNetwork for reading.5710 */5711 HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork,5712 bool aSaveSettings /*= true*/)5713 {5714 #ifdef VBOX_WITH_NAT_SERVICE5715 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);5716 5717 AutoCaller autoCaller(this);5718 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());5719 5720 AutoCaller natNetworkCaller(aNATNetwork);5721 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());5722 5723 Bstr name;5724 HRESULT rc;5725 rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam());5726 if (FAILED(rc)) return rc;5727 5728 ComPtr<INATNetwork> existing;5729 rc = FindNATNetworkByName(name.raw(), existing.asOutParam());5730 if (SUCCEEDED(rc))5731 return E_INVALIDARG;5732 5733 rc = S_OK;5734 5735 m->allNATNetworks.addChild(aNATNetwork);5736 5737 if (aSaveSettings)5738 {5739 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);5740 rc = saveSettings();5741 vboxLock.release();5742 5743 if (FAILED(rc))5744 unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */);5745 }5746 5747 return rc;5748 #else5749 NOREF(aNATNetwork);5750 NOREF(aSaveSettings);5751 /* No panic please (silently ignore) */5752 return S_OK;5753 #endif5754 }5755 5756 /**5757 * Removes the given NAT network from the settings.5758 *5759 * @param aNATNetwork NAT network object to remove.5760 * @param aSaveSettings @c true to save settings to disk (default).5761 *5762 * When @a aSaveSettings is @c true, this operation may fail because of the5763 * failed #saveSettings() method it calls. In this case, the DHCP server5764 * will NOT be removed from the settingsi when this method returns.5765 *5766 * @note Locks this object for writing.5767 */5768 HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork,5769 bool aSaveSettings /*= true*/)5770 {5771 #ifdef VBOX_WITH_NAT_SERVICE5772 AssertReturn(aNATNetwork != NULL, E_INVALIDARG);5773 5774 AutoCaller autoCaller(this);5775 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());5776 5777 AutoCaller natNetworkCaller(aNATNetwork);5778 AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc());5779 5780 m->allNATNetworks.removeChild(aNATNetwork);5781 5782 HRESULT rc = S_OK;5783 5784 if (aSaveSettings)5785 {5786 AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS);5787 rc = saveSettings();5788 vboxLock.release();5789 5790 if (FAILED(rc))5791 registerNATNetwork(aNATNetwork, false /* aSaveSettings */);5792 }5793 5794 return rc;5795 #else5796 NOREF(aNATNetwork);5797 NOREF(aSaveSettings);5798 return E_NOTIMPL;5799 #endif5800 }5801 678 /* vi: set tabstop=4 shiftwidth=4 expandtab: */ -
trunk/src/VBox/Main/src-server/MachineImpl.cpp
r47401 r47561 24 24 #endif 25 25 26 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER27 # include <errno.h>28 # include <sys/types.h>29 # include <sys/stat.h>30 # include <sys/ipc.h>31 # include <sys/sem.h>32 #endif33 34 26 #include "Logging.h" 35 27 #include "VirtualBoxImpl.h" 36 28 #include "MachineImpl.h" 29 #include "ClientToken.h" 37 30 #include "ProgressImpl.h" 38 31 #include "ProgressProxyImpl.h" … … 3712 3705 mData->mSession.mState = SessionState_Spawning; 3713 3706 3707 /* Get the client token ID to be passed to the client process */ 3708 Utf8Str strTokenId; 3709 sessionMachine->getTokenId(strTokenId); 3710 Assert(!strTokenId.isEmpty()); 3711 3714 3712 /* 3715 3713 * Release the lock before calling the client process -- it will call … … 3725 3723 3726 3724 LogFlowThisFunc(("Calling AssignMachine()...\n")); 3727 rc = pSessionControl->AssignMachine(sessionMachine, lockType );3725 rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw()); 3728 3726 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc)); 3729 3727 … … 3792 3790 /* Close the remote session, remove the remote control from the list 3793 3791 * and reset session state to Closed (@note keep the code in sync 3794 * with the relevant part in openSession()). */3792 * with the relevant part in checkForSpawnFailure()). */ 3795 3793 3796 3794 Assert(mData->mSession.mRemoteControls.size() == 1); … … 8096 8094 * Releasing the lock here is dangerous because we didn't prepare the 8097 8095 * launch data yet, but the client we've just started may happen to be 8098 * too fast and call openSession() that will fail (because of PID, etc.),8096 * too fast and call LockMachine() that will fail (because of PID, etc.), 8099 8097 * so that the Machine will never get out of the Spawning session state. 8100 8098 */ … … 8102 8100 /* inform the session that it will be a remote one */ 8103 8101 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n")); 8104 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write );8102 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw()); 8105 8103 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc)); 8106 8104 … … 8127 8125 8128 8126 /** 8129 * Returns @c true if the given machine has an open direct session and returns8130 * the session machine instance and additional session data (on some platforms)8131 * if so.8127 * Returns @c true if the given session machine instance has an open direct 8128 * session (and optionally also for direct sessions which are closing) and 8129 * returns the session control machine instance if so. 8132 8130 * 8133 8131 * Note that when the method returns @c false, the arguments remain unchanged. 8134 8132 * 8135 * @param aMachine Session machine object. 8136 * @param aControl Direct session control object (optional). 8137 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional). 8133 * @param aMachine Session machine object. 8134 * @param aControl Direct session control object (optional). 8135 * @param aAllowClosing If true then additionally a session which is currently 8136 * being closed will also be allowed. 8138 8137 * 8139 8138 * @note locks this object for reading. 8140 8139 */ 8141 #if defined(RT_OS_WINDOWS)8142 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,8143 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,8144 HANDLE *aIPCSem /*= NULL*/,8145 bool aAllowClosing /*= false*/)8146 #elif defined(RT_OS_OS2)8147 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine,8148 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,8149 HMTX *aIPCSem /*= NULL*/,8150 bool aAllowClosing /*= false*/)8151 #else8152 8140 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 8153 8141 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 8154 8142 bool aAllowClosing /*= false*/) 8155 #endif8156 8143 { 8157 8144 AutoLimitedCaller autoCaller(this); … … 8175 8162 *aControl = mData->mSession.mDirectControl; 8176 8163 8177 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)8178 /* Additional session data */8179 if (aIPCSem != NULL)8180 *aIPCSem = aMachine->mIPCSem;8181 #endif8182 8164 return true; 8183 8165 } … … 8187 8169 8188 8170 /** 8189 * Returns @c true if the given machine has an spawning direct session and 8190 * returns and additional session data (on some platforms) if so. 8191 * 8192 * Note that when the method returns @c false, the arguments remain unchanged. 8193 * 8194 * @param aPID PID of the spawned direct session process. 8171 * Returns @c true if the given machine has an spawning direct session. 8195 8172 * 8196 8173 * @note locks this object for reading. 8197 8174 */ 8198 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)8199 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/)8200 #else8201 8175 bool Machine::isSessionSpawning() 8202 #endif8203 8176 { 8204 8177 AutoLimitedCaller autoCaller(this); … … 8212 8185 8213 8186 if (mData->mSession.mState == SessionState_Spawning) 8214 {8215 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)8216 /* Additional session data */8217 if (aPID != NULL)8218 {8219 AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false);8220 *aPID = mData->mSession.mPID;8221 }8222 #endif8223 8187 return true; 8224 }8225 8188 8226 8189 return false; … … 8251 8214 } 8252 8215 8253 /* VirtualBox::addProcessToReap() needs a write lock */ 8254 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); 8216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 8255 8217 8256 8218 if (mData->mSession.mState != SessionState_Spawning) … … 8263 8225 HRESULT rc = S_OK; 8264 8226 8265 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)8266 8267 /* the process was already unexpectedly terminated, we just need to set an8268 * error and finalize session spawning */8269 rc = setError(E_FAIL,8270 tr("The virtual machine '%s' has terminated unexpectedly during startup"),8271 getName().c_str());8272 #else8273 8274 8227 /* PID not yet initialized, skip check. */ 8275 8228 if (mData->mSession.mPID == NIL_RTPROCESS) … … 8277 8230 8278 8231 RTPROCSTATUS status; 8279 int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, 8280 &status); 8232 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status); 8281 8233 8282 8234 if (vrc != VERR_PROCESS_RUNNING) … … 8300 8252 } 8301 8253 8302 #endif8303 8304 8254 if (FAILED(rc)) 8305 8255 { 8306 8256 /* Close the remote session, remove the remote control from the list 8307 8257 * and reset session state to Closed (@note keep the code in sync with 8308 * the relevant part in checkForSpawnFailure()). */8258 * the relevant part in LockMachine()). */ 8309 8259 8310 8260 Assert(mData->mSession.mRemoteControls.size() == 1); … … 8325 8275 } 8326 8276 8327 mParent->addProcessToReap(mData->mSession.mPID);8328 8277 mData->mSession.mPID = NIL_RTPROCESS; 8329 8278 … … 12680 12629 LogFlowThisFunc(("\n")); 12681 12630 12682 #if defined(RT_OS_WINDOWS) 12683 mIPCSem = NULL; 12684 #elif defined(RT_OS_OS2) 12685 mIPCSem = NULLHANDLE; 12686 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12687 mIPCSem = -1; 12688 #else 12689 # error "Port me!" 12690 #endif 12631 mClientToken = NULL; 12691 12632 12692 12633 return BaseFinalConstruct(); … … 12697 12638 LogFlowThisFunc(("\n")); 12698 12639 12640 Assert(!mClientToken); 12641 /* paranoia, should not hang around any more */ 12642 if (mClientToken) 12643 { 12644 delete mClientToken; 12645 mClientToken = NULL; 12646 } 12647 12699 12648 uninit(Uninit::Unexpected); 12700 12649 … … 12703 12652 12704 12653 /** 12705 * @note Must be called only by Machine:: openSession() from its own write lock.12654 * @note Must be called only by Machine::LockMachine() from its own write lock. 12706 12655 */ 12707 12656 HRESULT SessionMachine::init(Machine *aMachine) … … 12718 12667 AssertReturn(autoInitSpan.isOk(), E_FAIL); 12719 12668 12720 /* create the interprocess semaphore */ 12721 #if defined(RT_OS_WINDOWS) 12722 mIPCSemName = aMachine->mData->m_strConfigFileFull; 12723 for (size_t i = 0; i < mIPCSemName.length(); i++) 12724 if (mIPCSemName.raw()[i] == '\\') 12725 mIPCSemName.raw()[i] = '/'; 12726 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw()); 12727 ComAssertMsgRet(mIPCSem, 12728 ("Cannot create IPC mutex '%ls', err=%d", 12729 mIPCSemName.raw(), ::GetLastError()), 12730 E_FAIL); 12731 #elif defined(RT_OS_OS2) 12732 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}", 12733 aMachine->mData->mUuid.raw()); 12734 mIPCSemName = ipcSem; 12735 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE); 12736 ComAssertMsgRet(arc == NO_ERROR, 12737 ("Cannot create IPC mutex '%s', arc=%ld", 12738 ipcSem.c_str(), arc), 12739 E_FAIL); 12740 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12741 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 12742 # if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64) 12743 /** @todo Check that this still works correctly. */ 12744 AssertCompileSize(key_t, 8); 12745 # else 12746 AssertCompileSize(key_t, 4); 12747 # endif 12748 key_t key; 12749 mIPCSem = -1; 12750 mIPCKey = "0"; 12751 for (uint32_t i = 0; i < 1 << 24; i++) 12752 { 12753 key = ((uint32_t)'V' << 24) | i; 12754 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL); 12755 if (sem >= 0 || (errno != EEXIST && errno != EACCES)) 12756 { 12757 mIPCSem = sem; 12758 if (sem >= 0) 12759 mIPCKey = BstrFmt("%u", key); 12760 break; 12761 } 12762 } 12763 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 12764 Utf8Str semName = aMachine->mData->m_strConfigFileFull; 12765 char *pszSemName = NULL; 12766 RTStrUtf8ToCurrentCP(&pszSemName, semName); 12767 key_t key = ::ftok(pszSemName, 'V'); 12768 RTStrFree(pszSemName); 12769 12770 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT); 12771 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ 12772 12773 int errnoSave = errno; 12774 if (mIPCSem < 0 && errnoSave == ENOSYS) 12775 { 12776 setError(E_FAIL, 12777 tr("Cannot create IPC semaphore. Most likely your host kernel lacks " 12778 "support for SysV IPC. Check the host kernel configuration for " 12779 "CONFIG_SYSVIPC=y")); 12780 return E_FAIL; 12781 } 12782 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing 12783 * the IPC semaphores */ 12784 if (mIPCSem < 0 && errnoSave == ENOSPC) 12785 { 12786 #ifdef RT_OS_LINUX 12787 setError(E_FAIL, 12788 tr("Cannot create IPC semaphore because the system limit for the " 12789 "maximum number of semaphore sets (SEMMNI), or the system wide " 12790 "maximum number of semaphores (SEMMNS) would be exceeded. The " 12791 "current set of SysV IPC semaphores can be determined from " 12792 "the file /proc/sysvipc/sem")); 12793 #else 12794 setError(E_FAIL, 12795 tr("Cannot create IPC semaphore because the system-imposed limit " 12796 "on the maximum number of allowed semaphores or semaphore " 12797 "identifiers system-wide would be exceeded")); 12798 #endif 12799 return E_FAIL; 12800 } 12801 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave), 12802 E_FAIL); 12803 /* set the initial value to 1 */ 12804 int rv = ::semctl(mIPCSem, 0, SETVAL, 1); 12805 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno), 12806 E_FAIL); 12807 #else 12808 # error "Port me!" 12809 #endif 12669 HRESULT rc = S_OK; 12670 12671 /* create the machine client token */ 12672 try 12673 { 12674 mClientToken = new ClientToken(aMachine); 12675 if (!mClientToken->isReady()) 12676 { 12677 delete mClientToken; 12678 mClientToken = NULL; 12679 rc = E_FAIL; 12680 } 12681 } 12682 catch (std::bad_alloc &) 12683 { 12684 rc = E_OUTOFMEMORY; 12685 } 12686 if (FAILED(rc)) 12687 return rc; 12810 12688 12811 12689 /* memorize the peer Machine */ … … 12888 12766 12889 12767 LogFlowThisFuncLeave(); 12890 return S_OK;12768 return rc; 12891 12769 } 12892 12770 … … 12929 12807 */ 12930 12808 LogFlowThisFunc(("Initialization failed.\n")); 12931 #if defined(RT_OS_WINDOWS) 12932 if (mIPCSem) 12933 ::CloseHandle(mIPCSem); 12934 mIPCSem = NULL; 12935 #elif defined(RT_OS_OS2) 12936 if (mIPCSem != NULLHANDLE) 12937 ::DosCloseMutexSem(mIPCSem); 12938 mIPCSem = NULLHANDLE; 12939 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 12940 if (mIPCSem >= 0) 12941 ::semctl(mIPCSem, 0, IPC_RMID); 12942 mIPCSem = -1; 12943 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 12944 mIPCKey = "0"; 12945 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 12946 #else 12947 # error "Port me!" 12948 #endif 12809 /* destroy the machine client token */ 12810 if (mClientToken) 12811 { 12812 delete mClientToken; 12813 mClientToken = NULL; 12814 } 12949 12815 uninitDataAndChildObjects(); 12950 12816 mData.free(); … … 13134 13000 mData->mSession.mType.setNull(); 13135 13001 13136 /* close the interprocess semaphore before leaving the exclusive lock */ 13137 #if defined(RT_OS_WINDOWS) 13138 if (mIPCSem) 13139 ::CloseHandle(mIPCSem); 13140 mIPCSem = NULL; 13141 #elif defined(RT_OS_OS2) 13142 if (mIPCSem != NULLHANDLE) 13143 ::DosCloseMutexSem(mIPCSem); 13144 mIPCSem = NULLHANDLE; 13145 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 13146 if (mIPCSem >= 0) 13147 ::semctl(mIPCSem, 0, IPC_RMID); 13148 mIPCSem = -1; 13149 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN 13150 mIPCKey = "0"; 13151 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ 13152 #else 13153 # error "Port me!" 13154 #endif 13002 /* destroy the machine client token before leaving the exclusive lock */ 13003 if (mClientToken) 13004 { 13005 delete mClientToken; 13006 mClientToken = NULL; 13007 } 13155 13008 13156 13009 /* fire an event */ … … 13165 13018 multilock.release(); 13166 13019 13020 RTThreadSleep(500); 13021 mParent->AddRef(); 13022 LONG c = mParent->Release(); 13023 LogFlowThisFunc(("vbox ref=%d\n", c)); 13167 13024 unconst(mParent) = NULL; 13168 13025 unconst(mPeer) = NULL; … … 13252 13109 13253 13110 /** 13254 * @note Locks this object for reading.13255 */13256 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)13257 {13258 AutoCaller autoCaller(this);13259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());13260 13261 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);13262 13263 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)13264 mIPCSemName.cloneTo(aId);13265 return S_OK;13266 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)13267 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN13268 mIPCKey.cloneTo(aId);13269 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */13270 mData->m_strConfigFileFull.cloneTo(aId);13271 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */13272 return S_OK;13273 #else13274 # error "Port me!"13275 #endif13276 }13277 13278 /**13279 13111 * @note Locks this object for writing. 13280 13112 */ … … 14078 13910 Uninit::Abnormal; 14079 13911 14080 #if defined(RT_OS_WINDOWS) 14081 14082 AssertMsg(mIPCSem, ("semaphore must be created")); 14083 14084 /* release the IPC mutex */ 14085 ::ReleaseMutex(mIPCSem); 14086 14087 terminated = true; 14088 14089 #elif defined(RT_OS_OS2) 14090 14091 AssertMsg(mIPCSem, ("semaphore must be created")); 14092 14093 /* release the IPC mutex */ 14094 ::DosReleaseMutexSem(mIPCSem); 14095 14096 terminated = true; 14097 14098 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 14099 14100 AssertMsg(mIPCSem >= 0, ("semaphore must be created")); 14101 14102 int val = ::semctl(mIPCSem, 0, GETVAL); 14103 if (val > 0) 14104 { 14105 /* the semaphore is signaled, meaning the session is terminated */ 14106 terminated = true; 14107 } 14108 14109 #else 14110 # error "Port me!" 14111 #endif 14112 13912 if (mClientToken) 13913 terminated = mClientToken->release(); 14113 13914 } /* AutoCaller block */ 14114 13915 … … 14118 13919 return terminated; 14119 13920 } 13921 13922 void SessionMachine::getTokenId(Utf8Str &strTokenId) 13923 { 13924 LogFlowThisFunc(("\n")); 13925 13926 strTokenId.setNull(); 13927 13928 AutoCaller autoCaller(this); 13929 AssertComRCReturnVoid(autoCaller.rc()); 13930 13931 Assert(mClientToken); 13932 if (mClientToken) 13933 mClientToken->getId(strTokenId); 13934 } 13935 13936 Machine::ClientToken *SessionMachine::getClientToken() 13937 { 13938 LogFlowThisFunc(("\n")); 13939 13940 AutoCaller autoCaller(this); 13941 AssertComRCReturn(autoCaller.rc(), NULL); 13942 13943 return mClientToken; 13944 } 13945 14120 13946 14121 13947 /** -
trunk/src/VBox/Main/src-server/VirtualBoxImpl.cpp
r47018 r47561 36 36 #include <VBox/com/array.h> 37 37 #include "VBox/com/EventQueue.h" 38 #include "VBox/com/MultiResult.h" 38 39 39 40 #include <VBox/err.h> … … 73 74 #endif 74 75 #include "AutostartDb.h" 76 #include "ClientWatcher.h" 75 77 76 78 #include "AutoCaller.h" 77 79 #include "Logging.h" 78 #include "objectslist.h"79 80 80 81 #ifdef RT_OS_WINDOWS … … 111 112 // static 112 113 Bstr VirtualBox::sAPIVersion; 113 114 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER115 /** Table for adaptive timeouts in the client watcher. The counter starts at116 * the maximum value and decreases to 0. */117 static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 };118 #endif119 114 120 115 //////////////////////////////////////////////////////////////////////////////// … … 165 160 //////////////////////////////////////////////////////////////////////////////// 166 161 167 #if defined(RT_OS_WINDOWS)168 #define UPDATEREQARG NULL169 #define UPDATEREQTYPE HANDLE170 #elif defined(RT_OS_OS2)171 #define UPDATEREQARG NIL_RTSEMEVENT172 #define UPDATEREQTYPE RTSEMEVENT173 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)174 #define UPDATEREQARG175 #define UPDATEREQTYPE RTSEMEVENT176 #else177 # error "Port me!"178 #endif179 180 typedef ObjectsList<Machine> MachinesOList;181 162 typedef ObjectsList<Medium> MediaOList; 182 163 typedef ObjectsList<GuestOSType> GuestOSTypesOList; … … 214 195 allNATNetworks(lockNATNetworks), 215 196 mtxProgressOperations(LOCKCLASS_PROGRESSLIST), 216 updateReq(UPDATEREQARG), 217 threadClientWatcher(NIL_RTTHREAD), 197 pClientWatcher(NULL), 218 198 threadAsyncEvent(NIL_RTTHREAD), 219 199 pAsyncEventQ(NULL), … … 290 270 RWLockHandle lockDHCPServers; 291 271 DHCPServersOList allDHCPServers; 292 293 RWLockHandle 294 NATNetworksOList 272 273 RWLockHandle lockNATNetworks; 274 NATNetworksOList allNATNetworks; 295 275 296 276 RWLockHandle mtxProgressOperations; 297 277 ProgressMap mapProgressOperations; 298 278 299 // the following are data for the client watcher thread 300 const UPDATEREQTYPE updateReq; 301 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER 302 uint8_t updateAdaptCtr; 303 #endif 304 const RTTHREAD threadClientWatcher; 305 typedef std::list<RTPROCESS> ProcessList; 306 ProcessList llProcesses; 279 ClientWatcher * const pClientWatcher; 307 280 308 281 // the following are data for the async event thread … … 551 524 if (SUCCEEDED(rc)) 552 525 { 553 /* start the client watcher thread */ 554 #if defined(RT_OS_WINDOWS) 555 unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL); 556 #elif defined(RT_OS_OS2) 557 RTSemEventCreate(&unconst(m->updateReq)); 558 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 559 RTSemEventCreate(&unconst(m->updateReq)); 560 ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0); 561 #else 562 # error "Port me!" 563 #endif 564 int vrc = RTThreadCreate(&unconst(m->threadClientWatcher), 565 ClientWatcher, 566 (void *)this, 567 0, 568 RTTHREADTYPE_MAIN_WORKER, 569 RTTHREADFLAGS_WAITABLE, 570 "Watcher"); 571 ComAssertRC(vrc); 572 if (RT_FAILURE(vrc)) 573 rc = E_FAIL; 526 /* set up client monitoring */ 527 try 528 { 529 unconst(m->pClientWatcher) = new ClientWatcher(this); 530 if (!m->pClientWatcher->isReady()) 531 { 532 delete m->pClientWatcher; 533 unconst(m->pClientWatcher) = NULL; 534 rc = E_FAIL; 535 } 536 } 537 catch (std::bad_alloc &) 538 { 539 rc = E_OUTOFMEMORY; 540 } 574 541 } 575 542 … … 836 803 837 804 LogFlowThisFunc(("Terminating the client watcher...\n")); 838 if (m->threadClientWatcher != NIL_RTTHREAD) 839 { 840 /* signal the client watcher thread */ 841 updateClientWatcher(); 842 /* wait for the termination */ 843 RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL); 844 unconst(m->threadClientWatcher) = NIL_RTTHREAD; 845 } 846 m->llProcesses.clear(); 847 #if defined(RT_OS_WINDOWS) 848 if (m->updateReq != NULL) 849 { 850 ::CloseHandle(m->updateReq); 851 unconst(m->updateReq) = NULL; 852 } 853 #elif defined(RT_OS_OS2) 854 if (m->updateReq != NIL_RTSEMEVENT) 855 { 856 RTSemEventDestroy(m->updateReq); 857 unconst(m->updateReq) = NIL_RTSEMEVENT; 858 } 859 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 860 if (m->updateReq != NIL_RTSEMEVENT) 861 { 862 RTSemEventDestroy(m->updateReq); 863 unconst(m->updateReq) = NIL_RTSEMEVENT; 864 } 865 #else 866 # error "Port me!" 867 #endif 805 if (m->pClientWatcher) 806 { 807 delete m->pClientWatcher; 808 unconst(m->pClientWatcher) = NULL; 809 } 868 810 869 811 delete m->pAutostartDb; … … 888 830 AutoCaller autoCaller(this); 889 831 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 832 833 MultiResult mrc; 834 setError(E_INVALIDARG, "test error first"); 835 setError(E_FAIL, "test error"); 836 return 0x8000ffff; 890 837 891 838 sVersion.cloneTo(aVersion); … … 1153 1100 return S_OK; 1154 1101 #else /* !VBOX_WITH_RESOURCE_USAGE_API */ 1102 NOREF(aPerformanceCollector); 1155 1103 ReturnComNotImplemented(); 1156 1104 #endif /* !VBOX_WITH_RESOURCE_USAGE_API */ … … 2837 2785 2838 2786 /** 2839 * Sends a signal to the client watcher t hread to rescan the set of machines2787 * Sends a signal to the client watcher to rescan the set of machines 2840 2788 * that have open sessions. 2841 2789 * … … 2847 2795 AssertComRCReturnVoid(autoCaller.rc()); 2848 2796 2849 AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD); 2850 2851 /* sent an update request */ 2852 #if defined(RT_OS_WINDOWS) 2853 ::SetEvent(m->updateReq); 2854 #elif defined(RT_OS_OS2) 2855 RTSemEventSignal(m->updateReq); 2856 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) 2857 ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1); 2858 RTSemEventSignal(m->updateReq); 2859 #else 2860 # error "Port me!" 2861 #endif 2797 AssertPtrReturnVoid(m->pClientWatcher); 2798 m->pClientWatcher->update(); 2862 2799 } 2863 2800 … … 2865 2802 * Adds the given child process ID to the list of processes to be reaped. 2866 2803 * This call should be followed by #updateClientWatcher() to take the effect. 2804 * 2805 * @note Doesn't lock anything. 2867 2806 */ 2868 2807 void VirtualBox::addProcessToReap(RTPROCESS pid) … … 2871 2810 AssertComRCReturnVoid(autoCaller.rc()); 2872 2811 2873 /// @todo (dmik) Win32? 2874 #ifndef RT_OS_WINDOWS 2875 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2876 m->llProcesses.push_back(pid); 2877 #endif 2812 AssertPtrReturnVoid(m->pClientWatcher); 2813 m->pClientWatcher->addProcess(pid); 2878 2814 } 2879 2815 … … 3112 3048 } 3113 3049 3114 /** Event for onMachineUninit(), this is not a CallbackEvent */3115 class MachineUninitEvent : public Event3116 {3117 public:3118 3119 MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine)3120 : mVirtualBox(aVirtualBox), mMachine(aMachine)3121 {3122 Assert(aVirtualBox);3123 Assert(aMachine);3124 }3125 3126 void *handler()3127 {3128 #ifdef VBOX_WITH_RESOURCE_USAGE_API3129 /* Handle unregistering metrics here, as it is not vital to get3130 * it done immediately. It reduces the number of locks needed and3131 * the lock contention in SessionMachine::uninit. */3132 {3133 AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS);3134 mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine);3135 }3136 #endif /* VBOX_WITH_RESOURCE_USAGE_API */3137 3138 return NULL;3139 }3140 3141 private:3142 3143 /**3144 * Note that this is a weak ref -- the CallbackEvent handler thread3145 * is bound to the lifetime of the VirtualBox instance, so it's safe.3146 */3147 VirtualBox *mVirtualBox;3148 3149 /** Reference to the machine object. */3150 ComObjPtr<Machine> mMachine;3151 };3152 3153 /**3154 * Trigger internal event. This isn't meant to be signalled to clients.3155 * @note Doesn't lock any object.3156 */3157 void VirtualBox::onMachineUninit(Machine *aMachine)3158 {3159 postEvent(new MachineUninitEvent(this, aMachine));3160 }3161 3162 3050 /** 3163 3051 * @note Doesn't lock any object. … … 3180 3068 fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart); 3181 3069 } 3182 void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled, 3183 IN_BSTR aNetwork, IN_BSTR aGateway, 3184 BOOL aAdvertiseDefaultIpv6RouteEnabled, 3070 void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled, 3071 IN_BSTR aNetwork, IN_BSTR aGateway, 3072 BOOL aAdvertiseDefaultIpv6RouteEnabled, 3185 3073 BOOL fNeedDhcpServer) 3186 3074 { 3187 fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, 3075 fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, 3188 3076 aNetwork, aGateway, 3189 3077 aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer); 3190 3078 } 3191 3079 3192 void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6, 3193 IN_BSTR aRuleName, NATProtocol_T proto, 3194 IN_BSTR aHostIp, LONG aHostPort, 3080 void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6, 3081 IN_BSTR aRuleName, NATProtocol_T proto, 3082 IN_BSTR aHostIp, LONG aHostPort, 3195 3083 IN_BSTR aGuestIp, LONG aGuestPort) 3196 3084 { 3197 fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, 3085 fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, 3198 3086 fIpv6, aRuleName, proto, 3199 3087 aHostIp, aHostPort, … … 3257 3145 } 3258 3146 } 3147 } 3148 3149 /** 3150 * Gets a reference to the machine list. This is the real thing, not a copy, 3151 * so bad things will happen if the caller doesn't hold the necessary lock. 3152 * 3153 * @returns reference to machine list 3154 * 3155 * @note Caller must hold the VirtualBox object lock at least for reading. 3156 */ 3157 VirtualBox::MachinesOList &VirtualBox::getMachinesList(void) 3158 { 3159 return m->allMachines; 3259 3160 } 3260 3161 … … 4849 4750 4850 4751 /** 4851 * Thread function that watches the termination of all client processes4852 * that have opened sessions using IMachine::LockMachine()4853 */4854 // static4855 DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser)4856 {4857 LogFlowFuncEnter();4858 4859 VirtualBox *that = (VirtualBox*)pvUser;4860 Assert(that);4861 4862 typedef std::vector< ComObjPtr<Machine> > MachineVector;4863 typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector;4864 4865 SessionMachineVector machines;4866 MachineVector spawnedMachines;4867 4868 size_t cnt = 0;4869 size_t cntSpawned = 0;4870 4871 VirtualBoxBase::initializeComForThread();4872 4873 #if defined(RT_OS_WINDOWS)4874 4875 /// @todo (dmik) processes reaping!4876 4877 HANDLE handles[MAXIMUM_WAIT_OBJECTS];4878 handles[0] = that->m->updateReq;4879 4880 do4881 {4882 AutoCaller autoCaller(that);4883 /* VirtualBox has been early uninitialized, terminate */4884 if (!autoCaller.isOk())4885 break;4886 4887 do4888 {4889 /* release the caller to let uninit() ever proceed */4890 autoCaller.release();4891 4892 DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned),4893 handles,4894 FALSE,4895 INFINITE);4896 4897 /* Restore the caller before using VirtualBox. If it fails, this4898 * means VirtualBox is being uninitialized and we must terminate. */4899 autoCaller.add();4900 if (!autoCaller.isOk())4901 break;4902 4903 bool update = false;4904 4905 if (rc == WAIT_OBJECT_0)4906 {4907 /* update event is signaled */4908 update = true;4909 }4910 else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt))4911 {4912 /* machine mutex is released */4913 (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath();4914 update = true;4915 }4916 else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt))4917 {4918 /* machine mutex is abandoned due to client process termination */4919 (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath();4920 update = true;4921 }4922 else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned))4923 {4924 /* spawned VM process has terminated (normally or abnormally) */4925 (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])->4926 checkForSpawnFailure();4927 update = true;4928 }4929 4930 if (update)4931 {4932 /* close old process handles */4933 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i)4934 CloseHandle(handles[i]);4935 4936 // lock the machines list for reading4937 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);4938 4939 /* obtain a new set of opened machines */4940 cnt = 0;4941 machines.clear();4942 4943 for (MachinesOList::iterator it = that->m->allMachines.begin();4944 it != that->m->allMachines.end();4945 ++it)4946 {4947 /// @todo handle situations with more than 64 objects4948 AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS,4949 ("MAXIMUM_WAIT_OBJECTS reached"));4950 4951 ComObjPtr<SessionMachine> sm;4952 HANDLE ipcSem;4953 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))4954 {4955 machines.push_back(sm);4956 handles[1 + cnt] = ipcSem;4957 ++cnt;4958 }4959 }4960 4961 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));4962 4963 /* obtain a new set of spawned machines */4964 cntSpawned = 0;4965 spawnedMachines.clear();4966 4967 for (MachinesOList::iterator it = that->m->allMachines.begin();4968 it != that->m->allMachines.end();4969 ++it)4970 {4971 /// @todo handle situations with more than 64 objects4972 AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS,4973 ("MAXIMUM_WAIT_OBJECTS reached"));4974 4975 RTPROCESS pid;4976 if ((*it)->isSessionSpawning(&pid))4977 {4978 HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid);4979 AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n",4980 pid, GetLastError()));4981 if (rc == 0)4982 {4983 spawnedMachines.push_back(*it);4984 handles[1 + cnt + cntSpawned] = ph;4985 ++cntSpawned;4986 }4987 }4988 }4989 4990 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));4991 4992 // machines lock unwinds here4993 }4994 }4995 while (true);4996 }4997 while (0);4998 4999 /* close old process handles */5000 for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i)5001 CloseHandle(handles[i]);5002 5003 /* release sets of machines if any */5004 machines.clear();5005 spawnedMachines.clear();5006 5007 ::CoUninitialize();5008 5009 #elif defined(RT_OS_OS2)5010 5011 /// @todo (dmik) processes reaping!5012 5013 /* according to PMREF, 64 is the maximum for the muxwait list */5014 SEMRECORD handles[64];5015 5016 HMUX muxSem = NULLHANDLE;5017 5018 do5019 {5020 AutoCaller autoCaller(that);5021 /* VirtualBox has been early uninitialized, terminate */5022 if (!autoCaller.isOk())5023 break;5024 5025 do5026 {5027 /* release the caller to let uninit() ever proceed */5028 autoCaller.release();5029 5030 int vrc = RTSemEventWait(that->m->updateReq, 500);5031 5032 /* Restore the caller before using VirtualBox. If it fails, this5033 * means VirtualBox is being uninitialized and we must terminate. */5034 autoCaller.add();5035 if (!autoCaller.isOk())5036 break;5037 5038 bool update = false;5039 bool updateSpawned = false;5040 5041 if (RT_SUCCESS(vrc))5042 {5043 /* update event is signaled */5044 update = true;5045 updateSpawned = true;5046 }5047 else5048 {5049 AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED,5050 ("RTSemEventWait returned %Rrc\n", vrc));5051 5052 /* are there any mutexes? */5053 if (cnt > 0)5054 {5055 /* figure out what's going on with machines */5056 5057 unsigned long semId = 0;5058 APIRET arc = ::DosWaitMuxWaitSem(muxSem,5059 SEM_IMMEDIATE_RETURN, &semId);5060 5061 if (arc == NO_ERROR)5062 {5063 /* machine mutex is normally released */5064 Assert(semId >= 0 && semId < cnt);5065 if (semId >= 0 && semId < cnt)5066 {5067 #if 0//def DEBUG5068 {5069 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);5070 LogFlowFunc(("released mutex: machine='%ls'\n",5071 machines[semId]->name().raw()));5072 }5073 #endif5074 machines[semId]->checkForDeath();5075 }5076 update = true;5077 }5078 else if (arc == ERROR_SEM_OWNER_DIED)5079 {5080 /* machine mutex is abandoned due to client process5081 * termination; find which mutex is in the Owner Died5082 * state */5083 for (size_t i = 0; i < cnt; ++ i)5084 {5085 PID pid; TID tid;5086 unsigned long reqCnt;5087 arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt);5088 if (arc == ERROR_SEM_OWNER_DIED)5089 {5090 /* close the dead mutex as asked by PMREF */5091 ::DosCloseMutexSem((HMTX)handles[i].hsemCur);5092 5093 Assert(i >= 0 && i < cnt);5094 if (i >= 0 && i < cnt)5095 {5096 #if 0//def DEBUG5097 {5098 AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS);5099 LogFlowFunc(("mutex owner dead: machine='%ls'\n",5100 machines[i]->name().raw()));5101 }5102 #endif5103 machines[i]->checkForDeath();5104 }5105 }5106 }5107 update = true;5108 }5109 else5110 AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT,5111 ("DosWaitMuxWaitSem returned %d\n", arc));5112 }5113 5114 /* are there any spawning sessions? */5115 if (cntSpawned > 0)5116 {5117 for (size_t i = 0; i < cntSpawned; ++ i)5118 updateSpawned |= (spawnedMachines[i])->5119 checkForSpawnFailure();5120 }5121 }5122 5123 if (update || updateSpawned)5124 {5125 AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS);5126 5127 if (update)5128 {5129 /* close the old muxsem */5130 if (muxSem != NULLHANDLE)5131 ::DosCloseMuxWaitSem(muxSem);5132 5133 /* obtain a new set of opened machines */5134 cnt = 0;5135 machines.clear();5136 5137 for (MachinesOList::iterator it = that->m->allMachines.begin();5138 it != that->m->allMachines.end(); ++ it)5139 {5140 /// @todo handle situations with more than 64 objects5141 AssertMsg(cnt <= 64 /* according to PMREF */,5142 ("maximum of 64 mutex semaphores reached (%d)",5143 cnt));5144 5145 ComObjPtr<SessionMachine> sm;5146 HMTX ipcSem;5147 if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem))5148 {5149 machines.push_back(sm);5150 handles[cnt].hsemCur = (HSEM)ipcSem;5151 handles[cnt].ulUser = cnt;5152 ++ cnt;5153 }5154 }5155 5156 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));5157 5158 if (cnt > 0)5159 {5160 /* create a new muxsem */5161 APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt,5162 handles,5163 DCMW_WAIT_ANY);5164 AssertMsg(arc == NO_ERROR,5165 ("DosCreateMuxWaitSem returned %d\n", arc));5166 NOREF(arc);5167 }5168 }5169 5170 if (updateSpawned)5171 {5172 /* obtain a new set of spawned machines */5173 spawnedMachines.clear();5174 5175 for (MachinesOList::iterator it = that->m->allMachines.begin();5176 it != that->m->allMachines.end(); ++ it)5177 {5178 if ((*it)->isSessionSpawning())5179 spawnedMachines.push_back(*it);5180 }5181 5182 cntSpawned = spawnedMachines.size();5183 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));5184 }5185 }5186 }5187 while (true);5188 }5189 while (0);5190 5191 /* close the muxsem */5192 if (muxSem != NULLHANDLE)5193 ::DosCloseMuxWaitSem(muxSem);5194 5195 /* release sets of machines if any */5196 machines.clear();5197 spawnedMachines.clear();5198 5199 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)5200 5201 bool update = false;5202 bool updateSpawned = false;5203 5204 do5205 {5206 AutoCaller autoCaller(that);5207 if (!autoCaller.isOk())5208 break;5209 5210 do5211 {5212 /* release the caller to let uninit() ever proceed */5213 autoCaller.release();5214 5215 /* determine wait timeout adaptively: after updating information5216 * relevant to the client watcher, check a few times more5217 * frequently. This ensures good reaction time when the signalling5218 * has to be done a bit before the actual change for technical5219 * reasons, and saves CPU cycles when no activities are expected. */5220 RTMSINTERVAL cMillies;5221 {5222 uint8_t uOld, uNew;5223 do5224 {5225 uOld = ASMAtomicUoReadU8(&that->m->updateAdaptCtr);5226 uNew = uOld ? uOld - 1 : uOld;5227 } while (!ASMAtomicCmpXchgU8(&that->m->updateAdaptCtr, uNew, uOld));5228 Assert(uOld <= RT_ELEMENTS(s_updateAdaptTimeouts) - 1);5229 cMillies = s_updateAdaptTimeouts[uOld];5230 }5231 5232 int rc = RTSemEventWait(that->m->updateReq, cMillies);5233 5234 /*5235 * Restore the caller before using VirtualBox. If it fails, this5236 * means VirtualBox is being uninitialized and we must terminate.5237 */5238 autoCaller.add();5239 if (!autoCaller.isOk())5240 break;5241 5242 if (RT_SUCCESS(rc) || update || updateSpawned)5243 {5244 /* RT_SUCCESS(rc) means an update event is signaled */5245 5246 // lock the machines list for reading5247 AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS);5248 5249 if (RT_SUCCESS(rc) || update)5250 {5251 /* obtain a new set of opened machines */5252 machines.clear();5253 5254 for (MachinesOList::iterator it = that->m->allMachines.begin();5255 it != that->m->allMachines.end();5256 ++it)5257 {5258 ComObjPtr<SessionMachine> sm;5259 if ((*it)->isSessionOpenOrClosing(sm))5260 machines.push_back(sm);5261 }5262 5263 cnt = machines.size();5264 LogFlowFunc(("UPDATE: direct session count = %d\n", cnt));5265 }5266 5267 if (RT_SUCCESS(rc) || updateSpawned)5268 {5269 /* obtain a new set of spawned machines */5270 spawnedMachines.clear();5271 5272 for (MachinesOList::iterator it = that->m->allMachines.begin();5273 it != that->m->allMachines.end();5274 ++it)5275 {5276 if ((*it)->isSessionSpawning())5277 spawnedMachines.push_back(*it);5278 }5279 5280 cntSpawned = spawnedMachines.size();5281 LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned));5282 }5283 5284 // machines lock unwinds here5285 }5286 5287 update = false;5288 for (size_t i = 0; i < cnt; ++ i)5289 update |= (machines[i])->checkForDeath();5290 5291 updateSpawned = false;5292 for (size_t i = 0; i < cntSpawned; ++ i)5293 updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure();5294 5295 /* reap child processes */5296 {5297 AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS);5298 if (that->m->llProcesses.size())5299 {5300 LogFlowFunc(("UPDATE: child process count = %d\n",5301 that->m->llProcesses.size()));5302 VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin();5303 while (it != that->m->llProcesses.end())5304 {5305 RTPROCESS pid = *it;5306 RTPROCSTATUS status;5307 int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status);5308 if (vrc == VINF_SUCCESS)5309 {5310 LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n",5311 pid, pid, status.iStatus,5312 status.enmReason));5313 it = that->m->llProcesses.erase(it);5314 }5315 else5316 {5317 LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n",5318 pid, pid, vrc));5319 if (vrc != VERR_PROCESS_RUNNING)5320 {5321 /* remove the process if it is not already running */5322 it = that->m->llProcesses.erase(it);5323 }5324 else5325 ++ it;5326 }5327 }5328 }5329 }5330 }5331 while (true);5332 }5333 while (0);5334 5335 /* release sets of machines if any */5336 machines.clear();5337 spawnedMachines.clear();5338 5339 #else5340 # error "Port me!"5341 #endif5342 5343 VirtualBoxBase::uninitializeComForThread();5344 LogFlowFuncLeave();5345 return 0;5346 }5347 5348 /**5349 4752 * Thread function that handles custom events posted using #postEvent(). 5350 4753 */
Note:
See TracChangeset
for help on using the changeset viewer.