VirtualBox

source: vbox/trunk/src/VBox/Main/src-server/MachineImpl.cpp@ 56474

Last change on this file since 56474 was 56035, checked in by vboxsync, 10 years ago

Main/Machine+USBController+StorageController: Mix of deleting useless functionality, fixing of missing sanity checks and adding some additional functionality. The removed functionality is the possibility to specify patters of guest properties which will trigger notifications, which wasn't useful as it is per VM, sabotaging other API clients (e.g. the VM process assumes it gets the notifications it needs). The storage controller setters were lacking a lot of state and consistency sanity checking, which is now fixed. Both the USB and storage controllers can now be renamed (no API client uses this functionality though), all with very pessimistic assumptions (only when the VM is powered off).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 509.7 KB
Line 
1/* $Id: MachineImpl.cpp 56035 2015-05-22 16:03:35Z vboxsync $ */
2/** @file
3 * Implementation of IMachine in VBoxSVC.
4 */
5
6/*
7 * Copyright (C) 2004-2015 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
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
25
26#include "Logging.h"
27#include "VirtualBoxImpl.h"
28#include "MachineImpl.h"
29#include "ClientToken.h"
30#include "ProgressImpl.h"
31#include "ProgressProxyImpl.h"
32#include "MediumAttachmentImpl.h"
33#include "MediumImpl.h"
34#include "MediumLock.h"
35#include "USBControllerImpl.h"
36#include "USBDeviceFiltersImpl.h"
37#include "HostImpl.h"
38#include "SharedFolderImpl.h"
39#include "GuestOSTypeImpl.h"
40#include "VirtualBoxErrorInfoImpl.h"
41#include "StorageControllerImpl.h"
42#include "DisplayImpl.h"
43#include "DisplayUtils.h"
44#include "MachineImplCloneVM.h"
45#include "AutostartDb.h"
46#include "SystemPropertiesImpl.h"
47
48// generated header
49#include "VBoxEvents.h"
50
51#ifdef VBOX_WITH_USB
52# include "USBProxyService.h"
53#endif
54
55#include "AutoCaller.h"
56#include "HashedPw.h"
57#include "Performance.h"
58
59#include <iprt/asm.h>
60#include <iprt/path.h>
61#include <iprt/dir.h>
62#include <iprt/env.h>
63#include <iprt/lockvalidator.h>
64#include <iprt/process.h>
65#include <iprt/cpp/utils.h>
66#include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */
67#include <iprt/sha.h>
68#include <iprt/string.h>
69#include <iprt/base64.h>
70
71#include <VBox/com/array.h>
72#include <VBox/com/list.h>
73
74#include <VBox/err.h>
75#include <VBox/param.h>
76#include <VBox/settings.h>
77#include <VBox/vmm/ssm.h>
78
79#ifdef VBOX_WITH_GUEST_PROPS
80# include <VBox/HostServices/GuestPropertySvc.h>
81# include <VBox/com/array.h>
82#endif
83
84#include "VBox/com/MultiResult.h"
85
86#include <algorithm>
87
88#ifdef VBOX_WITH_DTRACE_R3_MAIN
89# include "dtrace/VBoxAPI.h"
90#endif
91
92#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
93# define HOSTSUFF_EXE ".exe"
94#else /* !RT_OS_WINDOWS */
95# define HOSTSUFF_EXE ""
96#endif /* !RT_OS_WINDOWS */
97
98// defines / prototypes
99/////////////////////////////////////////////////////////////////////////////
100
101/////////////////////////////////////////////////////////////////////////////
102// Machine::Data structure
103/////////////////////////////////////////////////////////////////////////////
104
105Machine::Data::Data()
106{
107 mRegistered = FALSE;
108 pMachineConfigFile = NULL;
109 /* Contains hints on what has changed when the user is using the VM (config
110 * changes, running the VM, ...). This is used to decide if a config needs
111 * to be written to disk. */
112 flModifications = 0;
113 /* VM modification usually also trigger setting the current state to
114 * "Modified". Although this is not always the case. An e.g. is the VM
115 * initialization phase or when snapshot related data is changed. The
116 * actually behavior is controlled by the following flag. */
117 m_fAllowStateModification = false;
118 mAccessible = FALSE;
119 /* mUuid is initialized in Machine::init() */
120
121 mMachineState = MachineState_PoweredOff;
122 RTTimeNow(&mLastStateChange);
123
124 mMachineStateDeps = 0;
125 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
126 mMachineStateChangePending = 0;
127
128 mCurrentStateModified = TRUE;
129 mGuestPropertiesModified = FALSE;
130
131 mSession.mPID = NIL_RTPROCESS;
132 mSession.mLockType = LockType_Null;
133 mSession.mState = SessionState_Unlocked;
134}
135
136Machine::Data::~Data()
137{
138 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI)
139 {
140 RTSemEventMultiDestroy(mMachineStateDepsSem);
141 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI;
142 }
143 if (pMachineConfigFile)
144 {
145 delete pMachineConfigFile;
146 pMachineConfigFile = NULL;
147 }
148}
149
150/////////////////////////////////////////////////////////////////////////////
151// Machine::HWData structure
152/////////////////////////////////////////////////////////////////////////////
153
154Machine::HWData::HWData()
155{
156 /* default values for a newly created machine */
157 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */
158 mMemorySize = 128;
159 mCPUCount = 1;
160 mCPUHotPlugEnabled = false;
161 mMemoryBalloonSize = 0;
162 mPageFusionEnabled = false;
163 mGraphicsControllerType = GraphicsControllerType_VBoxVGA;
164 mVRAMSize = 8;
165 mAccelerate3DEnabled = false;
166 mAccelerate2DVideoEnabled = false;
167 mMonitorCount = 1;
168 mVideoCaptureWidth = 1024;
169 mVideoCaptureHeight = 768;
170 mVideoCaptureRate = 512;
171 mVideoCaptureFPS = 25;
172 mVideoCaptureMaxTime = 0;
173 mVideoCaptureMaxFileSize = 0;
174 mVideoCaptureEnabled = false;
175 for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); ++i)
176 maVideoCaptureScreens[i] = true;
177
178 mHWVirtExEnabled = true;
179 mHWVirtExNestedPagingEnabled = true;
180#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
181 mHWVirtExLargePagesEnabled = true;
182#else
183 /* Not supported on 32 bits hosts. */
184 mHWVirtExLargePagesEnabled = false;
185#endif
186 mHWVirtExVPIDEnabled = true;
187 mHWVirtExUXEnabled = true;
188 mHWVirtExForceEnabled = false;
189#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
190 mPAEEnabled = true;
191#else
192 mPAEEnabled = false;
193#endif
194 mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
195 mTripleFaultReset = false;
196 mHPETEnabled = false;
197 mCpuExecutionCap = 100; /* Maximum CPU execution cap by default. */
198 mCpuIdPortabilityLevel = 0;
199
200 /* default boot order: floppy - DVD - HDD */
201 mBootOrder[0] = DeviceType_Floppy;
202 mBootOrder[1] = DeviceType_DVD;
203 mBootOrder[2] = DeviceType_HardDisk;
204 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i)
205 mBootOrder[i] = DeviceType_Null;
206
207 mClipboardMode = ClipboardMode_Disabled;
208 mDnDMode = DnDMode_Disabled;
209
210 mFirmwareType = FirmwareType_BIOS;
211 mKeyboardHIDType = KeyboardHIDType_PS2Keyboard;
212 mPointingHIDType = PointingHIDType_PS2Mouse;
213 mChipsetType = ChipsetType_PIIX3;
214 mParavirtProvider = ParavirtProvider_Default;
215 mEmulatedUSBCardReaderEnabled = FALSE;
216
217 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); ++i)
218 mCPUAttached[i] = false;
219
220 mIOCacheEnabled = true;
221 mIOCacheSize = 5; /* 5MB */
222}
223
224Machine::HWData::~HWData()
225{
226}
227
228/////////////////////////////////////////////////////////////////////////////
229// Machine::HDData structure
230/////////////////////////////////////////////////////////////////////////////
231
232Machine::MediaData::MediaData()
233{
234}
235
236Machine::MediaData::~MediaData()
237{
238}
239
240/////////////////////////////////////////////////////////////////////////////
241// Machine class
242/////////////////////////////////////////////////////////////////////////////
243
244// constructor / destructor
245/////////////////////////////////////////////////////////////////////////////
246
247Machine::Machine() :
248#ifdef VBOX_WITH_RESOURCE_USAGE_API
249 mCollectorGuest(NULL),
250#endif
251 mPeer(NULL),
252 mParent(NULL),
253 mSerialPorts(),
254 mParallelPorts(),
255 uRegistryNeedsSaving(0)
256{}
257
258Machine::~Machine()
259{}
260
261HRESULT Machine::FinalConstruct()
262{
263 LogFlowThisFunc(("\n"));
264 return BaseFinalConstruct();
265}
266
267void Machine::FinalRelease()
268{
269 LogFlowThisFunc(("\n"));
270 uninit();
271 BaseFinalRelease();
272}
273
274/**
275 * Initializes a new machine instance; this init() variant creates a new, empty machine.
276 * This gets called from VirtualBox::CreateMachine().
277 *
278 * @param aParent Associated parent object
279 * @param strConfigFile Local file system path to the VM settings file (can
280 * be relative to the VirtualBox config directory).
281 * @param strName name for the machine
282 * @param llGroups list of groups for the machine
283 * @param aOsType OS Type of this machine or NULL.
284 * @param aId UUID for the new machine.
285 * @param fForceOverwrite Whether to overwrite an existing machine settings file.
286 *
287 * @return Success indicator. if not S_OK, the machine object is invalid
288 */
289HRESULT Machine::init(VirtualBox *aParent,
290 const Utf8Str &strConfigFile,
291 const Utf8Str &strName,
292 const StringsList &llGroups,
293 GuestOSType *aOsType,
294 const Guid &aId,
295 bool fForceOverwrite,
296 bool fDirectoryIncludesUUID)
297{
298 LogFlowThisFuncEnter();
299 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str()));
300
301 /* Enclose the state transition NotReady->InInit->Ready */
302 AutoInitSpan autoInitSpan(this);
303 AssertReturn(autoInitSpan.isOk(), E_FAIL);
304
305 HRESULT rc = initImpl(aParent, strConfigFile);
306 if (FAILED(rc)) return rc;
307
308 rc = i_tryCreateMachineConfigFile(fForceOverwrite);
309 if (FAILED(rc)) return rc;
310
311 if (SUCCEEDED(rc))
312 {
313 // create an empty machine config
314 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
315
316 rc = initDataAndChildObjects();
317 }
318
319 if (SUCCEEDED(rc))
320 {
321 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
322 mData->mAccessible = TRUE;
323
324 unconst(mData->mUuid) = aId;
325
326 mUserData->s.strName = strName;
327
328 mUserData->s.llGroups = llGroups;
329
330 mUserData->s.fDirectoryIncludesUUID = fDirectoryIncludesUUID;
331 // the "name sync" flag determines whether the machine directory gets renamed along
332 // with the machine file; say so if the settings file name is the same as the
333 // settings file parent directory (machine directory)
334 mUserData->s.fNameSync = i_isInOwnDir();
335
336 // initialize the default snapshots folder
337 rc = COMSETTER(SnapshotFolder)(NULL);
338 AssertComRC(rc);
339
340 if (aOsType)
341 {
342 /* Store OS type */
343 mUserData->s.strOsType = aOsType->i_id();
344
345 /* Apply BIOS defaults */
346 mBIOSSettings->i_applyDefaults(aOsType);
347
348 /* Apply network adapters defaults */
349 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
350 mNetworkAdapters[slot]->i_applyDefaults(aOsType);
351
352 /* Apply serial port defaults */
353 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
354 mSerialPorts[slot]->i_applyDefaults(aOsType);
355
356 /* Let the OS type select 64-bit ness. */
357 mHWData->mLongMode = aOsType->i_is64Bit()
358 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled;
359 }
360
361 /* At this point the changing of the current state modification
362 * flag is allowed. */
363 i_allowStateModification();
364
365 /* commit all changes made during the initialization */
366 i_commit();
367 }
368
369 /* Confirm a successful initialization when it's the case */
370 if (SUCCEEDED(rc))
371 {
372 if (mData->mAccessible)
373 autoInitSpan.setSucceeded();
374 else
375 autoInitSpan.setLimited();
376 }
377
378 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n",
379 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
380 mData->mRegistered,
381 mData->mAccessible,
382 rc));
383
384 LogFlowThisFuncLeave();
385
386 return rc;
387}
388
389/**
390 * Initializes a new instance with data from machine XML (formerly Init_Registered).
391 * Gets called in two modes:
392 *
393 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the
394 * UUID is specified and we mark the machine as "registered";
395 *
396 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL
397 * and the machine remains unregistered until RegisterMachine() is called.
398 *
399 * @param aParent Associated parent object
400 * @param aConfigFile Local file system path to the VM settings file (can
401 * be relative to the VirtualBox config directory).
402 * @param aId UUID of the machine or NULL (see above).
403 *
404 * @return Success indicator. if not S_OK, the machine object is invalid
405 */
406HRESULT Machine::initFromSettings(VirtualBox *aParent,
407 const Utf8Str &strConfigFile,
408 const Guid *aId)
409{
410 LogFlowThisFuncEnter();
411 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str()));
412
413 /* Enclose the state transition NotReady->InInit->Ready */
414 AutoInitSpan autoInitSpan(this);
415 AssertReturn(autoInitSpan.isOk(), E_FAIL);
416
417 HRESULT rc = initImpl(aParent, strConfigFile);
418 if (FAILED(rc)) return rc;
419
420 if (aId)
421 {
422 // loading a registered VM:
423 unconst(mData->mUuid) = *aId;
424 mData->mRegistered = TRUE;
425 // now load the settings from XML:
426 rc = i_registeredInit();
427 // this calls initDataAndChildObjects() and loadSettings()
428 }
429 else
430 {
431 // opening an unregistered VM (VirtualBox::OpenMachine()):
432 rc = initDataAndChildObjects();
433
434 if (SUCCEEDED(rc))
435 {
436 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
437 mData->mAccessible = TRUE;
438
439 try
440 {
441 // load and parse machine XML; this will throw on XML or logic errors
442 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
443
444 // reject VM UUID duplicates, they can happen if someone
445 // tries to register an already known VM config again
446 if (aParent->i_findMachine(mData->pMachineConfigFile->uuid,
447 true /* fPermitInaccessible */,
448 false /* aDoSetError */,
449 NULL) != VBOX_E_OBJECT_NOT_FOUND)
450 {
451 throw setError(E_FAIL,
452 tr("Trying to open a VM config '%s' which has the same UUID as an existing virtual machine"),
453 mData->m_strConfigFile.c_str());
454 }
455
456 // use UUID from machine config
457 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid;
458
459 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
460 NULL /* puuidRegistry */);
461 if (FAILED(rc)) throw rc;
462
463 /* At this point the changing of the current state modification
464 * flag is allowed. */
465 i_allowStateModification();
466
467 i_commit();
468 }
469 catch (HRESULT err)
470 {
471 /* we assume that error info is set by the thrower */
472 rc = err;
473 }
474 catch (...)
475 {
476 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
477 }
478 }
479 }
480
481 /* Confirm a successful initialization when it's the case */
482 if (SUCCEEDED(rc))
483 {
484 if (mData->mAccessible)
485 autoInitSpan.setSucceeded();
486 else
487 {
488 autoInitSpan.setLimited();
489
490 // uninit media from this machine's media registry, or else
491 // reloading the settings will fail
492 mParent->i_unregisterMachineMedia(i_getId());
493 }
494 }
495
496 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
497 "rc=%08X\n",
498 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
499 mData->mRegistered, mData->mAccessible, rc));
500
501 LogFlowThisFuncLeave();
502
503 return rc;
504}
505
506/**
507 * Initializes a new instance from a machine config that is already in memory
508 * (import OVF case). Since we are importing, the UUID in the machine
509 * config is ignored and we always generate a fresh one.
510 *
511 * @param strName Name for the new machine; this overrides what is specified in config and is used
512 * for the settings file as well.
513 * @param config Machine configuration loaded and parsed from XML.
514 *
515 * @return Success indicator. if not S_OK, the machine object is invalid
516 */
517HRESULT Machine::init(VirtualBox *aParent,
518 const Utf8Str &strName,
519 const settings::MachineConfigFile &config)
520{
521 LogFlowThisFuncEnter();
522
523 /* Enclose the state transition NotReady->InInit->Ready */
524 AutoInitSpan autoInitSpan(this);
525 AssertReturn(autoInitSpan.isOk(), E_FAIL);
526
527 Utf8Str strConfigFile;
528 aParent->i_getDefaultMachineFolder(strConfigFile);
529 strConfigFile.append(RTPATH_DELIMITER);
530 strConfigFile.append(strName);
531 strConfigFile.append(RTPATH_DELIMITER);
532 strConfigFile.append(strName);
533 strConfigFile.append(".vbox");
534
535 HRESULT rc = initImpl(aParent, strConfigFile);
536 if (FAILED(rc)) return rc;
537
538 rc = i_tryCreateMachineConfigFile(false /* fForceOverwrite */);
539 if (FAILED(rc)) return rc;
540
541 rc = initDataAndChildObjects();
542
543 if (SUCCEEDED(rc))
544 {
545 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure
546 mData->mAccessible = TRUE;
547
548 // create empty machine config for instance data
549 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL);
550
551 // generate fresh UUID, ignore machine config
552 unconst(mData->mUuid).create();
553
554 rc = i_loadMachineDataFromSettings(config,
555 &mData->mUuid); // puuidRegistry: initialize media with this registry ID
556
557 // override VM name as well, it may be different
558 mUserData->s.strName = strName;
559
560 if (SUCCEEDED(rc))
561 {
562 /* At this point the changing of the current state modification
563 * flag is allowed. */
564 i_allowStateModification();
565
566 /* commit all changes made during the initialization */
567 i_commit();
568 }
569 }
570
571 /* Confirm a successful initialization when it's the case */
572 if (SUCCEEDED(rc))
573 {
574 if (mData->mAccessible)
575 autoInitSpan.setSucceeded();
576 else
577 {
578 /* Ignore all errors from unregistering, they would destroy
579- * the more interesting error information we already have,
580- * pinpointing the issue with the VM config. */
581 ErrorInfoKeeper eik;
582
583 autoInitSpan.setLimited();
584
585 // uninit media from this machine's media registry, or else
586 // reloading the settings will fail
587 mParent->i_unregisterMachineMedia(i_getId());
588 }
589 }
590
591 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool "
592 "rc=%08X\n",
593 !!mUserData ? mUserData->s.strName.c_str() : "NULL",
594 mData->mRegistered, mData->mAccessible, rc));
595
596 LogFlowThisFuncLeave();
597
598 return rc;
599}
600
601/**
602 * Shared code between the various init() implementations.
603 * @param aParent
604 * @return
605 */
606HRESULT Machine::initImpl(VirtualBox *aParent,
607 const Utf8Str &strConfigFile)
608{
609 LogFlowThisFuncEnter();
610
611 AssertReturn(aParent, E_INVALIDARG);
612 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG);
613
614 HRESULT rc = S_OK;
615
616 /* share the parent weakly */
617 unconst(mParent) = aParent;
618
619 /* allocate the essential machine data structure (the rest will be
620 * allocated later by initDataAndChildObjects() */
621 mData.allocate();
622
623 /* memorize the config file name (as provided) */
624 mData->m_strConfigFile = strConfigFile;
625
626 /* get the full file name */
627 int vrc1 = mParent->i_calculateFullPath(strConfigFile, mData->m_strConfigFileFull);
628 if (RT_FAILURE(vrc1))
629 return setError(VBOX_E_FILE_ERROR,
630 tr("Invalid machine settings file name '%s' (%Rrc)"),
631 strConfigFile.c_str(),
632 vrc1);
633
634 LogFlowThisFuncLeave();
635
636 return rc;
637}
638
639/**
640 * Tries to create a machine settings file in the path stored in the machine
641 * instance data. Used when a new machine is created to fail gracefully if
642 * the settings file could not be written (e.g. because machine dir is read-only).
643 * @return
644 */
645HRESULT Machine::i_tryCreateMachineConfigFile(bool fForceOverwrite)
646{
647 HRESULT rc = S_OK;
648
649 // when we create a new machine, we must be able to create the settings file
650 RTFILE f = NIL_RTFILE;
651 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE);
652 if ( RT_SUCCESS(vrc)
653 || vrc == VERR_SHARING_VIOLATION
654 )
655 {
656 if (RT_SUCCESS(vrc))
657 RTFileClose(f);
658 if (!fForceOverwrite)
659 rc = setError(VBOX_E_FILE_ERROR,
660 tr("Machine settings file '%s' already exists"),
661 mData->m_strConfigFileFull.c_str());
662 else
663 {
664 /* try to delete the config file, as otherwise the creation
665 * of a new settings file will fail. */
666 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str());
667 if (RT_FAILURE(vrc2))
668 rc = setError(VBOX_E_FILE_ERROR,
669 tr("Could not delete the existing settings file '%s' (%Rrc)"),
670 mData->m_strConfigFileFull.c_str(), vrc2);
671 }
672 }
673 else if ( vrc != VERR_FILE_NOT_FOUND
674 && vrc != VERR_PATH_NOT_FOUND
675 )
676 rc = setError(VBOX_E_FILE_ERROR,
677 tr("Invalid machine settings file name '%s' (%Rrc)"),
678 mData->m_strConfigFileFull.c_str(),
679 vrc);
680 return rc;
681}
682
683/**
684 * Initializes the registered machine by loading the settings file.
685 * This method is separated from #init() in order to make it possible to
686 * retry the operation after VirtualBox startup instead of refusing to
687 * startup the whole VirtualBox server in case if the settings file of some
688 * registered VM is invalid or inaccessible.
689 *
690 * @note Must be always called from this object's write lock
691 * (unless called from #init() that doesn't need any locking).
692 * @note Locks the mUSBController method for writing.
693 * @note Subclasses must not call this method.
694 */
695HRESULT Machine::i_registeredInit()
696{
697 AssertReturn(!i_isSessionMachine(), E_FAIL);
698 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
699 AssertReturn(mData->mUuid.isValid(), E_FAIL);
700 AssertReturn(!mData->mAccessible, E_FAIL);
701
702 HRESULT rc = initDataAndChildObjects();
703
704 if (SUCCEEDED(rc))
705 {
706 /* Temporarily reset the registered flag in order to let setters
707 * potentially called from loadSettings() succeed (isMutable() used in
708 * all setters will return FALSE for a Machine instance if mRegistered
709 * is TRUE). */
710 mData->mRegistered = FALSE;
711
712 try
713 {
714 // load and parse machine XML; this will throw on XML or logic errors
715 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull);
716
717 if (mData->mUuid != mData->pMachineConfigFile->uuid)
718 throw setError(E_FAIL,
719 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"),
720 mData->pMachineConfigFile->uuid.raw(),
721 mData->m_strConfigFileFull.c_str(),
722 mData->mUuid.toString().c_str(),
723 mParent->i_settingsFilePath().c_str());
724
725 rc = i_loadMachineDataFromSettings(*mData->pMachineConfigFile,
726 NULL /* const Guid *puuidRegistry */);
727 if (FAILED(rc)) throw rc;
728 }
729 catch (HRESULT err)
730 {
731 /* we assume that error info is set by the thrower */
732 rc = err;
733 }
734 catch (...)
735 {
736 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
737 }
738
739 /* Restore the registered flag (even on failure) */
740 mData->mRegistered = TRUE;
741 }
742
743 if (SUCCEEDED(rc))
744 {
745 /* Set mAccessible to TRUE only if we successfully locked and loaded
746 * the settings file */
747 mData->mAccessible = TRUE;
748
749 /* commit all changes made during loading the settings file */
750 i_commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive
751 /// @todo r=klaus for some reason the settings loading logic backs up
752 // the settings, and therefore a commit is needed. Should probably be changed.
753 }
754 else
755 {
756 /* If the machine is registered, then, instead of returning a
757 * failure, we mark it as inaccessible and set the result to
758 * success to give it a try later */
759
760 /* fetch the current error info */
761 mData->mAccessError = com::ErrorInfo();
762 Log1Warning(("Machine {%RTuuid} is inaccessible! [%ls]\n", mData->mUuid.raw(), mData->mAccessError.getText().raw()));
763
764 /* rollback all changes */
765 i_rollback(false /* aNotify */);
766
767 // uninit media from this machine's media registry, or else
768 // reloading the settings will fail
769 mParent->i_unregisterMachineMedia(i_getId());
770
771 /* uninitialize the common part to make sure all data is reset to
772 * default (null) values */
773 uninitDataAndChildObjects();
774
775 rc = S_OK;
776 }
777
778 return rc;
779}
780
781/**
782 * Uninitializes the instance.
783 * Called either from FinalRelease() or by the parent when it gets destroyed.
784 *
785 * @note The caller of this method must make sure that this object
786 * a) doesn't have active callers on the current thread and b) is not locked
787 * by the current thread; otherwise uninit() will hang either a) due to
788 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to
789 * a dead-lock caused by this thread waiting for all callers on the other
790 * threads are done but preventing them from doing so by holding a lock.
791 */
792void Machine::uninit()
793{
794 LogFlowThisFuncEnter();
795
796 Assert(!isWriteLockOnCurrentThread());
797
798 Assert(!uRegistryNeedsSaving);
799 if (uRegistryNeedsSaving)
800 {
801 AutoCaller autoCaller(this);
802 if (SUCCEEDED(autoCaller.rc()))
803 {
804 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
805 i_saveSettings(NULL, Machine::SaveS_Force);
806 }
807 }
808
809 /* Enclose the state transition Ready->InUninit->NotReady */
810 AutoUninitSpan autoUninitSpan(this);
811 if (autoUninitSpan.uninitDone())
812 return;
813
814 Assert(!i_isSnapshotMachine());
815 Assert(!i_isSessionMachine());
816 Assert(!!mData);
817
818 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed()));
819 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered));
820
821 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
822
823 if (!mData->mSession.mMachine.isNull())
824 {
825 /* Theoretically, this can only happen if the VirtualBox server has been
826 * terminated while there were clients running that owned open direct
827 * sessions. Since in this case we are definitely called by
828 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit()
829 * won't happen on the client watcher thread (because it does
830 * VirtualBox::addCaller() for the duration of the
831 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit()
832 * cannot happen until the VirtualBox caller is released). This is
833 * important, because SessionMachine::uninit() cannot correctly operate
834 * after we return from this method (it expects the Machine instance is
835 * still valid). We'll call it ourselves below.
836 */
837 Log1WarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n",
838 (SessionMachine*)mData->mSession.mMachine));
839
840 if (Global::IsOnlineOrTransient(mData->mMachineState))
841 {
842 Log1WarningThisFunc(("Setting state to Aborted!\n"));
843 /* set machine state using SessionMachine reimplementation */
844 static_cast<Machine*>(mData->mSession.mMachine)->i_setMachineState(MachineState_Aborted);
845 }
846
847 /*
848 * Uninitialize SessionMachine using public uninit() to indicate
849 * an unexpected uninitialization.
850 */
851 mData->mSession.mMachine->uninit();
852 /* SessionMachine::uninit() must set mSession.mMachine to null */
853 Assert(mData->mSession.mMachine.isNull());
854 }
855
856 // uninit media from this machine's media registry, if they're still there
857 Guid uuidMachine(i_getId());
858
859 /* the lock is no more necessary (SessionMachine is uninitialized) */
860 alock.release();
861
862 /* XXX This will fail with
863 * "cannot be closed because it is still attached to 1 virtual machines"
864 * because at this point we did not call uninitDataAndChildObjects() yet
865 * and therefore also removeBackReference() for all these mediums was not called! */
866
867 if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init
868 mParent->i_unregisterMachineMedia(uuidMachine);
869
870 // has machine been modified?
871 if (mData->flModifications)
872 {
873 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
874 i_rollback(false /* aNotify */);
875 }
876
877 if (mData->mAccessible)
878 uninitDataAndChildObjects();
879
880 /* free the essential data structure last */
881 mData.free();
882
883 LogFlowThisFuncLeave();
884}
885
886// Wrapped IMachine properties
887/////////////////////////////////////////////////////////////////////////////
888HRESULT Machine::getParent(ComPtr<IVirtualBox> &aParent)
889{
890 /* mParent is constant during life time, no need to lock */
891 ComObjPtr<VirtualBox> pVirtualBox(mParent);
892 aParent = pVirtualBox;
893
894 return S_OK;
895}
896
897
898HRESULT Machine::getAccessible(BOOL *aAccessible)
899{
900 /* In some cases (medium registry related), it is necessary to be able to
901 * go through the list of all machines. Happens when an inaccessible VM
902 * has a sensible medium registry. */
903 AutoReadLock mllock(mParent->i_getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS);
904 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
905
906 HRESULT rc = S_OK;
907
908 if (!mData->mAccessible)
909 {
910 /* try to initialize the VM once more if not accessible */
911
912 AutoReinitSpan autoReinitSpan(this);
913 AssertReturn(autoReinitSpan.isOk(), E_FAIL);
914
915#ifdef DEBUG
916 LogFlowThisFunc(("Dumping media backreferences\n"));
917 mParent->i_dumpAllBackRefs();
918#endif
919
920 if (mData->pMachineConfigFile)
921 {
922 // reset the XML file to force loadSettings() (called from registeredInit())
923 // to parse it again; the file might have changed
924 delete mData->pMachineConfigFile;
925 mData->pMachineConfigFile = NULL;
926 }
927
928 rc = i_registeredInit();
929
930 if (SUCCEEDED(rc) && mData->mAccessible)
931 {
932 autoReinitSpan.setSucceeded();
933
934 /* make sure interesting parties will notice the accessibility
935 * state change */
936 mParent->i_onMachineStateChange(mData->mUuid, mData->mMachineState);
937 mParent->i_onMachineDataChange(mData->mUuid);
938 }
939 }
940
941 if (SUCCEEDED(rc))
942 *aAccessible = mData->mAccessible;
943
944 LogFlowThisFuncLeave();
945
946 return rc;
947}
948
949HRESULT Machine::getAccessError(ComPtr<IVirtualBoxErrorInfo> &aAccessError)
950{
951 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
952
953 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable())
954 {
955 /* return shortly */
956 aAccessError = NULL;
957 return S_OK;
958 }
959
960 HRESULT rc = S_OK;
961
962 ComObjPtr<VirtualBoxErrorInfo> errorInfo;
963 rc = errorInfo.createObject();
964 if (SUCCEEDED(rc))
965 {
966 errorInfo->init(mData->mAccessError.getResultCode(),
967 mData->mAccessError.getInterfaceID().ref(),
968 Utf8Str(mData->mAccessError.getComponent()).c_str(),
969 Utf8Str(mData->mAccessError.getText()));
970 aAccessError = errorInfo;
971 }
972
973 return rc;
974}
975
976HRESULT Machine::getName(com::Utf8Str &aName)
977{
978 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
979
980 aName = mUserData->s.strName;
981
982 return S_OK;
983}
984
985HRESULT Machine::setName(const com::Utf8Str &aName)
986{
987 // prohibit setting a UUID only as the machine name, or else it can
988 // never be found by findMachine()
989 Guid test(aName);
990
991 if (test.isValid())
992 return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
993
994 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
995
996 HRESULT rc = i_checkStateDependency(MutableStateDep);
997 if (FAILED(rc)) return rc;
998
999 i_setModified(IsModified_MachineData);
1000 mUserData.backup();
1001 mUserData->s.strName = aName;
1002
1003 return S_OK;
1004}
1005
1006HRESULT Machine::getDescription(com::Utf8Str &aDescription)
1007{
1008 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1009
1010 aDescription = mUserData->s.strDescription;
1011
1012 return S_OK;
1013}
1014
1015HRESULT Machine::setDescription(const com::Utf8Str &aDescription)
1016{
1017 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1018
1019 // this can be done in principle in any state as it doesn't affect the VM
1020 // significantly, but play safe by not messing around while complex
1021 // activities are going on
1022 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
1023 if (FAILED(rc)) return rc;
1024
1025 i_setModified(IsModified_MachineData);
1026 mUserData.backup();
1027 mUserData->s.strDescription = aDescription;
1028
1029 return S_OK;
1030}
1031
1032HRESULT Machine::getId(com::Guid &aId)
1033{
1034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1035
1036 aId = mData->mUuid;
1037
1038 return S_OK;
1039}
1040
1041HRESULT Machine::getGroups(std::vector<com::Utf8Str> &aGroups)
1042{
1043 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1044 aGroups.resize(mUserData->s.llGroups.size());
1045 size_t i = 0;
1046 for (StringsList::const_iterator it = mUserData->s.llGroups.begin();
1047 it != mUserData->s.llGroups.end(); ++it, ++i)
1048 aGroups[i] = (*it);
1049
1050 return S_OK;
1051}
1052
1053HRESULT Machine::setGroups(const std::vector<com::Utf8Str> &aGroups)
1054{
1055 StringsList llGroups;
1056 HRESULT rc = mParent->i_convertMachineGroups(aGroups, &llGroups);
1057 if (FAILED(rc))
1058 return rc;
1059
1060 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1061
1062 rc = i_checkStateDependency(MutableOrSavedStateDep);
1063 if (FAILED(rc)) return rc;
1064
1065 i_setModified(IsModified_MachineData);
1066 mUserData.backup();
1067 mUserData->s.llGroups = llGroups;
1068
1069 return S_OK;
1070}
1071
1072HRESULT Machine::getOSTypeId(com::Utf8Str &aOSTypeId)
1073{
1074 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1075
1076 aOSTypeId = mUserData->s.strOsType;
1077
1078 return S_OK;
1079}
1080
1081HRESULT Machine::setOSTypeId(const com::Utf8Str &aOSTypeId)
1082{
1083 /* look up the object by Id to check it is valid */
1084 ComPtr<IGuestOSType> guestOSType;
1085 HRESULT rc = mParent->GetGuestOSType(Bstr(aOSTypeId).raw(), guestOSType.asOutParam());
1086 if (FAILED(rc)) return rc;
1087
1088 /* when setting, always use the "etalon" value for consistency -- lookup
1089 * by ID is case-insensitive and the input value may have different case */
1090 Bstr osTypeId;
1091 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam());
1092 if (FAILED(rc)) return rc;
1093
1094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1095
1096 rc = i_checkStateDependency(MutableStateDep);
1097 if (FAILED(rc)) return rc;
1098
1099 i_setModified(IsModified_MachineData);
1100 mUserData.backup();
1101 mUserData->s.strOsType = osTypeId;
1102
1103 return S_OK;
1104}
1105
1106HRESULT Machine::getFirmwareType(FirmwareType_T *aFirmwareType)
1107{
1108 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1109
1110 *aFirmwareType = mHWData->mFirmwareType;
1111
1112 return S_OK;
1113}
1114
1115HRESULT Machine::setFirmwareType(FirmwareType_T aFirmwareType)
1116{
1117 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1118
1119 HRESULT rc = i_checkStateDependency(MutableStateDep);
1120 if (FAILED(rc)) return rc;
1121
1122 i_setModified(IsModified_MachineData);
1123 mHWData.backup();
1124 mHWData->mFirmwareType = aFirmwareType;
1125
1126 return S_OK;
1127}
1128
1129HRESULT Machine::getKeyboardHIDType(KeyboardHIDType_T *aKeyboardHIDType)
1130{
1131 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1132
1133 *aKeyboardHIDType = mHWData->mKeyboardHIDType;
1134
1135 return S_OK;
1136}
1137
1138HRESULT Machine::setKeyboardHIDType(KeyboardHIDType_T aKeyboardHIDType)
1139{
1140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1141
1142 HRESULT rc = i_checkStateDependency(MutableStateDep);
1143 if (FAILED(rc)) return rc;
1144
1145 i_setModified(IsModified_MachineData);
1146 mHWData.backup();
1147 mHWData->mKeyboardHIDType = aKeyboardHIDType;
1148
1149 return S_OK;
1150}
1151
1152HRESULT Machine::getPointingHIDType(PointingHIDType_T *aPointingHIDType)
1153{
1154 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1155
1156 *aPointingHIDType = mHWData->mPointingHIDType;
1157
1158 return S_OK;
1159}
1160
1161HRESULT Machine::setPointingHIDType(PointingHIDType_T aPointingHIDType)
1162{
1163 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1164
1165 HRESULT rc = i_checkStateDependency(MutableStateDep);
1166 if (FAILED(rc)) return rc;
1167
1168 i_setModified(IsModified_MachineData);
1169 mHWData.backup();
1170 mHWData->mPointingHIDType = aPointingHIDType;
1171
1172 return S_OK;
1173}
1174
1175HRESULT Machine::getChipsetType(ChipsetType_T *aChipsetType)
1176{
1177 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1178
1179 *aChipsetType = mHWData->mChipsetType;
1180
1181 return S_OK;
1182}
1183
1184HRESULT Machine::setChipsetType(ChipsetType_T aChipsetType)
1185{
1186 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1187
1188 HRESULT rc = i_checkStateDependency(MutableStateDep);
1189 if (FAILED(rc)) return rc;
1190
1191 if (aChipsetType != mHWData->mChipsetType)
1192 {
1193 i_setModified(IsModified_MachineData);
1194 mHWData.backup();
1195 mHWData->mChipsetType = aChipsetType;
1196
1197 // Resize network adapter array, to be finalized on commit/rollback.
1198 // We must not throw away entries yet, otherwise settings are lost
1199 // without a way to roll back.
1200 size_t newCount = Global::getMaxNetworkAdapters(aChipsetType);
1201 size_t oldCount = mNetworkAdapters.size();
1202 if (newCount > oldCount)
1203 {
1204 mNetworkAdapters.resize(newCount);
1205 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); slot++)
1206 {
1207 unconst(mNetworkAdapters[slot]).createObject();
1208 mNetworkAdapters[slot]->init(this, (ULONG)slot);
1209 }
1210 }
1211 }
1212
1213 return S_OK;
1214}
1215
1216HRESULT Machine::getParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1217{
1218 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1219
1220 *aParavirtProvider = mHWData->mParavirtProvider;
1221
1222 return S_OK;
1223}
1224
1225HRESULT Machine::setParavirtProvider(ParavirtProvider_T aParavirtProvider)
1226{
1227 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1228
1229 HRESULT rc = i_checkStateDependency(MutableStateDep);
1230 if (FAILED(rc)) return rc;
1231
1232 if (aParavirtProvider != mHWData->mParavirtProvider)
1233 {
1234 i_setModified(IsModified_MachineData);
1235 mHWData.backup();
1236 mHWData->mParavirtProvider = aParavirtProvider;
1237 }
1238
1239 return S_OK;
1240}
1241
1242HRESULT Machine::getEffectiveParavirtProvider(ParavirtProvider_T *aParavirtProvider)
1243{
1244 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1245
1246 *aParavirtProvider = mHWData->mParavirtProvider;
1247 switch (mHWData->mParavirtProvider)
1248 {
1249 case ParavirtProvider_None:
1250 case ParavirtProvider_HyperV:
1251 case ParavirtProvider_KVM:
1252 case ParavirtProvider_Minimal:
1253 break;
1254
1255 /* Resolve dynamic provider types to the effective types. */
1256 default:
1257 {
1258 ComPtr<IGuestOSType> ptrGuestOSType;
1259 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
1260 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest OS type. hrc2=%Rhrc\n", hrc2), hrc2);
1261
1262 Bstr guestTypeFamilyId;
1263 hrc2 = ptrGuestOSType->COMGETTER(FamilyId)(guestTypeFamilyId.asOutParam());
1264 AssertMsgReturn(SUCCEEDED(hrc2), ("Failed to get guest family. hrc2=%Rhrc\n", hrc2), hrc2);
1265 BOOL fOsXGuest = guestTypeFamilyId == Bstr("MacOS");
1266
1267 switch (mHWData->mParavirtProvider)
1268 {
1269 case ParavirtProvider_Legacy:
1270 {
1271 if (fOsXGuest)
1272 *aParavirtProvider = ParavirtProvider_Minimal;
1273 else
1274 *aParavirtProvider = ParavirtProvider_None;
1275 break;
1276 }
1277
1278 case ParavirtProvider_Default:
1279 {
1280 if (fOsXGuest)
1281 *aParavirtProvider = ParavirtProvider_Minimal;
1282 else if ( mUserData->s.strOsType == "Windows10"
1283 || mUserData->s.strOsType == "Windows10_64"
1284 || mUserData->s.strOsType == "Windows81"
1285 || mUserData->s.strOsType == "Windows81_64"
1286 || mUserData->s.strOsType == "Windows8"
1287 || mUserData->s.strOsType == "Windows8_64"
1288 || mUserData->s.strOsType == "Windows7"
1289 || mUserData->s.strOsType == "Windows7_64"
1290 || mUserData->s.strOsType == "WindowsVista"
1291 || mUserData->s.strOsType == "WindowsVista_64"
1292 || mUserData->s.strOsType == "Windows2012"
1293 || mUserData->s.strOsType == "Windows2012_64"
1294 || mUserData->s.strOsType == "Windows2008"
1295 || mUserData->s.strOsType == "Windows2008_64")
1296 {
1297 *aParavirtProvider = ParavirtProvider_HyperV;
1298 }
1299 else if ( mUserData->s.strOsType == "Linux26" // Linux22 and Linux24 omitted as they're too old
1300 || mUserData->s.strOsType == "Linux26_64" // for having any KVM paravirtualization support.
1301 || mUserData->s.strOsType == "Linux"
1302 || mUserData->s.strOsType == "Linux_64"
1303 || mUserData->s.strOsType == "ArchLinux"
1304 || mUserData->s.strOsType == "ArchLinux_64"
1305 || mUserData->s.strOsType == "Debian"
1306 || mUserData->s.strOsType == "Debian_64"
1307 || mUserData->s.strOsType == "Fedora"
1308 || mUserData->s.strOsType == "Fedora_64"
1309 || mUserData->s.strOsType == "Gentoo"
1310 || mUserData->s.strOsType == "Gentoo_64"
1311 || mUserData->s.strOsType == "Mandriva"
1312 || mUserData->s.strOsType == "Mandriva_64"
1313 || mUserData->s.strOsType == "OpenSUSE"
1314 || mUserData->s.strOsType == "OpenSUSE_64"
1315 || mUserData->s.strOsType == "Oracle"
1316 || mUserData->s.strOsType == "Oracle_64"
1317 || mUserData->s.strOsType == "RedHat"
1318 || mUserData->s.strOsType == "RedHat_64"
1319 || mUserData->s.strOsType == "Turbolinux"
1320 || mUserData->s.strOsType == "Turbolinux_64"
1321 || mUserData->s.strOsType == "Ubuntu"
1322 || mUserData->s.strOsType == "Ubuntu_64"
1323 || mUserData->s.strOsType == "Xandros"
1324 || mUserData->s.strOsType == "Xandros_64")
1325 {
1326 *aParavirtProvider = ParavirtProvider_KVM;
1327 }
1328 else
1329 *aParavirtProvider = ParavirtProvider_None;
1330 break;
1331 }
1332 }
1333 break;
1334 }
1335 }
1336
1337 Assert( *aParavirtProvider == ParavirtProvider_None
1338 || *aParavirtProvider == ParavirtProvider_Minimal
1339 || *aParavirtProvider == ParavirtProvider_HyperV
1340 || *aParavirtProvider == ParavirtProvider_KVM);
1341 return S_OK;
1342}
1343
1344HRESULT Machine::getHardwareVersion(com::Utf8Str &aHardwareVersion)
1345{
1346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1347
1348 aHardwareVersion = mHWData->mHWVersion;
1349
1350 return S_OK;
1351}
1352
1353HRESULT Machine::setHardwareVersion(const com::Utf8Str &aHardwareVersion)
1354{
1355 /* check known version */
1356 Utf8Str hwVersion = aHardwareVersion;
1357 if ( hwVersion.compare("1") != 0
1358 && hwVersion.compare("2") != 0)
1359 return setError(E_INVALIDARG,
1360 tr("Invalid hardware version: %s\n"), aHardwareVersion.c_str());
1361
1362 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1363
1364 HRESULT rc = i_checkStateDependency(MutableStateDep);
1365 if (FAILED(rc)) return rc;
1366
1367 i_setModified(IsModified_MachineData);
1368 mHWData.backup();
1369 mHWData->mHWVersion = aHardwareVersion;
1370
1371 return S_OK;
1372}
1373
1374HRESULT Machine::getHardwareUUID(com::Guid &aHardwareUUID)
1375{
1376 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1377
1378 if (!mHWData->mHardwareUUID.isZero())
1379 aHardwareUUID = mHWData->mHardwareUUID;
1380 else
1381 aHardwareUUID = mData->mUuid;
1382
1383 return S_OK;
1384}
1385
1386HRESULT Machine::setHardwareUUID(const com::Guid &aHardwareUUID)
1387{
1388 if (!aHardwareUUID.isValid())
1389 return E_INVALIDARG;
1390
1391 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1392
1393 HRESULT rc = i_checkStateDependency(MutableStateDep);
1394 if (FAILED(rc)) return rc;
1395
1396 i_setModified(IsModified_MachineData);
1397 mHWData.backup();
1398 if (aHardwareUUID == mData->mUuid)
1399 mHWData->mHardwareUUID.clear();
1400 else
1401 mHWData->mHardwareUUID = aHardwareUUID;
1402
1403 return S_OK;
1404}
1405
1406HRESULT Machine::getMemorySize(ULONG *aMemorySize)
1407{
1408 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1409
1410 *aMemorySize = mHWData->mMemorySize;
1411
1412 return S_OK;
1413}
1414
1415HRESULT Machine::setMemorySize(ULONG aMemorySize)
1416{
1417 /* check RAM limits */
1418 if ( aMemorySize < MM_RAM_MIN_IN_MB
1419 || aMemorySize > MM_RAM_MAX_IN_MB
1420 )
1421 return setError(E_INVALIDARG,
1422 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1423 aMemorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB);
1424
1425 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1426
1427 HRESULT rc = i_checkStateDependency(MutableStateDep);
1428 if (FAILED(rc)) return rc;
1429
1430 i_setModified(IsModified_MachineData);
1431 mHWData.backup();
1432 mHWData->mMemorySize = aMemorySize;
1433
1434 return S_OK;
1435}
1436
1437HRESULT Machine::getCPUCount(ULONG *aCPUCount)
1438{
1439 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1440
1441 *aCPUCount = mHWData->mCPUCount;
1442
1443 return S_OK;
1444}
1445
1446HRESULT Machine::setCPUCount(ULONG aCPUCount)
1447{
1448 /* check CPU limits */
1449 if ( aCPUCount < SchemaDefs::MinCPUCount
1450 || aCPUCount > SchemaDefs::MaxCPUCount
1451 )
1452 return setError(E_INVALIDARG,
1453 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"),
1454 aCPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount);
1455
1456 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1457
1458 /* We cant go below the current number of CPUs attached if hotplug is enabled*/
1459 if (mHWData->mCPUHotPlugEnabled)
1460 {
1461 for (unsigned idx = aCPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
1462 {
1463 if (mHWData->mCPUAttached[idx])
1464 return setError(E_INVALIDARG,
1465 tr("There is still a CPU attached to socket %lu."
1466 "Detach the CPU before removing the socket"),
1467 aCPUCount, idx+1);
1468 }
1469 }
1470
1471 HRESULT rc = i_checkStateDependency(MutableStateDep);
1472 if (FAILED(rc)) return rc;
1473
1474 i_setModified(IsModified_MachineData);
1475 mHWData.backup();
1476 mHWData->mCPUCount = aCPUCount;
1477
1478 return S_OK;
1479}
1480
1481HRESULT Machine::getCPUExecutionCap(ULONG *aCPUExecutionCap)
1482{
1483 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1484
1485 *aCPUExecutionCap = mHWData->mCpuExecutionCap;
1486
1487 return S_OK;
1488}
1489
1490HRESULT Machine::setCPUExecutionCap(ULONG aCPUExecutionCap)
1491{
1492 HRESULT rc = S_OK;
1493
1494 /* check throttle limits */
1495 if ( aCPUExecutionCap < 1
1496 || aCPUExecutionCap > 100
1497 )
1498 return setError(E_INVALIDARG,
1499 tr("Invalid CPU execution cap value: %lu (must be in range [%lu, %lu])"),
1500 aCPUExecutionCap, 1, 100);
1501
1502 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1503
1504 alock.release();
1505 rc = i_onCPUExecutionCapChange(aCPUExecutionCap);
1506 alock.acquire();
1507 if (FAILED(rc)) return rc;
1508
1509 i_setModified(IsModified_MachineData);
1510 mHWData.backup();
1511 mHWData->mCpuExecutionCap = aCPUExecutionCap;
1512
1513 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1514 if (Global::IsOnline(mData->mMachineState))
1515 i_saveSettings(NULL);
1516
1517 return S_OK;
1518}
1519
1520HRESULT Machine::getCPUHotPlugEnabled(BOOL *aCPUHotPlugEnabled)
1521{
1522 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1523
1524 *aCPUHotPlugEnabled = mHWData->mCPUHotPlugEnabled;
1525
1526 return S_OK;
1527}
1528
1529HRESULT Machine::setCPUHotPlugEnabled(BOOL aCPUHotPlugEnabled)
1530{
1531 HRESULT rc = S_OK;
1532
1533 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1534
1535 rc = i_checkStateDependency(MutableStateDep);
1536 if (FAILED(rc)) return rc;
1537
1538 if (mHWData->mCPUHotPlugEnabled != aCPUHotPlugEnabled)
1539 {
1540 if (aCPUHotPlugEnabled)
1541 {
1542 i_setModified(IsModified_MachineData);
1543 mHWData.backup();
1544
1545 /* Add the amount of CPUs currently attached */
1546 for (unsigned i = 0; i < mHWData->mCPUCount; ++i)
1547 mHWData->mCPUAttached[i] = true;
1548 }
1549 else
1550 {
1551 /*
1552 * We can disable hotplug only if the amount of maximum CPUs is equal
1553 * to the amount of attached CPUs
1554 */
1555 unsigned cCpusAttached = 0;
1556 unsigned iHighestId = 0;
1557
1558 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; ++i)
1559 {
1560 if (mHWData->mCPUAttached[i])
1561 {
1562 cCpusAttached++;
1563 iHighestId = i;
1564 }
1565 }
1566
1567 if ( (cCpusAttached != mHWData->mCPUCount)
1568 || (iHighestId >= mHWData->mCPUCount))
1569 return setError(E_INVALIDARG,
1570 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
1571
1572 i_setModified(IsModified_MachineData);
1573 mHWData.backup();
1574 }
1575 }
1576
1577 mHWData->mCPUHotPlugEnabled = aCPUHotPlugEnabled;
1578
1579 return rc;
1580}
1581
1582HRESULT Machine::getCPUIDPortabilityLevel(ULONG *aCPUIDPortabilityLevel)
1583{
1584 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1585
1586 *aCPUIDPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
1587
1588 return S_OK;
1589}
1590
1591HRESULT Machine::setCPUIDPortabilityLevel(ULONG aCPUIDPortabilityLevel)
1592{
1593 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1594
1595 HRESULT hrc = i_checkStateDependency(MutableStateDep);
1596 if (SUCCEEDED(hrc))
1597 {
1598 i_setModified(IsModified_MachineData);
1599 mHWData.backup();
1600 mHWData->mCpuIdPortabilityLevel = aCPUIDPortabilityLevel;
1601 }
1602 return hrc;
1603}
1604
1605HRESULT Machine::getEmulatedUSBCardReaderEnabled(BOOL *aEmulatedUSBCardReaderEnabled)
1606{
1607#ifdef VBOX_WITH_USB_CARDREADER
1608 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1609
1610 *aEmulatedUSBCardReaderEnabled = mHWData->mEmulatedUSBCardReaderEnabled;
1611
1612 return S_OK;
1613#else
1614 NOREF(aEmulatedUSBCardReaderEnabled);
1615 return E_NOTIMPL;
1616#endif
1617}
1618
1619HRESULT Machine::setEmulatedUSBCardReaderEnabled(BOOL aEmulatedUSBCardReaderEnabled)
1620{
1621#ifdef VBOX_WITH_USB_CARDREADER
1622 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1623
1624 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
1625 if (FAILED(rc)) return rc;
1626
1627 i_setModified(IsModified_MachineData);
1628 mHWData.backup();
1629 mHWData->mEmulatedUSBCardReaderEnabled = aEmulatedUSBCardReaderEnabled;
1630
1631 return S_OK;
1632#else
1633 NOREF(aEmulatedUSBCardReaderEnabled);
1634 return E_NOTIMPL;
1635#endif
1636}
1637
1638HRESULT Machine::getHPETEnabled(BOOL *aHPETEnabled)
1639{
1640 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1641
1642 *aHPETEnabled = mHWData->mHPETEnabled;
1643
1644 return S_OK;
1645}
1646
1647HRESULT Machine::setHPETEnabled(BOOL aHPETEnabled)
1648{
1649 HRESULT rc = S_OK;
1650
1651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1652
1653 rc = i_checkStateDependency(MutableStateDep);
1654 if (FAILED(rc)) return rc;
1655
1656 i_setModified(IsModified_MachineData);
1657 mHWData.backup();
1658
1659 mHWData->mHPETEnabled = aHPETEnabled;
1660
1661 return rc;
1662}
1663
1664HRESULT Machine::getVideoCaptureEnabled(BOOL *aVideoCaptureEnabled)
1665{
1666 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1667
1668 *aVideoCaptureEnabled = mHWData->mVideoCaptureEnabled;
1669 return S_OK;
1670}
1671
1672HRESULT Machine::setVideoCaptureEnabled(BOOL aVideoCaptureEnabled)
1673{
1674 HRESULT rc = S_OK;
1675
1676 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1677
1678 i_setModified(IsModified_MachineData);
1679 mHWData.backup();
1680 mHWData->mVideoCaptureEnabled = aVideoCaptureEnabled;
1681
1682 alock.release();
1683 rc = i_onVideoCaptureChange();
1684 alock.acquire();
1685 if (FAILED(rc))
1686 {
1687 /*
1688 * Normally we would do the actual change _after_ i_onVideoCaptureChange() succeeded.
1689 * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to
1690 * determine if it should start or stop capturing. Therefore we need to manually
1691 * undo change.
1692 */
1693 mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled;
1694 return rc;
1695 }
1696
1697 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1698 if (Global::IsOnline(mData->mMachineState))
1699 i_saveSettings(NULL);
1700
1701 return rc;
1702}
1703
1704HRESULT Machine::getVideoCaptureScreens(std::vector<BOOL> &aVideoCaptureScreens)
1705{
1706 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1707 aVideoCaptureScreens.resize(mHWData->mMonitorCount);
1708 for (unsigned i = 0; i < mHWData->mMonitorCount; ++i)
1709 aVideoCaptureScreens[i] = mHWData->maVideoCaptureScreens[i];
1710 return S_OK;
1711}
1712
1713HRESULT Machine::setVideoCaptureScreens(const std::vector<BOOL> &aVideoCaptureScreens)
1714{
1715 AssertReturn(aVideoCaptureScreens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG);
1716 bool fChanged = false;
1717
1718 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1719
1720 for (unsigned i = 0; i < aVideoCaptureScreens.size(); ++i)
1721 {
1722 if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(aVideoCaptureScreens[i]))
1723 {
1724 mHWData->maVideoCaptureScreens[i] = RT_BOOL(aVideoCaptureScreens[i]);
1725 fChanged = true;
1726 }
1727 }
1728 if (fChanged)
1729 {
1730 alock.release();
1731 HRESULT rc = i_onVideoCaptureChange();
1732 alock.acquire();
1733 if (FAILED(rc)) return rc;
1734 i_setModified(IsModified_MachineData);
1735
1736 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
1737 if (Global::IsOnline(mData->mMachineState))
1738 i_saveSettings(NULL);
1739 }
1740
1741 return S_OK;
1742}
1743
1744HRESULT Machine::getVideoCaptureFile(com::Utf8Str &aVideoCaptureFile)
1745{
1746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1747 if (mHWData->mVideoCaptureFile.isEmpty())
1748 i_getDefaultVideoCaptureFile(aVideoCaptureFile);
1749 else
1750 aVideoCaptureFile = mHWData->mVideoCaptureFile;
1751 return S_OK;
1752}
1753
1754HRESULT Machine::setVideoCaptureFile(const com::Utf8Str &aVideoCaptureFile)
1755{
1756 Utf8Str strFile(aVideoCaptureFile);
1757 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1758
1759 if ( Global::IsOnline(mData->mMachineState)
1760 && mHWData->mVideoCaptureEnabled)
1761 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1762
1763 if (!RTPathStartsWithRoot(strFile.c_str()))
1764 return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str());
1765
1766 if (!strFile.isEmpty())
1767 {
1768 Utf8Str defaultFile;
1769 i_getDefaultVideoCaptureFile(defaultFile);
1770 if (!RTPathCompare(strFile.c_str(), defaultFile.c_str()))
1771 strFile.setNull();
1772 }
1773
1774 i_setModified(IsModified_MachineData);
1775 mHWData.backup();
1776 mHWData->mVideoCaptureFile = strFile;
1777
1778 return S_OK;
1779}
1780
1781HRESULT Machine::getVideoCaptureWidth(ULONG *aVideoCaptureWidth)
1782{
1783 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1784 *aVideoCaptureWidth = mHWData->mVideoCaptureWidth;
1785 return S_OK;
1786}
1787
1788HRESULT Machine::setVideoCaptureWidth(ULONG aVideoCaptureWidth)
1789{
1790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1791
1792 if ( Global::IsOnline(mData->mMachineState)
1793 && mHWData->mVideoCaptureEnabled)
1794 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1795
1796 i_setModified(IsModified_MachineData);
1797 mHWData.backup();
1798 mHWData->mVideoCaptureWidth = aVideoCaptureWidth;
1799
1800 return S_OK;
1801}
1802
1803HRESULT Machine::getVideoCaptureHeight(ULONG *aVideoCaptureHeight)
1804{
1805 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1806 *aVideoCaptureHeight = mHWData->mVideoCaptureHeight;
1807 return S_OK;
1808}
1809
1810HRESULT Machine::setVideoCaptureHeight(ULONG aVideoCaptureHeight)
1811{
1812 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1813
1814 if ( Global::IsOnline(mData->mMachineState)
1815 && mHWData->mVideoCaptureEnabled)
1816 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1817
1818 i_setModified(IsModified_MachineData);
1819 mHWData.backup();
1820 mHWData->mVideoCaptureHeight = aVideoCaptureHeight;
1821
1822 return S_OK;
1823}
1824
1825HRESULT Machine::getVideoCaptureRate(ULONG *aVideoCaptureRate)
1826{
1827 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1828 *aVideoCaptureRate = mHWData->mVideoCaptureRate;
1829 return S_OK;
1830}
1831
1832HRESULT Machine::setVideoCaptureRate(ULONG aVideoCaptureRate)
1833{
1834 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1835
1836 if ( Global::IsOnline(mData->mMachineState)
1837 && mHWData->mVideoCaptureEnabled)
1838 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1839
1840 i_setModified(IsModified_MachineData);
1841 mHWData.backup();
1842 mHWData->mVideoCaptureRate = aVideoCaptureRate;
1843
1844 return S_OK;
1845}
1846
1847HRESULT Machine::getVideoCaptureFPS(ULONG *aVideoCaptureFPS)
1848{
1849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1850 *aVideoCaptureFPS = mHWData->mVideoCaptureFPS;
1851 return S_OK;
1852}
1853
1854HRESULT Machine::setVideoCaptureFPS(ULONG aVideoCaptureFPS)
1855{
1856 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1857
1858 if ( Global::IsOnline(mData->mMachineState)
1859 && mHWData->mVideoCaptureEnabled)
1860 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1861
1862 i_setModified(IsModified_MachineData);
1863 mHWData.backup();
1864 mHWData->mVideoCaptureFPS = aVideoCaptureFPS;
1865
1866 return S_OK;
1867}
1868
1869HRESULT Machine::getVideoCaptureMaxTime(ULONG *aVideoCaptureMaxTime)
1870{
1871 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1872 *aVideoCaptureMaxTime = mHWData->mVideoCaptureMaxTime;
1873 return S_OK;
1874}
1875
1876HRESULT Machine::setVideoCaptureMaxTime(ULONG aVideoCaptureMaxTime)
1877{
1878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1879
1880 if ( Global::IsOnline(mData->mMachineState)
1881 && mHWData->mVideoCaptureEnabled)
1882 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1883
1884 i_setModified(IsModified_MachineData);
1885 mHWData.backup();
1886 mHWData->mVideoCaptureMaxTime = aVideoCaptureMaxTime;
1887
1888 return S_OK;
1889}
1890
1891HRESULT Machine::getVideoCaptureMaxFileSize(ULONG *aVideoCaptureMaxFileSize)
1892{
1893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1894 *aVideoCaptureMaxFileSize = mHWData->mVideoCaptureMaxFileSize;
1895 return S_OK;
1896}
1897
1898HRESULT Machine::setVideoCaptureMaxFileSize(ULONG aVideoCaptureMaxFileSize)
1899{
1900 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1901
1902 if ( Global::IsOnline(mData->mMachineState)
1903 && mHWData->mVideoCaptureEnabled)
1904 return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled"));
1905
1906 i_setModified(IsModified_MachineData);
1907 mHWData.backup();
1908 mHWData->mVideoCaptureMaxFileSize = aVideoCaptureMaxFileSize;
1909
1910 return S_OK;
1911}
1912
1913HRESULT Machine::getVideoCaptureOptions(com::Utf8Str &aVideoCaptureOptions)
1914{
1915 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1916
1917 aVideoCaptureOptions = mHWData->mVideoCaptureOptions;
1918 return S_OK;
1919}
1920
1921HRESULT Machine::setVideoCaptureOptions(const com::Utf8Str &aVideoCaptureOptions)
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 i_setModified(IsModified_MachineData);
1930 mHWData.backup();
1931 mHWData->mVideoCaptureOptions = aVideoCaptureOptions;
1932
1933 return S_OK;
1934}
1935
1936HRESULT Machine::getGraphicsControllerType(GraphicsControllerType_T *aGraphicsControllerType)
1937{
1938 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1939
1940 *aGraphicsControllerType = mHWData->mGraphicsControllerType;
1941
1942 return S_OK;
1943}
1944
1945HRESULT Machine::setGraphicsControllerType(GraphicsControllerType_T aGraphicsControllerType)
1946{
1947 switch (aGraphicsControllerType)
1948 {
1949 case GraphicsControllerType_Null:
1950 case GraphicsControllerType_VBoxVGA:
1951#ifdef VBOX_WITH_VMSVGA
1952 case GraphicsControllerType_VMSVGA:
1953#endif
1954 break;
1955 default:
1956 return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType);
1957 }
1958
1959 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1960
1961 HRESULT rc = i_checkStateDependency(MutableStateDep);
1962 if (FAILED(rc)) return rc;
1963
1964 i_setModified(IsModified_MachineData);
1965 mHWData.backup();
1966 mHWData->mGraphicsControllerType = aGraphicsControllerType;
1967
1968 return S_OK;
1969}
1970
1971HRESULT Machine::getVRAMSize(ULONG *aVRAMSize)
1972{
1973 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
1974
1975 *aVRAMSize = mHWData->mVRAMSize;
1976
1977 return S_OK;
1978}
1979
1980HRESULT Machine::setVRAMSize(ULONG aVRAMSize)
1981{
1982 /* check VRAM limits */
1983 if (aVRAMSize < SchemaDefs::MinGuestVRAM ||
1984 aVRAMSize > SchemaDefs::MaxGuestVRAM)
1985 return setError(E_INVALIDARG,
1986 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"),
1987 aVRAMSize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM);
1988
1989 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
1990
1991 HRESULT rc = i_checkStateDependency(MutableStateDep);
1992 if (FAILED(rc)) return rc;
1993
1994 i_setModified(IsModified_MachineData);
1995 mHWData.backup();
1996 mHWData->mVRAMSize = aVRAMSize;
1997
1998 return S_OK;
1999}
2000
2001/** @todo this method should not be public */
2002HRESULT Machine::getMemoryBalloonSize(ULONG *aMemoryBalloonSize)
2003{
2004 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2005
2006 *aMemoryBalloonSize = mHWData->mMemoryBalloonSize;
2007
2008 return S_OK;
2009}
2010
2011/**
2012 * Set the memory balloon size.
2013 *
2014 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so
2015 * we have to make sure that we never call IGuest from here.
2016 */
2017HRESULT Machine::setMemoryBalloonSize(ULONG aMemoryBalloonSize)
2018{
2019 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */
2020#if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD))
2021 /* check limits */
2022 if (aMemoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize))
2023 return setError(E_INVALIDARG,
2024 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"),
2025 aMemoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize));
2026
2027 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2028
2029 i_setModified(IsModified_MachineData);
2030 mHWData.backup();
2031 mHWData->mMemoryBalloonSize = aMemoryBalloonSize;
2032
2033 return S_OK;
2034#else
2035 NOREF(aMemoryBalloonSize);
2036 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts"));
2037#endif
2038}
2039
2040HRESULT Machine::getPageFusionEnabled(BOOL *aPageFusionEnabled)
2041{
2042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2043
2044 *aPageFusionEnabled = mHWData->mPageFusionEnabled;
2045 return S_OK;
2046}
2047
2048HRESULT Machine::setPageFusionEnabled(BOOL aPageFusionEnabled)
2049{
2050#ifdef VBOX_WITH_PAGE_SHARING
2051 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2052
2053 /** @todo must support changes for running vms and keep this in sync with IGuest. */
2054 i_setModified(IsModified_MachineData);
2055 mHWData.backup();
2056 mHWData->mPageFusionEnabled = aPageFusionEnabled;
2057 return S_OK;
2058#else
2059 NOREF(aPageFusionEnabled);
2060 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts"));
2061#endif
2062}
2063
2064HRESULT Machine::getAccelerate3DEnabled(BOOL *aAccelerate3DEnabled)
2065{
2066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2067
2068 *aAccelerate3DEnabled = mHWData->mAccelerate3DEnabled;
2069
2070 return S_OK;
2071}
2072
2073HRESULT Machine::setAccelerate3DEnabled(BOOL aAccelerate3DEnabled)
2074{
2075 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2076
2077 HRESULT rc = i_checkStateDependency(MutableStateDep);
2078 if (FAILED(rc)) return rc;
2079
2080 /** @todo check validity! */
2081
2082 i_setModified(IsModified_MachineData);
2083 mHWData.backup();
2084 mHWData->mAccelerate3DEnabled = aAccelerate3DEnabled;
2085
2086 return S_OK;
2087}
2088
2089
2090HRESULT Machine::getAccelerate2DVideoEnabled(BOOL *aAccelerate2DVideoEnabled)
2091{
2092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2093
2094 *aAccelerate2DVideoEnabled = mHWData->mAccelerate2DVideoEnabled;
2095
2096 return S_OK;
2097}
2098
2099HRESULT Machine::setAccelerate2DVideoEnabled(BOOL aAccelerate2DVideoEnabled)
2100{
2101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2102
2103 HRESULT rc = i_checkStateDependency(MutableStateDep);
2104 if (FAILED(rc)) return rc;
2105
2106 /** @todo check validity! */
2107 i_setModified(IsModified_MachineData);
2108 mHWData.backup();
2109 mHWData->mAccelerate2DVideoEnabled = aAccelerate2DVideoEnabled;
2110
2111 return S_OK;
2112}
2113
2114HRESULT Machine::getMonitorCount(ULONG *aMonitorCount)
2115{
2116 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2117
2118 *aMonitorCount = mHWData->mMonitorCount;
2119
2120 return S_OK;
2121}
2122
2123HRESULT Machine::setMonitorCount(ULONG aMonitorCount)
2124{
2125 /* make sure monitor count is a sensible number */
2126 if (aMonitorCount < 1 || aMonitorCount > SchemaDefs::MaxGuestMonitors)
2127 return setError(E_INVALIDARG,
2128 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"),
2129 aMonitorCount, 1, SchemaDefs::MaxGuestMonitors);
2130
2131 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2132
2133 HRESULT rc = i_checkStateDependency(MutableStateDep);
2134 if (FAILED(rc)) return rc;
2135
2136 i_setModified(IsModified_MachineData);
2137 mHWData.backup();
2138 mHWData->mMonitorCount = aMonitorCount;
2139
2140 return S_OK;
2141}
2142
2143HRESULT Machine::getBIOSSettings(ComPtr<IBIOSSettings> &aBIOSSettings)
2144{
2145 /* mBIOSSettings is constant during life time, no need to lock */
2146 aBIOSSettings = mBIOSSettings;
2147
2148 return S_OK;
2149}
2150
2151HRESULT Machine::getCPUProperty(CPUPropertyType_T aProperty, BOOL *aValue)
2152{
2153 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2154
2155 switch (aProperty)
2156 {
2157 case CPUPropertyType_PAE:
2158 *aValue = mHWData->mPAEEnabled;
2159 break;
2160
2161 case CPUPropertyType_LongMode:
2162 if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled)
2163 *aValue = TRUE;
2164 else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled)
2165 *aValue = FALSE;
2166#if HC_ARCH_BITS == 64
2167 else
2168 *aValue = TRUE;
2169#else
2170 else
2171 {
2172 *aValue = FALSE;
2173
2174 ComPtr<IGuestOSType> ptrGuestOSType;
2175 HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam());
2176 if (SUCCEEDED(hrc2))
2177 {
2178 BOOL fIs64Bit = FALSE;
2179 hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2);
2180 if (SUCCEEDED(hrc2) && fIs64Bit)
2181 {
2182 ComObjPtr<Host> ptrHost = mParent->i_host();
2183 alock.release();
2184
2185 hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aValue); AssertComRC(hrc2);
2186 if (FAILED(hrc2))
2187 *aValue = FALSE;
2188 }
2189 }
2190 }
2191#endif
2192 break;
2193
2194 case CPUPropertyType_TripleFaultReset:
2195 *aValue = mHWData->mTripleFaultReset;
2196 break;
2197
2198 default:
2199 return E_INVALIDARG;
2200 }
2201 return S_OK;
2202}
2203
2204HRESULT Machine::setCPUProperty(CPUPropertyType_T aProperty, BOOL aValue)
2205{
2206 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2207
2208 HRESULT rc = i_checkStateDependency(MutableStateDep);
2209 if (FAILED(rc)) return rc;
2210
2211 switch (aProperty)
2212 {
2213 case CPUPropertyType_PAE:
2214 i_setModified(IsModified_MachineData);
2215 mHWData.backup();
2216 mHWData->mPAEEnabled = !!aValue;
2217 break;
2218
2219 case CPUPropertyType_LongMode:
2220 i_setModified(IsModified_MachineData);
2221 mHWData.backup();
2222 mHWData->mLongMode = !aValue ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled;
2223 break;
2224
2225 case CPUPropertyType_TripleFaultReset:
2226 i_setModified(IsModified_MachineData);
2227 mHWData.backup();
2228 mHWData->mTripleFaultReset = !!aValue;
2229 break;
2230
2231 default:
2232 return E_INVALIDARG;
2233 }
2234 return S_OK;
2235}
2236
2237HRESULT Machine::getCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx)
2238{
2239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2240
2241 switch(aId)
2242 {
2243 case 0x0:
2244 case 0x1:
2245 case 0x2:
2246 case 0x3:
2247 case 0x4:
2248 case 0x5:
2249 case 0x6:
2250 case 0x7:
2251 case 0x8:
2252 case 0x9:
2253 case 0xA:
2254 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
2255 return E_INVALIDARG;
2256
2257 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
2258 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
2259 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx;
2260 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx;
2261 break;
2262
2263 case 0x80000000:
2264 case 0x80000001:
2265 case 0x80000002:
2266 case 0x80000003:
2267 case 0x80000004:
2268 case 0x80000005:
2269 case 0x80000006:
2270 case 0x80000007:
2271 case 0x80000008:
2272 case 0x80000009:
2273 case 0x8000000A:
2274 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
2275 return E_INVALIDARG;
2276
2277 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
2278 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
2279 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx;
2280 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx;
2281 break;
2282
2283 default:
2284 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2285 }
2286 return S_OK;
2287}
2288
2289
2290HRESULT Machine::setCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx)
2291{
2292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2293
2294 HRESULT rc = i_checkStateDependency(MutableStateDep);
2295 if (FAILED(rc)) return rc;
2296
2297 switch(aId)
2298 {
2299 case 0x0:
2300 case 0x1:
2301 case 0x2:
2302 case 0x3:
2303 case 0x4:
2304 case 0x5:
2305 case 0x6:
2306 case 0x7:
2307 case 0x8:
2308 case 0x9:
2309 case 0xA:
2310 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2311 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2312 i_setModified(IsModified_MachineData);
2313 mHWData.backup();
2314 mHWData->mCpuIdStdLeafs[aId].ulId = aId;
2315 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax;
2316 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx;
2317 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx;
2318 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx;
2319 break;
2320
2321 case 0x80000000:
2322 case 0x80000001:
2323 case 0x80000002:
2324 case 0x80000003:
2325 case 0x80000004:
2326 case 0x80000005:
2327 case 0x80000006:
2328 case 0x80000007:
2329 case 0x80000008:
2330 case 0x80000009:
2331 case 0x8000000A:
2332 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2333 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2334 i_setModified(IsModified_MachineData);
2335 mHWData.backup();
2336 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId;
2337 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax;
2338 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx;
2339 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx;
2340 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx;
2341 break;
2342
2343 default:
2344 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2345 }
2346 return S_OK;
2347}
2348
2349HRESULT Machine::removeCPUIDLeaf(ULONG aId)
2350{
2351 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2352
2353 HRESULT rc = i_checkStateDependency(MutableStateDep);
2354 if (FAILED(rc)) return rc;
2355
2356 switch(aId)
2357 {
2358 case 0x0:
2359 case 0x1:
2360 case 0x2:
2361 case 0x3:
2362 case 0x4:
2363 case 0x5:
2364 case 0x6:
2365 case 0x7:
2366 case 0x8:
2367 case 0x9:
2368 case 0xA:
2369 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xB);
2370 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs));
2371 i_setModified(IsModified_MachineData);
2372 mHWData.backup();
2373 /* Invalidate leaf. */
2374 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX;
2375 break;
2376
2377 case 0x80000000:
2378 case 0x80000001:
2379 case 0x80000002:
2380 case 0x80000003:
2381 case 0x80000004:
2382 case 0x80000005:
2383 case 0x80000006:
2384 case 0x80000007:
2385 case 0x80000008:
2386 case 0x80000009:
2387 case 0x8000000A:
2388 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xB);
2389 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs));
2390 i_setModified(IsModified_MachineData);
2391 mHWData.backup();
2392 /* Invalidate leaf. */
2393 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX;
2394 break;
2395
2396 default:
2397 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId);
2398 }
2399 return S_OK;
2400}
2401
2402HRESULT Machine::removeAllCPUIDLeaves()
2403{
2404 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2405
2406 HRESULT rc = i_checkStateDependency(MutableStateDep);
2407 if (FAILED(rc)) return rc;
2408
2409 i_setModified(IsModified_MachineData);
2410 mHWData.backup();
2411
2412 /* Invalidate all standard leafs. */
2413 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++i)
2414 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX;
2415
2416 /* Invalidate all extended leafs. */
2417 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++i)
2418 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX;
2419
2420 return S_OK;
2421}
2422HRESULT Machine::getHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL *aValue)
2423{
2424 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2425
2426 switch(aProperty)
2427 {
2428 case HWVirtExPropertyType_Enabled:
2429 *aValue = mHWData->mHWVirtExEnabled;
2430 break;
2431
2432 case HWVirtExPropertyType_VPID:
2433 *aValue = mHWData->mHWVirtExVPIDEnabled;
2434 break;
2435
2436 case HWVirtExPropertyType_NestedPaging:
2437 *aValue = mHWData->mHWVirtExNestedPagingEnabled;
2438 break;
2439
2440 case HWVirtExPropertyType_UnrestrictedExecution:
2441 *aValue = mHWData->mHWVirtExUXEnabled;
2442 break;
2443
2444 case HWVirtExPropertyType_LargePages:
2445 *aValue = mHWData->mHWVirtExLargePagesEnabled;
2446#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
2447 *aValue = FALSE;
2448#endif
2449 break;
2450
2451 case HWVirtExPropertyType_Force:
2452 *aValue = mHWData->mHWVirtExForceEnabled;
2453 break;
2454
2455 default:
2456 return E_INVALIDARG;
2457 }
2458 return S_OK;
2459}
2460
2461HRESULT Machine::setHWVirtExProperty(HWVirtExPropertyType_T aProperty, BOOL aValue)
2462{
2463 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2464
2465 HRESULT rc = i_checkStateDependency(MutableStateDep);
2466 if (FAILED(rc)) return rc;
2467
2468 switch(aProperty)
2469 {
2470 case HWVirtExPropertyType_Enabled:
2471 i_setModified(IsModified_MachineData);
2472 mHWData.backup();
2473 mHWData->mHWVirtExEnabled = !!aValue;
2474 break;
2475
2476 case HWVirtExPropertyType_VPID:
2477 i_setModified(IsModified_MachineData);
2478 mHWData.backup();
2479 mHWData->mHWVirtExVPIDEnabled = !!aValue;
2480 break;
2481
2482 case HWVirtExPropertyType_NestedPaging:
2483 i_setModified(IsModified_MachineData);
2484 mHWData.backup();
2485 mHWData->mHWVirtExNestedPagingEnabled = !!aValue;
2486 break;
2487
2488 case HWVirtExPropertyType_UnrestrictedExecution:
2489 i_setModified(IsModified_MachineData);
2490 mHWData.backup();
2491 mHWData->mHWVirtExUXEnabled = !!aValue;
2492 break;
2493
2494 case HWVirtExPropertyType_LargePages:
2495 i_setModified(IsModified_MachineData);
2496 mHWData.backup();
2497 mHWData->mHWVirtExLargePagesEnabled = !!aValue;
2498 break;
2499
2500 case HWVirtExPropertyType_Force:
2501 i_setModified(IsModified_MachineData);
2502 mHWData.backup();
2503 mHWData->mHWVirtExForceEnabled = !!aValue;
2504 break;
2505
2506 default:
2507 return E_INVALIDARG;
2508 }
2509
2510 return S_OK;
2511}
2512
2513HRESULT Machine::getSnapshotFolder(com::Utf8Str &aSnapshotFolder)
2514{
2515 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2516
2517 i_calculateFullPath(mUserData->s.strSnapshotFolder, aSnapshotFolder);
2518
2519 return S_OK;
2520}
2521
2522HRESULT Machine::setSnapshotFolder(const com::Utf8Str &aSnapshotFolder)
2523{
2524 /* @todo (r=dmik):
2525 * 1. Allow to change the name of the snapshot folder containing snapshots
2526 * 2. Rename the folder on disk instead of just changing the property
2527 * value (to be smart and not to leave garbage). Note that it cannot be
2528 * done here because the change may be rolled back. Thus, the right
2529 * place is #saveSettings().
2530 */
2531
2532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2533
2534 HRESULT rc = i_checkStateDependency(MutableStateDep);
2535 if (FAILED(rc)) return rc;
2536
2537 if (!mData->mCurrentSnapshot.isNull())
2538 return setError(E_FAIL,
2539 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)"));
2540
2541 Utf8Str strSnapshotFolder(aSnapshotFolder); // keep original
2542
2543 if (strSnapshotFolder.isEmpty())
2544 strSnapshotFolder = "Snapshots";
2545 int vrc = i_calculateFullPath(strSnapshotFolder,
2546 strSnapshotFolder);
2547 if (RT_FAILURE(vrc))
2548 return setError(E_FAIL,
2549 tr("Invalid snapshot folder '%s' (%Rrc)"),
2550 strSnapshotFolder.c_str(), vrc);
2551
2552 i_setModified(IsModified_MachineData);
2553 mUserData.backup();
2554
2555 i_copyPathRelativeToMachine(strSnapshotFolder, mUserData->s.strSnapshotFolder);
2556
2557 return S_OK;
2558}
2559
2560HRESULT Machine::getMediumAttachments(std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
2561{
2562 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2563
2564 aMediumAttachments.resize(mMediaData->mAttachments.size());
2565 size_t i = 0;
2566 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
2567 it != mMediaData->mAttachments.end(); ++it, ++i)
2568 aMediumAttachments[i] = *it;
2569
2570 return S_OK;
2571}
2572
2573HRESULT Machine::getVRDEServer(ComPtr<IVRDEServer> &aVRDEServer)
2574{
2575 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2576
2577 Assert(!!mVRDEServer);
2578
2579 aVRDEServer = mVRDEServer;
2580
2581 return S_OK;
2582}
2583
2584HRESULT Machine::getAudioAdapter(ComPtr<IAudioAdapter> &aAudioAdapter)
2585{
2586 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2587
2588 aAudioAdapter = mAudioAdapter;
2589
2590 return S_OK;
2591}
2592
2593HRESULT Machine::getUSBControllers(std::vector<ComPtr<IUSBController> > &aUSBControllers)
2594{
2595#ifdef VBOX_WITH_VUSB
2596 clearError();
2597 MultiResult rc(S_OK);
2598
2599# ifdef VBOX_WITH_USB
2600 rc = mParent->i_host()->i_checkUSBProxyService();
2601 if (FAILED(rc)) return rc;
2602# endif
2603
2604 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2605
2606 USBControllerList data = *mUSBControllers.data();
2607 aUSBControllers.resize(data.size());
2608 size_t i = 0;
2609 for (USBControllerList::iterator it = data.begin(); it != data.end(); ++i, ++it)
2610 aUSBControllers[i] = *it;
2611
2612 return S_OK;
2613#else
2614 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2615 * extended error info to indicate that USB is simply not available
2616 * (w/o treating it as a failure), for example, as in OSE */
2617 NOREF(aUSBControllers);
2618 ReturnComNotImplemented();
2619#endif /* VBOX_WITH_VUSB */
2620}
2621
2622HRESULT Machine::getUSBDeviceFilters(ComPtr<IUSBDeviceFilters> &aUSBDeviceFilters)
2623{
2624#ifdef VBOX_WITH_VUSB
2625 clearError();
2626 MultiResult rc(S_OK);
2627
2628# ifdef VBOX_WITH_USB
2629 rc = mParent->i_host()->i_checkUSBProxyService();
2630 if (FAILED(rc)) return rc;
2631# endif
2632
2633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2634
2635 aUSBDeviceFilters = mUSBDeviceFilters;
2636 return rc;
2637#else
2638 /* Note: The GUI depends on this method returning E_NOTIMPL with no
2639 * extended error info to indicate that USB is simply not available
2640 * (w/o treating it as a failure), for example, as in OSE */
2641 NOREF(aUSBDeviceFilters);
2642 ReturnComNotImplemented();
2643#endif /* VBOX_WITH_VUSB */
2644}
2645
2646HRESULT Machine::getSettingsFilePath(com::Utf8Str &aSettingsFilePath)
2647{
2648 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2649
2650 aSettingsFilePath = mData->m_strConfigFileFull;
2651
2652 return S_OK;
2653}
2654
2655HRESULT Machine::getSettingsModified(BOOL *aSettingsModified)
2656{
2657 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2658
2659 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2660 if (FAILED(rc)) return rc;
2661
2662 if (!mData->pMachineConfigFile->fileExists())
2663 // this is a new machine, and no config file exists yet:
2664 *aSettingsModified = TRUE;
2665 else
2666 *aSettingsModified = (mData->flModifications != 0);
2667
2668 return S_OK;
2669}
2670
2671HRESULT Machine::getSessionState(SessionState_T *aSessionState)
2672{
2673 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2674
2675 *aSessionState = mData->mSession.mState;
2676
2677 return S_OK;
2678}
2679
2680HRESULT Machine::getSessionName(com::Utf8Str &aSessionName)
2681{
2682 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2683
2684 aSessionName = mData->mSession.mName;
2685
2686 return S_OK;
2687}
2688
2689HRESULT Machine::getSessionPID(ULONG *aSessionPID)
2690{
2691 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2692
2693 *aSessionPID = mData->mSession.mPID;
2694
2695 return S_OK;
2696}
2697
2698HRESULT Machine::getState(MachineState_T *aState)
2699{
2700 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2701
2702 *aState = mData->mMachineState;
2703 Assert(mData->mMachineState != MachineState_Null);
2704
2705 return S_OK;
2706}
2707
2708HRESULT Machine::getLastStateChange(LONG64 *aLastStateChange)
2709{
2710 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2711
2712 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange);
2713
2714 return S_OK;
2715}
2716
2717HRESULT Machine::getStateFilePath(com::Utf8Str &aStateFilePath)
2718{
2719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2720
2721 aStateFilePath = mSSData->strStateFilePath;
2722
2723 return S_OK;
2724}
2725
2726HRESULT Machine::getLogFolder(com::Utf8Str &aLogFolder)
2727{
2728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2729
2730 i_getLogFolder(aLogFolder);
2731
2732 return S_OK;
2733}
2734
2735HRESULT Machine::getCurrentSnapshot(ComPtr<ISnapshot> &aCurrentSnapshot)
2736{
2737 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2738
2739 aCurrentSnapshot = mData->mCurrentSnapshot;
2740
2741 return S_OK;
2742}
2743
2744HRESULT Machine::getSnapshotCount(ULONG *aSnapshotCount)
2745{
2746 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2747
2748 *aSnapshotCount = mData->mFirstSnapshot.isNull()
2749 ? 0
2750 : mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
2751
2752 return S_OK;
2753}
2754
2755HRESULT Machine::getCurrentStateModified(BOOL *aCurrentStateModified)
2756{
2757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2758
2759 /* Note: for machines with no snapshots, we always return FALSE
2760 * (mData->mCurrentStateModified will be TRUE in this case, for historical
2761 * reasons :) */
2762
2763 *aCurrentStateModified = mData->mFirstSnapshot.isNull()
2764 ? FALSE
2765 : mData->mCurrentStateModified;
2766
2767 return S_OK;
2768}
2769
2770HRESULT Machine::getSharedFolders(std::vector<ComPtr<ISharedFolder> > &aSharedFolders)
2771{
2772 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2773
2774 aSharedFolders.resize(mHWData->mSharedFolders.size());
2775 size_t i = 0;
2776 for (std::list<ComObjPtr<SharedFolder> >::iterator it = mHWData->mSharedFolders.begin();
2777 it != mHWData->mSharedFolders.end(); ++i, ++it)
2778 aSharedFolders[i] = *it;
2779
2780 return S_OK;
2781}
2782
2783HRESULT Machine::getClipboardMode(ClipboardMode_T *aClipboardMode)
2784{
2785 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2786
2787 *aClipboardMode = mHWData->mClipboardMode;
2788
2789 return S_OK;
2790}
2791
2792HRESULT Machine::setClipboardMode(ClipboardMode_T aClipboardMode)
2793{
2794 HRESULT rc = S_OK;
2795
2796 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2797
2798 alock.release();
2799 rc = i_onClipboardModeChange(aClipboardMode);
2800 alock.acquire();
2801 if (FAILED(rc)) return rc;
2802
2803 i_setModified(IsModified_MachineData);
2804 mHWData.backup();
2805 mHWData->mClipboardMode = aClipboardMode;
2806
2807 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2808 if (Global::IsOnline(mData->mMachineState))
2809 i_saveSettings(NULL);
2810
2811 return S_OK;
2812}
2813
2814HRESULT Machine::getDnDMode(DnDMode_T *aDnDMode)
2815{
2816 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2817
2818 *aDnDMode = mHWData->mDnDMode;
2819
2820 return S_OK;
2821}
2822
2823HRESULT Machine::setDnDMode(DnDMode_T aDnDMode)
2824{
2825 HRESULT rc = S_OK;
2826
2827 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2828
2829 alock.release();
2830 rc = i_onDnDModeChange(aDnDMode);
2831
2832 alock.acquire();
2833 if (FAILED(rc)) return rc;
2834
2835 i_setModified(IsModified_MachineData);
2836 mHWData.backup();
2837 mHWData->mDnDMode = aDnDMode;
2838
2839 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
2840 if (Global::IsOnline(mData->mMachineState))
2841 i_saveSettings(NULL);
2842
2843 return S_OK;
2844}
2845
2846HRESULT Machine::getStorageControllers(std::vector<ComPtr<IStorageController> > &aStorageControllers)
2847{
2848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2849 StorageControllerList data = *mStorageControllers.data();
2850 size_t i = 0;
2851 aStorageControllers.resize(data.size());
2852 for (StorageControllerList::iterator it = data.begin(); it != data.end(); ++it, ++i)
2853 aStorageControllers[i] = *it;
2854 return S_OK;
2855}
2856
2857HRESULT Machine::getTeleporterEnabled(BOOL *aEnabled)
2858{
2859 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2860
2861 *aEnabled = mUserData->s.fTeleporterEnabled;
2862
2863 return S_OK;
2864}
2865
2866HRESULT Machine::setTeleporterEnabled(BOOL aTeleporterEnabled)
2867{
2868 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2869
2870 /* Only allow it to be set to true when PoweredOff or Aborted.
2871 (Clearing it is always permitted.) */
2872 if ( aTeleporterEnabled
2873 && mData->mRegistered
2874 && ( !i_isSessionMachine()
2875 || ( mData->mMachineState != MachineState_PoweredOff
2876 && mData->mMachineState != MachineState_Teleported
2877 && mData->mMachineState != MachineState_Aborted
2878 )
2879 )
2880 )
2881 return setError(VBOX_E_INVALID_VM_STATE,
2882 tr("The machine is not powered off (state is %s)"),
2883 Global::stringifyMachineState(mData->mMachineState));
2884
2885 i_setModified(IsModified_MachineData);
2886 mUserData.backup();
2887 mUserData->s.fTeleporterEnabled = !! aTeleporterEnabled;
2888
2889 return S_OK;
2890}
2891
2892HRESULT Machine::getTeleporterPort(ULONG *aTeleporterPort)
2893{
2894 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2895
2896 *aTeleporterPort = (ULONG)mUserData->s.uTeleporterPort;
2897
2898 return S_OK;
2899}
2900
2901HRESULT Machine::setTeleporterPort(ULONG aTeleporterPort)
2902{
2903 if (aTeleporterPort >= _64K)
2904 return setError(E_INVALIDARG, tr("Invalid port number %d"), aTeleporterPort);
2905
2906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2907
2908 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2909 if (FAILED(rc)) return rc;
2910
2911 i_setModified(IsModified_MachineData);
2912 mUserData.backup();
2913 mUserData->s.uTeleporterPort = (uint32_t)aTeleporterPort;
2914
2915 return S_OK;
2916}
2917
2918HRESULT Machine::getTeleporterAddress(com::Utf8Str &aTeleporterAddress)
2919{
2920 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2921
2922 aTeleporterAddress = mUserData->s.strTeleporterAddress;
2923
2924 return S_OK;
2925}
2926
2927HRESULT Machine::setTeleporterAddress(const com::Utf8Str &aTeleporterAddress)
2928{
2929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2930
2931 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
2932 if (FAILED(rc)) return rc;
2933
2934 i_setModified(IsModified_MachineData);
2935 mUserData.backup();
2936 mUserData->s.strTeleporterAddress = aTeleporterAddress;
2937
2938 return S_OK;
2939}
2940
2941HRESULT Machine::getTeleporterPassword(com::Utf8Str &aTeleporterPassword)
2942{
2943 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2944 aTeleporterPassword = mUserData->s.strTeleporterPassword;
2945
2946 return S_OK;
2947}
2948
2949HRESULT Machine::setTeleporterPassword(const com::Utf8Str &aTeleporterPassword)
2950{
2951 /*
2952 * Hash the password first.
2953 */
2954 com::Utf8Str aT = aTeleporterPassword;
2955
2956 if (!aT.isEmpty())
2957 {
2958 if (VBoxIsPasswordHashed(&aT))
2959 return setError(E_INVALIDARG, tr("Cannot set an already hashed password, only plain text password please"));
2960 VBoxHashPassword(&aT);
2961 }
2962
2963 /*
2964 * Do the update.
2965 */
2966 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2967 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
2968 if (SUCCEEDED(hrc))
2969 {
2970 i_setModified(IsModified_MachineData);
2971 mUserData.backup();
2972 mUserData->s.strTeleporterPassword = aT;
2973 }
2974
2975 return hrc;
2976}
2977
2978HRESULT Machine::getFaultToleranceState(FaultToleranceState_T *aFaultToleranceState)
2979{
2980 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
2981
2982 *aFaultToleranceState = mUserData->s.enmFaultToleranceState;
2983 return S_OK;
2984}
2985
2986HRESULT Machine::setFaultToleranceState(FaultToleranceState_T aFaultToleranceState)
2987{
2988 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
2989
2990 /* @todo deal with running state change. */
2991 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
2992 if (FAILED(rc)) return rc;
2993
2994 i_setModified(IsModified_MachineData);
2995 mUserData.backup();
2996 mUserData->s.enmFaultToleranceState = aFaultToleranceState;
2997 return S_OK;
2998}
2999
3000HRESULT Machine::getFaultToleranceAddress(com::Utf8Str &aFaultToleranceAddress)
3001{
3002 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3003
3004 aFaultToleranceAddress = mUserData->s.strFaultToleranceAddress;
3005 return S_OK;
3006}
3007
3008HRESULT Machine::setFaultToleranceAddress(const com::Utf8Str &aFaultToleranceAddress)
3009{
3010 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3011
3012 /* @todo deal with running state change. */
3013 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3014 if (FAILED(rc)) return rc;
3015
3016 i_setModified(IsModified_MachineData);
3017 mUserData.backup();
3018 mUserData->s.strFaultToleranceAddress = aFaultToleranceAddress;
3019 return S_OK;
3020}
3021
3022HRESULT Machine::getFaultTolerancePort(ULONG *aFaultTolerancePort)
3023{
3024 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3025
3026 *aFaultTolerancePort = mUserData->s.uFaultTolerancePort;
3027 return S_OK;
3028}
3029
3030HRESULT Machine::setFaultTolerancePort(ULONG aFaultTolerancePort)
3031{
3032 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3033
3034 /* @todo deal with running state change. */
3035 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3036 if (FAILED(rc)) return rc;
3037
3038 i_setModified(IsModified_MachineData);
3039 mUserData.backup();
3040 mUserData->s.uFaultTolerancePort = aFaultTolerancePort;
3041 return S_OK;
3042}
3043
3044HRESULT Machine::getFaultTolerancePassword(com::Utf8Str &aFaultTolerancePassword)
3045{
3046 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3047
3048 aFaultTolerancePassword = mUserData->s.strFaultTolerancePassword;
3049
3050 return S_OK;
3051}
3052
3053HRESULT Machine::setFaultTolerancePassword(const com::Utf8Str &aFaultTolerancePassword)
3054{
3055 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3056
3057 /* @todo deal with running state change. */
3058 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3059 if (FAILED(rc)) return rc;
3060
3061 i_setModified(IsModified_MachineData);
3062 mUserData.backup();
3063 mUserData->s.strFaultTolerancePassword = aFaultTolerancePassword;
3064
3065 return S_OK;
3066}
3067
3068HRESULT Machine::getFaultToleranceSyncInterval(ULONG *aFaultToleranceSyncInterval)
3069{
3070 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3071
3072 *aFaultToleranceSyncInterval = mUserData->s.uFaultToleranceInterval;
3073 return S_OK;
3074}
3075
3076HRESULT Machine::setFaultToleranceSyncInterval(ULONG aFaultToleranceSyncInterval)
3077{
3078 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3079
3080 /* @todo deal with running state change. */
3081 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
3082 if (FAILED(rc)) return rc;
3083
3084 i_setModified(IsModified_MachineData);
3085 mUserData.backup();
3086 mUserData->s.uFaultToleranceInterval = aFaultToleranceSyncInterval;
3087 return S_OK;
3088}
3089
3090HRESULT Machine::getRTCUseUTC(BOOL *aRTCUseUTC)
3091{
3092 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3093
3094 *aRTCUseUTC = mUserData->s.fRTCUseUTC;
3095
3096 return S_OK;
3097}
3098
3099HRESULT Machine::setRTCUseUTC(BOOL aRTCUseUTC)
3100{
3101 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3102
3103 /* Only allow it to be set to true when PoweredOff or Aborted.
3104 (Clearing it is always permitted.) */
3105 if ( aRTCUseUTC
3106 && mData->mRegistered
3107 && ( !i_isSessionMachine()
3108 || ( mData->mMachineState != MachineState_PoweredOff
3109 && mData->mMachineState != MachineState_Teleported
3110 && mData->mMachineState != MachineState_Aborted
3111 )
3112 )
3113 )
3114 return setError(VBOX_E_INVALID_VM_STATE,
3115 tr("The machine is not powered off (state is %s)"),
3116 Global::stringifyMachineState(mData->mMachineState));
3117
3118 i_setModified(IsModified_MachineData);
3119 mUserData.backup();
3120 mUserData->s.fRTCUseUTC = !!aRTCUseUTC;
3121
3122 return S_OK;
3123}
3124
3125HRESULT Machine::getIOCacheEnabled(BOOL *aIOCacheEnabled)
3126{
3127 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3128
3129 *aIOCacheEnabled = mHWData->mIOCacheEnabled;
3130
3131 return S_OK;
3132}
3133
3134HRESULT Machine::setIOCacheEnabled(BOOL aIOCacheEnabled)
3135{
3136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3137
3138 HRESULT rc = i_checkStateDependency(MutableStateDep);
3139 if (FAILED(rc)) return rc;
3140
3141 i_setModified(IsModified_MachineData);
3142 mHWData.backup();
3143 mHWData->mIOCacheEnabled = aIOCacheEnabled;
3144
3145 return S_OK;
3146}
3147
3148HRESULT Machine::getIOCacheSize(ULONG *aIOCacheSize)
3149{
3150 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3151
3152 *aIOCacheSize = mHWData->mIOCacheSize;
3153
3154 return S_OK;
3155}
3156
3157HRESULT Machine::setIOCacheSize(ULONG aIOCacheSize)
3158{
3159 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3160
3161 HRESULT rc = i_checkStateDependency(MutableStateDep);
3162 if (FAILED(rc)) return rc;
3163
3164 i_setModified(IsModified_MachineData);
3165 mHWData.backup();
3166 mHWData->mIOCacheSize = aIOCacheSize;
3167
3168 return S_OK;
3169}
3170
3171
3172/**
3173 * @note Locks objects!
3174 */
3175HRESULT Machine::lockMachine(const ComPtr<ISession> &aSession,
3176 LockType_T aLockType)
3177{
3178 /* check the session state */
3179 SessionState_T state;
3180 HRESULT rc = aSession->COMGETTER(State)(&state);
3181 if (FAILED(rc)) return rc;
3182
3183 if (state != SessionState_Unlocked)
3184 return setError(VBOX_E_INVALID_OBJECT_STATE,
3185 tr("The given session is busy"));
3186
3187 // get the client's IInternalSessionControl interface
3188 ComPtr<IInternalSessionControl> pSessionControl = aSession;
3189 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"),
3190 E_INVALIDARG);
3191
3192 // session name (only used in some code paths)
3193 Utf8Str strSessionName;
3194
3195 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3196
3197 if (!mData->mRegistered)
3198 return setError(E_UNEXPECTED,
3199 tr("The machine '%s' is not registered"),
3200 mUserData->s.strName.c_str());
3201
3202 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3203
3204 SessionState_T oldState = mData->mSession.mState;
3205 /* Hack: in case the session is closing and there is a progress object
3206 * which allows waiting for the session to be closed, take the opportunity
3207 * and do a limited wait (max. 1 second). This helps a lot when the system
3208 * is busy and thus session closing can take a little while. */
3209 if ( mData->mSession.mState == SessionState_Unlocking
3210 && mData->mSession.mProgress)
3211 {
3212 alock.release();
3213 mData->mSession.mProgress->WaitForCompletion(1000);
3214 alock.acquire();
3215 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
3216 }
3217
3218 // try again now
3219 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already
3220 // (i.e. session machine exists)
3221 && (aLockType == LockType_Shared) // caller wants a shared link to the
3222 // existing session that holds the write lock:
3223 )
3224 {
3225 // OK, share the session... we are now dealing with three processes:
3226 // 1) VBoxSVC (where this code runs);
3227 // 2) process C: the caller's client process (who wants a shared session);
3228 // 3) process W: the process which already holds the write lock on the machine (write-locking session)
3229
3230 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL)
3231 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl;
3232 ComAssertRet(!pSessionW.isNull(), E_FAIL);
3233 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine;
3234 AssertReturn(!pSessionMachine.isNull(), E_FAIL);
3235
3236 /*
3237 * Release the lock before calling the client process. It's safe here
3238 * since the only thing to do after we get the lock again is to add
3239 * the remote control to the list (which doesn't directly influence
3240 * anything).
3241 */
3242 alock.release();
3243
3244 // get the console of the session holding the write lock (this is a remote call)
3245 ComPtr<IConsole> pConsoleW;
3246 if (mData->mSession.mLockType == LockType_VM)
3247 {
3248 LogFlowThisFunc(("Calling GetRemoteConsole()...\n"));
3249 rc = pSessionW->COMGETTER(RemoteConsole)(pConsoleW.asOutParam());
3250 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc));
3251 if (FAILED(rc))
3252 // the failure may occur w/o any error info (from RPC), so provide one
3253 return setError(VBOX_E_VM_ERROR,
3254 tr("Failed to get a console object from the direct session (%Rhrc)"), rc);
3255 ComAssertRet(!pConsoleW.isNull(), E_FAIL);
3256 }
3257
3258 // share the session machine and W's console with the caller's session
3259 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3260 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW);
3261 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3262
3263 if (FAILED(rc))
3264 // the failure may occur w/o any error info (from RPC), so provide one
3265 return setError(VBOX_E_VM_ERROR,
3266 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3267 alock.acquire();
3268
3269 // need to revalidate the state after acquiring the lock again
3270 if (mData->mSession.mState != SessionState_Locked)
3271 {
3272 pSessionControl->Uninitialize();
3273 return setError(VBOX_E_INVALID_SESSION_STATE,
3274 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"),
3275 mUserData->s.strName.c_str());
3276 }
3277
3278 // add the caller's session to the list
3279 mData->mSession.mRemoteControls.push_back(pSessionControl);
3280 }
3281 else if ( mData->mSession.mState == SessionState_Locked
3282 || mData->mSession.mState == SessionState_Unlocking
3283 )
3284 {
3285 // sharing not permitted, or machine still unlocking:
3286 return setError(VBOX_E_INVALID_OBJECT_STATE,
3287 tr("The machine '%s' is already locked for a session (or being unlocked)"),
3288 mUserData->s.strName.c_str());
3289 }
3290 else
3291 {
3292 // machine is not locked: then write-lock the machine (create the session machine)
3293
3294 // must not be busy
3295 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
3296
3297 // get the caller's session PID
3298 RTPROCESS pid = NIL_RTPROCESS;
3299 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS));
3300 pSessionControl->COMGETTER(PID)((ULONG*)&pid);
3301 Assert(pid != NIL_RTPROCESS);
3302
3303 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning);
3304
3305 if (fLaunchingVMProcess)
3306 {
3307 if (mData->mSession.mPID == NIL_RTPROCESS)
3308 {
3309 // two or more clients racing for a lock, the one which set the
3310 // session state to Spawning will win, the others will get an
3311 // error as we can't decide here if waiting a little would help
3312 // (only for shared locks this would avoid an error)
3313 return setError(VBOX_E_INVALID_OBJECT_STATE,
3314 tr("The machine '%s' already has a lock request pending"),
3315 mUserData->s.strName.c_str());
3316 }
3317
3318 // this machine is awaiting for a spawning session to be opened:
3319 // then the calling process must be the one that got started by
3320 // LaunchVMProcess()
3321
3322 LogFlowThisFunc(("mSession.mPID=%d(0x%x)\n", mData->mSession.mPID, mData->mSession.mPID));
3323 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid));
3324
3325#if defined(VBOX_WITH_HARDENING) && defined(RT_OS_WINDOWS)
3326 /* Hardened windows builds spawns three processes when a VM is
3327 launched, the 3rd one is the one that will end up here. */
3328 RTPROCESS ppid;
3329 int rc = RTProcQueryParent(pid, &ppid);
3330 if (RT_SUCCESS(rc))
3331 rc = RTProcQueryParent(ppid, &ppid);
3332 if ( (RT_SUCCESS(rc) && mData->mSession.mPID == ppid)
3333 || rc == VERR_ACCESS_DENIED)
3334 {
3335 LogFlowThisFunc(("mSession.mPID => %d(%#x) - windows hardening stub\n", mData->mSession.mPID, pid));
3336 mData->mSession.mPID = pid;
3337 }
3338#endif
3339
3340 if (mData->mSession.mPID != pid)
3341 return setError(E_ACCESSDENIED,
3342 tr("An unexpected process (PID=0x%08X) has tried to lock the "
3343 "machine '%s', while only the process started by LaunchVMProcess (PID=0x%08X) is allowed"),
3344 pid, mUserData->s.strName.c_str(), mData->mSession.mPID);
3345 }
3346
3347 // create the mutable SessionMachine from the current machine
3348 ComObjPtr<SessionMachine> sessionMachine;
3349 sessionMachine.createObject();
3350 rc = sessionMachine->init(this);
3351 AssertComRC(rc);
3352
3353 /* NOTE: doing return from this function after this point but
3354 * before the end is forbidden since it may call SessionMachine::uninit()
3355 * (through the ComObjPtr's destructor) which requests the VirtualBox write
3356 * lock while still holding the Machine lock in alock so that a deadlock
3357 * is possible due to the wrong lock order. */
3358
3359 if (SUCCEEDED(rc))
3360 {
3361 /*
3362 * Set the session state to Spawning to protect against subsequent
3363 * attempts to open a session and to unregister the machine after
3364 * we release the lock.
3365 */
3366 SessionState_T origState = mData->mSession.mState;
3367 mData->mSession.mState = SessionState_Spawning;
3368
3369#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3370 /* Get the client token ID to be passed to the client process */
3371 Utf8Str strTokenId;
3372 sessionMachine->i_getTokenId(strTokenId);
3373 Assert(!strTokenId.isEmpty());
3374#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3375 /* Get the client token to be passed to the client process */
3376 ComPtr<IToken> pToken(sessionMachine->i_getToken());
3377 /* The token is now "owned" by pToken, fix refcount */
3378 if (!pToken.isNull())
3379 pToken->Release();
3380#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3381
3382 /*
3383 * Release the lock before calling the client process -- it will call
3384 * Machine/SessionMachine methods. Releasing the lock here is quite safe
3385 * because the state is Spawning, so that LaunchVMProcess() and
3386 * LockMachine() calls will fail. This method, called before we
3387 * acquire the lock again, will fail because of the wrong PID.
3388 *
3389 * Note that mData->mSession.mRemoteControls accessed outside
3390 * the lock may not be modified when state is Spawning, so it's safe.
3391 */
3392 alock.release();
3393
3394 LogFlowThisFunc(("Calling AssignMachine()...\n"));
3395#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
3396 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, Bstr(strTokenId).raw());
3397#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3398 rc = pSessionControl->AssignMachine(sessionMachine, aLockType, pToken);
3399 /* Now the token is owned by the client process. */
3400 pToken.setNull();
3401#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
3402 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc));
3403
3404 /* The failure may occur w/o any error info (from RPC), so provide one */
3405 if (FAILED(rc))
3406 setError(VBOX_E_VM_ERROR,
3407 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
3408
3409 // get session name, either to remember or to compare against
3410 // the already known session name.
3411 {
3412 Bstr bstrSessionName;
3413 HRESULT rc2 = aSession->COMGETTER(Name)(bstrSessionName.asOutParam());
3414 if (SUCCEEDED(rc2))
3415 strSessionName = bstrSessionName;
3416 }
3417
3418 if ( SUCCEEDED(rc)
3419 && fLaunchingVMProcess
3420 )
3421 {
3422 /* complete the remote session initialization */
3423
3424 /* get the console from the direct session */
3425 ComPtr<IConsole> console;
3426 rc = pSessionControl->COMGETTER(RemoteConsole)(console.asOutParam());
3427 ComAssertComRC(rc);
3428
3429 if (SUCCEEDED(rc) && !console)
3430 {
3431 ComAssert(!!console);
3432 rc = E_FAIL;
3433 }
3434
3435 /* assign machine & console to the remote session */
3436 if (SUCCEEDED(rc))
3437 {
3438 /*
3439 * after LaunchVMProcess(), the first and the only
3440 * entry in remoteControls is that remote session
3441 */
3442 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n"));
3443 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console);
3444 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc));
3445
3446 /* The failure may occur w/o any error info (from RPC), so provide one */
3447 if (FAILED(rc))
3448 setError(VBOX_E_VM_ERROR,
3449 tr("Failed to assign the machine to the remote session (%Rhrc)"), rc);
3450 }
3451
3452 if (FAILED(rc))
3453 pSessionControl->Uninitialize();
3454 }
3455
3456 /* acquire the lock again */
3457 alock.acquire();
3458
3459 /* Restore the session state */
3460 mData->mSession.mState = origState;
3461 }
3462
3463 // finalize spawning anyway (this is why we don't return on errors above)
3464 if (fLaunchingVMProcess)
3465 {
3466 Assert(mData->mSession.mName == strSessionName);
3467 /* Note that the progress object is finalized later */
3468 /** @todo Consider checking mData->mSession.mProgress for cancellation
3469 * around here. */
3470
3471 /* We don't reset mSession.mPID here because it is necessary for
3472 * SessionMachine::uninit() to reap the child process later. */
3473
3474 if (FAILED(rc))
3475 {
3476 /* Close the remote session, remove the remote control from the list
3477 * and reset session state to Closed (@note keep the code in sync
3478 * with the relevant part in checkForSpawnFailure()). */
3479
3480 Assert(mData->mSession.mRemoteControls.size() == 1);
3481 if (mData->mSession.mRemoteControls.size() == 1)
3482 {
3483 ErrorInfoKeeper eik;
3484 mData->mSession.mRemoteControls.front()->Uninitialize();
3485 }
3486
3487 mData->mSession.mRemoteControls.clear();
3488 mData->mSession.mState = SessionState_Unlocked;
3489 }
3490 }
3491 else
3492 {
3493 /* memorize PID of the directly opened session */
3494 if (SUCCEEDED(rc))
3495 mData->mSession.mPID = pid;
3496 }
3497
3498 if (SUCCEEDED(rc))
3499 {
3500 mData->mSession.mLockType = aLockType;
3501 /* memorize the direct session control and cache IUnknown for it */
3502 mData->mSession.mDirectControl = pSessionControl;
3503 mData->mSession.mState = SessionState_Locked;
3504 if (!fLaunchingVMProcess)
3505 mData->mSession.mName = strSessionName;
3506 /* associate the SessionMachine with this Machine */
3507 mData->mSession.mMachine = sessionMachine;
3508
3509 /* request an IUnknown pointer early from the remote party for later
3510 * identity checks (it will be internally cached within mDirectControl
3511 * at least on XPCOM) */
3512 ComPtr<IUnknown> unk = mData->mSession.mDirectControl;
3513 NOREF(unk);
3514 }
3515
3516 /* Release the lock since SessionMachine::uninit() locks VirtualBox which
3517 * would break the lock order */
3518 alock.release();
3519
3520 /* uninitialize the created session machine on failure */
3521 if (FAILED(rc))
3522 sessionMachine->uninit();
3523 }
3524
3525 if (SUCCEEDED(rc))
3526 {
3527 /*
3528 * tell the client watcher thread to update the set of
3529 * machines that have open sessions
3530 */
3531 mParent->i_updateClientWatcher();
3532
3533 if (oldState != SessionState_Locked)
3534 /* fire an event */
3535 mParent->i_onSessionStateChange(i_getId(), SessionState_Locked);
3536 }
3537
3538 return rc;
3539}
3540
3541/**
3542 * @note Locks objects!
3543 */
3544HRESULT Machine::launchVMProcess(const ComPtr<ISession> &aSession,
3545 const com::Utf8Str &aName,
3546 const com::Utf8Str &aEnvironment,
3547 ComPtr<IProgress> &aProgress)
3548{
3549 Utf8Str strFrontend(aName);
3550 /* "emergencystop" doesn't need the session, so skip the checks/interface
3551 * retrieval. This code doesn't quite fit in here, but introducing a
3552 * special API method would be even more effort, and would require explicit
3553 * support by every API client. It's better to hide the feature a bit. */
3554 if (strFrontend != "emergencystop")
3555 CheckComArgNotNull(aSession);
3556
3557 HRESULT rc = S_OK;
3558 if (strFrontend.isEmpty())
3559 {
3560 Bstr bstrFrontend;
3561 rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3562 if (FAILED(rc))
3563 return rc;
3564 strFrontend = bstrFrontend;
3565 if (strFrontend.isEmpty())
3566 {
3567 ComPtr<ISystemProperties> systemProperties;
3568 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
3569 if (FAILED(rc))
3570 return rc;
3571 rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam());
3572 if (FAILED(rc))
3573 return rc;
3574 strFrontend = bstrFrontend;
3575 }
3576 /* paranoia - emergencystop is not a valid default */
3577 if (strFrontend == "emergencystop")
3578 strFrontend = Utf8Str::Empty;
3579 }
3580 /* default frontend: Qt GUI */
3581 if (strFrontend.isEmpty())
3582 strFrontend = "GUI/Qt";
3583
3584 if (strFrontend != "emergencystop")
3585 {
3586 /* check the session state */
3587 SessionState_T state;
3588 rc = aSession->COMGETTER(State)(&state);
3589 if (FAILED(rc))
3590 return rc;
3591
3592 if (state != SessionState_Unlocked)
3593 return setError(VBOX_E_INVALID_OBJECT_STATE,
3594 tr("The given session is busy"));
3595
3596 /* get the IInternalSessionControl interface */
3597 ComPtr<IInternalSessionControl> control(aSession);
3598 ComAssertMsgRet(!control.isNull(),
3599 ("No IInternalSessionControl interface"),
3600 E_INVALIDARG);
3601
3602 /* get the teleporter enable state for the progress object init. */
3603 BOOL fTeleporterEnabled;
3604 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled);
3605 if (FAILED(rc))
3606 return rc;
3607
3608 /* create a progress object */
3609 ComObjPtr<ProgressProxy> progress;
3610 progress.createObject();
3611 rc = progress->init(mParent,
3612 static_cast<IMachine*>(this),
3613 Bstr(tr("Starting VM")).raw(),
3614 TRUE /* aCancelable */,
3615 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */,
3616 BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"),
3617 mUserData->s.strName.c_str(), strFrontend.c_str()).raw(),
3618 2 /* uFirstOperationWeight */,
3619 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */);
3620
3621 if (SUCCEEDED(rc))
3622 {
3623 rc = i_launchVMProcess(control, strFrontend, aEnvironment, progress);
3624 if (SUCCEEDED(rc))
3625 {
3626 aProgress = progress;
3627
3628 /* signal the client watcher thread */
3629 mParent->i_updateClientWatcher();
3630
3631 /* fire an event */
3632 mParent->i_onSessionStateChange(i_getId(), SessionState_Spawning);
3633 }
3634 }
3635 }
3636 else
3637 {
3638 /* no progress object - either instant success or failure */
3639 aProgress = NULL;
3640
3641 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3642
3643 if (mData->mSession.mState != SessionState_Locked)
3644 return setError(VBOX_E_INVALID_OBJECT_STATE,
3645 tr("The machine '%s' is not locked by a session"),
3646 mUserData->s.strName.c_str());
3647
3648 /* must have a VM process associated - do not kill normal API clients
3649 * with an open session */
3650 if (!Global::IsOnline(mData->mMachineState))
3651 return setError(VBOX_E_INVALID_OBJECT_STATE,
3652 tr("The machine '%s' does not have a VM process"),
3653 mUserData->s.strName.c_str());
3654
3655 /* forcibly terminate the VM process */
3656 if (mData->mSession.mPID != NIL_RTPROCESS)
3657 RTProcTerminate(mData->mSession.mPID);
3658
3659 /* signal the client watcher thread, as most likely the client has
3660 * been terminated */
3661 mParent->i_updateClientWatcher();
3662 }
3663
3664 return rc;
3665}
3666
3667HRESULT Machine::setBootOrder(ULONG aPosition, DeviceType_T aDevice)
3668{
3669 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3670 return setError(E_INVALIDARG,
3671 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3672 aPosition, SchemaDefs::MaxBootPosition);
3673
3674 if (aDevice == DeviceType_USB)
3675 return setError(E_NOTIMPL,
3676 tr("Booting from USB device is currently not supported"));
3677
3678 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
3679
3680 HRESULT rc = i_checkStateDependency(MutableStateDep);
3681 if (FAILED(rc)) return rc;
3682
3683 i_setModified(IsModified_MachineData);
3684 mHWData.backup();
3685 mHWData->mBootOrder[aPosition - 1] = aDevice;
3686
3687 return S_OK;
3688}
3689
3690HRESULT Machine::getBootOrder(ULONG aPosition, DeviceType_T *aDevice)
3691{
3692 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition)
3693 return setError(E_INVALIDARG,
3694 tr("Invalid boot position: %lu (must be in range [1, %lu])"),
3695 aPosition, SchemaDefs::MaxBootPosition);
3696
3697 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
3698
3699 *aDevice = mHWData->mBootOrder[aPosition - 1];
3700
3701 return S_OK;
3702}
3703
3704HRESULT Machine::attachDevice(const com::Utf8Str &aName,
3705 LONG aControllerPort,
3706 LONG aDevice,
3707 DeviceType_T aType,
3708 const ComPtr<IMedium> &aMedium)
3709{
3710 IMedium *aM = aMedium;
3711 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
3712 aName.c_str(), aControllerPort, aDevice, aType, aM));
3713
3714 // request the host lock first, since might be calling Host methods for getting host drives;
3715 // next, protect the media tree all the while we're in here, as well as our member variables
3716 AutoMultiWriteLock2 alock(mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
3717 AutoWriteLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
3718
3719 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
3720 if (FAILED(rc)) return rc;
3721
3722 /// @todo NEWMEDIA implicit machine registration
3723 if (!mData->mRegistered)
3724 return setError(VBOX_E_INVALID_OBJECT_STATE,
3725 tr("Cannot attach storage devices to an unregistered machine"));
3726
3727 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
3728
3729 /* Check for an existing controller. */
3730 ComObjPtr<StorageController> ctl;
3731 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
3732 if (FAILED(rc)) return rc;
3733
3734 StorageControllerType_T ctrlType;
3735 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
3736 if (FAILED(rc))
3737 return setError(E_FAIL,
3738 tr("Could not get type of controller '%s'"),
3739 aName.c_str());
3740
3741 bool fSilent = false;
3742 Utf8Str strReconfig;
3743
3744 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
3745 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
3746 if ( mData->mMachineState == MachineState_Paused
3747 && strReconfig == "1")
3748 fSilent = true;
3749
3750 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
3751 bool fHotplug = false;
3752 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
3753 fHotplug = true;
3754
3755 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
3756 return setError(VBOX_E_INVALID_VM_STATE,
3757 tr("Controller '%s' does not support hotplugging"),
3758 aName.c_str());
3759
3760 // check that the port and device are not out of range
3761 rc = ctl->i_checkPortAndDeviceValid(aControllerPort, aDevice);
3762 if (FAILED(rc)) return rc;
3763
3764 /* check if the device slot is already busy */
3765 MediumAttachment *pAttachTemp;
3766 if ((pAttachTemp = i_findAttachment(mMediaData->mAttachments,
3767 Bstr(aName).raw(),
3768 aControllerPort,
3769 aDevice)))
3770 {
3771 Medium *pMedium = pAttachTemp->i_getMedium();
3772 if (pMedium)
3773 {
3774 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
3775 return setError(VBOX_E_OBJECT_IN_USE,
3776 tr("Medium '%s' is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3777 pMedium->i_getLocationFull().c_str(),
3778 aControllerPort,
3779 aDevice,
3780 aName.c_str());
3781 }
3782 else
3783 return setError(VBOX_E_OBJECT_IN_USE,
3784 tr("Device is already attached to port %d, device %d of controller '%s' of this virtual machine"),
3785 aControllerPort, aDevice, aName.c_str());
3786 }
3787
3788 ComObjPtr<Medium> medium = static_cast<Medium*>(aM);
3789 if (aMedium && medium.isNull())
3790 return setError(E_INVALIDARG, "The given medium pointer is invalid");
3791
3792 AutoCaller mediumCaller(medium);
3793 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
3794
3795 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS);
3796
3797 if ( (pAttachTemp = i_findAttachment(mMediaData->mAttachments, medium))
3798 && !medium.isNull()
3799 )
3800 return setError(VBOX_E_OBJECT_IN_USE,
3801 tr("Medium '%s' is already attached to this virtual machine"),
3802 medium->i_getLocationFull().c_str());
3803
3804 if (!medium.isNull())
3805 {
3806 MediumType_T mtype = medium->i_getType();
3807 // MediumType_Readonly is also new, but only applies to DVDs and floppies.
3808 // For DVDs it's not written to the config file, so needs no global config
3809 // version bump. For floppies it's a new attribute "type", which is ignored
3810 // by older VirtualBox version, so needs no global config version bump either.
3811 // For hard disks this type is not accepted.
3812 if (mtype == MediumType_MultiAttach)
3813 {
3814 // This type is new with VirtualBox 4.0 and therefore requires settings
3815 // version 1.11 in the settings backend. Unfortunately it is not enough to do
3816 // the usual routine in MachineConfigFile::bumpSettingsVersionIfNeeded() for
3817 // two reasons: The medium type is a property of the media registry tree, which
3818 // can reside in the global config file (for pre-4.0 media); we would therefore
3819 // possibly need to bump the global config version. We don't want to do that though
3820 // because that might make downgrading to pre-4.0 impossible.
3821 // As a result, we can only use these two new types if the medium is NOT in the
3822 // global registry:
3823 const Guid &uuidGlobalRegistry = mParent->i_getGlobalRegistryId();
3824 if ( medium->i_isInRegistry(uuidGlobalRegistry)
3825 || !mData->pMachineConfigFile->canHaveOwnMediaRegistry()
3826 )
3827 return setError(VBOX_E_INVALID_OBJECT_STATE,
3828 tr("Cannot attach medium '%s': the media type 'MultiAttach' can only be attached "
3829 "to machines that were created with VirtualBox 4.0 or later"),
3830 medium->i_getLocationFull().c_str());
3831 }
3832 }
3833
3834 bool fIndirect = false;
3835 if (!medium.isNull())
3836 fIndirect = medium->i_isReadOnly();
3837 bool associate = true;
3838
3839 do
3840 {
3841 if ( aType == DeviceType_HardDisk
3842 && mMediaData.isBackedUp())
3843 {
3844 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3845
3846 /* check if the medium was attached to the VM before we started
3847 * changing attachments in which case the attachment just needs to
3848 * be restored */
3849 if ((pAttachTemp = i_findAttachment(oldAtts, medium)))
3850 {
3851 AssertReturn(!fIndirect, E_FAIL);
3852
3853 /* see if it's the same bus/channel/device */
3854 if (pAttachTemp->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3855 {
3856 /* the simplest case: restore the whole attachment
3857 * and return, nothing else to do */
3858 mMediaData->mAttachments.push_back(pAttachTemp);
3859
3860 /* Reattach the medium to the VM. */
3861 if (fHotplug || fSilent)
3862 {
3863 mediumLock.release();
3864 treeLock.release();
3865 alock.release();
3866
3867 MediumLockList *pMediumLockList(new MediumLockList());
3868
3869 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3870 true /* fMediumLockWrite */,
3871 false /* fMediumLockWriteAll */,
3872 NULL,
3873 *pMediumLockList);
3874 alock.acquire();
3875 if (FAILED(rc))
3876 delete pMediumLockList;
3877 else
3878 {
3879 mData->mSession.mLockedMedia.Unlock();
3880 alock.release();
3881 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3882 mData->mSession.mLockedMedia.Lock();
3883 alock.acquire();
3884 }
3885 alock.release();
3886
3887 if (SUCCEEDED(rc))
3888 {
3889 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3890 /* Remove lock list in case of error. */
3891 if (FAILED(rc))
3892 {
3893 mData->mSession.mLockedMedia.Unlock();
3894 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3895 mData->mSession.mLockedMedia.Lock();
3896 }
3897 }
3898 }
3899
3900 return S_OK;
3901 }
3902
3903 /* bus/channel/device differ; we need a new attachment object,
3904 * but don't try to associate it again */
3905 associate = false;
3906 break;
3907 }
3908 }
3909
3910 /* go further only if the attachment is to be indirect */
3911 if (!fIndirect)
3912 break;
3913
3914 /* perform the so called smart attachment logic for indirect
3915 * attachments. Note that smart attachment is only applicable to base
3916 * hard disks. */
3917
3918 if (medium->i_getParent().isNull())
3919 {
3920 /* first, investigate the backup copy of the current hard disk
3921 * attachments to make it possible to re-attach existing diffs to
3922 * another device slot w/o losing their contents */
3923 if (mMediaData.isBackedUp())
3924 {
3925 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
3926
3927 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end();
3928 uint32_t foundLevel = 0;
3929
3930 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
3931 {
3932 uint32_t level = 0;
3933 MediumAttachment *pAttach = *it;
3934 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
3935 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
3936 if (pMedium.isNull())
3937 continue;
3938
3939 if (pMedium->i_getBase(&level) == medium)
3940 {
3941 /* skip the hard disk if its currently attached (we
3942 * cannot attach the same hard disk twice) */
3943 if (i_findAttachment(mMediaData->mAttachments,
3944 pMedium))
3945 continue;
3946
3947 /* matched device, channel and bus (i.e. attached to the
3948 * same place) will win and immediately stop the search;
3949 * otherwise the attachment that has the youngest
3950 * descendant of medium will be used
3951 */
3952 if (pAttach->i_matches(Bstr(aName).raw(), aControllerPort, aDevice))
3953 {
3954 /* the simplest case: restore the whole attachment
3955 * and return, nothing else to do */
3956 mMediaData->mAttachments.push_back(*it);
3957
3958 /* Reattach the medium to the VM. */
3959 if (fHotplug || fSilent)
3960 {
3961 mediumLock.release();
3962 treeLock.release();
3963 alock.release();
3964
3965 MediumLockList *pMediumLockList(new MediumLockList());
3966
3967 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
3968 true /* fMediumLockWrite */,
3969 false /* fMediumLockWriteAll */,
3970 NULL,
3971 *pMediumLockList);
3972 alock.acquire();
3973 if (FAILED(rc))
3974 delete pMediumLockList;
3975 else
3976 {
3977 mData->mSession.mLockedMedia.Unlock();
3978 alock.release();
3979 rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList);
3980 mData->mSession.mLockedMedia.Lock();
3981 alock.acquire();
3982 }
3983 alock.release();
3984
3985 if (SUCCEEDED(rc))
3986 {
3987 rc = i_onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent);
3988 /* Remove lock list in case of error. */
3989 if (FAILED(rc))
3990 {
3991 mData->mSession.mLockedMedia.Unlock();
3992 mData->mSession.mLockedMedia.Remove(pAttachTemp);
3993 mData->mSession.mLockedMedia.Lock();
3994 }
3995 }
3996 }
3997
3998 return S_OK;
3999 }
4000 else if ( foundIt == oldAtts.end()
4001 || level > foundLevel /* prefer younger */
4002 )
4003 {
4004 foundIt = it;
4005 foundLevel = level;
4006 }
4007 }
4008 }
4009
4010 if (foundIt != oldAtts.end())
4011 {
4012 /* use the previously attached hard disk */
4013 medium = (*foundIt)->i_getMedium();
4014 mediumCaller.attach(medium);
4015 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4016 mediumLock.attach(medium);
4017 /* not implicit, doesn't require association with this VM */
4018 fIndirect = false;
4019 associate = false;
4020 /* go right to the MediumAttachment creation */
4021 break;
4022 }
4023 }
4024
4025 /* must give up the medium lock and medium tree lock as below we
4026 * go over snapshots, which needs a lock with higher lock order. */
4027 mediumLock.release();
4028 treeLock.release();
4029
4030 /* then, search through snapshots for the best diff in the given
4031 * hard disk's chain to base the new diff on */
4032
4033 ComObjPtr<Medium> base;
4034 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot;
4035 while (snap)
4036 {
4037 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS);
4038
4039 const MediaData::AttachmentList &snapAtts = snap->i_getSnapshotMachine()->mMediaData->mAttachments;
4040
4041 MediumAttachment *pAttachFound = NULL;
4042 uint32_t foundLevel = 0;
4043
4044 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); it != snapAtts.end(); ++it)
4045 {
4046 MediumAttachment *pAttach = *it;
4047 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
4048 Assert(!pMedium.isNull() || pAttach->i_getType() != DeviceType_HardDisk);
4049 if (pMedium.isNull())
4050 continue;
4051
4052 uint32_t level = 0;
4053 if (pMedium->i_getBase(&level) == medium)
4054 {
4055 /* matched device, channel and bus (i.e. attached to the
4056 * same place) will win and immediately stop the search;
4057 * otherwise the attachment that has the youngest
4058 * descendant of medium will be used
4059 */
4060 if ( pAttach->i_getDevice() == aDevice
4061 && pAttach->i_getPort() == aControllerPort
4062 && pAttach->i_getControllerName() == aName
4063 )
4064 {
4065 pAttachFound = pAttach;
4066 break;
4067 }
4068 else if ( !pAttachFound
4069 || level > foundLevel /* prefer younger */
4070 )
4071 {
4072 pAttachFound = pAttach;
4073 foundLevel = level;
4074 }
4075 }
4076 }
4077
4078 if (pAttachFound)
4079 {
4080 base = pAttachFound->i_getMedium();
4081 break;
4082 }
4083
4084 snap = snap->i_getParent();
4085 }
4086
4087 /* re-lock medium tree and the medium, as we need it below */
4088 treeLock.acquire();
4089 mediumLock.acquire();
4090
4091 /* found a suitable diff, use it as a base */
4092 if (!base.isNull())
4093 {
4094 medium = base;
4095 mediumCaller.attach(medium);
4096 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4097 mediumLock.attach(medium);
4098 }
4099 }
4100
4101 Utf8Str strFullSnapshotFolder;
4102 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
4103
4104 ComObjPtr<Medium> diff;
4105 diff.createObject();
4106 // store this diff in the same registry as the parent
4107 Guid uuidRegistryParent;
4108 if (!medium->i_getFirstRegistryMachineId(uuidRegistryParent))
4109 {
4110 // parent image has no registry: this can happen if we're attaching a new immutable
4111 // image that has not yet been attached (medium then points to the base and we're
4112 // creating the diff image for the immutable, and the parent is not yet registered);
4113 // put the parent in the machine registry then
4114 mediumLock.release();
4115 treeLock.release();
4116 alock.release();
4117 i_addMediumToRegistry(medium);
4118 alock.acquire();
4119 treeLock.acquire();
4120 mediumLock.acquire();
4121 medium->i_getFirstRegistryMachineId(uuidRegistryParent);
4122 }
4123 rc = diff->init(mParent,
4124 medium->i_getPreferredDiffFormat(),
4125 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
4126 uuidRegistryParent,
4127 DeviceType_HardDisk);
4128 if (FAILED(rc)) return rc;
4129
4130 /* Apply the normal locking logic to the entire chain. */
4131 MediumLockList *pMediumLockList(new MediumLockList());
4132 mediumLock.release();
4133 treeLock.release();
4134 rc = diff->i_createMediumLockList(true /* fFailIfInaccessible */,
4135 true /* fMediumLockWrite */,
4136 false /* fMediumLockWriteAll */,
4137 medium,
4138 *pMediumLockList);
4139 treeLock.acquire();
4140 mediumLock.acquire();
4141 if (SUCCEEDED(rc))
4142 {
4143 mediumLock.release();
4144 treeLock.release();
4145 rc = pMediumLockList->Lock();
4146 treeLock.acquire();
4147 mediumLock.acquire();
4148 if (FAILED(rc))
4149 setError(rc,
4150 tr("Could not lock medium when creating diff '%s'"),
4151 diff->i_getLocationFull().c_str());
4152 else
4153 {
4154 /* will release the lock before the potentially lengthy
4155 * operation, so protect with the special state */
4156 MachineState_T oldState = mData->mMachineState;
4157 i_setMachineState(MachineState_SettingUp);
4158
4159 mediumLock.release();
4160 treeLock.release();
4161 alock.release();
4162
4163 rc = medium->i_createDiffStorage(diff,
4164 MediumVariant_Standard,
4165 pMediumLockList,
4166 NULL /* aProgress */,
4167 true /* aWait */);
4168
4169 alock.acquire();
4170 treeLock.acquire();
4171 mediumLock.acquire();
4172
4173 i_setMachineState(oldState);
4174 }
4175 }
4176
4177 /* Unlock the media and free the associated memory. */
4178 delete pMediumLockList;
4179
4180 if (FAILED(rc)) return rc;
4181
4182 /* use the created diff for the actual attachment */
4183 medium = diff;
4184 mediumCaller.attach(medium);
4185 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4186 mediumLock.attach(medium);
4187 }
4188 while (0);
4189
4190 ComObjPtr<MediumAttachment> attachment;
4191 attachment.createObject();
4192 rc = attachment->init(this,
4193 medium,
4194 aName,
4195 aControllerPort,
4196 aDevice,
4197 aType,
4198 fIndirect,
4199 false /* fPassthrough */,
4200 false /* fTempEject */,
4201 false /* fNonRotational */,
4202 false /* fDiscard */,
4203 fHotplug /* fHotPluggable */,
4204 Utf8Str::Empty);
4205 if (FAILED(rc)) return rc;
4206
4207 if (associate && !medium.isNull())
4208 {
4209 // as the last step, associate the medium to the VM
4210 rc = medium->i_addBackReference(mData->mUuid);
4211 // here we can fail because of Deleting, or being in process of creating a Diff
4212 if (FAILED(rc)) return rc;
4213
4214 mediumLock.release();
4215 treeLock.release();
4216 alock.release();
4217 i_addMediumToRegistry(medium);
4218 alock.acquire();
4219 treeLock.acquire();
4220 mediumLock.acquire();
4221 }
4222
4223 /* success: finally remember the attachment */
4224 i_setModified(IsModified_Storage);
4225 mMediaData.backup();
4226 mMediaData->mAttachments.push_back(attachment);
4227
4228 mediumLock.release();
4229 treeLock.release();
4230 alock.release();
4231
4232 if (fHotplug || fSilent)
4233 {
4234 if (!medium.isNull())
4235 {
4236 MediumLockList *pMediumLockList(new MediumLockList());
4237
4238 rc = medium->i_createMediumLockList(true /* fFailIfInaccessible */,
4239 true /* fMediumLockWrite */,
4240 false /* fMediumLockWriteAll */,
4241 NULL,
4242 *pMediumLockList);
4243 alock.acquire();
4244 if (FAILED(rc))
4245 delete pMediumLockList;
4246 else
4247 {
4248 mData->mSession.mLockedMedia.Unlock();
4249 alock.release();
4250 rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList);
4251 mData->mSession.mLockedMedia.Lock();
4252 alock.acquire();
4253 }
4254 alock.release();
4255 }
4256
4257 if (SUCCEEDED(rc))
4258 {
4259 rc = i_onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent);
4260 /* Remove lock list in case of error. */
4261 if (FAILED(rc))
4262 {
4263 mData->mSession.mLockedMedia.Unlock();
4264 mData->mSession.mLockedMedia.Remove(attachment);
4265 mData->mSession.mLockedMedia.Lock();
4266 }
4267 }
4268 }
4269
4270 /* Save modified registries, but skip this machine as it's the caller's
4271 * job to save its settings like all other settings changes. */
4272 mParent->i_unmarkRegistryModified(i_getId());
4273 mParent->i_saveModifiedRegistries();
4274
4275 return rc;
4276}
4277
4278HRESULT Machine::detachDevice(const com::Utf8Str &aName, LONG aControllerPort,
4279 LONG aDevice)
4280{
4281 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
4282 aName.c_str(), aControllerPort, aDevice));
4283
4284 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4285
4286 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
4287 if (FAILED(rc)) return rc;
4288
4289 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4290
4291 /* Check for an existing controller. */
4292 ComObjPtr<StorageController> ctl;
4293 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4294 if (FAILED(rc)) return rc;
4295
4296 StorageControllerType_T ctrlType;
4297 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4298 if (FAILED(rc))
4299 return setError(E_FAIL,
4300 tr("Could not get type of controller '%s'"),
4301 aName.c_str());
4302
4303 bool fSilent = false;
4304 Utf8Str strReconfig;
4305
4306 /* Check whether the flag to allow silent storage attachment reconfiguration is set. */
4307 strReconfig = i_getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused"));
4308 if ( mData->mMachineState == MachineState_Paused
4309 && strReconfig == "1")
4310 fSilent = true;
4311
4312 /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
4313 bool fHotplug = false;
4314 if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState))
4315 fHotplug = true;
4316
4317 if (fHotplug && !i_isControllerHotplugCapable(ctrlType))
4318 return setError(VBOX_E_INVALID_VM_STATE,
4319 tr("Controller '%s' does not support hotplugging"),
4320 aName.c_str());
4321
4322 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4323 Bstr(aName).raw(),
4324 aControllerPort,
4325 aDevice);
4326 if (!pAttach)
4327 return setError(VBOX_E_OBJECT_NOT_FOUND,
4328 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4329 aDevice, aControllerPort, aName.c_str());
4330
4331 if (fHotplug && !pAttach->i_getHotPluggable())
4332 return setError(VBOX_E_NOT_SUPPORTED,
4333 tr("The device slot %d on port %d of controller '%s' does not support hotplugging"),
4334 aDevice, aControllerPort, aName.c_str());
4335
4336 /*
4337 * The VM has to detach the device before we delete any implicit diffs.
4338 * If this fails we can roll back without loosing data.
4339 */
4340 if (fHotplug || fSilent)
4341 {
4342 alock.release();
4343 rc = i_onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent);
4344 alock.acquire();
4345 }
4346 if (FAILED(rc)) return rc;
4347
4348 /* If we are here everything went well and we can delete the implicit now. */
4349 rc = i_detachDevice(pAttach, alock, NULL /* pSnapshot */);
4350
4351 alock.release();
4352
4353 /* Save modified registries, but skip this machine as it's the caller's
4354 * job to save its settings like all other settings changes. */
4355 mParent->i_unmarkRegistryModified(i_getId());
4356 mParent->i_saveModifiedRegistries();
4357
4358 return rc;
4359}
4360
4361HRESULT Machine::passthroughDevice(const com::Utf8Str &aName, LONG aControllerPort,
4362 LONG aDevice, BOOL aPassthrough)
4363{
4364 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
4365 aName.c_str(), aControllerPort, aDevice, aPassthrough));
4366
4367 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4368
4369 HRESULT rc = i_checkStateDependency(MutableStateDep);
4370 if (FAILED(rc)) return rc;
4371
4372 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4373
4374 if (Global::IsOnlineOrTransient(mData->mMachineState))
4375 return setError(VBOX_E_INVALID_VM_STATE,
4376 tr("Invalid machine state: %s"),
4377 Global::stringifyMachineState(mData->mMachineState));
4378
4379 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4380 Bstr(aName).raw(),
4381 aControllerPort,
4382 aDevice);
4383 if (!pAttach)
4384 return setError(VBOX_E_OBJECT_NOT_FOUND,
4385 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4386 aDevice, aControllerPort, aName.c_str());
4387
4388
4389 i_setModified(IsModified_Storage);
4390 mMediaData.backup();
4391
4392 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4393
4394 if (pAttach->i_getType() != DeviceType_DVD)
4395 return setError(E_INVALIDARG,
4396 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4397 aDevice, aControllerPort, aName.c_str());
4398 pAttach->i_updatePassthrough(!!aPassthrough);
4399
4400 return S_OK;
4401}
4402
4403HRESULT Machine::temporaryEjectDevice(const com::Utf8Str &aName, LONG aControllerPort,
4404 LONG aDevice, BOOL aTemporaryEject)
4405{
4406
4407 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
4408 aName.c_str(), aControllerPort, aDevice, aTemporaryEject));
4409
4410 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4411
4412 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4413 if (FAILED(rc)) return rc;
4414
4415 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4416 Bstr(aName).raw(),
4417 aControllerPort,
4418 aDevice);
4419 if (!pAttach)
4420 return setError(VBOX_E_OBJECT_NOT_FOUND,
4421 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4422 aDevice, aControllerPort, aName.c_str());
4423
4424
4425 i_setModified(IsModified_Storage);
4426 mMediaData.backup();
4427
4428 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4429
4430 if (pAttach->i_getType() != DeviceType_DVD)
4431 return setError(E_INVALIDARG,
4432 tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a DVD"),
4433 aDevice, aControllerPort, aName.c_str());
4434 pAttach->i_updateTempEject(!!aTemporaryEject);
4435
4436 return S_OK;
4437}
4438
4439HRESULT Machine::nonRotationalDevice(const com::Utf8Str &aName, LONG aControllerPort,
4440 LONG aDevice, BOOL aNonRotational)
4441{
4442
4443 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
4444 aName.c_str(), aControllerPort, aDevice, aNonRotational));
4445
4446 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4447
4448 HRESULT rc = i_checkStateDependency(MutableStateDep);
4449 if (FAILED(rc)) return rc;
4450
4451 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4452
4453 if (Global::IsOnlineOrTransient(mData->mMachineState))
4454 return setError(VBOX_E_INVALID_VM_STATE,
4455 tr("Invalid machine state: %s"),
4456 Global::stringifyMachineState(mData->mMachineState));
4457
4458 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4459 Bstr(aName).raw(),
4460 aControllerPort,
4461 aDevice);
4462 if (!pAttach)
4463 return setError(VBOX_E_OBJECT_NOT_FOUND,
4464 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4465 aDevice, aControllerPort, aName.c_str());
4466
4467
4468 i_setModified(IsModified_Storage);
4469 mMediaData.backup();
4470
4471 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4472
4473 if (pAttach->i_getType() != DeviceType_HardDisk)
4474 return setError(E_INVALIDARG,
4475 tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4476 aDevice, aControllerPort, aName.c_str());
4477 pAttach->i_updateNonRotational(!!aNonRotational);
4478
4479 return S_OK;
4480}
4481
4482HRESULT Machine::setAutoDiscardForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4483 LONG aDevice, BOOL aDiscard)
4484{
4485
4486 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aDiscard=%d\n",
4487 aName.c_str(), aControllerPort, aDevice, aDiscard));
4488
4489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4490
4491 HRESULT rc = i_checkStateDependency(MutableStateDep);
4492 if (FAILED(rc)) return rc;
4493
4494 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4495
4496 if (Global::IsOnlineOrTransient(mData->mMachineState))
4497 return setError(VBOX_E_INVALID_VM_STATE,
4498 tr("Invalid machine state: %s"),
4499 Global::stringifyMachineState(mData->mMachineState));
4500
4501 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4502 Bstr(aName).raw(),
4503 aControllerPort,
4504 aDevice);
4505 if (!pAttach)
4506 return setError(VBOX_E_OBJECT_NOT_FOUND,
4507 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4508 aDevice, aControllerPort, aName.c_str());
4509
4510
4511 i_setModified(IsModified_Storage);
4512 mMediaData.backup();
4513
4514 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4515
4516 if (pAttach->i_getType() != DeviceType_HardDisk)
4517 return setError(E_INVALIDARG,
4518 tr("Setting the discard medium flag rejected as the device attached to device slot %d on port %d of controller '%s' is not a hard disk"),
4519 aDevice, aControllerPort, aName.c_str());
4520 pAttach->i_updateDiscard(!!aDiscard);
4521
4522 return S_OK;
4523}
4524
4525HRESULT Machine::setHotPluggableForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4526 LONG aDevice, BOOL aHotPluggable)
4527{
4528 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n",
4529 aName.c_str(), aControllerPort, aDevice, aHotPluggable));
4530
4531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4532
4533 HRESULT rc = i_checkStateDependency(MutableStateDep);
4534 if (FAILED(rc)) return rc;
4535
4536 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
4537
4538 if (Global::IsOnlineOrTransient(mData->mMachineState))
4539 return setError(VBOX_E_INVALID_VM_STATE,
4540 tr("Invalid machine state: %s"),
4541 Global::stringifyMachineState(mData->mMachineState));
4542
4543 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4544 Bstr(aName).raw(),
4545 aControllerPort,
4546 aDevice);
4547 if (!pAttach)
4548 return setError(VBOX_E_OBJECT_NOT_FOUND,
4549 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4550 aDevice, aControllerPort, aName.c_str());
4551
4552 /* Check for an existing controller. */
4553 ComObjPtr<StorageController> ctl;
4554 rc = i_getStorageControllerByName(aName, ctl, true /* aSetError */);
4555 if (FAILED(rc)) return rc;
4556
4557 StorageControllerType_T ctrlType;
4558 rc = ctl->COMGETTER(ControllerType)(&ctrlType);
4559 if (FAILED(rc))
4560 return setError(E_FAIL,
4561 tr("Could not get type of controller '%s'"),
4562 aName.c_str());
4563
4564 if (!i_isControllerHotplugCapable(ctrlType))
4565 return setError(VBOX_E_NOT_SUPPORTED,
4566 tr("Controller '%s' does not support changing the hot-pluggable device flag"),
4567 aName.c_str());
4568
4569 i_setModified(IsModified_Storage);
4570 mMediaData.backup();
4571
4572 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4573
4574 if (pAttach->i_getType() == DeviceType_Floppy)
4575 return setError(E_INVALIDARG,
4576 tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%s' is a floppy drive"),
4577 aDevice, aControllerPort, aName.c_str());
4578 pAttach->i_updateHotPluggable(!!aHotPluggable);
4579
4580 return S_OK;
4581}
4582
4583HRESULT Machine::setNoBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4584 LONG aDevice)
4585{
4586 int rc = S_OK;
4587 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4588 aName.c_str(), aControllerPort, aDevice));
4589
4590 rc = setBandwidthGroupForDevice(aName, aControllerPort, aDevice, NULL);
4591
4592 return rc;
4593}
4594
4595HRESULT Machine::setBandwidthGroupForDevice(const com::Utf8Str &aName, LONG aControllerPort,
4596 LONG aDevice, const ComPtr<IBandwidthGroup> &aBandwidthGroup)
4597{
4598 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4599 aName.c_str(), aControllerPort, aDevice));
4600
4601 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4602
4603 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
4604 if (FAILED(rc)) return rc;
4605
4606 if (Global::IsOnlineOrTransient(mData->mMachineState))
4607 return setError(VBOX_E_INVALID_VM_STATE,
4608 tr("Invalid machine state: %s"),
4609 Global::stringifyMachineState(mData->mMachineState));
4610
4611 MediumAttachment *pAttach = i_findAttachment(mMediaData->mAttachments,
4612 Bstr(aName).raw(),
4613 aControllerPort,
4614 aDevice);
4615 if (!pAttach)
4616 return setError(VBOX_E_OBJECT_NOT_FOUND,
4617 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4618 aDevice, aControllerPort, aName.c_str());
4619
4620
4621 i_setModified(IsModified_Storage);
4622 mMediaData.backup();
4623
4624 IBandwidthGroup *iB = aBandwidthGroup;
4625 ComObjPtr<BandwidthGroup> group = static_cast<BandwidthGroup*>(iB);
4626 if (aBandwidthGroup && group.isNull())
4627 return setError(E_INVALIDARG, "The given bandwidth group pointer is invalid");
4628
4629 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4630
4631 const Utf8Str strBandwidthGroupOld = pAttach->i_getBandwidthGroup();
4632 if (strBandwidthGroupOld.isNotEmpty())
4633 {
4634 /* Get the bandwidth group object and release it - this must not fail. */
4635 ComObjPtr<BandwidthGroup> pBandwidthGroupOld;
4636 rc = i_getBandwidthGroup(strBandwidthGroupOld, pBandwidthGroupOld, false);
4637 Assert(SUCCEEDED(rc));
4638
4639 pBandwidthGroupOld->i_release();
4640 pAttach->i_updateBandwidthGroup(Utf8Str::Empty);
4641 }
4642
4643 if (!group.isNull())
4644 {
4645 group->i_reference();
4646 pAttach->i_updateBandwidthGroup(group->i_getName());
4647 }
4648
4649 return S_OK;
4650}
4651
4652HRESULT Machine::attachDeviceWithoutMedium(const com::Utf8Str &aName,
4653 LONG aControllerPort,
4654 LONG aDevice,
4655 DeviceType_T aType)
4656{
4657 HRESULT rc = S_OK;
4658
4659 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aType=%d aMedium=%p\n",
4660 aName.c_str(), aControllerPort, aDevice, aType));
4661
4662 rc = AttachDevice(Bstr(aName).raw(), aControllerPort, aDevice, aType, NULL);
4663
4664 return rc;
4665}
4666
4667
4668HRESULT Machine::unmountMedium(const com::Utf8Str &aName,
4669 LONG aControllerPort,
4670 LONG aDevice,
4671 BOOL aForce)
4672{
4673 int rc = S_OK;
4674 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d",
4675 aName.c_str(), aControllerPort, aForce));
4676
4677 rc = mountMedium(aName, aControllerPort, aDevice, NULL, aForce);
4678
4679 return rc;
4680}
4681
4682HRESULT Machine::mountMedium(const com::Utf8Str &aName,
4683 LONG aControllerPort,
4684 LONG aDevice,
4685 const ComPtr<IMedium> &aMedium,
4686 BOOL aForce)
4687{
4688 int rc = S_OK;
4689 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d aForce=%d\n",
4690 aName.c_str(), aControllerPort, aDevice, aForce));
4691
4692 // request the host lock first, since might be calling Host methods for getting host drives;
4693 // next, protect the media tree all the while we're in here, as well as our member variables
4694 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
4695 this->lockHandle(),
4696 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
4697
4698 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4699 Bstr(aName).raw(),
4700 aControllerPort,
4701 aDevice);
4702 if (pAttach.isNull())
4703 return setError(VBOX_E_OBJECT_NOT_FOUND,
4704 tr("No drive attached to device slot %d on port %d of controller '%s'"),
4705 aDevice, aControllerPort, aName.c_str());
4706
4707 /* Remember previously mounted medium. The medium before taking the
4708 * backup is not necessarily the same thing. */
4709 ComObjPtr<Medium> oldmedium;
4710 oldmedium = pAttach->i_getMedium();
4711
4712 IMedium *iM = aMedium;
4713 ComObjPtr<Medium> pMedium = static_cast<Medium*>(iM);
4714 if (aMedium && pMedium.isNull())
4715 return setError(E_INVALIDARG, "The given medium pointer is invalid");
4716
4717 AutoCaller mediumCaller(pMedium);
4718 if (FAILED(mediumCaller.rc())) return mediumCaller.rc();
4719
4720 AutoWriteLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS);
4721 if (pMedium)
4722 {
4723 DeviceType_T mediumType = pAttach->i_getType();
4724 switch (mediumType)
4725 {
4726 case DeviceType_DVD:
4727 case DeviceType_Floppy:
4728 break;
4729
4730 default:
4731 return setError(VBOX_E_INVALID_OBJECT_STATE,
4732 tr("The device at port %d, device %d of controller '%s' of this virtual machine is not removeable"),
4733 aControllerPort,
4734 aDevice,
4735 aName.c_str());
4736 }
4737 }
4738
4739 i_setModified(IsModified_Storage);
4740 mMediaData.backup();
4741
4742 {
4743 // The backup operation makes the pAttach reference point to the
4744 // old settings. Re-get the correct reference.
4745 pAttach = i_findAttachment(mMediaData->mAttachments,
4746 Bstr(aName).raw(),
4747 aControllerPort,
4748 aDevice);
4749 if (!oldmedium.isNull())
4750 oldmedium->i_removeBackReference(mData->mUuid);
4751 if (!pMedium.isNull())
4752 {
4753 pMedium->i_addBackReference(mData->mUuid);
4754
4755 mediumLock.release();
4756 multiLock.release();
4757 i_addMediumToRegistry(pMedium);
4758 multiLock.acquire();
4759 mediumLock.acquire();
4760 }
4761
4762 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4763 pAttach->i_updateMedium(pMedium);
4764 }
4765
4766 i_setModified(IsModified_Storage);
4767
4768 mediumLock.release();
4769 multiLock.release();
4770 rc = i_onMediumChange(pAttach, aForce);
4771 multiLock.acquire();
4772 mediumLock.acquire();
4773
4774 /* On error roll back this change only. */
4775 if (FAILED(rc))
4776 {
4777 if (!pMedium.isNull())
4778 pMedium->i_removeBackReference(mData->mUuid);
4779 pAttach = i_findAttachment(mMediaData->mAttachments,
4780 Bstr(aName).raw(),
4781 aControllerPort,
4782 aDevice);
4783 /* If the attachment is gone in the meantime, bail out. */
4784 if (pAttach.isNull())
4785 return rc;
4786 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
4787 if (!oldmedium.isNull())
4788 oldmedium->i_addBackReference(mData->mUuid);
4789 pAttach->i_updateMedium(oldmedium);
4790 }
4791
4792 mediumLock.release();
4793 multiLock.release();
4794
4795 /* Save modified registries, but skip this machine as it's the caller's
4796 * job to save its settings like all other settings changes. */
4797 mParent->i_unmarkRegistryModified(i_getId());
4798 mParent->i_saveModifiedRegistries();
4799
4800 return rc;
4801}
4802HRESULT Machine::getMedium(const com::Utf8Str &aName,
4803 LONG aControllerPort,
4804 LONG aDevice,
4805 ComPtr<IMedium> &aMedium)
4806{
4807 LogFlowThisFunc(("aName=\"%s\" aControllerPort=%d aDevice=%d\n",
4808 aName.c_str(), aControllerPort, aDevice));
4809
4810 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4811
4812 aMedium = NULL;
4813
4814 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
4815 Bstr(aName).raw(),
4816 aControllerPort,
4817 aDevice);
4818 if (pAttach.isNull())
4819 return setError(VBOX_E_OBJECT_NOT_FOUND,
4820 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
4821 aDevice, aControllerPort, aName.c_str());
4822
4823 aMedium = pAttach->i_getMedium();
4824
4825 return S_OK;
4826}
4827
4828HRESULT Machine::getSerialPort(ULONG aSlot, ComPtr<ISerialPort> &aPort)
4829{
4830
4831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4832
4833 mSerialPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4834
4835 return S_OK;
4836}
4837
4838HRESULT Machine::getParallelPort(ULONG aSlot, ComPtr<IParallelPort> &aPort)
4839{
4840 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4841
4842 mParallelPorts[aSlot].queryInterfaceTo(aPort.asOutParam());
4843
4844 return S_OK;
4845}
4846
4847HRESULT Machine::getNetworkAdapter(ULONG aSlot, ComPtr<INetworkAdapter> &aAdapter)
4848{
4849 /* Do not assert if slot is out of range, just return the advertised
4850 status. testdriver/vbox.py triggers this in logVmInfo. */
4851 if (aSlot >= mNetworkAdapters.size())
4852 return setError(E_INVALIDARG,
4853 tr("No network adapter in slot %RU32 (total %RU32 adapters)"),
4854 aSlot, mNetworkAdapters.size());
4855
4856 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4857
4858 mNetworkAdapters[aSlot].queryInterfaceTo(aAdapter.asOutParam());
4859
4860 return S_OK;
4861}
4862
4863HRESULT Machine::getExtraDataKeys(std::vector<com::Utf8Str> &aKeys)
4864{
4865 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4866
4867 aKeys.resize(mData->pMachineConfigFile->mapExtraDataItems.size());
4868 size_t i = 0;
4869 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin();
4870 it != mData->pMachineConfigFile->mapExtraDataItems.end();
4871 ++it, ++i)
4872 aKeys[i] = it->first;
4873
4874 return S_OK;
4875}
4876
4877 /**
4878 * @note Locks this object for reading.
4879 */
4880HRESULT Machine::getExtraData(const com::Utf8Str &aKey,
4881 com::Utf8Str &aValue)
4882{
4883 /* start with nothing found */
4884 aValue = "";
4885
4886 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
4887
4888 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4889 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4890 // found:
4891 aValue = it->second; // source is a Utf8Str
4892
4893 /* return the result to caller (may be empty) */
4894 return S_OK;
4895}
4896
4897 /**
4898 * @note Locks mParent for writing + this object for writing.
4899 */
4900HRESULT Machine::setExtraData(const com::Utf8Str &aKey, const com::Utf8Str &aValue)
4901{
4902 Utf8Str strOldValue; // empty
4903
4904 // locking note: we only hold the read lock briefly to look up the old value,
4905 // then release it and call the onExtraCanChange callbacks. There is a small
4906 // chance of a race insofar as the callback might be called twice if two callers
4907 // change the same key at the same time, but that's a much better solution
4908 // than the deadlock we had here before. The actual changing of the extradata
4909 // is then performed under the write lock and race-free.
4910
4911 // look up the old value first; if nothing has changed then we need not do anything
4912 {
4913 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up
4914
4915 // For snapshots don't even think about allowing changes, extradata
4916 // is global for a machine, so there is nothing snapshot specific.
4917 if (i_isSnapshotMachine())
4918 return setError(VBOX_E_INVALID_VM_STATE,
4919 tr("Cannot set extradata for a snapshot"));
4920
4921 // check if the right IMachine instance is used
4922 if (mData->mRegistered && !i_isSessionMachine())
4923 return setError(VBOX_E_INVALID_VM_STATE,
4924 tr("Cannot set extradata for an immutable machine"));
4925
4926 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(aKey);
4927 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
4928 strOldValue = it->second;
4929 }
4930
4931 bool fChanged;
4932 if ((fChanged = (strOldValue != aValue)))
4933 {
4934 // ask for permission from all listeners outside the locks;
4935 // i_onExtraDataCanChange() only briefly requests the VirtualBox
4936 // lock to copy the list of callbacks to invoke
4937 Bstr error;
4938 Bstr bstrValue(aValue);
4939
4940 if (!mParent->i_onExtraDataCanChange(mData->mUuid, Bstr(aKey).raw(), bstrValue.raw(), error))
4941 {
4942 const char *sep = error.isEmpty() ? "" : ": ";
4943 CBSTR err = error.raw();
4944 Log1WarningFunc(("Someone vetoed! Change refused%s%ls\n", sep, err));
4945 return setError(E_ACCESSDENIED,
4946 tr("Could not set extra data because someone refused the requested change of '%s' to '%s'%s%ls"),
4947 aKey.c_str(),
4948 aValue.c_str(),
4949 sep,
4950 err);
4951 }
4952
4953 // data is changing and change not vetoed: then write it out under the lock
4954 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
4955
4956 if (aValue.isEmpty())
4957 mData->pMachineConfigFile->mapExtraDataItems.erase(aKey);
4958 else
4959 mData->pMachineConfigFile->mapExtraDataItems[aKey] = aValue;
4960 // creates a new key if needed
4961
4962 bool fNeedsGlobalSaveSettings = false;
4963 // This saving of settings is tricky: there is no "old state" for the
4964 // extradata items at all (unlike all other settings), so the old/new
4965 // settings comparison would give a wrong result!
4966 i_saveSettings(&fNeedsGlobalSaveSettings, SaveS_Force);
4967
4968 if (fNeedsGlobalSaveSettings)
4969 {
4970 // save the global settings; for that we should hold only the VirtualBox lock
4971 alock.release();
4972 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS);
4973 mParent->i_saveSettings();
4974 }
4975 }
4976
4977 // fire notification outside the lock
4978 if (fChanged)
4979 mParent->i_onExtraDataChange(mData->mUuid, Bstr(aKey).raw(), Bstr(aValue).raw());
4980
4981 return S_OK;
4982}
4983
4984HRESULT Machine::setSettingsFilePath(const com::Utf8Str &aSettingsFilePath, ComPtr<IProgress> &aProgress)
4985{
4986 aProgress = NULL;
4987 NOREF(aSettingsFilePath);
4988 ReturnComNotImplemented();
4989}
4990
4991HRESULT Machine::saveSettings()
4992{
4993 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS);
4994
4995 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
4996 if (FAILED(rc)) return rc;
4997
4998 /* the settings file path may never be null */
4999 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL);
5000
5001 /* save all VM data excluding snapshots */
5002 bool fNeedsGlobalSaveSettings = false;
5003 rc = i_saveSettings(&fNeedsGlobalSaveSettings);
5004 mlock.release();
5005
5006 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings)
5007 {
5008 // save the global settings; for that we should hold only the VirtualBox lock
5009 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS);
5010 rc = mParent->i_saveSettings();
5011 }
5012
5013 return rc;
5014}
5015
5016
5017HRESULT Machine::discardSettings()
5018{
5019 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5020
5021 HRESULT rc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
5022 if (FAILED(rc)) return rc;
5023
5024 /*
5025 * during this rollback, the session will be notified if data has
5026 * been actually changed
5027 */
5028 i_rollback(true /* aNotify */);
5029
5030 return S_OK;
5031}
5032
5033/** @note Locks objects! */
5034HRESULT Machine::unregister(AutoCaller &autoCaller,
5035 CleanupMode_T aCleanupMode,
5036 std::vector<ComPtr<IMedium> > &aMedia)
5037{
5038 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5039
5040 Guid id(i_getId());
5041
5042 if (mData->mSession.mState != SessionState_Unlocked)
5043 return setError(VBOX_E_INVALID_OBJECT_STATE,
5044 tr("Cannot unregister the machine '%s' while it is locked"),
5045 mUserData->s.strName.c_str());
5046
5047 // wait for state dependents to drop to zero
5048 i_ensureNoStateDependencies();
5049
5050 if (!mData->mAccessible)
5051 {
5052 // inaccessible maschines can only be unregistered; uninitialize ourselves
5053 // here because currently there may be no unregistered that are inaccessible
5054 // (this state combination is not supported). Note releasing the caller and
5055 // leaving the lock before calling uninit()
5056 alock.release();
5057 autoCaller.release();
5058
5059 uninit();
5060
5061 mParent->i_unregisterMachine(this, id);
5062 // calls VirtualBox::i_saveSettings()
5063
5064 return S_OK;
5065 }
5066
5067 HRESULT rc = S_OK;
5068
5069 /// @todo r=klaus this is stupid... why is the saved state always deleted?
5070 // discard saved state
5071 if (mData->mMachineState == MachineState_Saved)
5072 {
5073 // add the saved state file to the list of files the caller should delete
5074 Assert(!mSSData->strStateFilePath.isEmpty());
5075 mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
5076
5077 mSSData->strStateFilePath.setNull();
5078
5079 // unconditionally set the machine state to powered off, we now
5080 // know no session has locked the machine
5081 mData->mMachineState = MachineState_PoweredOff;
5082 }
5083
5084 size_t cSnapshots = 0;
5085 if (mData->mFirstSnapshot)
5086 cSnapshots = mData->mFirstSnapshot->i_getAllChildrenCount() + 1;
5087 if (cSnapshots && aCleanupMode == CleanupMode_UnregisterOnly)
5088 // fail now before we start detaching media
5089 return setError(VBOX_E_INVALID_OBJECT_STATE,
5090 tr("Cannot unregister the machine '%s' because it has %d snapshots"),
5091 mUserData->s.strName.c_str(), cSnapshots);
5092
5093 // This list collects the medium objects from all medium attachments
5094 // which we will detach from the machine and its snapshots, in a specific
5095 // order which allows for closing all media without getting "media in use"
5096 // errors, simply by going through the list from the front to the back:
5097 // 1) first media from machine attachments (these have the "leaf" attachments with snapshots
5098 // and must be closed before the parent media from the snapshots, or closing the parents
5099 // will fail because they still have children);
5100 // 2) media from the youngest snapshots followed by those from the parent snapshots until
5101 // the root ("first") snapshot of the machine.
5102 MediaList llMedia;
5103
5104 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible
5105 && mMediaData->mAttachments.size()
5106 )
5107 {
5108 // we have media attachments: detach them all and add the Medium objects to our list
5109 if (aCleanupMode != CleanupMode_UnregisterOnly)
5110 i_detachAllMedia(alock, NULL /* pSnapshot */, aCleanupMode, llMedia);
5111 else
5112 return setError(VBOX_E_INVALID_OBJECT_STATE,
5113 tr("Cannot unregister the machine '%s' because it has %d media attachments"),
5114 mUserData->s.strName.c_str(), mMediaData->mAttachments.size());
5115 }
5116
5117 if (cSnapshots)
5118 {
5119 // add the media from the medium attachments of the snapshots to llMedia
5120 // as well, after the "main" machine media; Snapshot::uninitRecursively()
5121 // calls Machine::detachAllMedia() for the snapshot machine, recursing
5122 // into the children first
5123
5124 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this
5125 MachineState_T oldState = mData->mMachineState;
5126 mData->mMachineState = MachineState_DeletingSnapshot;
5127
5128 // make a copy of the first snapshot so the refcount does not drop to 0
5129 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs
5130 // because of the AutoCaller voodoo)
5131 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot;
5132
5133 // GO!
5134 pFirstSnapshot->i_uninitRecursively(alock, aCleanupMode, llMedia, mData->llFilesToDelete);
5135
5136 mData->mMachineState = oldState;
5137 }
5138
5139 if (FAILED(rc))
5140 {
5141 i_rollbackMedia();
5142 return rc;
5143 }
5144
5145 // commit all the media changes made above
5146 i_commitMedia();
5147
5148 mData->mRegistered = false;
5149
5150 // machine lock no longer needed
5151 alock.release();
5152
5153 // return media to caller
5154 size_t i = 0;
5155 aMedia.resize(llMedia.size());
5156 for (MediaList::iterator it = llMedia.begin(); it != llMedia.end(); ++it, ++i)
5157 (*it).queryInterfaceTo(aMedia[i].asOutParam());
5158
5159 mParent->i_unregisterMachine(this, id);
5160 // calls VirtualBox::i_saveSettings() and VirtualBox::saveModifiedRegistries()
5161
5162 return S_OK;
5163}
5164
5165/**
5166 * Task record for deleting a machine config.
5167 */
5168struct Machine::DeleteConfigTask
5169 : public Machine::Task
5170{
5171 DeleteConfigTask(Machine *m,
5172 Progress *p,
5173 const Utf8Str &t,
5174 const RTCList<ComPtr<IMedium> > &llMediums,
5175 const StringsList &llFilesToDelete)
5176 : Task(m, p, t),
5177 m_llMediums(llMediums),
5178 m_llFilesToDelete(llFilesToDelete)
5179 {}
5180
5181 void handler()
5182 {
5183 m_pMachine->i_deleteConfigHandler(*this);
5184 }
5185
5186 RTCList<ComPtr<IMedium> > m_llMediums;
5187 StringsList m_llFilesToDelete;
5188};
5189
5190/**
5191 * Task thread implementation for SessionMachine::DeleteConfig(), called from
5192 * SessionMachine::taskHandler().
5193 *
5194 * @note Locks this object for writing.
5195 *
5196 * @param task
5197 * @return
5198 */
5199void Machine::i_deleteConfigHandler(DeleteConfigTask &task)
5200{
5201 LogFlowThisFuncEnter();
5202
5203 AutoCaller autoCaller(this);
5204 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
5205 if (FAILED(autoCaller.rc()))
5206 {
5207 /* we might have been uninitialized because the session was accidentally
5208 * closed by the client, so don't assert */
5209 HRESULT rc = setError(E_FAIL,
5210 tr("The session has been accidentally closed"));
5211 task.m_pProgress->i_notifyComplete(rc);
5212 LogFlowThisFuncLeave();
5213 return;
5214 }
5215
5216 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5217
5218 HRESULT rc = S_OK;
5219
5220 try
5221 {
5222 ULONG uLogHistoryCount = 3;
5223 ComPtr<ISystemProperties> systemProperties;
5224 rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
5225 if (FAILED(rc)) throw rc;
5226
5227 if (!systemProperties.isNull())
5228 {
5229 rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
5230 if (FAILED(rc)) throw rc;
5231 }
5232
5233 MachineState_T oldState = mData->mMachineState;
5234 i_setMachineState(MachineState_SettingUp);
5235 alock.release();
5236 for (size_t i = 0; i < task.m_llMediums.size(); ++i)
5237 {
5238 ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)(task.m_llMediums.at(i));
5239 {
5240 AutoCaller mac(pMedium);
5241 if (FAILED(mac.rc())) throw mac.rc();
5242 Utf8Str strLocation = pMedium->i_getLocationFull();
5243 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
5244 if (FAILED(rc)) throw rc;
5245 LogFunc(("Deleting file %s\n", strLocation.c_str()));
5246 }
5247 if (pMedium->i_isMediumFormatFile())
5248 {
5249 ComPtr<IProgress> pProgress2;
5250 rc = pMedium->DeleteStorage(pProgress2.asOutParam());
5251 if (FAILED(rc)) throw rc;
5252 rc = task.m_pProgress->WaitForAsyncProgressCompletion(pProgress2);
5253 if (FAILED(rc)) throw rc;
5254 /* Check the result of the asynchronous process. */
5255 LONG iRc;
5256 rc = pProgress2->COMGETTER(ResultCode)(&iRc);
5257 if (FAILED(rc)) throw rc;
5258 /* If the thread of the progress object has an error, then
5259 * retrieve the error info from there, or it'll be lost. */
5260 if (FAILED(iRc))
5261 throw setError(ProgressErrorInfo(pProgress2));
5262 }
5263
5264 /* Close the medium, deliberately without checking the return
5265 * code, and without leaving any trace in the error info, as
5266 * a failure here is a very minor issue, which shouldn't happen
5267 * as above we even managed to delete the medium. */
5268 {
5269 ErrorInfoKeeper eik;
5270 pMedium->Close();
5271 }
5272 }
5273 i_setMachineState(oldState);
5274 alock.acquire();
5275
5276 // delete the files pushed on the task list by Machine::Delete()
5277 // (this includes saved states of the machine and snapshots and
5278 // medium storage files from the IMedium list passed in, and the
5279 // machine XML file)
5280 StringsList::const_iterator it = task.m_llFilesToDelete.begin();
5281 while (it != task.m_llFilesToDelete.end())
5282 {
5283 const Utf8Str &strFile = *it;
5284 LogFunc(("Deleting file %s\n", strFile.c_str()));
5285 int vrc = RTFileDelete(strFile.c_str());
5286 if (RT_FAILURE(vrc))
5287 throw setError(VBOX_E_IPRT_ERROR,
5288 tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
5289
5290 ++it;
5291 if (it == task.m_llFilesToDelete.end())
5292 {
5293 rc = task.m_pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
5294 if (FAILED(rc)) throw rc;
5295 break;
5296 }
5297
5298 rc = task.m_pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
5299 if (FAILED(rc)) throw rc;
5300 }
5301
5302 /* delete the settings only when the file actually exists */
5303 if (mData->pMachineConfigFile->fileExists())
5304 {
5305 /* Delete any backup or uncommitted XML files. Ignore failures.
5306 See the fSafe parameter of xml::XmlFileWriter::write for details. */
5307 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
5308 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
5309 RTFileDelete(otherXml.c_str());
5310 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
5311 RTFileDelete(otherXml.c_str());
5312
5313 /* delete the Logs folder, nothing important should be left
5314 * there (we don't check for errors because the user might have
5315 * some private files there that we don't want to delete) */
5316 Utf8Str logFolder;
5317 getLogFolder(logFolder);
5318 Assert(logFolder.length());
5319 if (RTDirExists(logFolder.c_str()))
5320 {
5321 /* Delete all VBox.log[.N] files from the Logs folder
5322 * (this must be in sync with the rotation logic in
5323 * Console::powerUpThread()). Also, delete the VBox.png[.N]
5324 * files that may have been created by the GUI. */
5325 Utf8Str log = Utf8StrFmt("%s%cVBox.log",
5326 logFolder.c_str(), RTPATH_DELIMITER);
5327 RTFileDelete(log.c_str());
5328 log = Utf8StrFmt("%s%cVBox.png",
5329 logFolder.c_str(), RTPATH_DELIMITER);
5330 RTFileDelete(log.c_str());
5331 for (int i = uLogHistoryCount; i > 0; i--)
5332 {
5333 log = Utf8StrFmt("%s%cVBox.log.%d",
5334 logFolder.c_str(), RTPATH_DELIMITER, i);
5335 RTFileDelete(log.c_str());
5336 log = Utf8StrFmt("%s%cVBox.png.%d",
5337 logFolder.c_str(), RTPATH_DELIMITER, i);
5338 RTFileDelete(log.c_str());
5339 }
5340#if defined(RT_OS_WINDOWS)
5341 log = Utf8StrFmt("%s%cVBoxStartup.log",
5342 logFolder.c_str(), RTPATH_DELIMITER);
5343 RTFileDelete(log.c_str());
5344#endif
5345
5346 RTDirRemove(logFolder.c_str());
5347 }
5348
5349 /* delete the Snapshots folder, nothing important should be left
5350 * there (we don't check for errors because the user might have
5351 * some private files there that we don't want to delete) */
5352 Utf8Str strFullSnapshotFolder;
5353 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5354 Assert(!strFullSnapshotFolder.isEmpty());
5355 if (RTDirExists(strFullSnapshotFolder.c_str()))
5356 RTDirRemove(strFullSnapshotFolder.c_str());
5357
5358 // delete the directory that contains the settings file, but only
5359 // if it matches the VM name
5360 Utf8Str settingsDir;
5361 if (i_isInOwnDir(&settingsDir))
5362 RTDirRemove(settingsDir.c_str());
5363 }
5364
5365 alock.release();
5366
5367 mParent->i_saveModifiedRegistries();
5368 }
5369 catch (HRESULT aRC) { rc = aRC; }
5370
5371 task.m_pProgress->i_notifyComplete(rc);
5372
5373 LogFlowThisFuncLeave();
5374}
5375
5376HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5377{
5378 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5379
5380 HRESULT rc = i_checkStateDependency(MutableStateDep);
5381 if (FAILED(rc)) return rc;
5382
5383 if (mData->mRegistered)
5384 return setError(VBOX_E_INVALID_VM_STATE,
5385 tr("Cannot delete settings of a registered machine"));
5386
5387 // collect files to delete
5388 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5389 if (mData->pMachineConfigFile->fileExists())
5390 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5391
5392 RTCList<ComPtr<IMedium> > llMediums;
5393 for (size_t i = 0; i < aMedia.size(); ++i)
5394 {
5395 IMedium *pIMedium(aMedia[i]);
5396 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5397 if (pMedium.isNull())
5398 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5399 SafeArray<BSTR> ids;
5400 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5401 if (FAILED(rc)) return rc;
5402 /* At this point the medium should not have any back references
5403 * anymore. If it has it is attached to another VM and *must* not
5404 * deleted. */
5405 if (ids.size() < 1)
5406 llMediums.append(pMedium);
5407 }
5408
5409 ComObjPtr<Progress> pProgress;
5410 pProgress.createObject();
5411 rc = pProgress->init(i_getVirtualBox(),
5412 static_cast<IMachine*>(this) /* aInitiator */,
5413 Bstr(tr("Deleting files")).raw(),
5414 true /* fCancellable */,
5415 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5416 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5417 if (FAILED(rc))
5418 return rc;
5419
5420 /* create and start the task on a separate thread (note that it will not
5421 * start working until we release alock) */
5422 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5423 rc = pTask->createThread();
5424 if (FAILED(rc))
5425 return rc;
5426
5427 pProgress.queryInterfaceTo(aProgress.asOutParam());
5428
5429 LogFlowFuncLeave();
5430
5431 return S_OK;
5432}
5433
5434HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5435{
5436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5437
5438 ComObjPtr<Snapshot> pSnapshot;
5439 HRESULT rc;
5440
5441 if (aNameOrId.isEmpty())
5442 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5443 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5444 else
5445 {
5446 Guid uuid(aNameOrId);
5447 if (uuid.isValid())
5448 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5449 else
5450 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5451 }
5452 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5453
5454 return rc;
5455}
5456
5457HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5458{
5459 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5460
5461 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5462 if (FAILED(rc)) return rc;
5463
5464 ComObjPtr<SharedFolder> sharedFolder;
5465 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5466 if (SUCCEEDED(rc))
5467 return setError(VBOX_E_OBJECT_IN_USE,
5468 tr("Shared folder named '%s' already exists"),
5469 aName.c_str());
5470
5471 sharedFolder.createObject();
5472 rc = sharedFolder->init(i_getMachine(),
5473 aName,
5474 aHostPath,
5475 !!aWritable,
5476 !!aAutomount,
5477 true /* fFailOnError */);
5478 if (FAILED(rc)) return rc;
5479
5480 i_setModified(IsModified_SharedFolders);
5481 mHWData.backup();
5482 mHWData->mSharedFolders.push_back(sharedFolder);
5483
5484 /* inform the direct session if any */
5485 alock.release();
5486 i_onSharedFolderChange();
5487
5488 return S_OK;
5489}
5490
5491HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5492{
5493 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5494
5495 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5496 if (FAILED(rc)) return rc;
5497
5498 ComObjPtr<SharedFolder> sharedFolder;
5499 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5500 if (FAILED(rc)) return rc;
5501
5502 i_setModified(IsModified_SharedFolders);
5503 mHWData.backup();
5504 mHWData->mSharedFolders.remove(sharedFolder);
5505
5506 /* inform the direct session if any */
5507 alock.release();
5508 i_onSharedFolderChange();
5509
5510 return S_OK;
5511}
5512
5513HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5514{
5515 /* start with No */
5516 *aCanShow = FALSE;
5517
5518 ComPtr<IInternalSessionControl> directControl;
5519 {
5520 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5521
5522 if (mData->mSession.mState != SessionState_Locked)
5523 return setError(VBOX_E_INVALID_VM_STATE,
5524 tr("Machine is not locked for session (session state: %s)"),
5525 Global::stringifySessionState(mData->mSession.mState));
5526
5527 if (mData->mSession.mLockType == LockType_VM)
5528 directControl = mData->mSession.mDirectControl;
5529 }
5530
5531 /* ignore calls made after #OnSessionEnd() is called */
5532 if (!directControl)
5533 return S_OK;
5534
5535 LONG64 dummy;
5536 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5537}
5538
5539HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5540{
5541 ComPtr<IInternalSessionControl> directControl;
5542 {
5543 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5544
5545 if (mData->mSession.mState != SessionState_Locked)
5546 return setError(E_FAIL,
5547 tr("Machine is not locked for session (session state: %s)"),
5548 Global::stringifySessionState(mData->mSession.mState));
5549
5550 if (mData->mSession.mLockType == LockType_VM)
5551 directControl = mData->mSession.mDirectControl;
5552 }
5553
5554 /* ignore calls made after #OnSessionEnd() is called */
5555 if (!directControl)
5556 return S_OK;
5557
5558 BOOL dummy;
5559 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5560}
5561
5562#ifdef VBOX_WITH_GUEST_PROPS
5563/**
5564 * Look up a guest property in VBoxSVC's internal structures.
5565 */
5566HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5567 com::Utf8Str &aValue,
5568 LONG64 *aTimestamp,
5569 com::Utf8Str &aFlags) const
5570{
5571 using namespace guestProp;
5572
5573 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5574 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5575
5576 if (it != mHWData->mGuestProperties.end())
5577 {
5578 char szFlags[MAX_FLAGS_LEN + 1];
5579 aValue = it->second.strValue;
5580 *aTimestamp = it->second.mTimestamp;
5581 writeFlags(it->second.mFlags, szFlags);
5582 aFlags = Utf8Str(szFlags);
5583 }
5584
5585 return S_OK;
5586}
5587
5588/**
5589 * Query the VM that a guest property belongs to for the property.
5590 * @returns E_ACCESSDENIED if the VM process is not available or not
5591 * currently handling queries and the lookup should then be done in
5592 * VBoxSVC.
5593 */
5594HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5595 com::Utf8Str &aValue,
5596 LONG64 *aTimestamp,
5597 com::Utf8Str &aFlags) const
5598{
5599 HRESULT rc = S_OK;
5600 BSTR bValue = NULL;
5601 BSTR bFlags = NULL;
5602
5603 ComPtr<IInternalSessionControl> directControl;
5604 {
5605 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5606 if (mData->mSession.mLockType == LockType_VM)
5607 directControl = mData->mSession.mDirectControl;
5608 }
5609
5610 /* ignore calls made after #OnSessionEnd() is called */
5611 if (!directControl)
5612 rc = E_ACCESSDENIED;
5613 else
5614 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5615 0 /* accessMode */,
5616 &bValue, aTimestamp, &bFlags);
5617
5618 aValue = bValue;
5619 aFlags = bFlags;
5620
5621 return rc;
5622}
5623#endif // VBOX_WITH_GUEST_PROPS
5624
5625HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5626 com::Utf8Str &aValue,
5627 LONG64 *aTimestamp,
5628 com::Utf8Str &aFlags)
5629{
5630#ifndef VBOX_WITH_GUEST_PROPS
5631 ReturnComNotImplemented();
5632#else // VBOX_WITH_GUEST_PROPS
5633
5634 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5635
5636 if (rc == E_ACCESSDENIED)
5637 /* The VM is not running or the service is not (yet) accessible */
5638 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5639 return rc;
5640#endif // VBOX_WITH_GUEST_PROPS
5641}
5642
5643HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5644{
5645 LONG64 dummyTimestamp;
5646 com::Utf8Str dummyFlags;
5647 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5648 return rc;
5649
5650}
5651HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5652{
5653 com::Utf8Str dummyFlags;
5654 com::Utf8Str dummyValue;
5655 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5656 return rc;
5657}
5658
5659#ifdef VBOX_WITH_GUEST_PROPS
5660/**
5661 * Set a guest property in VBoxSVC's internal structures.
5662 */
5663HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5664 const com::Utf8Str &aFlags, bool fDelete)
5665{
5666 using namespace guestProp;
5667
5668 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5669 HRESULT rc = S_OK;
5670
5671 rc = i_checkStateDependency(MutableOrSavedStateDep);
5672 if (FAILED(rc)) return rc;
5673
5674 try
5675 {
5676 uint32_t fFlags = NILFLAG;
5677 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5678 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5679
5680 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5681 if (it == mHWData->mGuestProperties.end())
5682 {
5683 if (!fDelete)
5684 {
5685 i_setModified(IsModified_MachineData);
5686 mHWData.backupEx();
5687
5688 RTTIMESPEC time;
5689 HWData::GuestProperty prop;
5690 prop.strValue = Bstr(aValue).raw();
5691 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5692 prop.mFlags = fFlags;
5693 mHWData->mGuestProperties[aName] = prop;
5694 }
5695 }
5696 else
5697 {
5698 if (it->second.mFlags & (RDONLYHOST))
5699 {
5700 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5701 }
5702 else
5703 {
5704 i_setModified(IsModified_MachineData);
5705 mHWData.backupEx();
5706
5707 /* The backupEx() operation invalidates our iterator,
5708 * so get a new one. */
5709 it = mHWData->mGuestProperties.find(aName);
5710 Assert(it != mHWData->mGuestProperties.end());
5711
5712 if (!fDelete)
5713 {
5714 RTTIMESPEC time;
5715 it->second.strValue = aValue;
5716 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5717 it->second.mFlags = fFlags;
5718 }
5719 else
5720 mHWData->mGuestProperties.erase(it);
5721 }
5722 }
5723
5724 if (SUCCEEDED(rc))
5725 {
5726 alock.release();
5727
5728 mParent->i_onGuestPropertyChange(mData->mUuid,
5729 Bstr(aName).raw(),
5730 Bstr(aValue).raw(),
5731 Bstr(aFlags).raw());
5732 }
5733 }
5734 catch (std::bad_alloc &)
5735 {
5736 rc = E_OUTOFMEMORY;
5737 }
5738
5739 return rc;
5740}
5741
5742/**
5743 * Set a property on the VM that that property belongs to.
5744 * @returns E_ACCESSDENIED if the VM process is not available or not
5745 * currently handling queries and the setting should then be done in
5746 * VBoxSVC.
5747 */
5748HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5749 const com::Utf8Str &aFlags, bool fDelete)
5750{
5751 HRESULT rc;
5752
5753 try
5754 {
5755 ComPtr<IInternalSessionControl> directControl;
5756 {
5757 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5758 if (mData->mSession.mLockType == LockType_VM)
5759 directControl = mData->mSession.mDirectControl;
5760 }
5761
5762 BSTR dummy = NULL; /* will not be changed (setter) */
5763 LONG64 dummy64;
5764 if (!directControl)
5765 rc = E_ACCESSDENIED;
5766 else
5767 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5768 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5769 fDelete? 2: 1 /* accessMode */,
5770 &dummy, &dummy64, &dummy);
5771 }
5772 catch (std::bad_alloc &)
5773 {
5774 rc = E_OUTOFMEMORY;
5775 }
5776
5777 return rc;
5778}
5779#endif // VBOX_WITH_GUEST_PROPS
5780
5781HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5782 const com::Utf8Str &aFlags)
5783{
5784#ifndef VBOX_WITH_GUEST_PROPS
5785 ReturnComNotImplemented();
5786#else // VBOX_WITH_GUEST_PROPS
5787 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5788 if (rc == E_ACCESSDENIED)
5789 /* The VM is not running or the service is not (yet) accessible */
5790 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5791 return rc;
5792#endif // VBOX_WITH_GUEST_PROPS
5793}
5794
5795HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5796{
5797 return setGuestProperty(aProperty, aValue, "");
5798}
5799
5800HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5801{
5802#ifndef VBOX_WITH_GUEST_PROPS
5803 ReturnComNotImplemented();
5804#else // VBOX_WITH_GUEST_PROPS
5805 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5806 if (rc == E_ACCESSDENIED)
5807 /* The VM is not running or the service is not (yet) accessible */
5808 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5809 return rc;
5810#endif // VBOX_WITH_GUEST_PROPS
5811}
5812
5813#ifdef VBOX_WITH_GUEST_PROPS
5814/**
5815 * Enumerate the guest properties in VBoxSVC's internal structures.
5816 */
5817HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5818 std::vector<com::Utf8Str> &aNames,
5819 std::vector<com::Utf8Str> &aValues,
5820 std::vector<LONG64> &aTimestamps,
5821 std::vector<com::Utf8Str> &aFlags)
5822{
5823 using namespace guestProp;
5824
5825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5826 Utf8Str strPatterns(aPatterns);
5827
5828 HWData::GuestPropertyMap propMap;
5829
5830 /*
5831 * Look for matching patterns and build up a list.
5832 */
5833 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5834 while (it != mHWData->mGuestProperties.end())
5835 {
5836 if ( strPatterns.isEmpty()
5837 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5838 RTSTR_MAX,
5839 it->first.c_str(),
5840 RTSTR_MAX,
5841 NULL)
5842 )
5843 propMap.insert(*it);
5844 ++it;
5845 }
5846
5847 alock.release();
5848
5849 /*
5850 * And build up the arrays for returning the property information.
5851 */
5852 size_t cEntries = propMap.size();
5853
5854 aNames.resize(cEntries);
5855 aValues.resize(cEntries);
5856 aTimestamps.resize(cEntries);
5857 aFlags.resize(cEntries);
5858
5859 char szFlags[MAX_FLAGS_LEN + 1];
5860 size_t i= 0;
5861 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5862 {
5863 aNames[i] = it->first;
5864 aValues[i] = it->second.strValue;
5865 aTimestamps[i] = it->second.mTimestamp;
5866 writeFlags(it->second.mFlags, szFlags);
5867 aFlags[i] = Utf8Str(szFlags);
5868 }
5869
5870 return S_OK;
5871}
5872
5873/**
5874 * Enumerate the properties managed by a VM.
5875 * @returns E_ACCESSDENIED if the VM process is not available or not
5876 * currently handling queries and the setting should then be done in
5877 * VBoxSVC.
5878 */
5879HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5880 std::vector<com::Utf8Str> &aNames,
5881 std::vector<com::Utf8Str> &aValues,
5882 std::vector<LONG64> &aTimestamps,
5883 std::vector<com::Utf8Str> &aFlags)
5884{
5885 HRESULT rc;
5886 ComPtr<IInternalSessionControl> directControl;
5887 {
5888 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5889 if (mData->mSession.mLockType == LockType_VM)
5890 directControl = mData->mSession.mDirectControl;
5891 }
5892
5893 com::SafeArray<BSTR> bNames;
5894 com::SafeArray<BSTR> bValues;
5895 com::SafeArray<LONG64> bTimestamps;
5896 com::SafeArray<BSTR> bFlags;
5897
5898 if (!directControl)
5899 rc = E_ACCESSDENIED;
5900 else
5901 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5902 ComSafeArrayAsOutParam(bNames),
5903 ComSafeArrayAsOutParam(bValues),
5904 ComSafeArrayAsOutParam(bTimestamps),
5905 ComSafeArrayAsOutParam(bFlags));
5906 size_t i;
5907 aNames.resize(bNames.size());
5908 for (i = 0; i < bNames.size(); ++i)
5909 aNames[i] = Utf8Str(bNames[i]);
5910 aValues.resize(bValues.size());
5911 for (i = 0; i < bValues.size(); ++i)
5912 aValues[i] = Utf8Str(bValues[i]);
5913 aTimestamps.resize(bTimestamps.size());
5914 for (i = 0; i < bTimestamps.size(); ++i)
5915 aTimestamps[i] = bTimestamps[i];
5916 aFlags.resize(bFlags.size());
5917 for (i = 0; i < bFlags.size(); ++i)
5918 aFlags[i] = Utf8Str(bFlags[i]);
5919
5920 return rc;
5921}
5922#endif // VBOX_WITH_GUEST_PROPS
5923HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5924 std::vector<com::Utf8Str> &aNames,
5925 std::vector<com::Utf8Str> &aValues,
5926 std::vector<LONG64> &aTimestamps,
5927 std::vector<com::Utf8Str> &aFlags)
5928{
5929#ifndef VBOX_WITH_GUEST_PROPS
5930 ReturnComNotImplemented();
5931#else // VBOX_WITH_GUEST_PROPS
5932
5933 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5934
5935 if (rc == E_ACCESSDENIED)
5936 /* The VM is not running or the service is not (yet) accessible */
5937 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5938 return rc;
5939#endif // VBOX_WITH_GUEST_PROPS
5940}
5941
5942HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5943 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5944{
5945 MediaData::AttachmentList atts;
5946
5947 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5948 if (FAILED(rc)) return rc;
5949
5950 size_t i = 0;
5951 aMediumAttachments.resize(atts.size());
5952 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5953 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5954
5955 return S_OK;
5956}
5957
5958HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5959 LONG aControllerPort,
5960 LONG aDevice,
5961 ComPtr<IMediumAttachment> &aAttachment)
5962{
5963 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5964 aName.c_str(), aControllerPort, aDevice));
5965
5966 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5967
5968 aAttachment = NULL;
5969
5970 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5971 Bstr(aName).raw(),
5972 aControllerPort,
5973 aDevice);
5974 if (pAttach.isNull())
5975 return setError(VBOX_E_OBJECT_NOT_FOUND,
5976 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5977 aDevice, aControllerPort, aName.c_str());
5978
5979 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5980
5981 return S_OK;
5982}
5983
5984
5985HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5986 StorageBus_T aConnectionType,
5987 ComPtr<IStorageController> &aController)
5988{
5989 if ( (aConnectionType <= StorageBus_Null)
5990 || (aConnectionType > StorageBus_USB))
5991 return setError(E_INVALIDARG,
5992 tr("Invalid connection type: %d"),
5993 aConnectionType);
5994
5995 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5996
5997 HRESULT rc = i_checkStateDependency(MutableStateDep);
5998 if (FAILED(rc)) return rc;
5999
6000 /* try to find one with the name first. */
6001 ComObjPtr<StorageController> ctrl;
6002
6003 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6004 if (SUCCEEDED(rc))
6005 return setError(VBOX_E_OBJECT_IN_USE,
6006 tr("Storage controller named '%s' already exists"),
6007 aName.c_str());
6008
6009 ctrl.createObject();
6010
6011 /* get a new instance number for the storage controller */
6012 ULONG ulInstance = 0;
6013 bool fBootable = true;
6014 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6015 it != mStorageControllers->end();
6016 ++it)
6017 {
6018 if ((*it)->i_getStorageBus() == aConnectionType)
6019 {
6020 ULONG ulCurInst = (*it)->i_getInstance();
6021
6022 if (ulCurInst >= ulInstance)
6023 ulInstance = ulCurInst + 1;
6024
6025 /* Only one controller of each type can be marked as bootable. */
6026 if ((*it)->i_getBootable())
6027 fBootable = false;
6028 }
6029 }
6030
6031 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6032 if (FAILED(rc)) return rc;
6033
6034 i_setModified(IsModified_Storage);
6035 mStorageControllers.backup();
6036 mStorageControllers->push_back(ctrl);
6037
6038 ctrl.queryInterfaceTo(aController.asOutParam());
6039
6040 /* inform the direct session if any */
6041 alock.release();
6042 i_onStorageControllerChange();
6043
6044 return S_OK;
6045}
6046
6047HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6048 ComPtr<IStorageController> &aStorageController)
6049{
6050 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6051
6052 ComObjPtr<StorageController> ctrl;
6053
6054 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6055 if (SUCCEEDED(rc))
6056 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6057
6058 return rc;
6059}
6060
6061HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6062 ULONG aInstance,
6063 ComPtr<IStorageController> &aStorageController)
6064{
6065 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6066
6067 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6068 it != mStorageControllers->end();
6069 ++it)
6070 {
6071 if ( (*it)->i_getStorageBus() == aConnectionType
6072 && (*it)->i_getInstance() == aInstance)
6073 {
6074 (*it).queryInterfaceTo(aStorageController.asOutParam());
6075 return S_OK;
6076 }
6077 }
6078
6079 return setError(VBOX_E_OBJECT_NOT_FOUND,
6080 tr("Could not find a storage controller with instance number '%lu'"),
6081 aInstance);
6082}
6083
6084HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6085{
6086 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6087
6088 HRESULT rc = i_checkStateDependency(MutableStateDep);
6089 if (FAILED(rc)) return rc;
6090
6091 ComObjPtr<StorageController> ctrl;
6092
6093 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6094 if (SUCCEEDED(rc))
6095 {
6096 /* Ensure that only one controller of each type is marked as bootable. */
6097 if (aBootable == TRUE)
6098 {
6099 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6100 it != mStorageControllers->end();
6101 ++it)
6102 {
6103 ComObjPtr<StorageController> aCtrl = (*it);
6104
6105 if ( (aCtrl->i_getName() != aName)
6106 && aCtrl->i_getBootable() == TRUE
6107 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6108 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6109 {
6110 aCtrl->i_setBootable(FALSE);
6111 break;
6112 }
6113 }
6114 }
6115
6116 if (SUCCEEDED(rc))
6117 {
6118 ctrl->i_setBootable(aBootable);
6119 i_setModified(IsModified_Storage);
6120 }
6121 }
6122
6123 if (SUCCEEDED(rc))
6124 {
6125 /* inform the direct session if any */
6126 alock.release();
6127 i_onStorageControllerChange();
6128 }
6129
6130 return rc;
6131}
6132
6133HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6134{
6135 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6136
6137 HRESULT rc = i_checkStateDependency(MutableStateDep);
6138 if (FAILED(rc)) return rc;
6139
6140 ComObjPtr<StorageController> ctrl;
6141 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6142 if (FAILED(rc)) return rc;
6143
6144 {
6145 /* find all attached devices to the appropriate storage controller and detach them all */
6146 // make a temporary list because detachDevice invalidates iterators into
6147 // mMediaData->mAttachments
6148 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6149
6150 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6151 it != llAttachments2.end();
6152 ++it)
6153 {
6154 MediumAttachment *pAttachTemp = *it;
6155
6156 AutoCaller localAutoCaller(pAttachTemp);
6157 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6158
6159 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6160
6161 if (pAttachTemp->i_getControllerName() == aName)
6162 {
6163 rc = i_detachDevice(pAttachTemp, alock, NULL);
6164 if (FAILED(rc)) return rc;
6165 }
6166 }
6167 }
6168
6169 /* We can remove it now. */
6170 i_setModified(IsModified_Storage);
6171 mStorageControllers.backup();
6172
6173 ctrl->i_unshare();
6174
6175 mStorageControllers->remove(ctrl);
6176
6177 /* inform the direct session if any */
6178 alock.release();
6179 i_onStorageControllerChange();
6180
6181 return S_OK;
6182}
6183
6184HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6185 ComPtr<IUSBController> &aController)
6186{
6187 if ( (aType <= USBControllerType_Null)
6188 || (aType >= USBControllerType_Last))
6189 return setError(E_INVALIDARG,
6190 tr("Invalid USB controller type: %d"),
6191 aType);
6192
6193 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6194
6195 HRESULT rc = i_checkStateDependency(MutableStateDep);
6196 if (FAILED(rc)) return rc;
6197
6198 /* try to find one with the same type first. */
6199 ComObjPtr<USBController> ctrl;
6200
6201 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6202 if (SUCCEEDED(rc))
6203 return setError(VBOX_E_OBJECT_IN_USE,
6204 tr("USB controller named '%s' already exists"),
6205 aName.c_str());
6206
6207 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6208 ULONG maxInstances;
6209 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6210 if (FAILED(rc))
6211 return rc;
6212
6213 ULONG cInstances = i_getUSBControllerCountByType(aType);
6214 if (cInstances >= maxInstances)
6215 return setError(E_INVALIDARG,
6216 tr("Too many USB controllers of this type"));
6217
6218 ctrl.createObject();
6219
6220 rc = ctrl->init(this, aName, aType);
6221 if (FAILED(rc)) return rc;
6222
6223 i_setModified(IsModified_USB);
6224 mUSBControllers.backup();
6225 mUSBControllers->push_back(ctrl);
6226
6227 ctrl.queryInterfaceTo(aController.asOutParam());
6228
6229 /* inform the direct session if any */
6230 alock.release();
6231 i_onUSBControllerChange();
6232
6233 return S_OK;
6234}
6235
6236HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6237{
6238 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6239
6240 ComObjPtr<USBController> ctrl;
6241
6242 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6243 if (SUCCEEDED(rc))
6244 ctrl.queryInterfaceTo(aController.asOutParam());
6245
6246 return rc;
6247}
6248
6249HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6250 ULONG *aControllers)
6251{
6252 if ( (aType <= USBControllerType_Null)
6253 || (aType >= USBControllerType_Last))
6254 return setError(E_INVALIDARG,
6255 tr("Invalid USB controller type: %d"),
6256 aType);
6257
6258 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6259
6260 ComObjPtr<USBController> ctrl;
6261
6262 *aControllers = i_getUSBControllerCountByType(aType);
6263
6264 return S_OK;
6265}
6266
6267HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6268{
6269
6270 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6271
6272 HRESULT rc = i_checkStateDependency(MutableStateDep);
6273 if (FAILED(rc)) return rc;
6274
6275 ComObjPtr<USBController> ctrl;
6276 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6277 if (FAILED(rc)) return rc;
6278
6279 i_setModified(IsModified_USB);
6280 mUSBControllers.backup();
6281
6282 ctrl->i_unshare();
6283
6284 mUSBControllers->remove(ctrl);
6285
6286 /* inform the direct session if any */
6287 alock.release();
6288 i_onUSBControllerChange();
6289
6290 return S_OK;
6291}
6292
6293HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6294 ULONG *aOriginX,
6295 ULONG *aOriginY,
6296 ULONG *aWidth,
6297 ULONG *aHeight,
6298 BOOL *aEnabled)
6299{
6300 uint32_t u32OriginX= 0;
6301 uint32_t u32OriginY= 0;
6302 uint32_t u32Width = 0;
6303 uint32_t u32Height = 0;
6304 uint16_t u16Flags = 0;
6305
6306 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6307 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6308 if (RT_FAILURE(vrc))
6309 {
6310#ifdef RT_OS_WINDOWS
6311 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6312 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6313 * So just assign fEnable to TRUE again.
6314 * The right fix would be to change GUI API wrappers to make sure that parameters
6315 * are changed only if API succeeds.
6316 */
6317 *aEnabled = TRUE;
6318#endif
6319 return setError(VBOX_E_IPRT_ERROR,
6320 tr("Saved guest size is not available (%Rrc)"),
6321 vrc);
6322 }
6323
6324 *aOriginX = u32OriginX;
6325 *aOriginY = u32OriginY;
6326 *aWidth = u32Width;
6327 *aHeight = u32Height;
6328 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6329
6330 return S_OK;
6331}
6332
6333HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6334 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6335{
6336 if (aScreenId != 0)
6337 return E_NOTIMPL;
6338
6339 if ( aBitmapFormat != BitmapFormat_BGR0
6340 && aBitmapFormat != BitmapFormat_BGRA
6341 && aBitmapFormat != BitmapFormat_RGBA
6342 && aBitmapFormat != BitmapFormat_PNG)
6343 return setError(E_NOTIMPL,
6344 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6345
6346 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6347
6348 uint8_t *pu8Data = NULL;
6349 uint32_t cbData = 0;
6350 uint32_t u32Width = 0;
6351 uint32_t u32Height = 0;
6352
6353 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6354
6355 if (RT_FAILURE(vrc))
6356 return setError(VBOX_E_IPRT_ERROR,
6357 tr("Saved thumbnail data is not available (%Rrc)"),
6358 vrc);
6359
6360 HRESULT hr = S_OK;
6361
6362 *aWidth = u32Width;
6363 *aHeight = u32Height;
6364
6365 if (cbData > 0)
6366 {
6367 /* Convert pixels to the format expected by the API caller. */
6368 if (aBitmapFormat == BitmapFormat_BGR0)
6369 {
6370 /* [0] B, [1] G, [2] R, [3] 0. */
6371 aData.resize(cbData);
6372 memcpy(&aData.front(), pu8Data, cbData);
6373 }
6374 else if (aBitmapFormat == BitmapFormat_BGRA)
6375 {
6376 /* [0] B, [1] G, [2] R, [3] A. */
6377 aData.resize(cbData);
6378 for (uint32_t i = 0; i < cbData; i += 4)
6379 {
6380 aData[i] = pu8Data[i];
6381 aData[i + 1] = pu8Data[i + 1];
6382 aData[i + 2] = pu8Data[i + 2];
6383 aData[i + 3] = 0xff;
6384 }
6385 }
6386 else if (aBitmapFormat == BitmapFormat_RGBA)
6387 {
6388 /* [0] R, [1] G, [2] B, [3] A. */
6389 aData.resize(cbData);
6390 for (uint32_t i = 0; i < cbData; i += 4)
6391 {
6392 aData[i] = pu8Data[i + 2];
6393 aData[i + 1] = pu8Data[i + 1];
6394 aData[i + 2] = pu8Data[i];
6395 aData[i + 3] = 0xff;
6396 }
6397 }
6398 else if (aBitmapFormat == BitmapFormat_PNG)
6399 {
6400 uint8_t *pu8PNG = NULL;
6401 uint32_t cbPNG = 0;
6402 uint32_t cxPNG = 0;
6403 uint32_t cyPNG = 0;
6404
6405 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6406
6407 if (RT_SUCCESS(vrc))
6408 {
6409 aData.resize(cbPNG);
6410 if (cbPNG)
6411 memcpy(&aData.front(), pu8PNG, cbPNG);
6412 }
6413 else
6414 hr = setError(VBOX_E_IPRT_ERROR,
6415 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6416 vrc);
6417
6418 RTMemFree(pu8PNG);
6419 }
6420 }
6421
6422 freeSavedDisplayScreenshot(pu8Data);
6423
6424 return hr;
6425}
6426
6427HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6428 ULONG *aWidth,
6429 ULONG *aHeight,
6430 std::vector<BitmapFormat_T> &aBitmapFormats)
6431{
6432 if (aScreenId != 0)
6433 return E_NOTIMPL;
6434
6435 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6436
6437 uint8_t *pu8Data = NULL;
6438 uint32_t cbData = 0;
6439 uint32_t u32Width = 0;
6440 uint32_t u32Height = 0;
6441
6442 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6443
6444 if (RT_FAILURE(vrc))
6445 return setError(VBOX_E_IPRT_ERROR,
6446 tr("Saved screenshot data is not available (%Rrc)"),
6447 vrc);
6448
6449 *aWidth = u32Width;
6450 *aHeight = u32Height;
6451 aBitmapFormats.resize(1);
6452 aBitmapFormats[0] = BitmapFormat_PNG;
6453
6454 freeSavedDisplayScreenshot(pu8Data);
6455
6456 return S_OK;
6457}
6458
6459HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6460 BitmapFormat_T aBitmapFormat,
6461 ULONG *aWidth,
6462 ULONG *aHeight,
6463 std::vector<BYTE> &aData)
6464{
6465 if (aScreenId != 0)
6466 return E_NOTIMPL;
6467
6468 if (aBitmapFormat != BitmapFormat_PNG)
6469 return E_NOTIMPL;
6470
6471 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6472
6473 uint8_t *pu8Data = NULL;
6474 uint32_t cbData = 0;
6475 uint32_t u32Width = 0;
6476 uint32_t u32Height = 0;
6477
6478 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6479
6480 if (RT_FAILURE(vrc))
6481 return setError(VBOX_E_IPRT_ERROR,
6482 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6483 vrc);
6484
6485 *aWidth = u32Width;
6486 *aHeight = u32Height;
6487
6488 aData.resize(cbData);
6489 if (cbData)
6490 memcpy(&aData.front(), pu8Data, cbData);
6491
6492 freeSavedDisplayScreenshot(pu8Data);
6493
6494 return S_OK;
6495}
6496
6497HRESULT Machine::hotPlugCPU(ULONG aCpu)
6498{
6499 HRESULT rc = S_OK;
6500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6501
6502 if (!mHWData->mCPUHotPlugEnabled)
6503 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6504
6505 if (aCpu >= mHWData->mCPUCount)
6506 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6507
6508 if (mHWData->mCPUAttached[aCpu])
6509 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6510
6511 alock.release();
6512 rc = i_onCPUChange(aCpu, false);
6513 alock.acquire();
6514 if (FAILED(rc)) return rc;
6515
6516 i_setModified(IsModified_MachineData);
6517 mHWData.backup();
6518 mHWData->mCPUAttached[aCpu] = true;
6519
6520 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6521 if (Global::IsOnline(mData->mMachineState))
6522 i_saveSettings(NULL);
6523
6524 return S_OK;
6525}
6526
6527HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6528{
6529 HRESULT rc = S_OK;
6530
6531 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6532
6533 if (!mHWData->mCPUHotPlugEnabled)
6534 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6535
6536 if (aCpu >= SchemaDefs::MaxCPUCount)
6537 return setError(E_INVALIDARG,
6538 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6539 SchemaDefs::MaxCPUCount);
6540
6541 if (!mHWData->mCPUAttached[aCpu])
6542 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6543
6544 /* CPU 0 can't be detached */
6545 if (aCpu == 0)
6546 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6547
6548 alock.release();
6549 rc = i_onCPUChange(aCpu, true);
6550 alock.acquire();
6551 if (FAILED(rc)) return rc;
6552
6553 i_setModified(IsModified_MachineData);
6554 mHWData.backup();
6555 mHWData->mCPUAttached[aCpu] = false;
6556
6557 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6558 if (Global::IsOnline(mData->mMachineState))
6559 i_saveSettings(NULL);
6560
6561 return S_OK;
6562}
6563
6564HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6565{
6566 *aAttached = false;
6567
6568 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6569
6570 /* If hotplug is enabled the CPU is always enabled. */
6571 if (!mHWData->mCPUHotPlugEnabled)
6572 {
6573 if (aCpu < mHWData->mCPUCount)
6574 *aAttached = true;
6575 }
6576 else
6577 {
6578 if (aCpu < SchemaDefs::MaxCPUCount)
6579 *aAttached = mHWData->mCPUAttached[aCpu];
6580 }
6581
6582 return S_OK;
6583}
6584
6585HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6586{
6587 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6588
6589 Utf8Str log = i_queryLogFilename(aIdx);
6590 if (!RTFileExists(log.c_str()))
6591 log.setNull();
6592 aFilename = log;
6593
6594 return S_OK;
6595}
6596
6597HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6598{
6599 if (aSize < 0)
6600 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6601
6602 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6603
6604 HRESULT rc = S_OK;
6605 Utf8Str log = i_queryLogFilename(aIdx);
6606
6607 /* do not unnecessarily hold the lock while doing something which does
6608 * not need the lock and potentially takes a long time. */
6609 alock.release();
6610
6611 /* Limit the chunk size to 32K for now, as that gives better performance
6612 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6613 * One byte expands to approx. 25 bytes of breathtaking XML. */
6614 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6615 aData.resize(cbData);
6616
6617 RTFILE LogFile;
6618 int vrc = RTFileOpen(&LogFile, log.c_str(),
6619 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6620 if (RT_SUCCESS(vrc))
6621 {
6622 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6623 if (RT_SUCCESS(vrc))
6624 aData.resize(cbData);
6625 else
6626 rc = setError(VBOX_E_IPRT_ERROR,
6627 tr("Could not read log file '%s' (%Rrc)"),
6628 log.c_str(), vrc);
6629 RTFileClose(LogFile);
6630 }
6631 else
6632 rc = setError(VBOX_E_IPRT_ERROR,
6633 tr("Could not open log file '%s' (%Rrc)"),
6634 log.c_str(), vrc);
6635
6636 if (FAILED(rc))
6637 aData.resize(0);
6638
6639 return rc;
6640}
6641
6642
6643/**
6644 * Currently this method doesn't attach device to the running VM,
6645 * just makes sure it's plugged on next VM start.
6646 */
6647HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6648{
6649 // lock scope
6650 {
6651 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6652
6653 HRESULT rc = i_checkStateDependency(MutableStateDep);
6654 if (FAILED(rc)) return rc;
6655
6656 ChipsetType_T aChipset = ChipsetType_PIIX3;
6657 COMGETTER(ChipsetType)(&aChipset);
6658
6659 if (aChipset != ChipsetType_ICH9)
6660 {
6661 return setError(E_INVALIDARG,
6662 tr("Host PCI attachment only supported with ICH9 chipset"));
6663 }
6664
6665 // check if device with this host PCI address already attached
6666 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6667 it != mHWData->mPCIDeviceAssignments.end();
6668 ++it)
6669 {
6670 LONG iHostAddress = -1;
6671 ComPtr<PCIDeviceAttachment> pAttach;
6672 pAttach = *it;
6673 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6674 if (iHostAddress == aHostAddress)
6675 return setError(E_INVALIDARG,
6676 tr("Device with host PCI address already attached to this VM"));
6677 }
6678
6679 ComObjPtr<PCIDeviceAttachment> pda;
6680 char name[32];
6681
6682 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6683 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6684 Bstr bname(name);
6685 pda.createObject();
6686 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6687 i_setModified(IsModified_MachineData);
6688 mHWData.backup();
6689 mHWData->mPCIDeviceAssignments.push_back(pda);
6690 }
6691
6692 return S_OK;
6693}
6694
6695/**
6696 * Currently this method doesn't detach device from the running VM,
6697 * just makes sure it's not plugged on next VM start.
6698 */
6699HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6700{
6701 ComObjPtr<PCIDeviceAttachment> pAttach;
6702 bool fRemoved = false;
6703 HRESULT rc;
6704
6705 // lock scope
6706 {
6707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6708
6709 rc = i_checkStateDependency(MutableStateDep);
6710 if (FAILED(rc)) return rc;
6711
6712 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6713 it != mHWData->mPCIDeviceAssignments.end();
6714 ++it)
6715 {
6716 LONG iHostAddress = -1;
6717 pAttach = *it;
6718 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6719 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6720 {
6721 i_setModified(IsModified_MachineData);
6722 mHWData.backup();
6723 mHWData->mPCIDeviceAssignments.remove(pAttach);
6724 fRemoved = true;
6725 break;
6726 }
6727 }
6728 }
6729
6730
6731 /* Fire event outside of the lock */
6732 if (fRemoved)
6733 {
6734 Assert(!pAttach.isNull());
6735 ComPtr<IEventSource> es;
6736 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6737 Assert(SUCCEEDED(rc));
6738 Bstr mid;
6739 rc = this->COMGETTER(Id)(mid.asOutParam());
6740 Assert(SUCCEEDED(rc));
6741 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6742 }
6743
6744 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6745 tr("No host PCI device %08x attached"),
6746 aHostAddress
6747 );
6748}
6749
6750HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6751{
6752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6753
6754 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6755
6756 size_t i = 0;
6757 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6758 it != mHWData->mPCIDeviceAssignments.end();
6759 ++i, ++it)
6760 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6761
6762 return S_OK;
6763}
6764
6765HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6766{
6767 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6768
6769 return S_OK;
6770}
6771
6772HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6773{
6774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6775
6776 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6777
6778 return S_OK;
6779}
6780
6781HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6782{
6783 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6784 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6785 if (SUCCEEDED(hrc))
6786 {
6787 hrc = mHWData.backupEx();
6788 if (SUCCEEDED(hrc))
6789 {
6790 i_setModified(IsModified_MachineData);
6791 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6792 }
6793 }
6794 return hrc;
6795}
6796
6797HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6798{
6799 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6800 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6801 return S_OK;
6802}
6803
6804HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6805{
6806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6807 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6808 if (SUCCEEDED(hrc))
6809 {
6810 hrc = mHWData.backupEx();
6811 if (SUCCEEDED(hrc))
6812 {
6813 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6814 if (SUCCEEDED(hrc))
6815 i_setModified(IsModified_MachineData);
6816 }
6817 }
6818 return hrc;
6819}
6820
6821HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6822{
6823 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6824
6825 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6826
6827 return S_OK;
6828}
6829
6830HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6831{
6832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6833 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6834 if (SUCCEEDED(hrc))
6835 {
6836 hrc = mHWData.backupEx();
6837 if (SUCCEEDED(hrc))
6838 {
6839 i_setModified(IsModified_MachineData);
6840 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6841 }
6842 }
6843 return hrc;
6844}
6845
6846HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6847{
6848 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6849
6850 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6851
6852 return S_OK;
6853}
6854
6855HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6856{
6857 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6858
6859 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6860 if ( SUCCEEDED(hrc)
6861 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6862 {
6863 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6864 int vrc;
6865
6866 if (aAutostartEnabled)
6867 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6868 else
6869 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6870
6871 if (RT_SUCCESS(vrc))
6872 {
6873 hrc = mHWData.backupEx();
6874 if (SUCCEEDED(hrc))
6875 {
6876 i_setModified(IsModified_MachineData);
6877 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6878 }
6879 }
6880 else if (vrc == VERR_NOT_SUPPORTED)
6881 hrc = setError(VBOX_E_NOT_SUPPORTED,
6882 tr("The VM autostart feature is not supported on this platform"));
6883 else if (vrc == VERR_PATH_NOT_FOUND)
6884 hrc = setError(E_FAIL,
6885 tr("The path to the autostart database is not set"));
6886 else
6887 hrc = setError(E_UNEXPECTED,
6888 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6889 aAutostartEnabled ? "Adding" : "Removing",
6890 mUserData->s.strName.c_str(), vrc);
6891 }
6892 return hrc;
6893}
6894
6895HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6896{
6897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6898
6899 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6900
6901 return S_OK;
6902}
6903
6904HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6905{
6906 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6907 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6908 if (SUCCEEDED(hrc))
6909 {
6910 hrc = mHWData.backupEx();
6911 if (SUCCEEDED(hrc))
6912 {
6913 i_setModified(IsModified_MachineData);
6914 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6915 }
6916 }
6917 return hrc;
6918}
6919
6920HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6921{
6922 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6923
6924 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6925
6926 return S_OK;
6927}
6928
6929HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6930{
6931 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6932 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6933 if ( SUCCEEDED(hrc)
6934 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6935 {
6936 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6937 int vrc;
6938
6939 if (aAutostopType != AutostopType_Disabled)
6940 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6941 else
6942 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6943
6944 if (RT_SUCCESS(vrc))
6945 {
6946 hrc = mHWData.backupEx();
6947 if (SUCCEEDED(hrc))
6948 {
6949 i_setModified(IsModified_MachineData);
6950 mHWData->mAutostart.enmAutostopType = aAutostopType;
6951 }
6952 }
6953 else if (vrc == VERR_NOT_SUPPORTED)
6954 hrc = setError(VBOX_E_NOT_SUPPORTED,
6955 tr("The VM autostop feature is not supported on this platform"));
6956 else if (vrc == VERR_PATH_NOT_FOUND)
6957 hrc = setError(E_FAIL,
6958 tr("The path to the autostart database is not set"));
6959 else
6960 hrc = setError(E_UNEXPECTED,
6961 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6962 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6963 mUserData->s.strName.c_str(), vrc);
6964 }
6965 return hrc;
6966}
6967
6968HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6969{
6970 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6971
6972 aDefaultFrontend = mHWData->mDefaultFrontend;
6973
6974 return S_OK;
6975}
6976
6977HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6978{
6979 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6980 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6981 if (SUCCEEDED(hrc))
6982 {
6983 hrc = mHWData.backupEx();
6984 if (SUCCEEDED(hrc))
6985 {
6986 i_setModified(IsModified_MachineData);
6987 mHWData->mDefaultFrontend = aDefaultFrontend;
6988 }
6989 }
6990 return hrc;
6991}
6992
6993HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6994{
6995 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6996 size_t cbIcon = mUserData->mIcon.size();
6997 aIcon.resize(cbIcon);
6998 if (cbIcon)
6999 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7000 return S_OK;
7001}
7002
7003HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7004{
7005 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7006 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7007 if (SUCCEEDED(hrc))
7008 {
7009 i_setModified(IsModified_MachineData);
7010 mUserData.backup();
7011 size_t cbIcon = aIcon.size();
7012 mUserData->mIcon.resize(cbIcon);
7013 if (cbIcon)
7014 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7015 }
7016 return hrc;
7017}
7018
7019HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7020{
7021#ifdef VBOX_WITH_USB
7022 *aUSBProxyAvailable = true;
7023#else
7024 *aUSBProxyAvailable = false;
7025#endif
7026 return S_OK;
7027}
7028
7029HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7030 ComPtr<IProgress> &aProgress)
7031{
7032 ComObjPtr<Progress> pP;
7033 Progress *ppP = pP;
7034 IProgress *iP = static_cast<IProgress *>(ppP);
7035 IProgress **pProgress = &iP;
7036
7037 IMachine *pTarget = aTarget;
7038
7039 /* Convert the options. */
7040 RTCList<CloneOptions_T> optList;
7041 if (aOptions.size())
7042 for (size_t i = 0; i < aOptions.size(); ++i)
7043 optList.append(aOptions[i]);
7044
7045 if (optList.contains(CloneOptions_Link))
7046 {
7047 if (!i_isSnapshotMachine())
7048 return setError(E_INVALIDARG,
7049 tr("Linked clone can only be created from a snapshot"));
7050 if (aMode != CloneMode_MachineState)
7051 return setError(E_INVALIDARG,
7052 tr("Linked clone can only be created for a single machine state"));
7053 }
7054 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7055
7056 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7057
7058 HRESULT rc = pWorker->start(pProgress);
7059
7060 pP = static_cast<Progress *>(*pProgress);
7061 pP.queryInterfaceTo(aProgress.asOutParam());
7062
7063 return rc;
7064
7065}
7066
7067HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7068{
7069 NOREF(aProgress);
7070 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7071
7072 // This check should always fail.
7073 HRESULT rc = i_checkStateDependency(MutableStateDep);
7074 if (FAILED(rc)) return rc;
7075
7076 AssertFailedReturn(E_NOTIMPL);
7077}
7078
7079HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7080{
7081 NOREF(aSavedStateFile);
7082 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7083
7084 // This check should always fail.
7085 HRESULT rc = i_checkStateDependency(MutableStateDep);
7086 if (FAILED(rc)) return rc;
7087
7088 AssertFailedReturn(E_NOTIMPL);
7089}
7090
7091HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7092{
7093 NOREF(aFRemoveFile);
7094 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7095
7096 // This check should always fail.
7097 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7098 if (FAILED(rc)) return rc;
7099
7100 AssertFailedReturn(E_NOTIMPL);
7101}
7102
7103// public methods for internal purposes
7104/////////////////////////////////////////////////////////////////////////////
7105
7106/**
7107 * Adds the given IsModified_* flag to the dirty flags of the machine.
7108 * This must be called either during i_loadSettings or under the machine write lock.
7109 * @param fl
7110 */
7111void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7112{
7113 mData->flModifications |= fl;
7114 if (fAllowStateModification && i_isStateModificationAllowed())
7115 mData->mCurrentStateModified = true;
7116}
7117
7118/**
7119 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7120 * care of the write locking.
7121 *
7122 * @param fModifications The flag to add.
7123 */
7124void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7125{
7126 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7127 i_setModified(fModification, fAllowStateModification);
7128}
7129
7130/**
7131 * Saves the registry entry of this machine to the given configuration node.
7132 *
7133 * @param aEntryNode Node to save the registry entry to.
7134 *
7135 * @note locks this object for reading.
7136 */
7137HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7138{
7139 AutoLimitedCaller autoCaller(this);
7140 AssertComRCReturnRC(autoCaller.rc());
7141
7142 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7143
7144 data.uuid = mData->mUuid;
7145 data.strSettingsFile = mData->m_strConfigFile;
7146
7147 return S_OK;
7148}
7149
7150/**
7151 * Calculates the absolute path of the given path taking the directory of the
7152 * machine settings file as the current directory.
7153 *
7154 * @param aPath Path to calculate the absolute path for.
7155 * @param aResult Where to put the result (used only on success, can be the
7156 * same Utf8Str instance as passed in @a aPath).
7157 * @return IPRT result.
7158 *
7159 * @note Locks this object for reading.
7160 */
7161int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7162{
7163 AutoCaller autoCaller(this);
7164 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7165
7166 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7167
7168 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7169
7170 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7171
7172 strSettingsDir.stripFilename();
7173 char folder[RTPATH_MAX];
7174 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7175 if (RT_SUCCESS(vrc))
7176 aResult = folder;
7177
7178 return vrc;
7179}
7180
7181/**
7182 * Copies strSource to strTarget, making it relative to the machine folder
7183 * if it is a subdirectory thereof, or simply copying it otherwise.
7184 *
7185 * @param strSource Path to evaluate and copy.
7186 * @param strTarget Buffer to receive target path.
7187 *
7188 * @note Locks this object for reading.
7189 */
7190void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7191 Utf8Str &strTarget)
7192{
7193 AutoCaller autoCaller(this);
7194 AssertComRCReturn(autoCaller.rc(), (void)0);
7195
7196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7197
7198 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7199 // use strTarget as a temporary buffer to hold the machine settings dir
7200 strTarget = mData->m_strConfigFileFull;
7201 strTarget.stripFilename();
7202 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7203 {
7204 // is relative: then append what's left
7205 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7206 // for empty paths (only possible for subdirs) use "." to avoid
7207 // triggering default settings for not present config attributes.
7208 if (strTarget.isEmpty())
7209 strTarget = ".";
7210 }
7211 else
7212 // is not relative: then overwrite
7213 strTarget = strSource;
7214}
7215
7216/**
7217 * Returns the full path to the machine's log folder in the
7218 * \a aLogFolder argument.
7219 */
7220void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7221{
7222 AutoCaller autoCaller(this);
7223 AssertComRCReturnVoid(autoCaller.rc());
7224
7225 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7226
7227 char szTmp[RTPATH_MAX];
7228 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7229 if (RT_SUCCESS(vrc))
7230 {
7231 if (szTmp[0] && !mUserData.isNull())
7232 {
7233 char szTmp2[RTPATH_MAX];
7234 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7235 if (RT_SUCCESS(vrc))
7236 aLogFolder = BstrFmt("%s%c%s",
7237 szTmp2,
7238 RTPATH_DELIMITER,
7239 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7240 }
7241 else
7242 vrc = VERR_PATH_IS_RELATIVE;
7243 }
7244
7245 if (RT_FAILURE(vrc))
7246 {
7247 // fallback if VBOX_USER_LOGHOME is not set or invalid
7248 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7249 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7250 aLogFolder.append(RTPATH_DELIMITER);
7251 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7252 }
7253}
7254
7255/**
7256 * Returns the full path to the machine's log file for an given index.
7257 */
7258Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7259 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7260{
7261 Utf8Str logFolder;
7262 getLogFolder(logFolder);
7263 Assert(logFolder.length());
7264 Utf8Str log;
7265 if (idx == 0)
7266 log = Utf8StrFmt("%s%cVBox.log",
7267 logFolder.c_str(), RTPATH_DELIMITER);
7268 else
7269 log = Utf8StrFmt("%s%cVBox.log.%d",
7270 logFolder.c_str(), RTPATH_DELIMITER, idx);
7271 return log;
7272}
7273
7274/**
7275 * Returns the full path to the machine's (hardened) startup log file.
7276 */
7277Utf8Str Machine::i_getStartupLogFilename(void)
7278{
7279 Utf8Str strFilename;
7280 getLogFolder(strFilename);
7281 Assert(strFilename.length());
7282 strFilename.append(RTPATH_SLASH_STR "VBoxStartup.log");
7283 return strFilename;
7284}
7285
7286
7287/**
7288 * Composes a unique saved state filename based on the current system time. The filename is
7289 * granular to the second so this will work so long as no more than one snapshot is taken on
7290 * a machine per second.
7291 *
7292 * Before version 4.1, we used this formula for saved state files:
7293 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7294 * which no longer works because saved state files can now be shared between the saved state of the
7295 * "saved" machine and an online snapshot, and the following would cause problems:
7296 * 1) save machine
7297 * 2) create online snapshot from that machine state --> reusing saved state file
7298 * 3) save machine again --> filename would be reused, breaking the online snapshot
7299 *
7300 * So instead we now use a timestamp.
7301 *
7302 * @param str
7303 */
7304
7305void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7306{
7307 AutoCaller autoCaller(this);
7308 AssertComRCReturnVoid(autoCaller.rc());
7309
7310 {
7311 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7312 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7313 }
7314
7315 RTTIMESPEC ts;
7316 RTTimeNow(&ts);
7317 RTTIME time;
7318 RTTimeExplode(&time, &ts);
7319
7320 strStateFilePath += RTPATH_DELIMITER;
7321 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7322 time.i32Year, time.u8Month, time.u8MonthDay,
7323 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7324}
7325
7326/**
7327 * Returns the full path to the default video capture file.
7328 */
7329void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7330{
7331 AutoCaller autoCaller(this);
7332 AssertComRCReturnVoid(autoCaller.rc());
7333
7334 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7335
7336 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7337 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7338 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7339}
7340
7341/**
7342 * Returns whether at least one USB controller is present for the VM.
7343 */
7344bool Machine::i_isUSBControllerPresent()
7345{
7346 AutoCaller autoCaller(this);
7347 AssertComRCReturn(autoCaller.rc(), false);
7348
7349 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7350
7351 return (mUSBControllers->size() > 0);
7352}
7353
7354/**
7355 * @note Locks this object for writing, calls the client process
7356 * (inside the lock).
7357 */
7358HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7359 const Utf8Str &strFrontend,
7360 const Utf8Str &strEnvironment,
7361 ProgressProxy *aProgress)
7362{
7363 LogFlowThisFuncEnter();
7364
7365 AssertReturn(aControl, E_FAIL);
7366 AssertReturn(aProgress, E_FAIL);
7367 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7368
7369 AutoCaller autoCaller(this);
7370 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7371
7372 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7373
7374 if (!mData->mRegistered)
7375 return setError(E_UNEXPECTED,
7376 tr("The machine '%s' is not registered"),
7377 mUserData->s.strName.c_str());
7378
7379 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7380
7381 /* The process started when launching a VM with separate UI/VM processes is always
7382 * the UI process, i.e. needs special handling as it won't claim the session. */
7383 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7384
7385 if (fSeparate)
7386 {
7387 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7388 return setError(VBOX_E_INVALID_OBJECT_STATE,
7389 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7390 mUserData->s.strName.c_str());
7391 }
7392 else
7393 {
7394 if ( mData->mSession.mState == SessionState_Locked
7395 || mData->mSession.mState == SessionState_Spawning
7396 || mData->mSession.mState == SessionState_Unlocking)
7397 return setError(VBOX_E_INVALID_OBJECT_STATE,
7398 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7399 mUserData->s.strName.c_str());
7400
7401 /* may not be busy */
7402 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7403 }
7404
7405 /* get the path to the executable */
7406 char szPath[RTPATH_MAX];
7407 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7408 size_t cchBufLeft = strlen(szPath);
7409 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7410 szPath[cchBufLeft] = 0;
7411 char *pszNamePart = szPath + cchBufLeft;
7412 cchBufLeft = sizeof(szPath) - cchBufLeft;
7413
7414 int vrc = VINF_SUCCESS;
7415 RTPROCESS pid = NIL_RTPROCESS;
7416
7417 RTENV env = RTENV_DEFAULT;
7418
7419 if (!strEnvironment.isEmpty())
7420 {
7421 char *newEnvStr = NULL;
7422
7423 do
7424 {
7425 /* clone the current environment */
7426 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7427 AssertRCBreakStmt(vrc2, vrc = vrc2);
7428
7429 newEnvStr = RTStrDup(strEnvironment.c_str());
7430 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7431
7432 /* put new variables to the environment
7433 * (ignore empty variable names here since RTEnv API
7434 * intentionally doesn't do that) */
7435 char *var = newEnvStr;
7436 for (char *p = newEnvStr; *p; ++p)
7437 {
7438 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7439 {
7440 *p = '\0';
7441 if (*var)
7442 {
7443 char *val = strchr(var, '=');
7444 if (val)
7445 {
7446 *val++ = '\0';
7447 vrc2 = RTEnvSetEx(env, var, val);
7448 }
7449 else
7450 vrc2 = RTEnvUnsetEx(env, var);
7451 if (RT_FAILURE(vrc2))
7452 break;
7453 }
7454 var = p + 1;
7455 }
7456 }
7457 if (RT_SUCCESS(vrc2) && *var)
7458 vrc2 = RTEnvPutEx(env, var);
7459
7460 AssertRCBreakStmt(vrc2, vrc = vrc2);
7461 }
7462 while (0);
7463
7464 if (newEnvStr != NULL)
7465 RTStrFree(newEnvStr);
7466 }
7467
7468 /* Hardened startup logging */
7469#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7470 Utf8Str strSupStartLogArg("--sup-startup-log=");
7471 {
7472 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7473 int vrc2 = RTFileDelete(strStartupLogFile.c_str());
7474 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7475 {
7476 Utf8Str strStartupLogDir = strStartupLogFile;
7477 strStartupLogDir.stripFilename();
7478 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7479 file without stripping the file. */
7480 }
7481 strSupStartLogArg.append(strStartupLogFile);
7482 }
7483 const char *pszSupStartupLogArg = strSupStartLogArg.c_str();
7484#else
7485 const char *pszSupStartupLogArg = NULL;
7486#endif
7487
7488 Utf8Str strCanonicalName;
7489
7490#ifdef VBOX_WITH_QTGUI
7491 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7492 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7493 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7494 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7495 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7496 {
7497 strCanonicalName = "GUI/Qt";
7498# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7499 /* Modify the base path so that we don't need to use ".." below. */
7500 RTPathStripTrailingSlash(szPath);
7501 RTPathStripFilename(szPath);
7502 cchBufLeft = strlen(szPath);
7503 pszNamePart = szPath + cchBufLeft;
7504 cchBufLeft = sizeof(szPath) - cchBufLeft;
7505
7506# define OSX_APP_NAME "VirtualBoxVM"
7507# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7508
7509 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7510 if ( strAppOverride.contains(".")
7511 || strAppOverride.contains("/")
7512 || strAppOverride.contains("\\")
7513 || strAppOverride.contains(":"))
7514 strAppOverride.setNull();
7515 Utf8Str strAppPath;
7516 if (!strAppOverride.isEmpty())
7517 {
7518 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7519 Utf8Str strFullPath(szPath);
7520 strFullPath.append(strAppPath);
7521 /* there is a race, but people using this deserve the failure */
7522 if (!RTFileExists(strFullPath.c_str()))
7523 strAppOverride.setNull();
7524 }
7525 if (strAppOverride.isEmpty())
7526 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7527 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7528 strcpy(pszNamePart, strAppPath.c_str());
7529# else
7530 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7531 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7532 strcpy(pszNamePart, s_szVirtualBox_exe);
7533# endif
7534
7535 Utf8Str idStr = mData->mUuid.toString();
7536 const char *apszArgs[] =
7537 {
7538 szPath,
7539 "--comment", mUserData->s.strName.c_str(),
7540 "--startvm", idStr.c_str(),
7541 "--no-startvm-errormsgbox",
7542 NULL, /* For "--separate". */
7543 NULL, /* For "--sup-startup-log". */
7544 NULL
7545 };
7546 unsigned iArg = 6;
7547 if (fSeparate)
7548 apszArgs[iArg++] = "--separate";
7549 apszArgs[iArg++] = pszSupStartupLogArg;
7550
7551 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7552 }
7553#else /* !VBOX_WITH_QTGUI */
7554 if (0)
7555 ;
7556#endif /* VBOX_WITH_QTGUI */
7557
7558 else
7559
7560#ifdef VBOX_WITH_VBOXSDL
7561 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7562 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7563 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7564 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7565 {
7566 strCanonicalName = "GUI/SDL";
7567 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7568 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7569 strcpy(pszNamePart, s_szVBoxSDL_exe);
7570
7571 Utf8Str idStr = mData->mUuid.toString();
7572 const char *apszArgs[] =
7573 {
7574 szPath,
7575 "--comment", mUserData->s.strName.c_str(),
7576 "--startvm", idStr.c_str(),
7577 NULL, /* For "--separate". */
7578 NULL, /* For "--sup-startup-log". */
7579 NULL
7580 };
7581 unsigned iArg = 5;
7582 if (fSeparate)
7583 apszArgs[iArg++] = "--separate";
7584 apszArgs[iArg++] = pszSupStartupLogArg;
7585
7586 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7587 }
7588#else /* !VBOX_WITH_VBOXSDL */
7589 if (0)
7590 ;
7591#endif /* !VBOX_WITH_VBOXSDL */
7592
7593 else
7594
7595#ifdef VBOX_WITH_HEADLESS
7596 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7597 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7598 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7599 )
7600 {
7601 strCanonicalName = "headless";
7602 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7603 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7604 * and a VM works even if the server has not been installed.
7605 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7606 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7607 * differently in 4.0 and 3.x.
7608 */
7609 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7610 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7611 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7612
7613 Utf8Str idStr = mData->mUuid.toString();
7614 const char *apszArgs[] =
7615 {
7616 szPath,
7617 "--comment", mUserData->s.strName.c_str(),
7618 "--startvm", idStr.c_str(),
7619 "--vrde", "config",
7620 NULL, /* For "--capture". */
7621 NULL, /* For "--sup-startup-log". */
7622 NULL
7623 };
7624 unsigned iArg = 7;
7625 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7626 apszArgs[iArg++] = "--capture";
7627 apszArgs[iArg++] = pszSupStartupLogArg;
7628
7629# ifdef RT_OS_WINDOWS
7630 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7631# else
7632 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7633# endif
7634 }
7635#else /* !VBOX_WITH_HEADLESS */
7636 if (0)
7637 ;
7638#endif /* !VBOX_WITH_HEADLESS */
7639 else
7640 {
7641 RTEnvDestroy(env);
7642 return setError(E_INVALIDARG,
7643 tr("Invalid frontend name: '%s'"),
7644 strFrontend.c_str());
7645 }
7646
7647 RTEnvDestroy(env);
7648
7649 if (RT_FAILURE(vrc))
7650 return setError(VBOX_E_IPRT_ERROR,
7651 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7652 mUserData->s.strName.c_str(), vrc);
7653
7654 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7655
7656 if (!fSeparate)
7657 {
7658 /*
7659 * Note that we don't release the lock here before calling the client,
7660 * because it doesn't need to call us back if called with a NULL argument.
7661 * Releasing the lock here is dangerous because we didn't prepare the
7662 * launch data yet, but the client we've just started may happen to be
7663 * too fast and call LockMachine() that will fail (because of PID, etc.),
7664 * so that the Machine will never get out of the Spawning session state.
7665 */
7666
7667 /* inform the session that it will be a remote one */
7668 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7669#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7670 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7671#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7672 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7673#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7674 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7675
7676 if (FAILED(rc))
7677 {
7678 /* restore the session state */
7679 mData->mSession.mState = SessionState_Unlocked;
7680 alock.release();
7681 mParent->i_addProcessToReap(pid);
7682 /* The failure may occur w/o any error info (from RPC), so provide one */
7683 return setError(VBOX_E_VM_ERROR,
7684 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7685 }
7686
7687 /* attach launch data to the machine */
7688 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7689 mData->mSession.mRemoteControls.push_back(aControl);
7690 mData->mSession.mProgress = aProgress;
7691 mData->mSession.mPID = pid;
7692 mData->mSession.mState = SessionState_Spawning;
7693 Assert(strCanonicalName.isNotEmpty());
7694 mData->mSession.mName = strCanonicalName;
7695 }
7696 else
7697 {
7698 /* For separate UI process we declare the launch as completed instantly, as the
7699 * actual headless VM start may or may not come. No point in remembering anything
7700 * yet, as what matters for us is when the headless VM gets started. */
7701 aProgress->i_notifyComplete(S_OK);
7702 }
7703
7704 alock.release();
7705 mParent->i_addProcessToReap(pid);
7706
7707 LogFlowThisFuncLeave();
7708 return S_OK;
7709}
7710
7711/**
7712 * Returns @c true if the given session machine instance has an open direct
7713 * session (and optionally also for direct sessions which are closing) and
7714 * returns the session control machine instance if so.
7715 *
7716 * Note that when the method returns @c false, the arguments remain unchanged.
7717 *
7718 * @param aMachine Session machine object.
7719 * @param aControl Direct session control object (optional).
7720 * @param aAllowClosing If true then additionally a session which is currently
7721 * being closed will also be allowed.
7722 *
7723 * @note locks this object for reading.
7724 */
7725bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7726 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7727 bool aAllowClosing /*= false*/)
7728{
7729 AutoLimitedCaller autoCaller(this);
7730 AssertComRCReturn(autoCaller.rc(), false);
7731
7732 /* just return false for inaccessible machines */
7733 if (getObjectState().getState() != ObjectState::Ready)
7734 return false;
7735
7736 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7737
7738 if ( ( mData->mSession.mState == SessionState_Locked
7739 && mData->mSession.mLockType == LockType_VM)
7740 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7741 )
7742 {
7743 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7744
7745 aMachine = mData->mSession.mMachine;
7746
7747 if (aControl != NULL)
7748 *aControl = mData->mSession.mDirectControl;
7749
7750 return true;
7751 }
7752
7753 return false;
7754}
7755
7756/**
7757 * Returns @c true if the given machine has an spawning direct session.
7758 *
7759 * @note locks this object for reading.
7760 */
7761bool Machine::i_isSessionSpawning()
7762{
7763 AutoLimitedCaller autoCaller(this);
7764 AssertComRCReturn(autoCaller.rc(), false);
7765
7766 /* just return false for inaccessible machines */
7767 if (getObjectState().getState() != ObjectState::Ready)
7768 return false;
7769
7770 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7771
7772 if (mData->mSession.mState == SessionState_Spawning)
7773 return true;
7774
7775 return false;
7776}
7777
7778/**
7779 * Called from the client watcher thread to check for unexpected client process
7780 * death during Session_Spawning state (e.g. before it successfully opened a
7781 * direct session).
7782 *
7783 * On Win32 and on OS/2, this method is called only when we've got the
7784 * direct client's process termination notification, so it always returns @c
7785 * true.
7786 *
7787 * On other platforms, this method returns @c true if the client process is
7788 * terminated and @c false if it's still alive.
7789 *
7790 * @note Locks this object for writing.
7791 */
7792bool Machine::i_checkForSpawnFailure()
7793{
7794 AutoCaller autoCaller(this);
7795 if (!autoCaller.isOk())
7796 {
7797 /* nothing to do */
7798 LogFlowThisFunc(("Already uninitialized!\n"));
7799 return true;
7800 }
7801
7802 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7803
7804 if (mData->mSession.mState != SessionState_Spawning)
7805 {
7806 /* nothing to do */
7807 LogFlowThisFunc(("Not spawning any more!\n"));
7808 return true;
7809 }
7810
7811 HRESULT rc = S_OK;
7812
7813 /* PID not yet initialized, skip check. */
7814 if (mData->mSession.mPID == NIL_RTPROCESS)
7815 return false;
7816
7817 RTPROCSTATUS status;
7818 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7819
7820 if (vrc != VERR_PROCESS_RUNNING)
7821 {
7822 Utf8Str strExtraInfo;
7823
7824#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7825 /* If the startup logfile exists and is of non-zero length, tell the
7826 user to look there for more details to encourage them to attach it
7827 when reporting startup issues. */
7828 Utf8Str strStartupLogFile = i_getStartupLogFilename();
7829 uint64_t cbStartupLogFile = 0;
7830 int vrc2 = RTFileQuerySize(strStartupLogFile.c_str(), &cbStartupLogFile);
7831 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7832 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strStartupLogFile.c_str()));
7833#endif
7834
7835 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7836 rc = setError(E_FAIL,
7837 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7838 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7839 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7840 rc = setError(E_FAIL,
7841 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7842 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7843 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7844 rc = setError(E_FAIL,
7845 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7846 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7847 else
7848 rc = setError(E_FAIL,
7849 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7850 i_getName().c_str(), vrc, strExtraInfo.c_str());
7851 }
7852
7853 if (FAILED(rc))
7854 {
7855 /* Close the remote session, remove the remote control from the list
7856 * and reset session state to Closed (@note keep the code in sync with
7857 * the relevant part in LockMachine()). */
7858
7859 Assert(mData->mSession.mRemoteControls.size() == 1);
7860 if (mData->mSession.mRemoteControls.size() == 1)
7861 {
7862 ErrorInfoKeeper eik;
7863 mData->mSession.mRemoteControls.front()->Uninitialize();
7864 }
7865
7866 mData->mSession.mRemoteControls.clear();
7867 mData->mSession.mState = SessionState_Unlocked;
7868
7869 /* finalize the progress after setting the state */
7870 if (!mData->mSession.mProgress.isNull())
7871 {
7872 mData->mSession.mProgress->notifyComplete(rc);
7873 mData->mSession.mProgress.setNull();
7874 }
7875
7876 mData->mSession.mPID = NIL_RTPROCESS;
7877
7878 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7879 return true;
7880 }
7881
7882 return false;
7883}
7884
7885/**
7886 * Checks whether the machine can be registered. If so, commits and saves
7887 * all settings.
7888 *
7889 * @note Must be called from mParent's write lock. Locks this object and
7890 * children for writing.
7891 */
7892HRESULT Machine::i_prepareRegister()
7893{
7894 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7895
7896 AutoLimitedCaller autoCaller(this);
7897 AssertComRCReturnRC(autoCaller.rc());
7898
7899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7900
7901 /* wait for state dependents to drop to zero */
7902 i_ensureNoStateDependencies();
7903
7904 if (!mData->mAccessible)
7905 return setError(VBOX_E_INVALID_OBJECT_STATE,
7906 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7907 mUserData->s.strName.c_str(),
7908 mData->mUuid.toString().c_str());
7909
7910 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7911
7912 if (mData->mRegistered)
7913 return setError(VBOX_E_INVALID_OBJECT_STATE,
7914 tr("The machine '%s' with UUID {%s} is already registered"),
7915 mUserData->s.strName.c_str(),
7916 mData->mUuid.toString().c_str());
7917
7918 HRESULT rc = S_OK;
7919
7920 // Ensure the settings are saved. If we are going to be registered and
7921 // no config file exists yet, create it by calling i_saveSettings() too.
7922 if ( (mData->flModifications)
7923 || (!mData->pMachineConfigFile->fileExists())
7924 )
7925 {
7926 rc = i_saveSettings(NULL);
7927 // no need to check whether VirtualBox.xml needs saving too since
7928 // we can't have a machine XML file rename pending
7929 if (FAILED(rc)) return rc;
7930 }
7931
7932 /* more config checking goes here */
7933
7934 if (SUCCEEDED(rc))
7935 {
7936 /* we may have had implicit modifications we want to fix on success */
7937 i_commit();
7938
7939 mData->mRegistered = true;
7940 }
7941 else
7942 {
7943 /* we may have had implicit modifications we want to cancel on failure*/
7944 i_rollback(false /* aNotify */);
7945 }
7946
7947 return rc;
7948}
7949
7950/**
7951 * Increases the number of objects dependent on the machine state or on the
7952 * registered state. Guarantees that these two states will not change at least
7953 * until #releaseStateDependency() is called.
7954 *
7955 * Depending on the @a aDepType value, additional state checks may be made.
7956 * These checks will set extended error info on failure. See
7957 * #checkStateDependency() for more info.
7958 *
7959 * If this method returns a failure, the dependency is not added and the caller
7960 * is not allowed to rely on any particular machine state or registration state
7961 * value and may return the failed result code to the upper level.
7962 *
7963 * @param aDepType Dependency type to add.
7964 * @param aState Current machine state (NULL if not interested).
7965 * @param aRegistered Current registered state (NULL if not interested).
7966 *
7967 * @note Locks this object for writing.
7968 */
7969HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
7970 MachineState_T *aState /* = NULL */,
7971 BOOL *aRegistered /* = NULL */)
7972{
7973 AutoCaller autoCaller(this);
7974 AssertComRCReturnRC(autoCaller.rc());
7975
7976 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7977
7978 HRESULT rc = i_checkStateDependency(aDepType);
7979 if (FAILED(rc)) return rc;
7980
7981 {
7982 if (mData->mMachineStateChangePending != 0)
7983 {
7984 /* i_ensureNoStateDependencies() is waiting for state dependencies to
7985 * drop to zero so don't add more. It may make sense to wait a bit
7986 * and retry before reporting an error (since the pending state
7987 * transition should be really quick) but let's just assert for
7988 * now to see if it ever happens on practice. */
7989
7990 AssertFailed();
7991
7992 return setError(E_ACCESSDENIED,
7993 tr("Machine state change is in progress. Please retry the operation later."));
7994 }
7995
7996 ++mData->mMachineStateDeps;
7997 Assert(mData->mMachineStateDeps != 0 /* overflow */);
7998 }
7999
8000 if (aState)
8001 *aState = mData->mMachineState;
8002 if (aRegistered)
8003 *aRegistered = mData->mRegistered;
8004
8005 return S_OK;
8006}
8007
8008/**
8009 * Decreases the number of objects dependent on the machine state.
8010 * Must always complete the #addStateDependency() call after the state
8011 * dependency is no more necessary.
8012 */
8013void Machine::i_releaseStateDependency()
8014{
8015 AutoCaller autoCaller(this);
8016 AssertComRCReturnVoid(autoCaller.rc());
8017
8018 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8019
8020 /* releaseStateDependency() w/o addStateDependency()? */
8021 AssertReturnVoid(mData->mMachineStateDeps != 0);
8022 -- mData->mMachineStateDeps;
8023
8024 if (mData->mMachineStateDeps == 0)
8025 {
8026 /* inform i_ensureNoStateDependencies() that there are no more deps */
8027 if (mData->mMachineStateChangePending != 0)
8028 {
8029 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8030 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8031 }
8032 }
8033}
8034
8035Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8036{
8037 /* start with nothing found */
8038 Utf8Str strResult("");
8039
8040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8041
8042 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8043 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8044 // found:
8045 strResult = it->second; // source is a Utf8Str
8046
8047 return strResult;
8048}
8049
8050// protected methods
8051/////////////////////////////////////////////////////////////////////////////
8052
8053/**
8054 * Performs machine state checks based on the @a aDepType value. If a check
8055 * fails, this method will set extended error info, otherwise it will return
8056 * S_OK. It is supposed, that on failure, the caller will immediately return
8057 * the return value of this method to the upper level.
8058 *
8059 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8060 *
8061 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8062 * current state of this machine object allows to change settings of the
8063 * machine (i.e. the machine is not registered, or registered but not running
8064 * and not saved). It is useful to call this method from Machine setters
8065 * before performing any change.
8066 *
8067 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8068 * as for MutableStateDep except that if the machine is saved, S_OK is also
8069 * returned. This is useful in setters which allow changing machine
8070 * properties when it is in the saved state.
8071 *
8072 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8073 * if the current state of this machine object allows to change runtime
8074 * changeable settings of the machine (i.e. the machine is not registered, or
8075 * registered but either running or not running and not saved). It is useful
8076 * to call this method from Machine setters before performing any changes to
8077 * runtime changeable settings.
8078 *
8079 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8080 * the same as for MutableOrRunningStateDep except that if the machine is
8081 * saved, S_OK is also returned. This is useful in setters which allow
8082 * changing runtime and saved state changeable machine properties.
8083 *
8084 * @param aDepType Dependency type to check.
8085 *
8086 * @note Non Machine based classes should use #addStateDependency() and
8087 * #releaseStateDependency() methods or the smart AutoStateDependency
8088 * template.
8089 *
8090 * @note This method must be called from under this object's read or write
8091 * lock.
8092 */
8093HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8094{
8095 switch (aDepType)
8096 {
8097 case AnyStateDep:
8098 {
8099 break;
8100 }
8101 case MutableStateDep:
8102 {
8103 if ( mData->mRegistered
8104 && ( !i_isSessionMachine()
8105 || ( mData->mMachineState != MachineState_Aborted
8106 && mData->mMachineState != MachineState_Teleported
8107 && mData->mMachineState != MachineState_PoweredOff
8108 )
8109 )
8110 )
8111 return setError(VBOX_E_INVALID_VM_STATE,
8112 tr("The machine is not mutable (state is %s)"),
8113 Global::stringifyMachineState(mData->mMachineState));
8114 break;
8115 }
8116 case MutableOrSavedStateDep:
8117 {
8118 if ( mData->mRegistered
8119 && ( !i_isSessionMachine()
8120 || ( mData->mMachineState != MachineState_Aborted
8121 && mData->mMachineState != MachineState_Teleported
8122 && mData->mMachineState != MachineState_Saved
8123 && mData->mMachineState != MachineState_PoweredOff
8124 )
8125 )
8126 )
8127 return setError(VBOX_E_INVALID_VM_STATE,
8128 tr("The machine is not mutable (state is %s)"),
8129 Global::stringifyMachineState(mData->mMachineState));
8130 break;
8131 }
8132 case MutableOrRunningStateDep:
8133 {
8134 if ( mData->mRegistered
8135 && ( !i_isSessionMachine()
8136 || ( mData->mMachineState != MachineState_Aborted
8137 && mData->mMachineState != MachineState_Teleported
8138 && mData->mMachineState != MachineState_PoweredOff
8139 && !Global::IsOnline(mData->mMachineState)
8140 )
8141 )
8142 )
8143 return setError(VBOX_E_INVALID_VM_STATE,
8144 tr("The machine is not mutable (state is %s)"),
8145 Global::stringifyMachineState(mData->mMachineState));
8146 break;
8147 }
8148 case MutableOrSavedOrRunningStateDep:
8149 {
8150 if ( mData->mRegistered
8151 && ( !i_isSessionMachine()
8152 || ( mData->mMachineState != MachineState_Aborted
8153 && mData->mMachineState != MachineState_Teleported
8154 && mData->mMachineState != MachineState_Saved
8155 && mData->mMachineState != MachineState_PoweredOff
8156 && !Global::IsOnline(mData->mMachineState)
8157 )
8158 )
8159 )
8160 return setError(VBOX_E_INVALID_VM_STATE,
8161 tr("The machine is not mutable (state is %s)"),
8162 Global::stringifyMachineState(mData->mMachineState));
8163 break;
8164 }
8165 }
8166
8167 return S_OK;
8168}
8169
8170/**
8171 * Helper to initialize all associated child objects and allocate data
8172 * structures.
8173 *
8174 * This method must be called as a part of the object's initialization procedure
8175 * (usually done in the #init() method).
8176 *
8177 * @note Must be called only from #init() or from #registeredInit().
8178 */
8179HRESULT Machine::initDataAndChildObjects()
8180{
8181 AutoCaller autoCaller(this);
8182 AssertComRCReturnRC(autoCaller.rc());
8183 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8184 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8185
8186 AssertReturn(!mData->mAccessible, E_FAIL);
8187
8188 /* allocate data structures */
8189 mSSData.allocate();
8190 mUserData.allocate();
8191 mHWData.allocate();
8192 mMediaData.allocate();
8193 mStorageControllers.allocate();
8194 mUSBControllers.allocate();
8195
8196 /* initialize mOSTypeId */
8197 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8198
8199 /* create associated BIOS settings object */
8200 unconst(mBIOSSettings).createObject();
8201 mBIOSSettings->init(this);
8202
8203 /* create an associated VRDE object (default is disabled) */
8204 unconst(mVRDEServer).createObject();
8205 mVRDEServer->init(this);
8206
8207 /* create associated serial port objects */
8208 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8209 {
8210 unconst(mSerialPorts[slot]).createObject();
8211 mSerialPorts[slot]->init(this, slot);
8212 }
8213
8214 /* create associated parallel port objects */
8215 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8216 {
8217 unconst(mParallelPorts[slot]).createObject();
8218 mParallelPorts[slot]->init(this, slot);
8219 }
8220
8221 /* create the audio adapter object (always present, default is disabled) */
8222 unconst(mAudioAdapter).createObject();
8223 mAudioAdapter->init(this);
8224
8225 /* create the USB device filters object (always present) */
8226 unconst(mUSBDeviceFilters).createObject();
8227 mUSBDeviceFilters->init(this);
8228
8229 /* create associated network adapter objects */
8230 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8231 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8232 {
8233 unconst(mNetworkAdapters[slot]).createObject();
8234 mNetworkAdapters[slot]->init(this, slot);
8235 }
8236
8237 /* create the bandwidth control */
8238 unconst(mBandwidthControl).createObject();
8239 mBandwidthControl->init(this);
8240
8241 return S_OK;
8242}
8243
8244/**
8245 * Helper to uninitialize all associated child objects and to free all data
8246 * structures.
8247 *
8248 * This method must be called as a part of the object's uninitialization
8249 * procedure (usually done in the #uninit() method).
8250 *
8251 * @note Must be called only from #uninit() or from #registeredInit().
8252 */
8253void Machine::uninitDataAndChildObjects()
8254{
8255 AutoCaller autoCaller(this);
8256 AssertComRCReturnVoid(autoCaller.rc());
8257 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8258 || getObjectState().getState() == ObjectState::Limited);
8259
8260 /* tell all our other child objects we've been uninitialized */
8261 if (mBandwidthControl)
8262 {
8263 mBandwidthControl->uninit();
8264 unconst(mBandwidthControl).setNull();
8265 }
8266
8267 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8268 {
8269 if (mNetworkAdapters[slot])
8270 {
8271 mNetworkAdapters[slot]->uninit();
8272 unconst(mNetworkAdapters[slot]).setNull();
8273 }
8274 }
8275
8276 if (mUSBDeviceFilters)
8277 {
8278 mUSBDeviceFilters->uninit();
8279 unconst(mUSBDeviceFilters).setNull();
8280 }
8281
8282 if (mAudioAdapter)
8283 {
8284 mAudioAdapter->uninit();
8285 unconst(mAudioAdapter).setNull();
8286 }
8287
8288 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8289 {
8290 if (mParallelPorts[slot])
8291 {
8292 mParallelPorts[slot]->uninit();
8293 unconst(mParallelPorts[slot]).setNull();
8294 }
8295 }
8296
8297 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8298 {
8299 if (mSerialPorts[slot])
8300 {
8301 mSerialPorts[slot]->uninit();
8302 unconst(mSerialPorts[slot]).setNull();
8303 }
8304 }
8305
8306 if (mVRDEServer)
8307 {
8308 mVRDEServer->uninit();
8309 unconst(mVRDEServer).setNull();
8310 }
8311
8312 if (mBIOSSettings)
8313 {
8314 mBIOSSettings->uninit();
8315 unconst(mBIOSSettings).setNull();
8316 }
8317
8318 /* Deassociate media (only when a real Machine or a SnapshotMachine
8319 * instance is uninitialized; SessionMachine instances refer to real
8320 * Machine media). This is necessary for a clean re-initialization of
8321 * the VM after successfully re-checking the accessibility state. Note
8322 * that in case of normal Machine or SnapshotMachine uninitialization (as
8323 * a result of unregistering or deleting the snapshot), outdated media
8324 * attachments will already be uninitialized and deleted, so this
8325 * code will not affect them. */
8326 if ( !!mMediaData
8327 && (!i_isSessionMachine())
8328 )
8329 {
8330 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8331 it != mMediaData->mAttachments.end();
8332 ++it)
8333 {
8334 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8335 if (pMedium.isNull())
8336 continue;
8337 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8338 AssertComRC(rc);
8339 }
8340 }
8341
8342 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8343 {
8344 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8345 if (mData->mFirstSnapshot)
8346 {
8347 // snapshots tree is protected by machine write lock; strictly
8348 // this isn't necessary here since we're deleting the entire
8349 // machine, but otherwise we assert in Snapshot::uninit()
8350 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8351 mData->mFirstSnapshot->uninit();
8352 mData->mFirstSnapshot.setNull();
8353 }
8354
8355 mData->mCurrentSnapshot.setNull();
8356 }
8357
8358 /* free data structures (the essential mData structure is not freed here
8359 * since it may be still in use) */
8360 mMediaData.free();
8361 mStorageControllers.free();
8362 mUSBControllers.free();
8363 mHWData.free();
8364 mUserData.free();
8365 mSSData.free();
8366}
8367
8368/**
8369 * Returns a pointer to the Machine object for this machine that acts like a
8370 * parent for complex machine data objects such as shared folders, etc.
8371 *
8372 * For primary Machine objects and for SnapshotMachine objects, returns this
8373 * object's pointer itself. For SessionMachine objects, returns the peer
8374 * (primary) machine pointer.
8375 */
8376Machine* Machine::i_getMachine()
8377{
8378 if (i_isSessionMachine())
8379 return (Machine*)mPeer;
8380 return this;
8381}
8382
8383/**
8384 * Makes sure that there are no machine state dependents. If necessary, waits
8385 * for the number of dependents to drop to zero.
8386 *
8387 * Make sure this method is called from under this object's write lock to
8388 * guarantee that no new dependents may be added when this method returns
8389 * control to the caller.
8390 *
8391 * @note Locks this object for writing. The lock will be released while waiting
8392 * (if necessary).
8393 *
8394 * @warning To be used only in methods that change the machine state!
8395 */
8396void Machine::i_ensureNoStateDependencies()
8397{
8398 AssertReturnVoid(isWriteLockOnCurrentThread());
8399
8400 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8401
8402 /* Wait for all state dependents if necessary */
8403 if (mData->mMachineStateDeps != 0)
8404 {
8405 /* lazy semaphore creation */
8406 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8407 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8408
8409 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8410 mData->mMachineStateDeps));
8411
8412 ++mData->mMachineStateChangePending;
8413
8414 /* reset the semaphore before waiting, the last dependent will signal
8415 * it */
8416 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8417
8418 alock.release();
8419
8420 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8421
8422 alock.acquire();
8423
8424 -- mData->mMachineStateChangePending;
8425 }
8426}
8427
8428/**
8429 * Changes the machine state and informs callbacks.
8430 *
8431 * This method is not intended to fail so it either returns S_OK or asserts (and
8432 * returns a failure).
8433 *
8434 * @note Locks this object for writing.
8435 */
8436HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8437{
8438 LogFlowThisFuncEnter();
8439 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8440 Assert(aMachineState != MachineState_Null);
8441
8442 AutoCaller autoCaller(this);
8443 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8444
8445 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8446
8447 /* wait for state dependents to drop to zero */
8448 i_ensureNoStateDependencies();
8449
8450 MachineState_T const enmOldState = mData->mMachineState;
8451 if (enmOldState != aMachineState)
8452 {
8453 mData->mMachineState = aMachineState;
8454 RTTimeNow(&mData->mLastStateChange);
8455
8456#ifdef VBOX_WITH_DTRACE_R3_MAIN
8457 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8458#endif
8459 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8460 }
8461
8462 LogFlowThisFuncLeave();
8463 return S_OK;
8464}
8465
8466/**
8467 * Searches for a shared folder with the given logical name
8468 * in the collection of shared folders.
8469 *
8470 * @param aName logical name of the shared folder
8471 * @param aSharedFolder where to return the found object
8472 * @param aSetError whether to set the error info if the folder is
8473 * not found
8474 * @return
8475 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8476 *
8477 * @note
8478 * must be called from under the object's lock!
8479 */
8480HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8481 ComObjPtr<SharedFolder> &aSharedFolder,
8482 bool aSetError /* = false */)
8483{
8484 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8485 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8486 it != mHWData->mSharedFolders.end();
8487 ++it)
8488 {
8489 SharedFolder *pSF = *it;
8490 AutoCaller autoCaller(pSF);
8491 if (pSF->i_getName() == aName)
8492 {
8493 aSharedFolder = pSF;
8494 rc = S_OK;
8495 break;
8496 }
8497 }
8498
8499 if (aSetError && FAILED(rc))
8500 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8501
8502 return rc;
8503}
8504
8505/**
8506 * Initializes all machine instance data from the given settings structures
8507 * from XML. The exception is the machine UUID which needs special handling
8508 * depending on the caller's use case, so the caller needs to set that herself.
8509 *
8510 * This gets called in several contexts during machine initialization:
8511 *
8512 * -- When machine XML exists on disk already and needs to be loaded into memory,
8513 * for example, from registeredInit() to load all registered machines on
8514 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8515 * attached to the machine should be part of some media registry already.
8516 *
8517 * -- During OVF import, when a machine config has been constructed from an
8518 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8519 * ensure that the media listed as attachments in the config (which have
8520 * been imported from the OVF) receive the correct registry ID.
8521 *
8522 * -- During VM cloning.
8523 *
8524 * @param config Machine settings from XML.
8525 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8526 * for each attached medium in the config.
8527 * @return
8528 */
8529HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8530 const Guid *puuidRegistry)
8531{
8532 // copy name, description, OS type, teleporter, UTC etc.
8533 mUserData->s = config.machineUserData;
8534
8535 // Decode the Icon overide data from config userdata and set onto Machine.
8536 #define DECODE_STR_MAX _1M
8537 const char* pszStr = config.machineUserData.ovIcon.c_str();
8538 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8539 if (cbOut > DECODE_STR_MAX)
8540 return setError(E_FAIL,
8541 tr("Icon Data too long.'%d' > '%d'"),
8542 cbOut,
8543 DECODE_STR_MAX);
8544 mUserData->mIcon.resize(cbOut);
8545 int vrc = VINF_SUCCESS;
8546 if (cbOut)
8547 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8548 if (RT_FAILURE(vrc))
8549 {
8550 mUserData->mIcon.resize(0);
8551 return setError(E_FAIL,
8552 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8553 pszStr,
8554 vrc);
8555 }
8556
8557 // look up the object by Id to check it is valid
8558 ComPtr<IGuestOSType> guestOSType;
8559 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8560 guestOSType.asOutParam());
8561 if (FAILED(rc)) return rc;
8562
8563 // stateFile (optional)
8564 if (config.strStateFile.isEmpty())
8565 mSSData->strStateFilePath.setNull();
8566 else
8567 {
8568 Utf8Str stateFilePathFull(config.strStateFile);
8569 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8570 if (RT_FAILURE(vrc))
8571 return setError(E_FAIL,
8572 tr("Invalid saved state file path '%s' (%Rrc)"),
8573 config.strStateFile.c_str(),
8574 vrc);
8575 mSSData->strStateFilePath = stateFilePathFull;
8576 }
8577
8578 // snapshot folder needs special processing so set it again
8579 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8580 if (FAILED(rc)) return rc;
8581
8582 /* Copy the extra data items (Not in any case config is already the same as
8583 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8584 * make sure the extra data map is copied). */
8585 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8586
8587 /* currentStateModified (optional, default is true) */
8588 mData->mCurrentStateModified = config.fCurrentStateModified;
8589
8590 mData->mLastStateChange = config.timeLastStateChange;
8591
8592 /*
8593 * note: all mUserData members must be assigned prior this point because
8594 * we need to commit changes in order to let mUserData be shared by all
8595 * snapshot machine instances.
8596 */
8597 mUserData.commitCopy();
8598
8599 // machine registry, if present (must be loaded before snapshots)
8600 if (config.canHaveOwnMediaRegistry())
8601 {
8602 // determine machine folder
8603 Utf8Str strMachineFolder = i_getSettingsFileFull();
8604 strMachineFolder.stripFilename();
8605 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8606 config.mediaRegistry,
8607 strMachineFolder);
8608 if (FAILED(rc)) return rc;
8609 }
8610
8611 /* Snapshot node (optional) */
8612 size_t cRootSnapshots;
8613 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8614 {
8615 // there must be only one root snapshot
8616 Assert(cRootSnapshots == 1);
8617
8618 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8619
8620 rc = i_loadSnapshot(snap,
8621 config.uuidCurrentSnapshot,
8622 NULL); // no parent == first snapshot
8623 if (FAILED(rc)) return rc;
8624 }
8625
8626 // hardware data
8627 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8628 if (FAILED(rc)) return rc;
8629
8630 // load storage controllers
8631 rc = i_loadStorageControllers(config.storageMachine,
8632 puuidRegistry,
8633 NULL /* puuidSnapshot */);
8634 if (FAILED(rc)) return rc;
8635
8636 /*
8637 * NOTE: the assignment below must be the last thing to do,
8638 * otherwise it will be not possible to change the settings
8639 * somewhere in the code above because all setters will be
8640 * blocked by i_checkStateDependency(MutableStateDep).
8641 */
8642
8643 /* set the machine state to Aborted or Saved when appropriate */
8644 if (config.fAborted)
8645 {
8646 mSSData->strStateFilePath.setNull();
8647
8648 /* no need to use i_setMachineState() during init() */
8649 mData->mMachineState = MachineState_Aborted;
8650 }
8651 else if (!mSSData->strStateFilePath.isEmpty())
8652 {
8653 /* no need to use i_setMachineState() during init() */
8654 mData->mMachineState = MachineState_Saved;
8655 }
8656
8657 // after loading settings, we are no longer different from the XML on disk
8658 mData->flModifications = 0;
8659
8660 return S_OK;
8661}
8662
8663/**
8664 * Recursively loads all snapshots starting from the given.
8665 *
8666 * @param aNode <Snapshot> node.
8667 * @param aCurSnapshotId Current snapshot ID from the settings file.
8668 * @param aParentSnapshot Parent snapshot.
8669 */
8670HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8671 const Guid &aCurSnapshotId,
8672 Snapshot *aParentSnapshot)
8673{
8674 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8675 AssertReturn(!i_isSessionMachine(), E_FAIL);
8676
8677 HRESULT rc = S_OK;
8678
8679 Utf8Str strStateFile;
8680 if (!data.strStateFile.isEmpty())
8681 {
8682 /* optional */
8683 strStateFile = data.strStateFile;
8684 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8685 if (RT_FAILURE(vrc))
8686 return setError(E_FAIL,
8687 tr("Invalid saved state file path '%s' (%Rrc)"),
8688 strStateFile.c_str(),
8689 vrc);
8690 }
8691
8692 /* create a snapshot machine object */
8693 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8694 pSnapshotMachine.createObject();
8695 rc = pSnapshotMachine->initFromSettings(this,
8696 data.hardware,
8697 &data.debugging,
8698 &data.autostart,
8699 data.storage,
8700 data.uuid.ref(),
8701 strStateFile);
8702 if (FAILED(rc)) return rc;
8703
8704 /* create a snapshot object */
8705 ComObjPtr<Snapshot> pSnapshot;
8706 pSnapshot.createObject();
8707 /* initialize the snapshot */
8708 rc = pSnapshot->init(mParent, // VirtualBox object
8709 data.uuid,
8710 data.strName,
8711 data.strDescription,
8712 data.timestamp,
8713 pSnapshotMachine,
8714 aParentSnapshot);
8715 if (FAILED(rc)) return rc;
8716
8717 /* memorize the first snapshot if necessary */
8718 if (!mData->mFirstSnapshot)
8719 mData->mFirstSnapshot = pSnapshot;
8720
8721 /* memorize the current snapshot when appropriate */
8722 if ( !mData->mCurrentSnapshot
8723 && pSnapshot->i_getId() == aCurSnapshotId
8724 )
8725 mData->mCurrentSnapshot = pSnapshot;
8726
8727 // now create the children
8728 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8729 it != data.llChildSnapshots.end();
8730 ++it)
8731 {
8732 const settings::Snapshot &childData = *it;
8733 // recurse
8734 rc = i_loadSnapshot(childData,
8735 aCurSnapshotId,
8736 pSnapshot); // parent = the one we created above
8737 if (FAILED(rc)) return rc;
8738 }
8739
8740 return rc;
8741}
8742
8743/**
8744 * Loads settings into mHWData.
8745 *
8746 * @param data Reference to the hardware settings.
8747 * @param pDbg Pointer to the debugging settings.
8748 * @param pAutostart Pointer to the autostart settings.
8749 */
8750HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8751 const settings::Autostart *pAutostart)
8752{
8753 AssertReturn(!i_isSessionMachine(), E_FAIL);
8754
8755 HRESULT rc = S_OK;
8756
8757 try
8758 {
8759 /* The hardware version attribute (optional). */
8760 mHWData->mHWVersion = data.strVersion;
8761 mHWData->mHardwareUUID = data.uuid;
8762
8763 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8764 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8765 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8766 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8767 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8768 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8769 mHWData->mPAEEnabled = data.fPAE;
8770 mHWData->mLongMode = data.enmLongMode;
8771 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8772 mHWData->mCPUCount = data.cCPUs;
8773 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8774 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8775 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8776
8777 // cpu
8778 if (mHWData->mCPUHotPlugEnabled)
8779 {
8780 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8781 it != data.llCpus.end();
8782 ++it)
8783 {
8784 const settings::Cpu &cpu = *it;
8785
8786 mHWData->mCPUAttached[cpu.ulId] = true;
8787 }
8788 }
8789
8790 // cpuid leafs
8791 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8792 it != data.llCpuIdLeafs.end();
8793 ++it)
8794 {
8795 const settings::CpuIdLeaf &leaf = *it;
8796
8797 switch (leaf.ulId)
8798 {
8799 case 0x0:
8800 case 0x1:
8801 case 0x2:
8802 case 0x3:
8803 case 0x4:
8804 case 0x5:
8805 case 0x6:
8806 case 0x7:
8807 case 0x8:
8808 case 0x9:
8809 case 0xA:
8810 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8811 break;
8812
8813 case 0x80000000:
8814 case 0x80000001:
8815 case 0x80000002:
8816 case 0x80000003:
8817 case 0x80000004:
8818 case 0x80000005:
8819 case 0x80000006:
8820 case 0x80000007:
8821 case 0x80000008:
8822 case 0x80000009:
8823 case 0x8000000A:
8824 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8825 break;
8826
8827 default:
8828 /* just ignore */
8829 break;
8830 }
8831 }
8832
8833 mHWData->mMemorySize = data.ulMemorySizeMB;
8834 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8835
8836 // boot order
8837 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8838 {
8839 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8840 if (it == data.mapBootOrder.end())
8841 mHWData->mBootOrder[i] = DeviceType_Null;
8842 else
8843 mHWData->mBootOrder[i] = it->second;
8844 }
8845
8846 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8847 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8848 mHWData->mMonitorCount = data.cMonitors;
8849 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8850 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8851 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8852 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8853 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8854 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8855 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8856 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8857 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8858 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8859 if (!data.strVideoCaptureFile.isEmpty())
8860 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8861 else
8862 mHWData->mVideoCaptureFile.setNull();
8863 mHWData->mFirmwareType = data.firmwareType;
8864 mHWData->mPointingHIDType = data.pointingHIDType;
8865 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8866 mHWData->mChipsetType = data.chipsetType;
8867 mHWData->mParavirtProvider = data.paravirtProvider;
8868 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8869 mHWData->mHPETEnabled = data.fHPETEnabled;
8870
8871 /* VRDEServer */
8872 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8873 if (FAILED(rc)) return rc;
8874
8875 /* BIOS */
8876 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8877 if (FAILED(rc)) return rc;
8878
8879 // Bandwidth control (must come before network adapters)
8880 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8881 if (FAILED(rc)) return rc;
8882
8883 /* Shared folders */
8884 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8885 it != data.usbSettings.llUSBControllers.end();
8886 ++it)
8887 {
8888 const settings::USBController &settingsCtrl = *it;
8889 ComObjPtr<USBController> newCtrl;
8890
8891 newCtrl.createObject();
8892 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8893 mUSBControllers->push_back(newCtrl);
8894 }
8895
8896 /* USB device filters */
8897 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8898 if (FAILED(rc)) return rc;
8899
8900 // network adapters
8901 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8902 size_t oldCount = mNetworkAdapters.size();
8903 if (newCount > oldCount)
8904 {
8905 mNetworkAdapters.resize(newCount);
8906 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8907 {
8908 unconst(mNetworkAdapters[slot]).createObject();
8909 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8910 }
8911 }
8912 else if (newCount < oldCount)
8913 mNetworkAdapters.resize(newCount);
8914 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8915 it != data.llNetworkAdapters.end();
8916 ++it)
8917 {
8918 const settings::NetworkAdapter &nic = *it;
8919
8920 /* slot unicity is guaranteed by XML Schema */
8921 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8922 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8923 if (FAILED(rc)) return rc;
8924 }
8925
8926 // serial ports
8927 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8928 it != data.llSerialPorts.end();
8929 ++it)
8930 {
8931 const settings::SerialPort &s = *it;
8932
8933 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8934 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8935 if (FAILED(rc)) return rc;
8936 }
8937
8938 // parallel ports (optional)
8939 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8940 it != data.llParallelPorts.end();
8941 ++it)
8942 {
8943 const settings::ParallelPort &p = *it;
8944
8945 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8946 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8947 if (FAILED(rc)) return rc;
8948 }
8949
8950 /* AudioAdapter */
8951 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8952 if (FAILED(rc)) return rc;
8953
8954 /* Shared folders */
8955 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8956 it != data.llSharedFolders.end();
8957 ++it)
8958 {
8959 const settings::SharedFolder &sf = *it;
8960
8961 ComObjPtr<SharedFolder> sharedFolder;
8962 /* Check for double entries. Not allowed! */
8963 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
8964 if (SUCCEEDED(rc))
8965 return setError(VBOX_E_OBJECT_IN_USE,
8966 tr("Shared folder named '%s' already exists"),
8967 sf.strName.c_str());
8968
8969 /* Create the new shared folder. Don't break on error. This will be
8970 * reported when the machine starts. */
8971 sharedFolder.createObject();
8972 rc = sharedFolder->init(i_getMachine(),
8973 sf.strName,
8974 sf.strHostPath,
8975 RT_BOOL(sf.fWritable),
8976 RT_BOOL(sf.fAutoMount),
8977 false /* fFailOnError */);
8978 if (FAILED(rc)) return rc;
8979 mHWData->mSharedFolders.push_back(sharedFolder);
8980 }
8981
8982 // Clipboard
8983 mHWData->mClipboardMode = data.clipboardMode;
8984
8985 // drag'n'drop
8986 mHWData->mDnDMode = data.dndMode;
8987
8988 // guest settings
8989 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
8990
8991 // IO settings
8992 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
8993 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
8994
8995 // Host PCI devices
8996 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
8997 it != data.pciAttachments.end();
8998 ++it)
8999 {
9000 const settings::HostPCIDeviceAttachment &hpda = *it;
9001 ComObjPtr<PCIDeviceAttachment> pda;
9002
9003 pda.createObject();
9004 pda->i_loadSettings(this, hpda);
9005 mHWData->mPCIDeviceAssignments.push_back(pda);
9006 }
9007
9008 /*
9009 * (The following isn't really real hardware, but it lives in HWData
9010 * for reasons of convenience.)
9011 */
9012
9013#ifdef VBOX_WITH_GUEST_PROPS
9014 /* Guest properties (optional) */
9015
9016 /* Only load transient guest properties for configs which have saved
9017 * state, because there shouldn't be any for powered off VMs. The same
9018 * logic applies for snapshots, as offline snapshots shouldn't have
9019 * any such properties. They confuse the code in various places.
9020 * Note: can't rely on the machine state, as it isn't set yet. */
9021 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9022 /* apologies for the hacky unconst() usage, but this needs hacking
9023 * actually inconsistent settings into consistency, otherwise there
9024 * will be some corner cases where the inconsistency survives
9025 * surprisingly long without getting fixed, especially for snapshots
9026 * as there are no config changes. */
9027 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9028 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9029 it != llGuestProperties.end();
9030 /*nothing*/)
9031 {
9032 const settings::GuestProperty &prop = *it;
9033 uint32_t fFlags = guestProp::NILFLAG;
9034 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9035 if ( fSkipTransientGuestProperties
9036 && ( fFlags & guestProp::TRANSIENT
9037 || fFlags & guestProp::TRANSRESET))
9038 {
9039 it = llGuestProperties.erase(it);
9040 continue;
9041 }
9042 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9043 mHWData->mGuestProperties[prop.strName] = property;
9044 ++it;
9045 }
9046#endif /* VBOX_WITH_GUEST_PROPS defined */
9047
9048 rc = i_loadDebugging(pDbg);
9049 if (FAILED(rc))
9050 return rc;
9051
9052 mHWData->mAutostart = *pAutostart;
9053
9054 /* default frontend */
9055 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9056 }
9057 catch(std::bad_alloc &)
9058 {
9059 return E_OUTOFMEMORY;
9060 }
9061
9062 AssertComRC(rc);
9063 return rc;
9064}
9065
9066/**
9067 * Called from Machine::loadHardware() to load the debugging settings of the
9068 * machine.
9069 *
9070 * @param pDbg Pointer to the settings.
9071 */
9072HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9073{
9074 mHWData->mDebugging = *pDbg;
9075 /* no more processing currently required, this will probably change. */
9076 return S_OK;
9077}
9078
9079/**
9080 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9081 *
9082 * @param data
9083 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9084 * @param puuidSnapshot
9085 * @return
9086 */
9087HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9088 const Guid *puuidRegistry,
9089 const Guid *puuidSnapshot)
9090{
9091 AssertReturn(!i_isSessionMachine(), E_FAIL);
9092
9093 HRESULT rc = S_OK;
9094
9095 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9096 it != data.llStorageControllers.end();
9097 ++it)
9098 {
9099 const settings::StorageController &ctlData = *it;
9100
9101 ComObjPtr<StorageController> pCtl;
9102 /* Try to find one with the name first. */
9103 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9104 if (SUCCEEDED(rc))
9105 return setError(VBOX_E_OBJECT_IN_USE,
9106 tr("Storage controller named '%s' already exists"),
9107 ctlData.strName.c_str());
9108
9109 pCtl.createObject();
9110 rc = pCtl->init(this,
9111 ctlData.strName,
9112 ctlData.storageBus,
9113 ctlData.ulInstance,
9114 ctlData.fBootable);
9115 if (FAILED(rc)) return rc;
9116
9117 mStorageControllers->push_back(pCtl);
9118
9119 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9120 if (FAILED(rc)) return rc;
9121
9122 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9123 if (FAILED(rc)) return rc;
9124
9125 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9126 if (FAILED(rc)) return rc;
9127
9128 /* Set IDE emulation settings (only for AHCI controller). */
9129 if (ctlData.controllerType == StorageControllerType_IntelAhci)
9130 {
9131 if ( (FAILED(rc = pCtl->i_setIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort)))
9132 || (FAILED(rc = pCtl->i_setIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort)))
9133 || (FAILED(rc = pCtl->i_setIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort)))
9134 || (FAILED(rc = pCtl->i_setIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort)))
9135 )
9136 return rc;
9137 }
9138
9139 /* Load the attached devices now. */
9140 rc = i_loadStorageDevices(pCtl,
9141 ctlData,
9142 puuidRegistry,
9143 puuidSnapshot);
9144 if (FAILED(rc)) return rc;
9145 }
9146
9147 return S_OK;
9148}
9149
9150/**
9151 * Called from i_loadStorageControllers for a controller's devices.
9152 *
9153 * @param aStorageController
9154 * @param data
9155 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9156 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9157 * @return
9158 */
9159HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9160 const settings::StorageController &data,
9161 const Guid *puuidRegistry,
9162 const Guid *puuidSnapshot)
9163{
9164 HRESULT rc = S_OK;
9165
9166 /* paranoia: detect duplicate attachments */
9167 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9168 it != data.llAttachedDevices.end();
9169 ++it)
9170 {
9171 const settings::AttachedDevice &ad = *it;
9172
9173 for (settings::AttachedDevicesList::const_iterator it2 = it;
9174 it2 != data.llAttachedDevices.end();
9175 ++it2)
9176 {
9177 if (it == it2)
9178 continue;
9179
9180 const settings::AttachedDevice &ad2 = *it2;
9181
9182 if ( ad.lPort == ad2.lPort
9183 && ad.lDevice == ad2.lDevice)
9184 {
9185 return setError(E_FAIL,
9186 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9187 aStorageController->i_getName().c_str(),
9188 ad.lPort,
9189 ad.lDevice,
9190 mUserData->s.strName.c_str());
9191 }
9192 }
9193 }
9194
9195 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9196 it != data.llAttachedDevices.end();
9197 ++it)
9198 {
9199 const settings::AttachedDevice &dev = *it;
9200 ComObjPtr<Medium> medium;
9201
9202 switch (dev.deviceType)
9203 {
9204 case DeviceType_Floppy:
9205 case DeviceType_DVD:
9206 if (dev.strHostDriveSrc.isNotEmpty())
9207 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9208 false /* fRefresh */, medium);
9209 else
9210 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9211 dev.uuid,
9212 false /* fRefresh */,
9213 false /* aSetError */,
9214 medium);
9215 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9216 // This is not an error. The host drive or UUID might have vanished, so just go
9217 // ahead without this removeable medium attachment
9218 rc = S_OK;
9219 break;
9220
9221 case DeviceType_HardDisk:
9222 {
9223 /* find a hard disk by UUID */
9224 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9225 if (FAILED(rc))
9226 {
9227 if (i_isSnapshotMachine())
9228 {
9229 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9230 // so the user knows that the bad disk is in a snapshot somewhere
9231 com::ErrorInfo info;
9232 return setError(E_FAIL,
9233 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9234 puuidSnapshot->raw(),
9235 info.getText().raw());
9236 }
9237 else
9238 return rc;
9239 }
9240
9241 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9242
9243 if (medium->i_getType() == MediumType_Immutable)
9244 {
9245 if (i_isSnapshotMachine())
9246 return setError(E_FAIL,
9247 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9248 "of the virtual machine '%s' ('%s')"),
9249 medium->i_getLocationFull().c_str(),
9250 dev.uuid.raw(),
9251 puuidSnapshot->raw(),
9252 mUserData->s.strName.c_str(),
9253 mData->m_strConfigFileFull.c_str());
9254
9255 return setError(E_FAIL,
9256 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9257 medium->i_getLocationFull().c_str(),
9258 dev.uuid.raw(),
9259 mUserData->s.strName.c_str(),
9260 mData->m_strConfigFileFull.c_str());
9261 }
9262
9263 if (medium->i_getType() == MediumType_MultiAttach)
9264 {
9265 if (i_isSnapshotMachine())
9266 return setError(E_FAIL,
9267 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9268 "of the virtual machine '%s' ('%s')"),
9269 medium->i_getLocationFull().c_str(),
9270 dev.uuid.raw(),
9271 puuidSnapshot->raw(),
9272 mUserData->s.strName.c_str(),
9273 mData->m_strConfigFileFull.c_str());
9274
9275 return setError(E_FAIL,
9276 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9277 medium->i_getLocationFull().c_str(),
9278 dev.uuid.raw(),
9279 mUserData->s.strName.c_str(),
9280 mData->m_strConfigFileFull.c_str());
9281 }
9282
9283 if ( !i_isSnapshotMachine()
9284 && medium->i_getChildren().size() != 0
9285 )
9286 return setError(E_FAIL,
9287 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9288 "because it has %d differencing child hard disks"),
9289 medium->i_getLocationFull().c_str(),
9290 dev.uuid.raw(),
9291 mUserData->s.strName.c_str(),
9292 mData->m_strConfigFileFull.c_str(),
9293 medium->i_getChildren().size());
9294
9295 if (i_findAttachment(mMediaData->mAttachments,
9296 medium))
9297 return setError(E_FAIL,
9298 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9299 medium->i_getLocationFull().c_str(),
9300 dev.uuid.raw(),
9301 mUserData->s.strName.c_str(),
9302 mData->m_strConfigFileFull.c_str());
9303
9304 break;
9305 }
9306
9307 default:
9308 return setError(E_FAIL,
9309 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9310 medium->i_getLocationFull().c_str(),
9311 mUserData->s.strName.c_str(),
9312 mData->m_strConfigFileFull.c_str());
9313 }
9314
9315 if (FAILED(rc))
9316 break;
9317
9318 /* Bandwidth groups are loaded at this point. */
9319 ComObjPtr<BandwidthGroup> pBwGroup;
9320
9321 if (!dev.strBwGroup.isEmpty())
9322 {
9323 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9324 if (FAILED(rc))
9325 return setError(E_FAIL,
9326 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9327 medium->i_getLocationFull().c_str(),
9328 dev.strBwGroup.c_str(),
9329 mUserData->s.strName.c_str(),
9330 mData->m_strConfigFileFull.c_str());
9331 pBwGroup->i_reference();
9332 }
9333
9334 const Bstr controllerName = aStorageController->i_getName();
9335 ComObjPtr<MediumAttachment> pAttachment;
9336 pAttachment.createObject();
9337 rc = pAttachment->init(this,
9338 medium,
9339 controllerName,
9340 dev.lPort,
9341 dev.lDevice,
9342 dev.deviceType,
9343 false,
9344 dev.fPassThrough,
9345 dev.fTempEject,
9346 dev.fNonRotational,
9347 dev.fDiscard,
9348 dev.fHotPluggable,
9349 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9350 if (FAILED(rc)) break;
9351
9352 /* associate the medium with this machine and snapshot */
9353 if (!medium.isNull())
9354 {
9355 AutoCaller medCaller(medium);
9356 if (FAILED(medCaller.rc())) return medCaller.rc();
9357 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9358
9359 if (i_isSnapshotMachine())
9360 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9361 else
9362 rc = medium->i_addBackReference(mData->mUuid);
9363 /* If the medium->addBackReference fails it sets an appropriate
9364 * error message, so no need to do any guesswork here. */
9365
9366 if (puuidRegistry)
9367 // caller wants registry ID to be set on all attached media (OVF import case)
9368 medium->i_addRegistry(*puuidRegistry);
9369 }
9370
9371 if (FAILED(rc))
9372 break;
9373
9374 /* back up mMediaData to let registeredInit() properly rollback on failure
9375 * (= limited accessibility) */
9376 i_setModified(IsModified_Storage);
9377 mMediaData.backup();
9378 mMediaData->mAttachments.push_back(pAttachment);
9379 }
9380
9381 return rc;
9382}
9383
9384/**
9385 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9386 *
9387 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9388 * @param aSnapshot where to return the found snapshot
9389 * @param aSetError true to set extended error info on failure
9390 */
9391HRESULT Machine::i_findSnapshotById(const Guid &aId,
9392 ComObjPtr<Snapshot> &aSnapshot,
9393 bool aSetError /* = false */)
9394{
9395 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9396
9397 if (!mData->mFirstSnapshot)
9398 {
9399 if (aSetError)
9400 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9401 return E_FAIL;
9402 }
9403
9404 if (aId.isZero())
9405 aSnapshot = mData->mFirstSnapshot;
9406 else
9407 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9408
9409 if (!aSnapshot)
9410 {
9411 if (aSetError)
9412 return setError(E_FAIL,
9413 tr("Could not find a snapshot with UUID {%s}"),
9414 aId.toString().c_str());
9415 return E_FAIL;
9416 }
9417
9418 return S_OK;
9419}
9420
9421/**
9422 * Returns the snapshot with the given name or fails of no such snapshot.
9423 *
9424 * @param aName snapshot name to find
9425 * @param aSnapshot where to return the found snapshot
9426 * @param aSetError true to set extended error info on failure
9427 */
9428HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9429 ComObjPtr<Snapshot> &aSnapshot,
9430 bool aSetError /* = false */)
9431{
9432 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9433
9434 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9435
9436 if (!mData->mFirstSnapshot)
9437 {
9438 if (aSetError)
9439 return setError(VBOX_E_OBJECT_NOT_FOUND,
9440 tr("This machine does not have any snapshots"));
9441 return VBOX_E_OBJECT_NOT_FOUND;
9442 }
9443
9444 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9445
9446 if (!aSnapshot)
9447 {
9448 if (aSetError)
9449 return setError(VBOX_E_OBJECT_NOT_FOUND,
9450 tr("Could not find a snapshot named '%s'"), strName.c_str());
9451 return VBOX_E_OBJECT_NOT_FOUND;
9452 }
9453
9454 return S_OK;
9455}
9456
9457/**
9458 * Returns a storage controller object with the given name.
9459 *
9460 * @param aName storage controller name to find
9461 * @param aStorageController where to return the found storage controller
9462 * @param aSetError true to set extended error info on failure
9463 */
9464HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9465 ComObjPtr<StorageController> &aStorageController,
9466 bool aSetError /* = false */)
9467{
9468 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9469
9470 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9471 it != mStorageControllers->end();
9472 ++it)
9473 {
9474 if ((*it)->i_getName() == aName)
9475 {
9476 aStorageController = (*it);
9477 return S_OK;
9478 }
9479 }
9480
9481 if (aSetError)
9482 return setError(VBOX_E_OBJECT_NOT_FOUND,
9483 tr("Could not find a storage controller named '%s'"),
9484 aName.c_str());
9485 return VBOX_E_OBJECT_NOT_FOUND;
9486}
9487
9488/**
9489 * Returns a USB controller object with the given name.
9490 *
9491 * @param aName USB controller name to find
9492 * @param aUSBController where to return the found USB controller
9493 * @param aSetError true to set extended error info on failure
9494 */
9495HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9496 ComObjPtr<USBController> &aUSBController,
9497 bool aSetError /* = false */)
9498{
9499 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9500
9501 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9502 it != mUSBControllers->end();
9503 ++it)
9504 {
9505 if ((*it)->i_getName() == aName)
9506 {
9507 aUSBController = (*it);
9508 return S_OK;
9509 }
9510 }
9511
9512 if (aSetError)
9513 return setError(VBOX_E_OBJECT_NOT_FOUND,
9514 tr("Could not find a storage controller named '%s'"),
9515 aName.c_str());
9516 return VBOX_E_OBJECT_NOT_FOUND;
9517}
9518
9519/**
9520 * Returns the number of USB controller instance of the given type.
9521 *
9522 * @param enmType USB controller type.
9523 */
9524ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9525{
9526 ULONG cCtrls = 0;
9527
9528 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9529 it != mUSBControllers->end();
9530 ++it)
9531 {
9532 if ((*it)->i_getControllerType() == enmType)
9533 cCtrls++;
9534 }
9535
9536 return cCtrls;
9537}
9538
9539HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9540 MediaData::AttachmentList &atts)
9541{
9542 AutoCaller autoCaller(this);
9543 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9544
9545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9546
9547 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9548 it != mMediaData->mAttachments.end();
9549 ++it)
9550 {
9551 const ComObjPtr<MediumAttachment> &pAtt = *it;
9552 // should never happen, but deal with NULL pointers in the list.
9553 AssertStmt(!pAtt.isNull(), continue);
9554
9555 // getControllerName() needs caller+read lock
9556 AutoCaller autoAttCaller(pAtt);
9557 if (FAILED(autoAttCaller.rc()))
9558 {
9559 atts.clear();
9560 return autoAttCaller.rc();
9561 }
9562 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9563
9564 if (pAtt->i_getControllerName() == Bstr(aName).raw())
9565 atts.push_back(pAtt);
9566 }
9567
9568 return S_OK;
9569}
9570
9571
9572/**
9573 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9574 * file if the machine name was changed and about creating a new settings file
9575 * if this is a new machine.
9576 *
9577 * @note Must be never called directly but only from #saveSettings().
9578 */
9579HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9580{
9581 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9582
9583 HRESULT rc = S_OK;
9584
9585 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9586
9587 /// @todo need to handle primary group change, too
9588
9589 /* attempt to rename the settings file if machine name is changed */
9590 if ( mUserData->s.fNameSync
9591 && mUserData.isBackedUp()
9592 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9593 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9594 )
9595 {
9596 bool dirRenamed = false;
9597 bool fileRenamed = false;
9598
9599 Utf8Str configFile, newConfigFile;
9600 Utf8Str configFilePrev, newConfigFilePrev;
9601 Utf8Str configDir, newConfigDir;
9602
9603 do
9604 {
9605 int vrc = VINF_SUCCESS;
9606
9607 Utf8Str name = mUserData.backedUpData()->s.strName;
9608 Utf8Str newName = mUserData->s.strName;
9609 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9610 if (group == "/")
9611 group.setNull();
9612 Utf8Str newGroup = mUserData->s.llGroups.front();
9613 if (newGroup == "/")
9614 newGroup.setNull();
9615
9616 configFile = mData->m_strConfigFileFull;
9617
9618 /* first, rename the directory if it matches the group and machine name */
9619 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9620 group.c_str(), RTPATH_DELIMITER, name.c_str());
9621 /** @todo hack, make somehow use of ComposeMachineFilename */
9622 if (mUserData->s.fDirectoryIncludesUUID)
9623 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9624 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9625 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9626 /** @todo hack, make somehow use of ComposeMachineFilename */
9627 if (mUserData->s.fDirectoryIncludesUUID)
9628 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9629 configDir = configFile;
9630 configDir.stripFilename();
9631 newConfigDir = configDir;
9632 if ( configDir.length() >= groupPlusName.length()
9633 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9634 groupPlusName.c_str()))
9635 {
9636 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9637 Utf8Str newConfigBaseDir(newConfigDir);
9638 newConfigDir.append(newGroupPlusName);
9639 /* consistency: use \ if appropriate on the platform */
9640 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9641 /* new dir and old dir cannot be equal here because of 'if'
9642 * above and because name != newName */
9643 Assert(configDir != newConfigDir);
9644 if (!fSettingsFileIsNew)
9645 {
9646 /* perform real rename only if the machine is not new */
9647 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9648 if ( vrc == VERR_FILE_NOT_FOUND
9649 || vrc == VERR_PATH_NOT_FOUND)
9650 {
9651 /* create the parent directory, then retry renaming */
9652 Utf8Str parent(newConfigDir);
9653 parent.stripFilename();
9654 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9655 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9656 }
9657 if (RT_FAILURE(vrc))
9658 {
9659 rc = setError(E_FAIL,
9660 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9661 configDir.c_str(),
9662 newConfigDir.c_str(),
9663 vrc);
9664 break;
9665 }
9666 /* delete subdirectories which are no longer needed */
9667 Utf8Str dir(configDir);
9668 dir.stripFilename();
9669 while (dir != newConfigBaseDir && dir != ".")
9670 {
9671 vrc = RTDirRemove(dir.c_str());
9672 if (RT_FAILURE(vrc))
9673 break;
9674 dir.stripFilename();
9675 }
9676 dirRenamed = true;
9677 }
9678 }
9679
9680 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9681 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9682
9683 /* then try to rename the settings file itself */
9684 if (newConfigFile != configFile)
9685 {
9686 /* get the path to old settings file in renamed directory */
9687 configFile = Utf8StrFmt("%s%c%s",
9688 newConfigDir.c_str(),
9689 RTPATH_DELIMITER,
9690 RTPathFilename(configFile.c_str()));
9691 if (!fSettingsFileIsNew)
9692 {
9693 /* perform real rename only if the machine is not new */
9694 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9695 if (RT_FAILURE(vrc))
9696 {
9697 rc = setError(E_FAIL,
9698 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9699 configFile.c_str(),
9700 newConfigFile.c_str(),
9701 vrc);
9702 break;
9703 }
9704 fileRenamed = true;
9705 configFilePrev = configFile;
9706 configFilePrev += "-prev";
9707 newConfigFilePrev = newConfigFile;
9708 newConfigFilePrev += "-prev";
9709 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9710 }
9711 }
9712
9713 // update m_strConfigFileFull amd mConfigFile
9714 mData->m_strConfigFileFull = newConfigFile;
9715 // compute the relative path too
9716 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9717
9718 // store the old and new so that VirtualBox::i_saveSettings() can update
9719 // the media registry
9720 if ( mData->mRegistered
9721 && (configDir != newConfigDir || configFile != newConfigFile))
9722 {
9723 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9724
9725 if (pfNeedsGlobalSaveSettings)
9726 *pfNeedsGlobalSaveSettings = true;
9727 }
9728
9729 // in the saved state file path, replace the old directory with the new directory
9730 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9731 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9732
9733 // and do the same thing for the saved state file paths of all the online snapshots
9734 if (mData->mFirstSnapshot)
9735 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9736 newConfigDir.c_str());
9737 }
9738 while (0);
9739
9740 if (FAILED(rc))
9741 {
9742 /* silently try to rename everything back */
9743 if (fileRenamed)
9744 {
9745 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9746 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9747 }
9748 if (dirRenamed)
9749 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9750 }
9751
9752 if (FAILED(rc)) return rc;
9753 }
9754
9755 if (fSettingsFileIsNew)
9756 {
9757 /* create a virgin config file */
9758 int vrc = VINF_SUCCESS;
9759
9760 /* ensure the settings directory exists */
9761 Utf8Str path(mData->m_strConfigFileFull);
9762 path.stripFilename();
9763 if (!RTDirExists(path.c_str()))
9764 {
9765 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9766 if (RT_FAILURE(vrc))
9767 {
9768 return setError(E_FAIL,
9769 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9770 path.c_str(),
9771 vrc);
9772 }
9773 }
9774
9775 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9776 path = Utf8Str(mData->m_strConfigFileFull);
9777 RTFILE f = NIL_RTFILE;
9778 vrc = RTFileOpen(&f, path.c_str(),
9779 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9780 if (RT_FAILURE(vrc))
9781 return setError(E_FAIL,
9782 tr("Could not create the settings file '%s' (%Rrc)"),
9783 path.c_str(),
9784 vrc);
9785 RTFileClose(f);
9786 }
9787
9788 return rc;
9789}
9790
9791/**
9792 * Saves and commits machine data, user data and hardware data.
9793 *
9794 * Note that on failure, the data remains uncommitted.
9795 *
9796 * @a aFlags may combine the following flags:
9797 *
9798 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9799 * Used when saving settings after an operation that makes them 100%
9800 * correspond to the settings from the current snapshot.
9801 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9802 * #isReallyModified() returns false. This is necessary for cases when we
9803 * change machine data directly, not through the backup()/commit() mechanism.
9804 * - SaveS_Force: settings will be saved without doing a deep compare of the
9805 * settings structures. This is used when this is called because snapshots
9806 * have changed to avoid the overhead of the deep compare.
9807 *
9808 * @note Must be called from under this object's write lock. Locks children for
9809 * writing.
9810 *
9811 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9812 * initialized to false and that will be set to true by this function if
9813 * the caller must invoke VirtualBox::i_saveSettings() because the global
9814 * settings have changed. This will happen if a machine rename has been
9815 * saved and the global machine and media registries will therefore need
9816 * updating.
9817 */
9818HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9819 int aFlags /*= 0*/)
9820{
9821 LogFlowThisFuncEnter();
9822
9823 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9824
9825 /* make sure child objects are unable to modify the settings while we are
9826 * saving them */
9827 i_ensureNoStateDependencies();
9828
9829 AssertReturn(!i_isSnapshotMachine(),
9830 E_FAIL);
9831
9832 HRESULT rc = S_OK;
9833 bool fNeedsWrite = false;
9834
9835 /* First, prepare to save settings. It will care about renaming the
9836 * settings directory and file if the machine name was changed and about
9837 * creating a new settings file if this is a new machine. */
9838 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9839 if (FAILED(rc)) return rc;
9840
9841 // keep a pointer to the current settings structures
9842 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9843 settings::MachineConfigFile *pNewConfig = NULL;
9844
9845 try
9846 {
9847 // make a fresh one to have everyone write stuff into
9848 pNewConfig = new settings::MachineConfigFile(NULL);
9849 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9850
9851 // now go and copy all the settings data from COM to the settings structures
9852 // (this calles i_saveSettings() on all the COM objects in the machine)
9853 i_copyMachineDataToSettings(*pNewConfig);
9854
9855 if (aFlags & SaveS_ResetCurStateModified)
9856 {
9857 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9858 mData->mCurrentStateModified = FALSE;
9859 fNeedsWrite = true; // always, no need to compare
9860 }
9861 else if (aFlags & SaveS_Force)
9862 {
9863 fNeedsWrite = true; // always, no need to compare
9864 }
9865 else
9866 {
9867 if (!mData->mCurrentStateModified)
9868 {
9869 // do a deep compare of the settings that we just saved with the settings
9870 // previously stored in the config file; this invokes MachineConfigFile::operator==
9871 // which does a deep compare of all the settings, which is expensive but less expensive
9872 // than writing out XML in vain
9873 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9874
9875 // could still be modified if any settings changed
9876 mData->mCurrentStateModified = fAnySettingsChanged;
9877
9878 fNeedsWrite = fAnySettingsChanged;
9879 }
9880 else
9881 fNeedsWrite = true;
9882 }
9883
9884 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9885
9886 if (fNeedsWrite)
9887 // now spit it all out!
9888 pNewConfig->write(mData->m_strConfigFileFull);
9889
9890 mData->pMachineConfigFile = pNewConfig;
9891 delete pOldConfig;
9892 i_commit();
9893
9894 // after saving settings, we are no longer different from the XML on disk
9895 mData->flModifications = 0;
9896 }
9897 catch (HRESULT err)
9898 {
9899 // we assume that error info is set by the thrower
9900 rc = err;
9901
9902 // restore old config
9903 delete pNewConfig;
9904 mData->pMachineConfigFile = pOldConfig;
9905 }
9906 catch (...)
9907 {
9908 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9909 }
9910
9911 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9912 {
9913 /* Fire the data change event, even on failure (since we've already
9914 * committed all data). This is done only for SessionMachines because
9915 * mutable Machine instances are always not registered (i.e. private
9916 * to the client process that creates them) and thus don't need to
9917 * inform callbacks. */
9918 if (i_isSessionMachine())
9919 mParent->i_onMachineDataChange(mData->mUuid);
9920 }
9921
9922 LogFlowThisFunc(("rc=%08X\n", rc));
9923 LogFlowThisFuncLeave();
9924 return rc;
9925}
9926
9927/**
9928 * Implementation for saving the machine settings into the given
9929 * settings::MachineConfigFile instance. This copies machine extradata
9930 * from the previous machine config file in the instance data, if any.
9931 *
9932 * This gets called from two locations:
9933 *
9934 * -- Machine::i_saveSettings(), during the regular XML writing;
9935 *
9936 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9937 * exported to OVF and we write the VirtualBox proprietary XML
9938 * into a <vbox:Machine> tag.
9939 *
9940 * This routine fills all the fields in there, including snapshots, *except*
9941 * for the following:
9942 *
9943 * -- fCurrentStateModified. There is some special logic associated with that.
9944 *
9945 * The caller can then call MachineConfigFile::write() or do something else
9946 * with it.
9947 *
9948 * Caller must hold the machine lock!
9949 *
9950 * This throws XML errors and HRESULT, so the caller must have a catch block!
9951 */
9952void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9953{
9954 // deep copy extradata
9955 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9956
9957 config.uuid = mData->mUuid;
9958
9959 // copy name, description, OS type, teleport, UTC etc.
9960 config.machineUserData = mUserData->s;
9961
9962 // Encode the Icon Override data from Machine and store on config userdata.
9963 std::vector<BYTE> iconByte;
9964 getIcon(iconByte);
9965 ssize_t cbData = iconByte.size();
9966 if (cbData > 0)
9967 {
9968 ssize_t cchOut = RTBase64EncodedLength(cbData);
9969 Utf8Str strIconData;
9970 strIconData.reserve(cchOut+1);
9971 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9972 strIconData.mutableRaw(), strIconData.capacity(),
9973 NULL);
9974 if (RT_FAILURE(vrc))
9975 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
9976 strIconData.jolt();
9977 config.machineUserData.ovIcon = strIconData;
9978 }
9979 else
9980 config.machineUserData.ovIcon.setNull();
9981
9982 if ( mData->mMachineState == MachineState_Saved
9983 || mData->mMachineState == MachineState_Restoring
9984 // when doing certain snapshot operations we may or may not have
9985 // a saved state in the current state, so keep everything as is
9986 || ( ( mData->mMachineState == MachineState_Snapshotting
9987 || mData->mMachineState == MachineState_DeletingSnapshot
9988 || mData->mMachineState == MachineState_RestoringSnapshot)
9989 && (!mSSData->strStateFilePath.isEmpty())
9990 )
9991 )
9992 {
9993 Assert(!mSSData->strStateFilePath.isEmpty());
9994 /* try to make the file name relative to the settings file dir */
9995 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
9996 }
9997 else
9998 {
9999 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10000 config.strStateFile.setNull();
10001 }
10002
10003 if (mData->mCurrentSnapshot)
10004 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10005 else
10006 config.uuidCurrentSnapshot.clear();
10007
10008 config.timeLastStateChange = mData->mLastStateChange;
10009 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10010 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10011
10012 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10013 if (FAILED(rc)) throw rc;
10014
10015 rc = i_saveStorageControllers(config.storageMachine);
10016 if (FAILED(rc)) throw rc;
10017
10018 // save machine's media registry if this is VirtualBox 4.0 or later
10019 if (config.canHaveOwnMediaRegistry())
10020 {
10021 // determine machine folder
10022 Utf8Str strMachineFolder = i_getSettingsFileFull();
10023 strMachineFolder.stripFilename();
10024 mParent->i_saveMediaRegistry(config.mediaRegistry,
10025 i_getId(), // only media with registry ID == machine UUID
10026 strMachineFolder);
10027 // this throws HRESULT
10028 }
10029
10030 // save snapshots
10031 rc = i_saveAllSnapshots(config);
10032 if (FAILED(rc)) throw rc;
10033}
10034
10035/**
10036 * Saves all snapshots of the machine into the given machine config file. Called
10037 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10038 * @param config
10039 * @return
10040 */
10041HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10042{
10043 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10044
10045 HRESULT rc = S_OK;
10046
10047 try
10048 {
10049 config.llFirstSnapshot.clear();
10050
10051 if (mData->mFirstSnapshot)
10052 {
10053 // the settings use a list for "the first snapshot"
10054 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10055
10056 // get reference to the snapshot on the list and work on that
10057 // element straight in the list to avoid excessive copying later
10058 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10059 if (FAILED(rc)) throw rc;
10060 }
10061
10062// if (mType == IsSessionMachine)
10063// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10064
10065 }
10066 catch (HRESULT err)
10067 {
10068 /* we assume that error info is set by the thrower */
10069 rc = err;
10070 }
10071 catch (...)
10072 {
10073 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10074 }
10075
10076 return rc;
10077}
10078
10079/**
10080 * Saves the VM hardware configuration. It is assumed that the
10081 * given node is empty.
10082 *
10083 * @param data Reference to the settings object for the hardware config.
10084 * @param pDbg Pointer to the settings object for the debugging config
10085 * which happens to live in mHWData.
10086 * @param pAutostart Pointer to the settings object for the autostart config
10087 * which happens to live in mHWData.
10088 */
10089HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10090 settings::Autostart *pAutostart)
10091{
10092 HRESULT rc = S_OK;
10093
10094 try
10095 {
10096 /* The hardware version attribute (optional).
10097 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10098 if ( mHWData->mHWVersion == "1"
10099 && mSSData->strStateFilePath.isEmpty()
10100 )
10101 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10102 other point needs to be found where this can be done. */
10103
10104 data.strVersion = mHWData->mHWVersion;
10105 data.uuid = mHWData->mHardwareUUID;
10106
10107 // CPU
10108 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10109 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10110 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10111 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10112 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10113 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10114 data.fPAE = !!mHWData->mPAEEnabled;
10115 data.enmLongMode = mHWData->mLongMode;
10116 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10117 data.cCPUs = mHWData->mCPUCount;
10118 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10119 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10120 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10121
10122 data.llCpus.clear();
10123 if (data.fCpuHotPlug)
10124 {
10125 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10126 {
10127 if (mHWData->mCPUAttached[idx])
10128 {
10129 settings::Cpu cpu;
10130 cpu.ulId = idx;
10131 data.llCpus.push_back(cpu);
10132 }
10133 }
10134 }
10135
10136 /* Standard and Extended CPUID leafs. */
10137 data.llCpuIdLeafs.clear();
10138 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10139 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10140 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10141 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10142 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10143 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10144
10145 // memory
10146 data.ulMemorySizeMB = mHWData->mMemorySize;
10147 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10148
10149 // firmware
10150 data.firmwareType = mHWData->mFirmwareType;
10151
10152 // HID
10153 data.pointingHIDType = mHWData->mPointingHIDType;
10154 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10155
10156 // chipset
10157 data.chipsetType = mHWData->mChipsetType;
10158
10159 // paravirt
10160 data.paravirtProvider = mHWData->mParavirtProvider;
10161
10162
10163 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10164
10165 // HPET
10166 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10167
10168 // boot order
10169 data.mapBootOrder.clear();
10170 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10171 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10172
10173 // display
10174 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10175 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10176 data.cMonitors = mHWData->mMonitorCount;
10177 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10178 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10179 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10180 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10181 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10182 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10183 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10184 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10185 {
10186 if (mHWData->maVideoCaptureScreens[i])
10187 ASMBitSet(&data.u64VideoCaptureScreens, i);
10188 else
10189 ASMBitClear(&data.u64VideoCaptureScreens, i);
10190 }
10191 /* store relative video capture file if possible */
10192 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10193
10194 /* VRDEServer settings (optional) */
10195 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10196 if (FAILED(rc)) throw rc;
10197
10198 /* BIOS (required) */
10199 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10200 if (FAILED(rc)) throw rc;
10201
10202 /* USB Controller (required) */
10203 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10204 {
10205 ComObjPtr<USBController> ctrl = *it;
10206 settings::USBController settingsCtrl;
10207
10208 settingsCtrl.strName = ctrl->i_getName();
10209 settingsCtrl.enmType = ctrl->i_getControllerType();
10210
10211 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10212 }
10213
10214 /* USB device filters (required) */
10215 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10216 if (FAILED(rc)) throw rc;
10217
10218 /* Network adapters (required) */
10219 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10220 data.llNetworkAdapters.clear();
10221 /* Write out only the nominal number of network adapters for this
10222 * chipset type. Since Machine::commit() hasn't been called there
10223 * may be extra NIC settings in the vector. */
10224 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10225 {
10226 settings::NetworkAdapter nic;
10227 nic.ulSlot = (uint32_t)slot;
10228 /* paranoia check... must not be NULL, but must not crash either. */
10229 if (mNetworkAdapters[slot])
10230 {
10231 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10232 if (FAILED(rc)) throw rc;
10233
10234 data.llNetworkAdapters.push_back(nic);
10235 }
10236 }
10237
10238 /* Serial ports */
10239 data.llSerialPorts.clear();
10240 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10241 {
10242 settings::SerialPort s;
10243 s.ulSlot = slot;
10244 rc = mSerialPorts[slot]->i_saveSettings(s);
10245 if (FAILED(rc)) return rc;
10246
10247 data.llSerialPorts.push_back(s);
10248 }
10249
10250 /* Parallel ports */
10251 data.llParallelPorts.clear();
10252 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10253 {
10254 settings::ParallelPort p;
10255 p.ulSlot = slot;
10256 rc = mParallelPorts[slot]->i_saveSettings(p);
10257 if (FAILED(rc)) return rc;
10258
10259 data.llParallelPorts.push_back(p);
10260 }
10261
10262 /* Audio adapter */
10263 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10264 if (FAILED(rc)) return rc;
10265
10266 /* Shared folders */
10267 data.llSharedFolders.clear();
10268 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10269 it != mHWData->mSharedFolders.end();
10270 ++it)
10271 {
10272 SharedFolder *pSF = *it;
10273 AutoCaller sfCaller(pSF);
10274 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10275 settings::SharedFolder sf;
10276 sf.strName = pSF->i_getName();
10277 sf.strHostPath = pSF->i_getHostPath();
10278 sf.fWritable = !!pSF->i_isWritable();
10279 sf.fAutoMount = !!pSF->i_isAutoMounted();
10280
10281 data.llSharedFolders.push_back(sf);
10282 }
10283
10284 // clipboard
10285 data.clipboardMode = mHWData->mClipboardMode;
10286
10287 // drag'n'drop
10288 data.dndMode = mHWData->mDnDMode;
10289
10290 /* Guest */
10291 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10292
10293 // IO settings
10294 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10295 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10296
10297 /* BandwidthControl (required) */
10298 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10299 if (FAILED(rc)) throw rc;
10300
10301 /* Host PCI devices */
10302 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10303 it != mHWData->mPCIDeviceAssignments.end();
10304 ++it)
10305 {
10306 ComObjPtr<PCIDeviceAttachment> pda = *it;
10307 settings::HostPCIDeviceAttachment hpda;
10308
10309 rc = pda->i_saveSettings(hpda);
10310 if (FAILED(rc)) throw rc;
10311
10312 data.pciAttachments.push_back(hpda);
10313 }
10314
10315
10316 // guest properties
10317 data.llGuestProperties.clear();
10318#ifdef VBOX_WITH_GUEST_PROPS
10319 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10320 it != mHWData->mGuestProperties.end();
10321 ++it)
10322 {
10323 HWData::GuestProperty property = it->second;
10324
10325 /* Remove transient guest properties at shutdown unless we
10326 * are saving state. Note that restoring snapshot intentionally
10327 * keeps them, they will be removed if appropriate once the final
10328 * machine state is set (as crashes etc. need to work). */
10329 if ( ( mData->mMachineState == MachineState_PoweredOff
10330 || mData->mMachineState == MachineState_Aborted
10331 || mData->mMachineState == MachineState_Teleported)
10332 && ( property.mFlags & guestProp::TRANSIENT
10333 || property.mFlags & guestProp::TRANSRESET))
10334 continue;
10335 settings::GuestProperty prop;
10336 prop.strName = it->first;
10337 prop.strValue = property.strValue;
10338 prop.timestamp = property.mTimestamp;
10339 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10340 guestProp::writeFlags(property.mFlags, szFlags);
10341 prop.strFlags = szFlags;
10342
10343 data.llGuestProperties.push_back(prop);
10344 }
10345
10346 /* I presume this doesn't require a backup(). */
10347 mData->mGuestPropertiesModified = FALSE;
10348#endif /* VBOX_WITH_GUEST_PROPS defined */
10349
10350 *pDbg = mHWData->mDebugging;
10351 *pAutostart = mHWData->mAutostart;
10352
10353 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10354 }
10355 catch(std::bad_alloc &)
10356 {
10357 return E_OUTOFMEMORY;
10358 }
10359
10360 AssertComRC(rc);
10361 return rc;
10362}
10363
10364/**
10365 * Saves the storage controller configuration.
10366 *
10367 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10368 */
10369HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10370{
10371 data.llStorageControllers.clear();
10372
10373 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10374 it != mStorageControllers->end();
10375 ++it)
10376 {
10377 HRESULT rc;
10378 ComObjPtr<StorageController> pCtl = *it;
10379
10380 settings::StorageController ctl;
10381 ctl.strName = pCtl->i_getName();
10382 ctl.controllerType = pCtl->i_getControllerType();
10383 ctl.storageBus = pCtl->i_getStorageBus();
10384 ctl.ulInstance = pCtl->i_getInstance();
10385 ctl.fBootable = pCtl->i_getBootable();
10386
10387 /* Save the port count. */
10388 ULONG portCount;
10389 rc = pCtl->COMGETTER(PortCount)(&portCount);
10390 ComAssertComRCRet(rc, rc);
10391 ctl.ulPortCount = portCount;
10392
10393 /* Save fUseHostIOCache */
10394 BOOL fUseHostIOCache;
10395 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10396 ComAssertComRCRet(rc, rc);
10397 ctl.fUseHostIOCache = !!fUseHostIOCache;
10398
10399 /* Save IDE emulation settings. */
10400 if (ctl.controllerType == StorageControllerType_IntelAhci)
10401 {
10402 if ( (FAILED(rc = pCtl->i_getIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))
10403 || (FAILED(rc = pCtl->i_getIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))
10404 || (FAILED(rc = pCtl->i_getIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))
10405 || (FAILED(rc = pCtl->i_getIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))
10406 )
10407 ComAssertComRCRet(rc, rc);
10408 }
10409
10410 /* save the devices now. */
10411 rc = i_saveStorageDevices(pCtl, ctl);
10412 ComAssertComRCRet(rc, rc);
10413
10414 data.llStorageControllers.push_back(ctl);
10415 }
10416
10417 return S_OK;
10418}
10419
10420/**
10421 * Saves the hard disk configuration.
10422 */
10423HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10424 settings::StorageController &data)
10425{
10426 MediaData::AttachmentList atts;
10427
10428 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10429 if (FAILED(rc)) return rc;
10430
10431 data.llAttachedDevices.clear();
10432 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10433 it != atts.end();
10434 ++it)
10435 {
10436 settings::AttachedDevice dev;
10437 IMediumAttachment *iA = *it;
10438 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10439 Medium *pMedium = pAttach->i_getMedium();
10440
10441 dev.deviceType = pAttach->i_getType();
10442 dev.lPort = pAttach->i_getPort();
10443 dev.lDevice = pAttach->i_getDevice();
10444 dev.fPassThrough = pAttach->i_getPassthrough();
10445 dev.fHotPluggable = pAttach->i_getHotPluggable();
10446 if (pMedium)
10447 {
10448 if (pMedium->i_isHostDrive())
10449 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10450 else
10451 dev.uuid = pMedium->i_getId();
10452 dev.fTempEject = pAttach->i_getTempEject();
10453 dev.fNonRotational = pAttach->i_getNonRotational();
10454 dev.fDiscard = pAttach->i_getDiscard();
10455 }
10456
10457 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10458
10459 data.llAttachedDevices.push_back(dev);
10460 }
10461
10462 return S_OK;
10463}
10464
10465/**
10466 * Saves machine state settings as defined by aFlags
10467 * (SaveSTS_* values).
10468 *
10469 * @param aFlags Combination of SaveSTS_* flags.
10470 *
10471 * @note Locks objects for writing.
10472 */
10473HRESULT Machine::i_saveStateSettings(int aFlags)
10474{
10475 if (aFlags == 0)
10476 return S_OK;
10477
10478 AutoCaller autoCaller(this);
10479 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10480
10481 /* This object's write lock is also necessary to serialize file access
10482 * (prevent concurrent reads and writes) */
10483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10484
10485 HRESULT rc = S_OK;
10486
10487 Assert(mData->pMachineConfigFile);
10488
10489 try
10490 {
10491 if (aFlags & SaveSTS_CurStateModified)
10492 mData->pMachineConfigFile->fCurrentStateModified = true;
10493
10494 if (aFlags & SaveSTS_StateFilePath)
10495 {
10496 if (!mSSData->strStateFilePath.isEmpty())
10497 /* try to make the file name relative to the settings file dir */
10498 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10499 else
10500 mData->pMachineConfigFile->strStateFile.setNull();
10501 }
10502
10503 if (aFlags & SaveSTS_StateTimeStamp)
10504 {
10505 Assert( mData->mMachineState != MachineState_Aborted
10506 || mSSData->strStateFilePath.isEmpty());
10507
10508 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10509
10510 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10511//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10512 }
10513
10514 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10515 }
10516 catch (...)
10517 {
10518 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10519 }
10520
10521 return rc;
10522}
10523
10524/**
10525 * Ensures that the given medium is added to a media registry. If this machine
10526 * was created with 4.0 or later, then the machine registry is used. Otherwise
10527 * the global VirtualBox media registry is used.
10528 *
10529 * Caller must NOT hold machine lock, media tree or any medium locks!
10530 *
10531 * @param pMedium
10532 */
10533void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10534{
10535 /* Paranoia checks: do not hold machine or media tree locks. */
10536 AssertReturnVoid(!isWriteLockOnCurrentThread());
10537 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10538
10539 ComObjPtr<Medium> pBase;
10540 {
10541 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10542 pBase = pMedium->i_getBase();
10543 }
10544
10545 /* Paranoia checks: do not hold medium locks. */
10546 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10547 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10548
10549 // decide which medium registry to use now that the medium is attached:
10550 Guid uuid;
10551 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10552 // machine XML is VirtualBox 4.0 or higher:
10553 uuid = i_getId(); // machine UUID
10554 else
10555 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10556
10557 if (pMedium->i_addRegistry(uuid))
10558 mParent->i_markRegistryModified(uuid);
10559
10560 /* For more complex hard disk structures it can happen that the base
10561 * medium isn't yet associated with any medium registry. Do that now. */
10562 if (pMedium != pBase)
10563 {
10564 /* Tree lock needed by Medium::addRegistry when recursing. */
10565 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10566 if (pBase->i_addRegistryRecursive(uuid))
10567 {
10568 treeLock.release();
10569 mParent->i_markRegistryModified(uuid);
10570 }
10571 }
10572}
10573
10574/**
10575 * Creates differencing hard disks for all normal hard disks attached to this
10576 * machine and a new set of attachments to refer to created disks.
10577 *
10578 * Used when taking a snapshot or when deleting the current state. Gets called
10579 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10580 *
10581 * This method assumes that mMediaData contains the original hard disk attachments
10582 * it needs to create diffs for. On success, these attachments will be replaced
10583 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10584 * called to delete created diffs which will also rollback mMediaData and restore
10585 * whatever was backed up before calling this method.
10586 *
10587 * Attachments with non-normal hard disks are left as is.
10588 *
10589 * If @a aOnline is @c false then the original hard disks that require implicit
10590 * diffs will be locked for reading. Otherwise it is assumed that they are
10591 * already locked for writing (when the VM was started). Note that in the latter
10592 * case it is responsibility of the caller to lock the newly created diffs for
10593 * writing if this method succeeds.
10594 *
10595 * @param aProgress Progress object to run (must contain at least as
10596 * many operations left as the number of hard disks
10597 * attached).
10598 * @param aOnline Whether the VM was online prior to this operation.
10599 *
10600 * @note The progress object is not marked as completed, neither on success nor
10601 * on failure. This is a responsibility of the caller.
10602 *
10603 * @note Locks this object and the media tree for writing.
10604 */
10605HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10606 ULONG aWeight,
10607 bool aOnline)
10608{
10609 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10610
10611 AutoCaller autoCaller(this);
10612 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10613
10614 AutoMultiWriteLock2 alock(this->lockHandle(),
10615 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10616
10617 /* must be in a protective state because we release the lock below */
10618 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10619 || mData->mMachineState == MachineState_OnlineSnapshotting
10620 || mData->mMachineState == MachineState_LiveSnapshotting
10621 || mData->mMachineState == MachineState_RestoringSnapshot
10622 || mData->mMachineState == MachineState_DeletingSnapshot
10623 , E_FAIL);
10624
10625 HRESULT rc = S_OK;
10626
10627 // use appropriate locked media map (online or offline)
10628 MediumLockListMap lockedMediaOffline;
10629 MediumLockListMap *lockedMediaMap;
10630 if (aOnline)
10631 lockedMediaMap = &mData->mSession.mLockedMedia;
10632 else
10633 lockedMediaMap = &lockedMediaOffline;
10634
10635 try
10636 {
10637 if (!aOnline)
10638 {
10639 /* lock all attached hard disks early to detect "in use"
10640 * situations before creating actual diffs */
10641 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10642 it != mMediaData->mAttachments.end();
10643 ++it)
10644 {
10645 MediumAttachment* pAtt = *it;
10646 if (pAtt->i_getType() == DeviceType_HardDisk)
10647 {
10648 Medium* pMedium = pAtt->i_getMedium();
10649 Assert(pMedium);
10650
10651 MediumLockList *pMediumLockList(new MediumLockList());
10652 alock.release();
10653 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10654 false /* fMediumLockWrite */,
10655 false /* fMediumLockWriteAll */,
10656 NULL,
10657 *pMediumLockList);
10658 alock.acquire();
10659 if (FAILED(rc))
10660 {
10661 delete pMediumLockList;
10662 throw rc;
10663 }
10664 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10665 if (FAILED(rc))
10666 {
10667 throw setError(rc,
10668 tr("Collecting locking information for all attached media failed"));
10669 }
10670 }
10671 }
10672
10673 /* Now lock all media. If this fails, nothing is locked. */
10674 alock.release();
10675 rc = lockedMediaMap->Lock();
10676 alock.acquire();
10677 if (FAILED(rc))
10678 {
10679 throw setError(rc,
10680 tr("Locking of attached media failed"));
10681 }
10682 }
10683
10684 /* remember the current list (note that we don't use backup() since
10685 * mMediaData may be already backed up) */
10686 MediaData::AttachmentList atts = mMediaData->mAttachments;
10687
10688 /* start from scratch */
10689 mMediaData->mAttachments.clear();
10690
10691 /* go through remembered attachments and create diffs for normal hard
10692 * disks and attach them */
10693 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10694 it != atts.end();
10695 ++it)
10696 {
10697 MediumAttachment* pAtt = *it;
10698
10699 DeviceType_T devType = pAtt->i_getType();
10700 Medium* pMedium = pAtt->i_getMedium();
10701
10702 if ( devType != DeviceType_HardDisk
10703 || pMedium == NULL
10704 || pMedium->i_getType() != MediumType_Normal)
10705 {
10706 /* copy the attachment as is */
10707
10708 /** @todo the progress object created in SessionMachine::TakeSnaphot
10709 * only expects operations for hard disks. Later other
10710 * device types need to show up in the progress as well. */
10711 if (devType == DeviceType_HardDisk)
10712 {
10713 if (pMedium == NULL)
10714 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10715 aWeight); // weight
10716 else
10717 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10718 pMedium->i_getBase()->i_getName().c_str()).raw(),
10719 aWeight); // weight
10720 }
10721
10722 mMediaData->mAttachments.push_back(pAtt);
10723 continue;
10724 }
10725
10726 /* need a diff */
10727 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10728 pMedium->i_getBase()->i_getName().c_str()).raw(),
10729 aWeight); // weight
10730
10731 Utf8Str strFullSnapshotFolder;
10732 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10733
10734 ComObjPtr<Medium> diff;
10735 diff.createObject();
10736 // store the diff in the same registry as the parent
10737 // (this cannot fail here because we can't create implicit diffs for
10738 // unregistered images)
10739 Guid uuidRegistryParent;
10740 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10741 Assert(fInRegistry); NOREF(fInRegistry);
10742 rc = diff->init(mParent,
10743 pMedium->i_getPreferredDiffFormat(),
10744 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10745 uuidRegistryParent,
10746 DeviceType_HardDisk);
10747 if (FAILED(rc)) throw rc;
10748
10749 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10750 * the push_back? Looks like we're going to release medium with the
10751 * wrong kind of lock (general issue with if we fail anywhere at all)
10752 * and an orphaned VDI in the snapshots folder. */
10753
10754 /* update the appropriate lock list */
10755 MediumLockList *pMediumLockList;
10756 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10757 AssertComRCThrowRC(rc);
10758 if (aOnline)
10759 {
10760 alock.release();
10761 /* The currently attached medium will be read-only, change
10762 * the lock type to read. */
10763 rc = pMediumLockList->Update(pMedium, false);
10764 alock.acquire();
10765 AssertComRCThrowRC(rc);
10766 }
10767
10768 /* release the locks before the potentially lengthy operation */
10769 alock.release();
10770 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10771 pMediumLockList,
10772 NULL /* aProgress */,
10773 true /* aWait */);
10774 alock.acquire();
10775 if (FAILED(rc)) throw rc;
10776
10777 /* actual lock list update is done in Medium::commitMedia */
10778
10779 rc = diff->i_addBackReference(mData->mUuid);
10780 AssertComRCThrowRC(rc);
10781
10782 /* add a new attachment */
10783 ComObjPtr<MediumAttachment> attachment;
10784 attachment.createObject();
10785 rc = attachment->init(this,
10786 diff,
10787 pAtt->i_getControllerName(),
10788 pAtt->i_getPort(),
10789 pAtt->i_getDevice(),
10790 DeviceType_HardDisk,
10791 true /* aImplicit */,
10792 false /* aPassthrough */,
10793 false /* aTempEject */,
10794 pAtt->i_getNonRotational(),
10795 pAtt->i_getDiscard(),
10796 pAtt->i_getHotPluggable(),
10797 pAtt->i_getBandwidthGroup());
10798 if (FAILED(rc)) throw rc;
10799
10800 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10801 AssertComRCThrowRC(rc);
10802 mMediaData->mAttachments.push_back(attachment);
10803 }
10804 }
10805 catch (HRESULT aRC) { rc = aRC; }
10806
10807 /* unlock all hard disks we locked when there is no VM */
10808 if (!aOnline)
10809 {
10810 ErrorInfoKeeper eik;
10811
10812 HRESULT rc1 = lockedMediaMap->Clear();
10813 AssertComRC(rc1);
10814 }
10815
10816 return rc;
10817}
10818
10819/**
10820 * Deletes implicit differencing hard disks created either by
10821 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10822 *
10823 * Note that to delete hard disks created by #AttachDevice() this method is
10824 * called from #fixupMedia() when the changes are rolled back.
10825 *
10826 * @note Locks this object and the media tree for writing.
10827 */
10828HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10829{
10830 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10831
10832 AutoCaller autoCaller(this);
10833 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10834
10835 AutoMultiWriteLock2 alock(this->lockHandle(),
10836 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10837
10838 /* We absolutely must have backed up state. */
10839 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10840
10841 /* Check if there are any implicitly created diff images. */
10842 bool fImplicitDiffs = false;
10843 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10844 it != mMediaData->mAttachments.end();
10845 ++it)
10846 {
10847 const ComObjPtr<MediumAttachment> &pAtt = *it;
10848 if (pAtt->i_isImplicit())
10849 {
10850 fImplicitDiffs = true;
10851 break;
10852 }
10853 }
10854 /* If there is nothing to do, leave early. This saves lots of image locking
10855 * effort. It also avoids a MachineStateChanged event without real reason.
10856 * This is important e.g. when loading a VM config, because there should be
10857 * no events. Otherwise API clients can become thoroughly confused for
10858 * inaccessible VMs (the code for loading VM configs uses this method for
10859 * cleanup if the config makes no sense), as they take such events as an
10860 * indication that the VM is alive, and they would force the VM config to
10861 * be reread, leading to an endless loop. */
10862 if (!fImplicitDiffs)
10863 return S_OK;
10864
10865 HRESULT rc = S_OK;
10866 MachineState_T oldState = mData->mMachineState;
10867
10868 /* will release the lock before the potentially lengthy operation,
10869 * so protect with the special state (unless already protected) */
10870 if ( oldState != MachineState_Snapshotting
10871 && oldState != MachineState_OnlineSnapshotting
10872 && oldState != MachineState_LiveSnapshotting
10873 && oldState != MachineState_RestoringSnapshot
10874 && oldState != MachineState_DeletingSnapshot
10875 && oldState != MachineState_DeletingSnapshotOnline
10876 && oldState != MachineState_DeletingSnapshotPaused
10877 )
10878 i_setMachineState(MachineState_SettingUp);
10879
10880 // use appropriate locked media map (online or offline)
10881 MediumLockListMap lockedMediaOffline;
10882 MediumLockListMap *lockedMediaMap;
10883 if (aOnline)
10884 lockedMediaMap = &mData->mSession.mLockedMedia;
10885 else
10886 lockedMediaMap = &lockedMediaOffline;
10887
10888 try
10889 {
10890 if (!aOnline)
10891 {
10892 /* lock all attached hard disks early to detect "in use"
10893 * situations before deleting actual diffs */
10894 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10895 it != mMediaData->mAttachments.end();
10896 ++it)
10897 {
10898 MediumAttachment* pAtt = *it;
10899 if (pAtt->i_getType() == DeviceType_HardDisk)
10900 {
10901 Medium* pMedium = pAtt->i_getMedium();
10902 Assert(pMedium);
10903
10904 MediumLockList *pMediumLockList(new MediumLockList());
10905 alock.release();
10906 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10907 false /* fMediumLockWrite */,
10908 false /* fMediumLockWriteAll */,
10909 NULL,
10910 *pMediumLockList);
10911 alock.acquire();
10912
10913 if (FAILED(rc))
10914 {
10915 delete pMediumLockList;
10916 throw rc;
10917 }
10918
10919 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10920 if (FAILED(rc))
10921 throw rc;
10922 }
10923 }
10924
10925 if (FAILED(rc))
10926 throw rc;
10927 } // end of offline
10928
10929 /* Lock lists are now up to date and include implicitly created media */
10930
10931 /* Go through remembered attachments and delete all implicitly created
10932 * diffs and fix up the attachment information */
10933 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10934 MediaData::AttachmentList implicitAtts;
10935 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10936 it != mMediaData->mAttachments.end();
10937 ++it)
10938 {
10939 ComObjPtr<MediumAttachment> pAtt = *it;
10940 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10941 if (pMedium.isNull())
10942 continue;
10943
10944 // Implicit attachments go on the list for deletion and back references are removed.
10945 if (pAtt->i_isImplicit())
10946 {
10947 /* Deassociate and mark for deletion */
10948 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10949 rc = pMedium->i_removeBackReference(mData->mUuid);
10950 if (FAILED(rc))
10951 throw rc;
10952 implicitAtts.push_back(pAtt);
10953 continue;
10954 }
10955
10956 /* Was this medium attached before? */
10957 if (!i_findAttachment(oldAtts, pMedium))
10958 {
10959 /* no: de-associate */
10960 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10961 rc = pMedium->i_removeBackReference(mData->mUuid);
10962 if (FAILED(rc))
10963 throw rc;
10964 continue;
10965 }
10966 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10967 }
10968
10969 /* If there are implicit attachments to delete, throw away the lock
10970 * map contents (which will unlock all media) since the medium
10971 * attachments will be rolled back. Below we need to completely
10972 * recreate the lock map anyway since it is infinitely complex to
10973 * do this incrementally (would need reconstructing each attachment
10974 * change, which would be extremely hairy). */
10975 if (implicitAtts.size() != 0)
10976 {
10977 ErrorInfoKeeper eik;
10978
10979 HRESULT rc1 = lockedMediaMap->Clear();
10980 AssertComRC(rc1);
10981 }
10982
10983 /* rollback hard disk changes */
10984 mMediaData.rollback();
10985
10986 MultiResult mrc(S_OK);
10987
10988 // Delete unused implicit diffs.
10989 if (implicitAtts.size() != 0)
10990 {
10991 alock.release();
10992
10993 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
10994 {
10995 // Remove medium associated with this attachment.
10996 ComObjPtr<MediumAttachment> pAtt = *it;
10997 Assert(pAtt);
10998 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
10999 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11000 Assert(pMedium);
11001
11002 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11003 // continue on delete failure, just collect error messages
11004 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11005 pMedium->i_getLocationFull().c_str() ));
11006 mrc = rc;
11007 }
11008 // Clear the list of deleted implicit attachments now, while not
11009 // holding the lock, as it will ultimately trigger Medium::uninit()
11010 // calls which assume that the media tree lock isn't held.
11011 implicitAtts.clear();
11012
11013 alock.acquire();
11014
11015 /* if there is a VM recreate media lock map as mentioned above,
11016 * otherwise it is a waste of time and we leave things unlocked */
11017 if (aOnline)
11018 {
11019 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11020 /* must never be NULL, but better safe than sorry */
11021 if (!pMachine.isNull())
11022 {
11023 alock.release();
11024 rc = mData->mSession.mMachine->i_lockMedia();
11025 alock.acquire();
11026 if (FAILED(rc))
11027 throw rc;
11028 }
11029 }
11030 }
11031 }
11032 catch (HRESULT aRC) {rc = aRC;}
11033
11034 if (mData->mMachineState == MachineState_SettingUp)
11035 i_setMachineState(oldState);
11036
11037 /* unlock all hard disks we locked when there is no VM */
11038 if (!aOnline)
11039 {
11040 ErrorInfoKeeper eik;
11041
11042 HRESULT rc1 = lockedMediaMap->Clear();
11043 AssertComRC(rc1);
11044 }
11045
11046 return rc;
11047}
11048
11049
11050/**
11051 * Looks through the given list of media attachments for one with the given parameters
11052 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11053 * can be searched as well if needed.
11054 *
11055 * @param list
11056 * @param aControllerName
11057 * @param aControllerPort
11058 * @param aDevice
11059 * @return
11060 */
11061MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11062 IN_BSTR aControllerName,
11063 LONG aControllerPort,
11064 LONG aDevice)
11065{
11066 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11067 {
11068 MediumAttachment *pAttach = *it;
11069 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11070 return pAttach;
11071 }
11072
11073 return NULL;
11074}
11075
11076/**
11077 * Looks through the given list of media attachments for one with the given parameters
11078 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11079 * can be searched as well if needed.
11080 *
11081 * @param list
11082 * @param aControllerName
11083 * @param aControllerPort
11084 * @param aDevice
11085 * @return
11086 */
11087MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11088 ComObjPtr<Medium> pMedium)
11089{
11090 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11091 {
11092 MediumAttachment *pAttach = *it;
11093 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11094 if (pMediumThis == pMedium)
11095 return pAttach;
11096 }
11097
11098 return NULL;
11099}
11100
11101/**
11102 * Looks through the given list of media attachments for one with the given parameters
11103 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11104 * can be searched as well if needed.
11105 *
11106 * @param list
11107 * @param aControllerName
11108 * @param aControllerPort
11109 * @param aDevice
11110 * @return
11111 */
11112MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11113 Guid &id)
11114{
11115 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11116 {
11117 MediumAttachment *pAttach = *it;
11118 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11119 if (pMediumThis->i_getId() == id)
11120 return pAttach;
11121 }
11122
11123 return NULL;
11124}
11125
11126/**
11127 * Main implementation for Machine::DetachDevice. This also gets called
11128 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11129 *
11130 * @param pAttach Medium attachment to detach.
11131 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11132 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11133 * SnapshotMachine, and this must be its snapshot.
11134 * @return
11135 */
11136HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11137 AutoWriteLock &writeLock,
11138 Snapshot *pSnapshot)
11139{
11140 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11141 DeviceType_T mediumType = pAttach->i_getType();
11142
11143 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11144
11145 if (pAttach->i_isImplicit())
11146 {
11147 /* attempt to implicitly delete the implicitly created diff */
11148
11149 /// @todo move the implicit flag from MediumAttachment to Medium
11150 /// and forbid any hard disk operation when it is implicit. Or maybe
11151 /// a special media state for it to make it even more simple.
11152
11153 Assert(mMediaData.isBackedUp());
11154
11155 /* will release the lock before the potentially lengthy operation, so
11156 * protect with the special state */
11157 MachineState_T oldState = mData->mMachineState;
11158 i_setMachineState(MachineState_SettingUp);
11159
11160 writeLock.release();
11161
11162 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11163 true /*aWait*/);
11164
11165 writeLock.acquire();
11166
11167 i_setMachineState(oldState);
11168
11169 if (FAILED(rc)) return rc;
11170 }
11171
11172 i_setModified(IsModified_Storage);
11173 mMediaData.backup();
11174 mMediaData->mAttachments.remove(pAttach);
11175
11176 if (!oldmedium.isNull())
11177 {
11178 // if this is from a snapshot, do not defer detachment to commitMedia()
11179 if (pSnapshot)
11180 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11181 // else if non-hard disk media, do not defer detachment to commitMedia() either
11182 else if (mediumType != DeviceType_HardDisk)
11183 oldmedium->i_removeBackReference(mData->mUuid);
11184 }
11185
11186 return S_OK;
11187}
11188
11189/**
11190 * Goes thru all media of the given list and
11191 *
11192 * 1) calls i_detachDevice() on each of them for this machine and
11193 * 2) adds all Medium objects found in the process to the given list,
11194 * depending on cleanupMode.
11195 *
11196 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11197 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11198 * media to the list.
11199 *
11200 * This gets called from Machine::Unregister, both for the actual Machine and
11201 * the SnapshotMachine objects that might be found in the snapshots.
11202 *
11203 * Requires caller and locking. The machine lock must be passed in because it
11204 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11205 *
11206 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11207 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11208 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11209 * Full, then all media get added;
11210 * otherwise no media get added.
11211 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11212 * @return
11213 */
11214HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11215 Snapshot *pSnapshot,
11216 CleanupMode_T cleanupMode,
11217 MediaList &llMedia)
11218{
11219 Assert(isWriteLockOnCurrentThread());
11220
11221 HRESULT rc;
11222
11223 // make a temporary list because i_detachDevice invalidates iterators into
11224 // mMediaData->mAttachments
11225 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11226
11227 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11228 {
11229 ComObjPtr<MediumAttachment> &pAttach = *it;
11230 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11231
11232 if (!pMedium.isNull())
11233 {
11234 AutoCaller mac(pMedium);
11235 if (FAILED(mac.rc())) return mac.rc();
11236 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11237 DeviceType_T devType = pMedium->i_getDeviceType();
11238 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11239 && devType == DeviceType_HardDisk)
11240 || (cleanupMode == CleanupMode_Full)
11241 )
11242 {
11243 llMedia.push_back(pMedium);
11244 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11245 /* Not allowed to keep this lock as below we need the parent
11246 * medium lock, and the lock order is parent to child. */
11247 lock.release();
11248 /*
11249 * Search for medias which are not attached to any machine, but
11250 * in the chain to an attached disk. Mediums are only consided
11251 * if they are:
11252 * - have only one child
11253 * - no references to any machines
11254 * - are of normal medium type
11255 */
11256 while (!pParent.isNull())
11257 {
11258 AutoCaller mac1(pParent);
11259 if (FAILED(mac1.rc())) return mac1.rc();
11260 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11261 if (pParent->i_getChildren().size() == 1)
11262 {
11263 if ( pParent->i_getMachineBackRefCount() == 0
11264 && pParent->i_getType() == MediumType_Normal
11265 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11266 llMedia.push_back(pParent);
11267 }
11268 else
11269 break;
11270 pParent = pParent->i_getParent();
11271 }
11272 }
11273 }
11274
11275 // real machine: then we need to use the proper method
11276 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11277
11278 if (FAILED(rc))
11279 return rc;
11280 }
11281
11282 return S_OK;
11283}
11284
11285/**
11286 * Perform deferred hard disk detachments.
11287 *
11288 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11289 * backed up).
11290 *
11291 * If @a aOnline is @c true then this method will also unlock the old hard disks
11292 * for which the new implicit diffs were created and will lock these new diffs for
11293 * writing.
11294 *
11295 * @param aOnline Whether the VM was online prior to this operation.
11296 *
11297 * @note Locks this object for writing!
11298 */
11299void Machine::i_commitMedia(bool aOnline /*= false*/)
11300{
11301 AutoCaller autoCaller(this);
11302 AssertComRCReturnVoid(autoCaller.rc());
11303
11304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11305
11306 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11307
11308 HRESULT rc = S_OK;
11309
11310 /* no attach/detach operations -- nothing to do */
11311 if (!mMediaData.isBackedUp())
11312 return;
11313
11314 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11315 bool fMediaNeedsLocking = false;
11316
11317 /* enumerate new attachments */
11318 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11319 it != mMediaData->mAttachments.end();
11320 ++it)
11321 {
11322 MediumAttachment *pAttach = *it;
11323
11324 pAttach->i_commit();
11325
11326 Medium* pMedium = pAttach->i_getMedium();
11327 bool fImplicit = pAttach->i_isImplicit();
11328
11329 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11330 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11331 fImplicit));
11332
11333 /** @todo convert all this Machine-based voodoo to MediumAttachment
11334 * based commit logic. */
11335 if (fImplicit)
11336 {
11337 /* convert implicit attachment to normal */
11338 pAttach->i_setImplicit(false);
11339
11340 if ( aOnline
11341 && pMedium
11342 && pAttach->i_getType() == DeviceType_HardDisk
11343 )
11344 {
11345 /* update the appropriate lock list */
11346 MediumLockList *pMediumLockList;
11347 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11348 AssertComRC(rc);
11349 if (pMediumLockList)
11350 {
11351 /* unlock if there's a need to change the locking */
11352 if (!fMediaNeedsLocking)
11353 {
11354 rc = mData->mSession.mLockedMedia.Unlock();
11355 AssertComRC(rc);
11356 fMediaNeedsLocking = true;
11357 }
11358 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11359 AssertComRC(rc);
11360 rc = pMediumLockList->Append(pMedium, true);
11361 AssertComRC(rc);
11362 }
11363 }
11364
11365 continue;
11366 }
11367
11368 if (pMedium)
11369 {
11370 /* was this medium attached before? */
11371 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11372 {
11373 MediumAttachment *pOldAttach = *oldIt;
11374 if (pOldAttach->i_getMedium() == pMedium)
11375 {
11376 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11377
11378 /* yes: remove from old to avoid de-association */
11379 oldAtts.erase(oldIt);
11380 break;
11381 }
11382 }
11383 }
11384 }
11385
11386 /* enumerate remaining old attachments and de-associate from the
11387 * current machine state */
11388 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11389 {
11390 MediumAttachment *pAttach = *it;
11391 Medium* pMedium = pAttach->i_getMedium();
11392
11393 /* Detach only hard disks, since DVD/floppy media is detached
11394 * instantly in MountMedium. */
11395 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11396 {
11397 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11398
11399 /* now de-associate from the current machine state */
11400 rc = pMedium->i_removeBackReference(mData->mUuid);
11401 AssertComRC(rc);
11402
11403 if (aOnline)
11404 {
11405 /* unlock since medium is not used anymore */
11406 MediumLockList *pMediumLockList;
11407 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11408 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11409 {
11410 /* this happens for online snapshots, there the attachment
11411 * is changing, but only to a diff image created under
11412 * the old one, so there is no separate lock list */
11413 Assert(!pMediumLockList);
11414 }
11415 else
11416 {
11417 AssertComRC(rc);
11418 if (pMediumLockList)
11419 {
11420 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11421 AssertComRC(rc);
11422 }
11423 }
11424 }
11425 }
11426 }
11427
11428 /* take media locks again so that the locking state is consistent */
11429 if (fMediaNeedsLocking)
11430 {
11431 Assert(aOnline);
11432 rc = mData->mSession.mLockedMedia.Lock();
11433 AssertComRC(rc);
11434 }
11435
11436 /* commit the hard disk changes */
11437 mMediaData.commit();
11438
11439 if (i_isSessionMachine())
11440 {
11441 /*
11442 * Update the parent machine to point to the new owner.
11443 * This is necessary because the stored parent will point to the
11444 * session machine otherwise and cause crashes or errors later
11445 * when the session machine gets invalid.
11446 */
11447 /** @todo Change the MediumAttachment class to behave like any other
11448 * class in this regard by creating peer MediumAttachment
11449 * objects for session machines and share the data with the peer
11450 * machine.
11451 */
11452 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11453 it != mMediaData->mAttachments.end();
11454 ++it)
11455 (*it)->i_updateParentMachine(mPeer);
11456
11457 /* attach new data to the primary machine and reshare it */
11458 mPeer->mMediaData.attach(mMediaData);
11459 }
11460
11461 return;
11462}
11463
11464/**
11465 * Perform deferred deletion of implicitly created diffs.
11466 *
11467 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11468 * backed up).
11469 *
11470 * @note Locks this object for writing!
11471 */
11472void Machine::i_rollbackMedia()
11473{
11474 AutoCaller autoCaller(this);
11475 AssertComRCReturnVoid(autoCaller.rc());
11476
11477 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11478 LogFlowThisFunc(("Entering rollbackMedia\n"));
11479
11480 HRESULT rc = S_OK;
11481
11482 /* no attach/detach operations -- nothing to do */
11483 if (!mMediaData.isBackedUp())
11484 return;
11485
11486 /* enumerate new attachments */
11487 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11488 it != mMediaData->mAttachments.end();
11489 ++it)
11490 {
11491 MediumAttachment *pAttach = *it;
11492 /* Fix up the backrefs for DVD/floppy media. */
11493 if (pAttach->i_getType() != DeviceType_HardDisk)
11494 {
11495 Medium* pMedium = pAttach->i_getMedium();
11496 if (pMedium)
11497 {
11498 rc = pMedium->i_removeBackReference(mData->mUuid);
11499 AssertComRC(rc);
11500 }
11501 }
11502
11503 (*it)->i_rollback();
11504
11505 pAttach = *it;
11506 /* Fix up the backrefs for DVD/floppy media. */
11507 if (pAttach->i_getType() != DeviceType_HardDisk)
11508 {
11509 Medium* pMedium = pAttach->i_getMedium();
11510 if (pMedium)
11511 {
11512 rc = pMedium->i_addBackReference(mData->mUuid);
11513 AssertComRC(rc);
11514 }
11515 }
11516 }
11517
11518 /** @todo convert all this Machine-based voodoo to MediumAttachment
11519 * based rollback logic. */
11520 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11521
11522 return;
11523}
11524
11525/**
11526 * Returns true if the settings file is located in the directory named exactly
11527 * as the machine; this means, among other things, that the machine directory
11528 * should be auto-renamed.
11529 *
11530 * @param aSettingsDir if not NULL, the full machine settings file directory
11531 * name will be assigned there.
11532 *
11533 * @note Doesn't lock anything.
11534 * @note Not thread safe (must be called from this object's lock).
11535 */
11536bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11537{
11538 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11539 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11540 if (aSettingsDir)
11541 *aSettingsDir = strMachineDirName;
11542 strMachineDirName.stripPath(); // vmname
11543 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11544 strConfigFileOnly.stripPath() // vmname.vbox
11545 .stripSuffix(); // vmname
11546 /** @todo hack, make somehow use of ComposeMachineFilename */
11547 if (mUserData->s.fDirectoryIncludesUUID)
11548 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11549
11550 AssertReturn(!strMachineDirName.isEmpty(), false);
11551 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11552
11553 return strMachineDirName == strConfigFileOnly;
11554}
11555
11556/**
11557 * Discards all changes to machine settings.
11558 *
11559 * @param aNotify Whether to notify the direct session about changes or not.
11560 *
11561 * @note Locks objects for writing!
11562 */
11563void Machine::i_rollback(bool aNotify)
11564{
11565 AutoCaller autoCaller(this);
11566 AssertComRCReturn(autoCaller.rc(), (void)0);
11567
11568 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11569
11570 if (!mStorageControllers.isNull())
11571 {
11572 if (mStorageControllers.isBackedUp())
11573 {
11574 /* unitialize all new devices (absent in the backed up list). */
11575 StorageControllerList::const_iterator it = mStorageControllers->begin();
11576 StorageControllerList *backedList = mStorageControllers.backedUpData();
11577 while (it != mStorageControllers->end())
11578 {
11579 if ( std::find(backedList->begin(), backedList->end(), *it)
11580 == backedList->end()
11581 )
11582 {
11583 (*it)->uninit();
11584 }
11585 ++it;
11586 }
11587
11588 /* restore the list */
11589 mStorageControllers.rollback();
11590 }
11591
11592 /* rollback any changes to devices after restoring the list */
11593 if (mData->flModifications & IsModified_Storage)
11594 {
11595 StorageControllerList::const_iterator it = mStorageControllers->begin();
11596 while (it != mStorageControllers->end())
11597 {
11598 (*it)->i_rollback();
11599 ++it;
11600 }
11601 }
11602 }
11603
11604 if (!mUSBControllers.isNull())
11605 {
11606 if (mUSBControllers.isBackedUp())
11607 {
11608 /* unitialize all new devices (absent in the backed up list). */
11609 USBControllerList::const_iterator it = mUSBControllers->begin();
11610 USBControllerList *backedList = mUSBControllers.backedUpData();
11611 while (it != mUSBControllers->end())
11612 {
11613 if ( std::find(backedList->begin(), backedList->end(), *it)
11614 == backedList->end()
11615 )
11616 {
11617 (*it)->uninit();
11618 }
11619 ++it;
11620 }
11621
11622 /* restore the list */
11623 mUSBControllers.rollback();
11624 }
11625
11626 /* rollback any changes to devices after restoring the list */
11627 if (mData->flModifications & IsModified_USB)
11628 {
11629 USBControllerList::const_iterator it = mUSBControllers->begin();
11630 while (it != mUSBControllers->end())
11631 {
11632 (*it)->i_rollback();
11633 ++it;
11634 }
11635 }
11636 }
11637
11638 mUserData.rollback();
11639
11640 mHWData.rollback();
11641
11642 if (mData->flModifications & IsModified_Storage)
11643 i_rollbackMedia();
11644
11645 if (mBIOSSettings)
11646 mBIOSSettings->i_rollback();
11647
11648 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11649 mVRDEServer->i_rollback();
11650
11651 if (mAudioAdapter)
11652 mAudioAdapter->i_rollback();
11653
11654 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11655 mUSBDeviceFilters->i_rollback();
11656
11657 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11658 mBandwidthControl->i_rollback();
11659
11660 if (!mHWData.isNull())
11661 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11662 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11663 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11664 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11665
11666 if (mData->flModifications & IsModified_NetworkAdapters)
11667 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11668 if ( mNetworkAdapters[slot]
11669 && mNetworkAdapters[slot]->i_isModified())
11670 {
11671 mNetworkAdapters[slot]->i_rollback();
11672 networkAdapters[slot] = mNetworkAdapters[slot];
11673 }
11674
11675 if (mData->flModifications & IsModified_SerialPorts)
11676 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11677 if ( mSerialPorts[slot]
11678 && mSerialPorts[slot]->i_isModified())
11679 {
11680 mSerialPorts[slot]->i_rollback();
11681 serialPorts[slot] = mSerialPorts[slot];
11682 }
11683
11684 if (mData->flModifications & IsModified_ParallelPorts)
11685 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11686 if ( mParallelPorts[slot]
11687 && mParallelPorts[slot]->i_isModified())
11688 {
11689 mParallelPorts[slot]->i_rollback();
11690 parallelPorts[slot] = mParallelPorts[slot];
11691 }
11692
11693 if (aNotify)
11694 {
11695 /* inform the direct session about changes */
11696
11697 ComObjPtr<Machine> that = this;
11698 uint32_t flModifications = mData->flModifications;
11699 alock.release();
11700
11701 if (flModifications & IsModified_SharedFolders)
11702 that->i_onSharedFolderChange();
11703
11704 if (flModifications & IsModified_VRDEServer)
11705 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11706 if (flModifications & IsModified_USB)
11707 that->i_onUSBControllerChange();
11708
11709 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11710 if (networkAdapters[slot])
11711 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11712 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11713 if (serialPorts[slot])
11714 that->i_onSerialPortChange(serialPorts[slot]);
11715 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11716 if (parallelPorts[slot])
11717 that->i_onParallelPortChange(parallelPorts[slot]);
11718
11719 if (flModifications & IsModified_Storage)
11720 that->i_onStorageControllerChange();
11721
11722#if 0
11723 if (flModifications & IsModified_BandwidthControl)
11724 that->onBandwidthControlChange();
11725#endif
11726 }
11727}
11728
11729/**
11730 * Commits all the changes to machine settings.
11731 *
11732 * Note that this operation is supposed to never fail.
11733 *
11734 * @note Locks this object and children for writing.
11735 */
11736void Machine::i_commit()
11737{
11738 AutoCaller autoCaller(this);
11739 AssertComRCReturnVoid(autoCaller.rc());
11740
11741 AutoCaller peerCaller(mPeer);
11742 AssertComRCReturnVoid(peerCaller.rc());
11743
11744 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11745
11746 /*
11747 * use safe commit to ensure Snapshot machines (that share mUserData)
11748 * will still refer to a valid memory location
11749 */
11750 mUserData.commitCopy();
11751
11752 mHWData.commit();
11753
11754 if (mMediaData.isBackedUp())
11755 i_commitMedia(Global::IsOnline(mData->mMachineState));
11756
11757 mBIOSSettings->i_commit();
11758 mVRDEServer->i_commit();
11759 mAudioAdapter->i_commit();
11760 mUSBDeviceFilters->i_commit();
11761 mBandwidthControl->i_commit();
11762
11763 /* Since mNetworkAdapters is a list which might have been changed (resized)
11764 * without using the Backupable<> template we need to handle the copying
11765 * of the list entries manually, including the creation of peers for the
11766 * new objects. */
11767 bool commitNetworkAdapters = false;
11768 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11769 if (mPeer)
11770 {
11771 /* commit everything, even the ones which will go away */
11772 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11773 mNetworkAdapters[slot]->i_commit();
11774 /* copy over the new entries, creating a peer and uninit the original */
11775 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11776 for (size_t slot = 0; slot < newSize; slot++)
11777 {
11778 /* look if this adapter has a peer device */
11779 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11780 if (!peer)
11781 {
11782 /* no peer means the adapter is a newly created one;
11783 * create a peer owning data this data share it with */
11784 peer.createObject();
11785 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11786 }
11787 mPeer->mNetworkAdapters[slot] = peer;
11788 }
11789 /* uninit any no longer needed network adapters */
11790 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11791 mNetworkAdapters[slot]->uninit();
11792 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11793 {
11794 if (mPeer->mNetworkAdapters[slot])
11795 mPeer->mNetworkAdapters[slot]->uninit();
11796 }
11797 /* Keep the original network adapter count until this point, so that
11798 * discarding a chipset type change will not lose settings. */
11799 mNetworkAdapters.resize(newSize);
11800 mPeer->mNetworkAdapters.resize(newSize);
11801 }
11802 else
11803 {
11804 /* we have no peer (our parent is the newly created machine);
11805 * just commit changes to the network adapters */
11806 commitNetworkAdapters = true;
11807 }
11808 if (commitNetworkAdapters)
11809 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11810 mNetworkAdapters[slot]->i_commit();
11811
11812 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11813 mSerialPorts[slot]->i_commit();
11814 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11815 mParallelPorts[slot]->i_commit();
11816
11817 bool commitStorageControllers = false;
11818
11819 if (mStorageControllers.isBackedUp())
11820 {
11821 mStorageControllers.commit();
11822
11823 if (mPeer)
11824 {
11825 /* Commit all changes to new controllers (this will reshare data with
11826 * peers for those who have peers) */
11827 StorageControllerList *newList = new StorageControllerList();
11828 StorageControllerList::const_iterator it = mStorageControllers->begin();
11829 while (it != mStorageControllers->end())
11830 {
11831 (*it)->i_commit();
11832
11833 /* look if this controller has a peer device */
11834 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11835 if (!peer)
11836 {
11837 /* no peer means the device is a newly created one;
11838 * create a peer owning data this device share it with */
11839 peer.createObject();
11840 peer->init(mPeer, *it, true /* aReshare */);
11841 }
11842 else
11843 {
11844 /* remove peer from the old list */
11845 mPeer->mStorageControllers->remove(peer);
11846 }
11847 /* and add it to the new list */
11848 newList->push_back(peer);
11849
11850 ++it;
11851 }
11852
11853 /* uninit old peer's controllers that are left */
11854 it = mPeer->mStorageControllers->begin();
11855 while (it != mPeer->mStorageControllers->end())
11856 {
11857 (*it)->uninit();
11858 ++it;
11859 }
11860
11861 /* attach new list of controllers to our peer */
11862 mPeer->mStorageControllers.attach(newList);
11863 }
11864 else
11865 {
11866 /* we have no peer (our parent is the newly created machine);
11867 * just commit changes to devices */
11868 commitStorageControllers = true;
11869 }
11870 }
11871 else
11872 {
11873 /* the list of controllers itself is not changed,
11874 * just commit changes to controllers themselves */
11875 commitStorageControllers = true;
11876 }
11877
11878 if (commitStorageControllers)
11879 {
11880 StorageControllerList::const_iterator it = mStorageControllers->begin();
11881 while (it != mStorageControllers->end())
11882 {
11883 (*it)->i_commit();
11884 ++it;
11885 }
11886 }
11887
11888 bool commitUSBControllers = false;
11889
11890 if (mUSBControllers.isBackedUp())
11891 {
11892 mUSBControllers.commit();
11893
11894 if (mPeer)
11895 {
11896 /* Commit all changes to new controllers (this will reshare data with
11897 * peers for those who have peers) */
11898 USBControllerList *newList = new USBControllerList();
11899 USBControllerList::const_iterator it = mUSBControllers->begin();
11900 while (it != mUSBControllers->end())
11901 {
11902 (*it)->i_commit();
11903
11904 /* look if this controller has a peer device */
11905 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11906 if (!peer)
11907 {
11908 /* no peer means the device is a newly created one;
11909 * create a peer owning data this device share it with */
11910 peer.createObject();
11911 peer->init(mPeer, *it, true /* aReshare */);
11912 }
11913 else
11914 {
11915 /* remove peer from the old list */
11916 mPeer->mUSBControllers->remove(peer);
11917 }
11918 /* and add it to the new list */
11919 newList->push_back(peer);
11920
11921 ++it;
11922 }
11923
11924 /* uninit old peer's controllers that are left */
11925 it = mPeer->mUSBControllers->begin();
11926 while (it != mPeer->mUSBControllers->end())
11927 {
11928 (*it)->uninit();
11929 ++it;
11930 }
11931
11932 /* attach new list of controllers to our peer */
11933 mPeer->mUSBControllers.attach(newList);
11934 }
11935 else
11936 {
11937 /* we have no peer (our parent is the newly created machine);
11938 * just commit changes to devices */
11939 commitUSBControllers = true;
11940 }
11941 }
11942 else
11943 {
11944 /* the list of controllers itself is not changed,
11945 * just commit changes to controllers themselves */
11946 commitUSBControllers = true;
11947 }
11948
11949 if (commitUSBControllers)
11950 {
11951 USBControllerList::const_iterator it = mUSBControllers->begin();
11952 while (it != mUSBControllers->end())
11953 {
11954 (*it)->i_commit();
11955 ++it;
11956 }
11957 }
11958
11959 if (i_isSessionMachine())
11960 {
11961 /* attach new data to the primary machine and reshare it */
11962 mPeer->mUserData.attach(mUserData);
11963 mPeer->mHWData.attach(mHWData);
11964 /* mMediaData is reshared by fixupMedia */
11965 // mPeer->mMediaData.attach(mMediaData);
11966 Assert(mPeer->mMediaData.data() == mMediaData.data());
11967 }
11968}
11969
11970/**
11971 * Copies all the hardware data from the given machine.
11972 *
11973 * Currently, only called when the VM is being restored from a snapshot. In
11974 * particular, this implies that the VM is not running during this method's
11975 * call.
11976 *
11977 * @note This method must be called from under this object's lock.
11978 *
11979 * @note This method doesn't call #commit(), so all data remains backed up and
11980 * unsaved.
11981 */
11982void Machine::i_copyFrom(Machine *aThat)
11983{
11984 AssertReturnVoid(!i_isSnapshotMachine());
11985 AssertReturnVoid(aThat->i_isSnapshotMachine());
11986
11987 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
11988
11989 mHWData.assignCopy(aThat->mHWData);
11990
11991 // create copies of all shared folders (mHWData after attaching a copy
11992 // contains just references to original objects)
11993 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
11994 it != mHWData->mSharedFolders.end();
11995 ++it)
11996 {
11997 ComObjPtr<SharedFolder> folder;
11998 folder.createObject();
11999 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12000 AssertComRC(rc);
12001 *it = folder;
12002 }
12003
12004 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12005 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12006 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12007 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12008 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12009
12010 /* create private copies of all controllers */
12011 mStorageControllers.backup();
12012 mStorageControllers->clear();
12013 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12014 it != aThat->mStorageControllers->end();
12015 ++it)
12016 {
12017 ComObjPtr<StorageController> ctrl;
12018 ctrl.createObject();
12019 ctrl->initCopy(this, *it);
12020 mStorageControllers->push_back(ctrl);
12021 }
12022
12023 /* create private copies of all USB controllers */
12024 mUSBControllers.backup();
12025 mUSBControllers->clear();
12026 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12027 it != aThat->mUSBControllers->end();
12028 ++it)
12029 {
12030 ComObjPtr<USBController> ctrl;
12031 ctrl.createObject();
12032 ctrl->initCopy(this, *it);
12033 mUSBControllers->push_back(ctrl);
12034 }
12035
12036 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12037 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12038 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12039 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12040 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12041 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12042 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12043}
12044
12045/**
12046 * Returns whether the given storage controller is hotplug capable.
12047 *
12048 * @returns true if the controller supports hotplugging
12049 * false otherwise.
12050 * @param enmCtrlType The controller type to check for.
12051 */
12052bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12053{
12054 ComPtr<ISystemProperties> systemProperties;
12055 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12056 if (FAILED(rc))
12057 return false;
12058
12059 BOOL aHotplugCapable = FALSE;
12060 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12061
12062 return RT_BOOL(aHotplugCapable);
12063}
12064
12065#ifdef VBOX_WITH_RESOURCE_USAGE_API
12066
12067void Machine::i_getDiskList(MediaList &list)
12068{
12069 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12070 it != mMediaData->mAttachments.end();
12071 ++it)
12072 {
12073 MediumAttachment* pAttach = *it;
12074 /* just in case */
12075 AssertStmt(pAttach, continue);
12076
12077 AutoCaller localAutoCallerA(pAttach);
12078 if (FAILED(localAutoCallerA.rc())) continue;
12079
12080 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12081
12082 if (pAttach->i_getType() == DeviceType_HardDisk)
12083 list.push_back(pAttach->i_getMedium());
12084 }
12085}
12086
12087void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12088{
12089 AssertReturnVoid(isWriteLockOnCurrentThread());
12090 AssertPtrReturnVoid(aCollector);
12091
12092 pm::CollectorHAL *hal = aCollector->getHAL();
12093 /* Create sub metrics */
12094 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12095 "Percentage of processor time spent in user mode by the VM process.");
12096 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12097 "Percentage of processor time spent in kernel mode by the VM process.");
12098 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12099 "Size of resident portion of VM process in memory.");
12100 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12101 "Actual size of all VM disks combined.");
12102 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12103 "Network receive rate.");
12104 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12105 "Network transmit rate.");
12106 /* Create and register base metrics */
12107 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12108 cpuLoadUser, cpuLoadKernel);
12109 aCollector->registerBaseMetric(cpuLoad);
12110 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12111 ramUsageUsed);
12112 aCollector->registerBaseMetric(ramUsage);
12113 MediaList disks;
12114 i_getDiskList(disks);
12115 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12116 diskUsageUsed);
12117 aCollector->registerBaseMetric(diskUsage);
12118
12119 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12120 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12121 new pm::AggregateAvg()));
12122 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12123 new pm::AggregateMin()));
12124 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12125 new pm::AggregateMax()));
12126 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12127 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12128 new pm::AggregateAvg()));
12129 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12130 new pm::AggregateMin()));
12131 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12132 new pm::AggregateMax()));
12133
12134 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12135 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12136 new pm::AggregateAvg()));
12137 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12138 new pm::AggregateMin()));
12139 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12140 new pm::AggregateMax()));
12141
12142 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12143 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12144 new pm::AggregateAvg()));
12145 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12146 new pm::AggregateMin()));
12147 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12148 new pm::AggregateMax()));
12149
12150
12151 /* Guest metrics collector */
12152 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12153 aCollector->registerGuest(mCollectorGuest);
12154 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12155
12156 /* Create sub metrics */
12157 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12158 "Percentage of processor time spent in user mode as seen by the guest.");
12159 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12160 "Percentage of processor time spent in kernel mode as seen by the guest.");
12161 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12162 "Percentage of processor time spent idling as seen by the guest.");
12163
12164 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12165 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12166 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12167 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12168 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12169 pm::SubMetric *guestMemCache = new pm::SubMetric(
12170 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12171
12172 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12173 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12174
12175 /* Create and register base metrics */
12176 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12177 machineNetRx, machineNetTx);
12178 aCollector->registerBaseMetric(machineNetRate);
12179
12180 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12181 guestLoadUser, guestLoadKernel, guestLoadIdle);
12182 aCollector->registerBaseMetric(guestCpuLoad);
12183
12184 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12185 guestMemTotal, guestMemFree,
12186 guestMemBalloon, guestMemShared,
12187 guestMemCache, guestPagedTotal);
12188 aCollector->registerBaseMetric(guestCpuMem);
12189
12190 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12191 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12192 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12193 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12194
12195 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12196 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12197 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12198 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12199
12200 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12201 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12202 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12203 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12204
12205 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12206 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12207 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12208 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12209
12210 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12211 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12212 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12213 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12214
12215 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12216 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12217 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12218 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12219
12220 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12221 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12222 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12223 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12224
12225 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12226 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12227 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12228 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12229
12230 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12231 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12234
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12236 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12239
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12241 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12244}
12245
12246void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12247{
12248 AssertReturnVoid(isWriteLockOnCurrentThread());
12249
12250 if (aCollector)
12251 {
12252 aCollector->unregisterMetricsFor(aMachine);
12253 aCollector->unregisterBaseMetricsFor(aMachine);
12254 }
12255}
12256
12257#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12258
12259
12260////////////////////////////////////////////////////////////////////////////////
12261
12262DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12263
12264HRESULT SessionMachine::FinalConstruct()
12265{
12266 LogFlowThisFunc(("\n"));
12267
12268 mClientToken = NULL;
12269
12270 return BaseFinalConstruct();
12271}
12272
12273void SessionMachine::FinalRelease()
12274{
12275 LogFlowThisFunc(("\n"));
12276
12277 Assert(!mClientToken);
12278 /* paranoia, should not hang around any more */
12279 if (mClientToken)
12280 {
12281 delete mClientToken;
12282 mClientToken = NULL;
12283 }
12284
12285 uninit(Uninit::Unexpected);
12286
12287 BaseFinalRelease();
12288}
12289
12290/**
12291 * @note Must be called only by Machine::LockMachine() from its own write lock.
12292 */
12293HRESULT SessionMachine::init(Machine *aMachine)
12294{
12295 LogFlowThisFuncEnter();
12296 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12297
12298 AssertReturn(aMachine, E_INVALIDARG);
12299
12300 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12301
12302 /* Enclose the state transition NotReady->InInit->Ready */
12303 AutoInitSpan autoInitSpan(this);
12304 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12305
12306 HRESULT rc = S_OK;
12307
12308 /* create the machine client token */
12309 try
12310 {
12311 mClientToken = new ClientToken(aMachine, this);
12312 if (!mClientToken->isReady())
12313 {
12314 delete mClientToken;
12315 mClientToken = NULL;
12316 rc = E_FAIL;
12317 }
12318 }
12319 catch (std::bad_alloc &)
12320 {
12321 rc = E_OUTOFMEMORY;
12322 }
12323 if (FAILED(rc))
12324 return rc;
12325
12326 /* memorize the peer Machine */
12327 unconst(mPeer) = aMachine;
12328 /* share the parent pointer */
12329 unconst(mParent) = aMachine->mParent;
12330
12331 /* take the pointers to data to share */
12332 mData.share(aMachine->mData);
12333 mSSData.share(aMachine->mSSData);
12334
12335 mUserData.share(aMachine->mUserData);
12336 mHWData.share(aMachine->mHWData);
12337 mMediaData.share(aMachine->mMediaData);
12338
12339 mStorageControllers.allocate();
12340 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12341 it != aMachine->mStorageControllers->end();
12342 ++it)
12343 {
12344 ComObjPtr<StorageController> ctl;
12345 ctl.createObject();
12346 ctl->init(this, *it);
12347 mStorageControllers->push_back(ctl);
12348 }
12349
12350 mUSBControllers.allocate();
12351 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12352 it != aMachine->mUSBControllers->end();
12353 ++it)
12354 {
12355 ComObjPtr<USBController> ctl;
12356 ctl.createObject();
12357 ctl->init(this, *it);
12358 mUSBControllers->push_back(ctl);
12359 }
12360
12361 unconst(mBIOSSettings).createObject();
12362 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12363 /* create another VRDEServer object that will be mutable */
12364 unconst(mVRDEServer).createObject();
12365 mVRDEServer->init(this, aMachine->mVRDEServer);
12366 /* create another audio adapter object that will be mutable */
12367 unconst(mAudioAdapter).createObject();
12368 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12369 /* create a list of serial ports that will be mutable */
12370 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12371 {
12372 unconst(mSerialPorts[slot]).createObject();
12373 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12374 }
12375 /* create a list of parallel ports that will be mutable */
12376 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12377 {
12378 unconst(mParallelPorts[slot]).createObject();
12379 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12380 }
12381
12382 /* create another USB device filters object that will be mutable */
12383 unconst(mUSBDeviceFilters).createObject();
12384 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12385
12386 /* create a list of network adapters that will be mutable */
12387 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12388 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12389 {
12390 unconst(mNetworkAdapters[slot]).createObject();
12391 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12392 }
12393
12394 /* create another bandwidth control object that will be mutable */
12395 unconst(mBandwidthControl).createObject();
12396 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12397
12398 /* default is to delete saved state on Saved -> PoweredOff transition */
12399 mRemoveSavedState = true;
12400
12401 /* Confirm a successful initialization when it's the case */
12402 autoInitSpan.setSucceeded();
12403
12404 miNATNetworksStarted = 0;
12405
12406 LogFlowThisFuncLeave();
12407 return rc;
12408}
12409
12410/**
12411 * Uninitializes this session object. If the reason is other than
12412 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12413 * or the client watcher code.
12414 *
12415 * @param aReason uninitialization reason
12416 *
12417 * @note Locks mParent + this object for writing.
12418 */
12419void SessionMachine::uninit(Uninit::Reason aReason)
12420{
12421 LogFlowThisFuncEnter();
12422 LogFlowThisFunc(("reason=%d\n", aReason));
12423
12424 /*
12425 * Strongly reference ourselves to prevent this object deletion after
12426 * mData->mSession.mMachine.setNull() below (which can release the last
12427 * reference and call the destructor). Important: this must be done before
12428 * accessing any members (and before AutoUninitSpan that does it as well).
12429 * This self reference will be released as the very last step on return.
12430 */
12431 ComObjPtr<SessionMachine> selfRef = this;
12432
12433 /* Enclose the state transition Ready->InUninit->NotReady */
12434 AutoUninitSpan autoUninitSpan(this);
12435 if (autoUninitSpan.uninitDone())
12436 {
12437 LogFlowThisFunc(("Already uninitialized\n"));
12438 LogFlowThisFuncLeave();
12439 return;
12440 }
12441
12442 if (autoUninitSpan.initFailed())
12443 {
12444 /* We've been called by init() because it's failed. It's not really
12445 * necessary (nor it's safe) to perform the regular uninit sequence
12446 * below, the following is enough.
12447 */
12448 LogFlowThisFunc(("Initialization failed.\n"));
12449 /* destroy the machine client token */
12450 if (mClientToken)
12451 {
12452 delete mClientToken;
12453 mClientToken = NULL;
12454 }
12455 uninitDataAndChildObjects();
12456 mData.free();
12457 unconst(mParent) = NULL;
12458 unconst(mPeer) = NULL;
12459 LogFlowThisFuncLeave();
12460 return;
12461 }
12462
12463 MachineState_T lastState;
12464 {
12465 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12466 lastState = mData->mMachineState;
12467 }
12468 NOREF(lastState);
12469
12470#ifdef VBOX_WITH_USB
12471 // release all captured USB devices, but do this before requesting the locks below
12472 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12473 {
12474 /* Console::captureUSBDevices() is called in the VM process only after
12475 * setting the machine state to Starting or Restoring.
12476 * Console::detachAllUSBDevices() will be called upon successful
12477 * termination. So, we need to release USB devices only if there was
12478 * an abnormal termination of a running VM.
12479 *
12480 * This is identical to SessionMachine::DetachAllUSBDevices except
12481 * for the aAbnormal argument. */
12482 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12483 AssertComRC(rc);
12484 NOREF(rc);
12485
12486 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12487 if (service)
12488 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12489 }
12490#endif /* VBOX_WITH_USB */
12491
12492 // we need to lock this object in uninit() because the lock is shared
12493 // with mPeer (as well as data we modify below). mParent lock is needed
12494 // by several calls to it, and USB needs host lock.
12495 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12496
12497#ifdef VBOX_WITH_RESOURCE_USAGE_API
12498 /*
12499 * It is safe to call Machine::i_unregisterMetrics() here because
12500 * PerformanceCollector::samplerCallback no longer accesses guest methods
12501 * holding the lock.
12502 */
12503 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12504 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12505 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12506 if (mCollectorGuest)
12507 {
12508 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12509 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12510 mCollectorGuest = NULL;
12511 }
12512#endif
12513
12514 if (aReason == Uninit::Abnormal)
12515 {
12516 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12517
12518 /* reset the state to Aborted */
12519 if (mData->mMachineState != MachineState_Aborted)
12520 i_setMachineState(MachineState_Aborted);
12521 }
12522
12523 // any machine settings modified?
12524 if (mData->flModifications)
12525 {
12526 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12527 i_rollback(false /* aNotify */);
12528 }
12529
12530 mData->mSession.mPID = NIL_RTPROCESS;
12531
12532 if (aReason == Uninit::Unexpected)
12533 {
12534 /* Uninitialization didn't come from #checkForDeath(), so tell the
12535 * client watcher thread to update the set of machines that have open
12536 * sessions. */
12537 mParent->i_updateClientWatcher();
12538 }
12539
12540 /* uninitialize all remote controls */
12541 if (mData->mSession.mRemoteControls.size())
12542 {
12543 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12544 mData->mSession.mRemoteControls.size()));
12545
12546 Data::Session::RemoteControlList::iterator it =
12547 mData->mSession.mRemoteControls.begin();
12548 while (it != mData->mSession.mRemoteControls.end())
12549 {
12550 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12551 HRESULT rc = (*it)->Uninitialize();
12552 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12553 if (FAILED(rc))
12554 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12555 ++it;
12556 }
12557 mData->mSession.mRemoteControls.clear();
12558 }
12559
12560 /* Remove all references to the NAT network service. The service will stop
12561 * if all references (also from other VMs) are removed. */
12562 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12563 {
12564 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12565 {
12566 NetworkAttachmentType_T type;
12567 HRESULT hrc;
12568
12569 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12570 if ( SUCCEEDED(hrc)
12571 && type == NetworkAttachmentType_NATNetwork)
12572 {
12573 Bstr name;
12574 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12575 if (SUCCEEDED(hrc))
12576 {
12577 multilock.release();
12578 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12579 mUserData->s.strName.c_str(), name.raw()));
12580 mParent->i_natNetworkRefDec(name.raw());
12581 multilock.acquire();
12582 }
12583 }
12584 }
12585 }
12586
12587 /*
12588 * An expected uninitialization can come only from #checkForDeath().
12589 * Otherwise it means that something's gone really wrong (for example,
12590 * the Session implementation has released the VirtualBox reference
12591 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12592 * etc). However, it's also possible, that the client releases the IPC
12593 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12594 * but the VirtualBox release event comes first to the server process.
12595 * This case is practically possible, so we should not assert on an
12596 * unexpected uninit, just log a warning.
12597 */
12598
12599 if ((aReason == Uninit::Unexpected))
12600 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12601
12602 if (aReason != Uninit::Normal)
12603 {
12604 mData->mSession.mDirectControl.setNull();
12605 }
12606 else
12607 {
12608 /* this must be null here (see #OnSessionEnd()) */
12609 Assert(mData->mSession.mDirectControl.isNull());
12610 Assert(mData->mSession.mState == SessionState_Unlocking);
12611 Assert(!mData->mSession.mProgress.isNull());
12612 }
12613 if (mData->mSession.mProgress)
12614 {
12615 if (aReason == Uninit::Normal)
12616 mData->mSession.mProgress->i_notifyComplete(S_OK);
12617 else
12618 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12619 COM_IIDOF(ISession),
12620 getComponentName(),
12621 tr("The VM session was aborted"));
12622 mData->mSession.mProgress.setNull();
12623 }
12624
12625 /* remove the association between the peer machine and this session machine */
12626 Assert( (SessionMachine*)mData->mSession.mMachine == this
12627 || aReason == Uninit::Unexpected);
12628
12629 /* reset the rest of session data */
12630 mData->mSession.mLockType = LockType_Null;
12631 mData->mSession.mMachine.setNull();
12632 mData->mSession.mState = SessionState_Unlocked;
12633 mData->mSession.mName.setNull();
12634
12635 /* destroy the machine client token before leaving the exclusive lock */
12636 if (mClientToken)
12637 {
12638 delete mClientToken;
12639 mClientToken = NULL;
12640 }
12641
12642 /* fire an event */
12643 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12644
12645 uninitDataAndChildObjects();
12646
12647 /* free the essential data structure last */
12648 mData.free();
12649
12650 /* release the exclusive lock before setting the below two to NULL */
12651 multilock.release();
12652
12653 unconst(mParent) = NULL;
12654 unconst(mPeer) = NULL;
12655
12656 LogFlowThisFuncLeave();
12657}
12658
12659// util::Lockable interface
12660////////////////////////////////////////////////////////////////////////////////
12661
12662/**
12663 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12664 * with the primary Machine instance (mPeer).
12665 */
12666RWLockHandle *SessionMachine::lockHandle() const
12667{
12668 AssertReturn(mPeer != NULL, NULL);
12669 return mPeer->lockHandle();
12670}
12671
12672// IInternalMachineControl methods
12673////////////////////////////////////////////////////////////////////////////////
12674
12675/**
12676 * Passes collected guest statistics to performance collector object
12677 */
12678HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12679 ULONG aCpuKernel, ULONG aCpuIdle,
12680 ULONG aMemTotal, ULONG aMemFree,
12681 ULONG aMemBalloon, ULONG aMemShared,
12682 ULONG aMemCache, ULONG aPageTotal,
12683 ULONG aAllocVMM, ULONG aFreeVMM,
12684 ULONG aBalloonedVMM, ULONG aSharedVMM,
12685 ULONG aVmNetRx, ULONG aVmNetTx)
12686{
12687#ifdef VBOX_WITH_RESOURCE_USAGE_API
12688 if (mCollectorGuest)
12689 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12690 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12691 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12692 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12693
12694 return S_OK;
12695#else
12696 NOREF(aValidStats);
12697 NOREF(aCpuUser);
12698 NOREF(aCpuKernel);
12699 NOREF(aCpuIdle);
12700 NOREF(aMemTotal);
12701 NOREF(aMemFree);
12702 NOREF(aMemBalloon);
12703 NOREF(aMemShared);
12704 NOREF(aMemCache);
12705 NOREF(aPageTotal);
12706 NOREF(aAllocVMM);
12707 NOREF(aFreeVMM);
12708 NOREF(aBalloonedVMM);
12709 NOREF(aSharedVMM);
12710 NOREF(aVmNetRx);
12711 NOREF(aVmNetTx);
12712 return E_NOTIMPL;
12713#endif
12714}
12715
12716////////////////////////////////////////////////////////////////////////////////
12717//
12718// SessionMachine task records
12719//
12720////////////////////////////////////////////////////////////////////////////////
12721
12722/**
12723 * Task record for saving the machine state.
12724 */
12725struct SessionMachine::SaveStateTask
12726 : public Machine::Task
12727{
12728 SaveStateTask(SessionMachine *m,
12729 Progress *p,
12730 const Utf8Str &t,
12731 Reason_T enmReason,
12732 const Utf8Str &strStateFilePath)
12733 : Task(m, p, t),
12734 m_enmReason(enmReason),
12735 m_strStateFilePath(strStateFilePath)
12736 {}
12737
12738 void handler()
12739 {
12740 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12741 }
12742
12743 Reason_T m_enmReason;
12744 Utf8Str m_strStateFilePath;
12745};
12746
12747/**
12748 * Task thread implementation for SessionMachine::SaveState(), called from
12749 * SessionMachine::taskHandler().
12750 *
12751 * @note Locks this object for writing.
12752 *
12753 * @param task
12754 * @return
12755 */
12756void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12757{
12758 LogFlowThisFuncEnter();
12759
12760 AutoCaller autoCaller(this);
12761 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12762 if (FAILED(autoCaller.rc()))
12763 {
12764 /* we might have been uninitialized because the session was accidentally
12765 * closed by the client, so don't assert */
12766 HRESULT rc = setError(E_FAIL,
12767 tr("The session has been accidentally closed"));
12768 task.m_pProgress->i_notifyComplete(rc);
12769 LogFlowThisFuncLeave();
12770 return;
12771 }
12772
12773 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12774
12775 HRESULT rc = S_OK;
12776
12777 try
12778 {
12779 ComPtr<IInternalSessionControl> directControl;
12780 if (mData->mSession.mLockType == LockType_VM)
12781 directControl = mData->mSession.mDirectControl;
12782 if (directControl.isNull())
12783 throw setError(VBOX_E_INVALID_VM_STATE,
12784 tr("Trying to save state without a running VM"));
12785 alock.release();
12786 BOOL fSuspendedBySave;
12787 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12788 Assert(!fSuspendedBySave);
12789 alock.acquire();
12790
12791 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12792 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12793 throw E_FAIL);
12794
12795 if (SUCCEEDED(rc))
12796 {
12797 mSSData->strStateFilePath = task.m_strStateFilePath;
12798
12799 /* save all VM settings */
12800 rc = i_saveSettings(NULL);
12801 // no need to check whether VirtualBox.xml needs saving also since
12802 // we can't have a name change pending at this point
12803 }
12804 else
12805 {
12806 // On failure, set the state to the state we had at the beginning.
12807 i_setMachineState(task.m_machineStateBackup);
12808 i_updateMachineStateOnClient();
12809
12810 // Delete the saved state file (might have been already created).
12811 // No need to check whether this is shared with a snapshot here
12812 // because we certainly created a fresh saved state file here.
12813 RTFileDelete(task.m_strStateFilePath.c_str());
12814 }
12815 }
12816 catch (HRESULT aRC) { rc = aRC; }
12817
12818 task.m_pProgress->i_notifyComplete(rc);
12819
12820 LogFlowThisFuncLeave();
12821}
12822
12823/**
12824 * @note Locks this object for writing.
12825 */
12826HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12827{
12828 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12829}
12830
12831HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12832{
12833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12834
12835 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12836 if (FAILED(rc)) return rc;
12837
12838 if ( mData->mMachineState != MachineState_Running
12839 && mData->mMachineState != MachineState_Paused
12840 )
12841 return setError(VBOX_E_INVALID_VM_STATE,
12842 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12843 Global::stringifyMachineState(mData->mMachineState));
12844
12845 ComObjPtr<Progress> pProgress;
12846 pProgress.createObject();
12847 rc = pProgress->init(i_getVirtualBox(),
12848 static_cast<IMachine *>(this) /* aInitiator */,
12849 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12850 FALSE /* aCancelable */);
12851 if (FAILED(rc))
12852 return rc;
12853
12854 Utf8Str strStateFilePath;
12855 i_composeSavedStateFilename(strStateFilePath);
12856
12857 /* create and start the task on a separate thread (note that it will not
12858 * start working until we release alock) */
12859 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12860 rc = pTask->createThread();
12861 if (FAILED(rc))
12862 return rc;
12863
12864 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12865 i_setMachineState(MachineState_Saving);
12866 i_updateMachineStateOnClient();
12867
12868 pProgress.queryInterfaceTo(aProgress.asOutParam());
12869
12870 return S_OK;
12871}
12872
12873/**
12874 * @note Locks this object for writing.
12875 */
12876HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12877{
12878 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12879
12880 HRESULT rc = i_checkStateDependency(MutableStateDep);
12881 if (FAILED(rc)) return rc;
12882
12883 if ( mData->mMachineState != MachineState_PoweredOff
12884 && mData->mMachineState != MachineState_Teleported
12885 && mData->mMachineState != MachineState_Aborted
12886 )
12887 return setError(VBOX_E_INVALID_VM_STATE,
12888 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12889 Global::stringifyMachineState(mData->mMachineState));
12890
12891 com::Utf8Str stateFilePathFull;
12892 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12893 if (RT_FAILURE(vrc))
12894 return setError(VBOX_E_FILE_ERROR,
12895 tr("Invalid saved state file path '%s' (%Rrc)"),
12896 aSavedStateFile.c_str(),
12897 vrc);
12898
12899 mSSData->strStateFilePath = stateFilePathFull;
12900
12901 /* The below i_setMachineState() will detect the state transition and will
12902 * update the settings file */
12903
12904 return i_setMachineState(MachineState_Saved);
12905}
12906
12907/**
12908 * @note Locks this object for writing.
12909 */
12910HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12911{
12912 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12913
12914 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12915 if (FAILED(rc)) return rc;
12916
12917 if (mData->mMachineState != MachineState_Saved)
12918 return setError(VBOX_E_INVALID_VM_STATE,
12919 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12920 Global::stringifyMachineState(mData->mMachineState));
12921
12922 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12923
12924 /*
12925 * Saved -> PoweredOff transition will be detected in the SessionMachine
12926 * and properly handled.
12927 */
12928 rc = i_setMachineState(MachineState_PoweredOff);
12929 return rc;
12930}
12931
12932
12933/**
12934 * @note Locks the same as #i_setMachineState() does.
12935 */
12936HRESULT SessionMachine::updateState(MachineState_T aState)
12937{
12938 return i_setMachineState(aState);
12939}
12940
12941/**
12942 * @note Locks this object for writing.
12943 */
12944HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12945{
12946 IProgress* pProgress(aProgress);
12947
12948 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12949
12950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12951
12952 if (mData->mSession.mState != SessionState_Locked)
12953 return VBOX_E_INVALID_OBJECT_STATE;
12954
12955 if (!mData->mSession.mProgress.isNull())
12956 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12957
12958 /* If we didn't reference the NAT network service yet, add a reference to
12959 * force a start */
12960 if (miNATNetworksStarted < 1)
12961 {
12962 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12963 {
12964 NetworkAttachmentType_T type;
12965 HRESULT hrc;
12966 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12967 if ( SUCCEEDED(hrc)
12968 && type == NetworkAttachmentType_NATNetwork)
12969 {
12970 Bstr name;
12971 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12972 if (SUCCEEDED(hrc))
12973 {
12974 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12975 mUserData->s.strName.c_str(), name.raw()));
12976 mPeer->lockHandle()->unlockWrite();
12977 mParent->i_natNetworkRefInc(name.raw());
12978#ifdef RT_LOCK_STRICT
12979 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12980#else
12981 mPeer->lockHandle()->lockWrite();
12982#endif
12983 }
12984 }
12985 }
12986 miNATNetworksStarted++;
12987 }
12988
12989 LogFlowThisFunc(("returns S_OK.\n"));
12990 return S_OK;
12991}
12992
12993/**
12994 * @note Locks this object for writing.
12995 */
12996HRESULT SessionMachine::endPowerUp(LONG aResult)
12997{
12998 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12999
13000 if (mData->mSession.mState != SessionState_Locked)
13001 return VBOX_E_INVALID_OBJECT_STATE;
13002
13003 /* Finalize the LaunchVMProcess progress object. */
13004 if (mData->mSession.mProgress)
13005 {
13006 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13007 mData->mSession.mProgress.setNull();
13008 }
13009
13010 if (SUCCEEDED((HRESULT)aResult))
13011 {
13012#ifdef VBOX_WITH_RESOURCE_USAGE_API
13013 /* The VM has been powered up successfully, so it makes sense
13014 * now to offer the performance metrics for a running machine
13015 * object. Doing it earlier wouldn't be safe. */
13016 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13017 mData->mSession.mPID);
13018#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13019 }
13020
13021 return S_OK;
13022}
13023
13024/**
13025 * @note Locks this object for writing.
13026 */
13027HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13028{
13029 LogFlowThisFuncEnter();
13030
13031 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13032
13033 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13034 E_FAIL);
13035
13036 /* create a progress object to track operation completion */
13037 ComObjPtr<Progress> pProgress;
13038 pProgress.createObject();
13039 pProgress->init(i_getVirtualBox(),
13040 static_cast<IMachine *>(this) /* aInitiator */,
13041 Bstr(tr("Stopping the virtual machine")).raw(),
13042 FALSE /* aCancelable */);
13043
13044 /* fill in the console task data */
13045 mConsoleTaskData.mLastState = mData->mMachineState;
13046 mConsoleTaskData.mProgress = pProgress;
13047
13048 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13049 i_setMachineState(MachineState_Stopping);
13050
13051 pProgress.queryInterfaceTo(aProgress.asOutParam());
13052
13053 return S_OK;
13054}
13055
13056/**
13057 * @note Locks this object for writing.
13058 */
13059HRESULT SessionMachine::endPoweringDown(LONG aResult,
13060 const com::Utf8Str &aErrMsg)
13061{
13062 LogFlowThisFuncEnter();
13063
13064 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13065
13066 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13067 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13068 && mConsoleTaskData.mLastState != MachineState_Null,
13069 E_FAIL);
13070
13071 /*
13072 * On failure, set the state to the state we had when BeginPoweringDown()
13073 * was called (this is expected by Console::PowerDown() and the associated
13074 * task). On success the VM process already changed the state to
13075 * MachineState_PoweredOff, so no need to do anything.
13076 */
13077 if (FAILED(aResult))
13078 i_setMachineState(mConsoleTaskData.mLastState);
13079
13080 /* notify the progress object about operation completion */
13081 Assert(mConsoleTaskData.mProgress);
13082 if (SUCCEEDED(aResult))
13083 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13084 else
13085 {
13086 if (aErrMsg.length())
13087 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13088 COM_IIDOF(ISession),
13089 getComponentName(),
13090 aErrMsg.c_str());
13091 else
13092 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13093 }
13094
13095 /* clear out the temporary saved state data */
13096 mConsoleTaskData.mLastState = MachineState_Null;
13097 mConsoleTaskData.mProgress.setNull();
13098
13099 LogFlowThisFuncLeave();
13100 return S_OK;
13101}
13102
13103
13104/**
13105 * Goes through the USB filters of the given machine to see if the given
13106 * device matches any filter or not.
13107 *
13108 * @note Locks the same as USBController::hasMatchingFilter() does.
13109 */
13110HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13111 BOOL *aMatched,
13112 ULONG *aMaskedInterfaces)
13113{
13114 LogFlowThisFunc(("\n"));
13115
13116#ifdef VBOX_WITH_USB
13117 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13118#else
13119 NOREF(aDevice);
13120 NOREF(aMaskedInterfaces);
13121 *aMatched = FALSE;
13122#endif
13123
13124 return S_OK;
13125}
13126
13127/**
13128 * @note Locks the same as Host::captureUSBDevice() does.
13129 */
13130HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13131{
13132 LogFlowThisFunc(("\n"));
13133
13134#ifdef VBOX_WITH_USB
13135 /* if captureDeviceForVM() fails, it must have set extended error info */
13136 clearError();
13137 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13138 if (FAILED(rc)) return rc;
13139
13140 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13141 AssertReturn(service, E_FAIL);
13142 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13143#else
13144 NOREF(aId);
13145 return E_NOTIMPL;
13146#endif
13147}
13148
13149/**
13150 * @note Locks the same as Host::detachUSBDevice() does.
13151 */
13152HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13153 BOOL aDone)
13154{
13155 LogFlowThisFunc(("\n"));
13156
13157#ifdef VBOX_WITH_USB
13158 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13159 AssertReturn(service, E_FAIL);
13160 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13161#else
13162 NOREF(aId);
13163 NOREF(aDone);
13164 return E_NOTIMPL;
13165#endif
13166}
13167
13168/**
13169 * Inserts all machine filters to the USB proxy service and then calls
13170 * Host::autoCaptureUSBDevices().
13171 *
13172 * Called by Console from the VM process upon VM startup.
13173 *
13174 * @note Locks what called methods lock.
13175 */
13176HRESULT SessionMachine::autoCaptureUSBDevices()
13177{
13178 LogFlowThisFunc(("\n"));
13179
13180#ifdef VBOX_WITH_USB
13181 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13182 AssertComRC(rc);
13183 NOREF(rc);
13184
13185 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13186 AssertReturn(service, E_FAIL);
13187 return service->autoCaptureDevicesForVM(this);
13188#else
13189 return S_OK;
13190#endif
13191}
13192
13193/**
13194 * Removes all machine filters from the USB proxy service and then calls
13195 * Host::detachAllUSBDevices().
13196 *
13197 * Called by Console from the VM process upon normal VM termination or by
13198 * SessionMachine::uninit() upon abnormal VM termination (from under the
13199 * Machine/SessionMachine lock).
13200 *
13201 * @note Locks what called methods lock.
13202 */
13203HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13204{
13205 LogFlowThisFunc(("\n"));
13206
13207#ifdef VBOX_WITH_USB
13208 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13209 AssertComRC(rc);
13210 NOREF(rc);
13211
13212 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13213 AssertReturn(service, E_FAIL);
13214 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13215#else
13216 NOREF(aDone);
13217 return S_OK;
13218#endif
13219}
13220
13221/**
13222 * @note Locks this object for writing.
13223 */
13224HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13225 ComPtr<IProgress> &aProgress)
13226{
13227 LogFlowThisFuncEnter();
13228
13229 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13230 /*
13231 * We don't assert below because it might happen that a non-direct session
13232 * informs us it is closed right after we've been uninitialized -- it's ok.
13233 */
13234
13235 /* get IInternalSessionControl interface */
13236 ComPtr<IInternalSessionControl> control(aSession);
13237
13238 ComAssertRet(!control.isNull(), E_INVALIDARG);
13239
13240 /* Creating a Progress object requires the VirtualBox lock, and
13241 * thus locking it here is required by the lock order rules. */
13242 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13243
13244 if (control == mData->mSession.mDirectControl)
13245 {
13246 /* The direct session is being normally closed by the client process
13247 * ----------------------------------------------------------------- */
13248
13249 /* go to the closing state (essential for all open*Session() calls and
13250 * for #checkForDeath()) */
13251 Assert(mData->mSession.mState == SessionState_Locked);
13252 mData->mSession.mState = SessionState_Unlocking;
13253
13254 /* set direct control to NULL to release the remote instance */
13255 mData->mSession.mDirectControl.setNull();
13256 LogFlowThisFunc(("Direct control is set to NULL\n"));
13257
13258 if (mData->mSession.mProgress)
13259 {
13260 /* finalize the progress, someone might wait if a frontend
13261 * closes the session before powering on the VM. */
13262 mData->mSession.mProgress->notifyComplete(E_FAIL,
13263 COM_IIDOF(ISession),
13264 getComponentName(),
13265 tr("The VM session was closed before any attempt to power it on"));
13266 mData->mSession.mProgress.setNull();
13267 }
13268
13269 /* Create the progress object the client will use to wait until
13270 * #checkForDeath() is called to uninitialize this session object after
13271 * it releases the IPC semaphore.
13272 * Note! Because we're "reusing" mProgress here, this must be a proxy
13273 * object just like for LaunchVMProcess. */
13274 Assert(mData->mSession.mProgress.isNull());
13275 ComObjPtr<ProgressProxy> progress;
13276 progress.createObject();
13277 ComPtr<IUnknown> pPeer(mPeer);
13278 progress->init(mParent, pPeer,
13279 Bstr(tr("Closing session")).raw(),
13280 FALSE /* aCancelable */);
13281 progress.queryInterfaceTo(aProgress.asOutParam());
13282 mData->mSession.mProgress = progress;
13283 }
13284 else
13285 {
13286 /* the remote session is being normally closed */
13287 Data::Session::RemoteControlList::iterator it =
13288 mData->mSession.mRemoteControls.begin();
13289 while (it != mData->mSession.mRemoteControls.end())
13290 {
13291 if (control == *it)
13292 break;
13293 ++it;
13294 }
13295 BOOL found = it != mData->mSession.mRemoteControls.end();
13296 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13297 E_INVALIDARG);
13298 // This MUST be erase(it), not remove(*it) as the latter triggers a
13299 // very nasty use after free due to the place where the value "lives".
13300 mData->mSession.mRemoteControls.erase(it);
13301 }
13302
13303 /* signal the client watcher thread, because the client is going away */
13304 mParent->i_updateClientWatcher();
13305
13306 LogFlowThisFuncLeave();
13307 return S_OK;
13308}
13309
13310HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13311 std::vector<com::Utf8Str> &aValues,
13312 std::vector<LONG64> &aTimestamps,
13313 std::vector<com::Utf8Str> &aFlags)
13314{
13315 LogFlowThisFunc(("\n"));
13316
13317#ifdef VBOX_WITH_GUEST_PROPS
13318 using namespace guestProp;
13319
13320 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13321
13322 size_t cEntries = mHWData->mGuestProperties.size();
13323 aNames.resize(cEntries);
13324 aValues.resize(cEntries);
13325 aTimestamps.resize(cEntries);
13326 aFlags.resize(cEntries);
13327
13328 size_t i = 0;
13329 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13330 it != mHWData->mGuestProperties.end();
13331 ++it, ++i)
13332 {
13333 char szFlags[MAX_FLAGS_LEN + 1];
13334 aNames[i] = it->first;
13335 aValues[i] = it->second.strValue;
13336 aTimestamps[i] = it->second.mTimestamp;
13337
13338 /* If it is NULL, keep it NULL. */
13339 if (it->second.mFlags)
13340 {
13341 writeFlags(it->second.mFlags, szFlags);
13342 aFlags[i] = szFlags;
13343 }
13344 else
13345 aFlags[i] = "";
13346 }
13347 return S_OK;
13348#else
13349 ReturnComNotImplemented();
13350#endif
13351}
13352
13353HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13354 const com::Utf8Str &aValue,
13355 LONG64 aTimestamp,
13356 const com::Utf8Str &aFlags)
13357{
13358 LogFlowThisFunc(("\n"));
13359
13360#ifdef VBOX_WITH_GUEST_PROPS
13361 using namespace guestProp;
13362
13363 try
13364 {
13365 /*
13366 * Convert input up front.
13367 */
13368 uint32_t fFlags = NILFLAG;
13369 if (aFlags.length())
13370 {
13371 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13372 AssertRCReturn(vrc, E_INVALIDARG);
13373 }
13374
13375 /*
13376 * Now grab the object lock, validate the state and do the update.
13377 */
13378
13379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13380
13381 switch (mData->mMachineState)
13382 {
13383 case MachineState_Paused:
13384 case MachineState_Running:
13385 case MachineState_Teleporting:
13386 case MachineState_TeleportingPausedVM:
13387 case MachineState_OnlineSnapshotting:
13388 case MachineState_LiveSnapshotting:
13389 case MachineState_DeletingSnapshotOnline:
13390 case MachineState_DeletingSnapshotPaused:
13391 case MachineState_Saving:
13392 case MachineState_Stopping:
13393 break;
13394
13395 default:
13396 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13397 VBOX_E_INVALID_VM_STATE);
13398 }
13399
13400 i_setModified(IsModified_MachineData);
13401 mHWData.backup();
13402
13403 bool fDelete = !aValue.length();
13404 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13405 if (it != mHWData->mGuestProperties.end())
13406 {
13407 if (!fDelete)
13408 {
13409 it->second.strValue = aValue;
13410 it->second.mTimestamp = aTimestamp;
13411 it->second.mFlags = fFlags;
13412 }
13413 else
13414 mHWData->mGuestProperties.erase(it);
13415
13416 mData->mGuestPropertiesModified = TRUE;
13417 }
13418 else if (!fDelete)
13419 {
13420 HWData::GuestProperty prop;
13421 prop.strValue = aValue;
13422 prop.mTimestamp = aTimestamp;
13423 prop.mFlags = fFlags;
13424
13425 mHWData->mGuestProperties[aName] = prop;
13426 mData->mGuestPropertiesModified = TRUE;
13427 }
13428
13429 alock.release();
13430
13431 mParent->i_onGuestPropertyChange(mData->mUuid,
13432 Bstr(aName).raw(),
13433 Bstr(aValue).raw(),
13434 Bstr(aFlags).raw());
13435 }
13436 catch (...)
13437 {
13438 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13439 }
13440 return S_OK;
13441#else
13442 ReturnComNotImplemented();
13443#endif
13444}
13445
13446
13447HRESULT SessionMachine::lockMedia()
13448{
13449 AutoMultiWriteLock2 alock(this->lockHandle(),
13450 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13451
13452 AssertReturn( mData->mMachineState == MachineState_Starting
13453 || mData->mMachineState == MachineState_Restoring
13454 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13455
13456 clearError();
13457 alock.release();
13458 return i_lockMedia();
13459}
13460
13461HRESULT SessionMachine::unlockMedia()
13462{
13463 HRESULT hrc = i_unlockMedia();
13464 return hrc;
13465}
13466
13467HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13468 ComPtr<IMediumAttachment> &aNewAttachment)
13469{
13470 // request the host lock first, since might be calling Host methods for getting host drives;
13471 // next, protect the media tree all the while we're in here, as well as our member variables
13472 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13473 this->lockHandle(),
13474 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13475
13476 IMediumAttachment *iAttach = aAttachment;
13477 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13478
13479 Bstr ctrlName;
13480 LONG lPort;
13481 LONG lDevice;
13482 bool fTempEject;
13483 {
13484 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13485
13486 /* Need to query the details first, as the IMediumAttachment reference
13487 * might be to the original settings, which we are going to change. */
13488 ctrlName = pAttach->i_getControllerName();
13489 lPort = pAttach->i_getPort();
13490 lDevice = pAttach->i_getDevice();
13491 fTempEject = pAttach->i_getTempEject();
13492 }
13493
13494 if (!fTempEject)
13495 {
13496 /* Remember previously mounted medium. The medium before taking the
13497 * backup is not necessarily the same thing. */
13498 ComObjPtr<Medium> oldmedium;
13499 oldmedium = pAttach->i_getMedium();
13500
13501 i_setModified(IsModified_Storage);
13502 mMediaData.backup();
13503
13504 // The backup operation makes the pAttach reference point to the
13505 // old settings. Re-get the correct reference.
13506 pAttach = i_findAttachment(mMediaData->mAttachments,
13507 ctrlName.raw(),
13508 lPort,
13509 lDevice);
13510
13511 {
13512 AutoCaller autoAttachCaller(this);
13513 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13514
13515 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13516 if (!oldmedium.isNull())
13517 oldmedium->i_removeBackReference(mData->mUuid);
13518
13519 pAttach->i_updateMedium(NULL);
13520 pAttach->i_updateEjected();
13521 }
13522
13523 i_setModified(IsModified_Storage);
13524 }
13525 else
13526 {
13527 {
13528 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13529 pAttach->i_updateEjected();
13530 }
13531 }
13532
13533 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13534
13535 return S_OK;
13536}
13537
13538// public methods only for internal purposes
13539/////////////////////////////////////////////////////////////////////////////
13540
13541#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13542/**
13543 * Called from the client watcher thread to check for expected or unexpected
13544 * death of the client process that has a direct session to this machine.
13545 *
13546 * On Win32 and on OS/2, this method is called only when we've got the
13547 * mutex (i.e. the client has either died or terminated normally) so it always
13548 * returns @c true (the client is terminated, the session machine is
13549 * uninitialized).
13550 *
13551 * On other platforms, the method returns @c true if the client process has
13552 * terminated normally or abnormally and the session machine was uninitialized,
13553 * and @c false if the client process is still alive.
13554 *
13555 * @note Locks this object for writing.
13556 */
13557bool SessionMachine::i_checkForDeath()
13558{
13559 Uninit::Reason reason;
13560 bool terminated = false;
13561
13562 /* Enclose autoCaller with a block because calling uninit() from under it
13563 * will deadlock. */
13564 {
13565 AutoCaller autoCaller(this);
13566 if (!autoCaller.isOk())
13567 {
13568 /* return true if not ready, to cause the client watcher to exclude
13569 * the corresponding session from watching */
13570 LogFlowThisFunc(("Already uninitialized!\n"));
13571 return true;
13572 }
13573
13574 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13575
13576 /* Determine the reason of death: if the session state is Closing here,
13577 * everything is fine. Otherwise it means that the client did not call
13578 * OnSessionEnd() before it released the IPC semaphore. This may happen
13579 * either because the client process has abnormally terminated, or
13580 * because it simply forgot to call ISession::Close() before exiting. We
13581 * threat the latter also as an abnormal termination (see
13582 * Session::uninit() for details). */
13583 reason = mData->mSession.mState == SessionState_Unlocking ?
13584 Uninit::Normal :
13585 Uninit::Abnormal;
13586
13587 if (mClientToken)
13588 terminated = mClientToken->release();
13589 } /* AutoCaller block */
13590
13591 if (terminated)
13592 uninit(reason);
13593
13594 return terminated;
13595}
13596
13597void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13598{
13599 LogFlowThisFunc(("\n"));
13600
13601 strTokenId.setNull();
13602
13603 AutoCaller autoCaller(this);
13604 AssertComRCReturnVoid(autoCaller.rc());
13605
13606 Assert(mClientToken);
13607 if (mClientToken)
13608 mClientToken->getId(strTokenId);
13609}
13610#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13611IToken *SessionMachine::i_getToken()
13612{
13613 LogFlowThisFunc(("\n"));
13614
13615 AutoCaller autoCaller(this);
13616 AssertComRCReturn(autoCaller.rc(), NULL);
13617
13618 Assert(mClientToken);
13619 if (mClientToken)
13620 return mClientToken->getToken();
13621 else
13622 return NULL;
13623}
13624#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13625
13626Machine::ClientToken *SessionMachine::i_getClientToken()
13627{
13628 LogFlowThisFunc(("\n"));
13629
13630 AutoCaller autoCaller(this);
13631 AssertComRCReturn(autoCaller.rc(), NULL);
13632
13633 return mClientToken;
13634}
13635
13636
13637/**
13638 * @note Locks this object for reading.
13639 */
13640HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13641{
13642 LogFlowThisFunc(("\n"));
13643
13644 AutoCaller autoCaller(this);
13645 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13646
13647 ComPtr<IInternalSessionControl> directControl;
13648 {
13649 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13650 if (mData->mSession.mLockType == LockType_VM)
13651 directControl = mData->mSession.mDirectControl;
13652 }
13653
13654 /* ignore notifications sent after #OnSessionEnd() is called */
13655 if (!directControl)
13656 return S_OK;
13657
13658 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13659}
13660
13661/**
13662 * @note Locks this object for reading.
13663 */
13664HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13665 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13666 IN_BSTR aGuestIp, LONG aGuestPort)
13667{
13668 LogFlowThisFunc(("\n"));
13669
13670 AutoCaller autoCaller(this);
13671 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13672
13673 ComPtr<IInternalSessionControl> directControl;
13674 {
13675 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13676 if (mData->mSession.mLockType == LockType_VM)
13677 directControl = mData->mSession.mDirectControl;
13678 }
13679
13680 /* ignore notifications sent after #OnSessionEnd() is called */
13681 if (!directControl)
13682 return S_OK;
13683 /*
13684 * instead acting like callback we ask IVirtualBox deliver corresponding event
13685 */
13686
13687 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13688 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13689 return S_OK;
13690}
13691
13692/**
13693 * @note Locks this object for reading.
13694 */
13695HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13696{
13697 LogFlowThisFunc(("\n"));
13698
13699 AutoCaller autoCaller(this);
13700 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13701
13702 ComPtr<IInternalSessionControl> directControl;
13703 {
13704 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13705 if (mData->mSession.mLockType == LockType_VM)
13706 directControl = mData->mSession.mDirectControl;
13707 }
13708
13709 /* ignore notifications sent after #OnSessionEnd() is called */
13710 if (!directControl)
13711 return S_OK;
13712
13713 return directControl->OnSerialPortChange(serialPort);
13714}
13715
13716/**
13717 * @note Locks this object for reading.
13718 */
13719HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13720{
13721 LogFlowThisFunc(("\n"));
13722
13723 AutoCaller autoCaller(this);
13724 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13725
13726 ComPtr<IInternalSessionControl> directControl;
13727 {
13728 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13729 if (mData->mSession.mLockType == LockType_VM)
13730 directControl = mData->mSession.mDirectControl;
13731 }
13732
13733 /* ignore notifications sent after #OnSessionEnd() is called */
13734 if (!directControl)
13735 return S_OK;
13736
13737 return directControl->OnParallelPortChange(parallelPort);
13738}
13739
13740/**
13741 * @note Locks this object for reading.
13742 */
13743HRESULT SessionMachine::i_onStorageControllerChange()
13744{
13745 LogFlowThisFunc(("\n"));
13746
13747 AutoCaller autoCaller(this);
13748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13749
13750 ComPtr<IInternalSessionControl> directControl;
13751 {
13752 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13753 if (mData->mSession.mLockType == LockType_VM)
13754 directControl = mData->mSession.mDirectControl;
13755 }
13756
13757 /* ignore notifications sent after #OnSessionEnd() is called */
13758 if (!directControl)
13759 return S_OK;
13760
13761 return directControl->OnStorageControllerChange();
13762}
13763
13764/**
13765 * @note Locks this object for reading.
13766 */
13767HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13768{
13769 LogFlowThisFunc(("\n"));
13770
13771 AutoCaller autoCaller(this);
13772 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13773
13774 ComPtr<IInternalSessionControl> directControl;
13775 {
13776 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13777 if (mData->mSession.mLockType == LockType_VM)
13778 directControl = mData->mSession.mDirectControl;
13779 }
13780
13781 /* ignore notifications sent after #OnSessionEnd() is called */
13782 if (!directControl)
13783 return S_OK;
13784
13785 return directControl->OnMediumChange(aAttachment, aForce);
13786}
13787
13788/**
13789 * @note Locks this object for reading.
13790 */
13791HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13792{
13793 LogFlowThisFunc(("\n"));
13794
13795 AutoCaller autoCaller(this);
13796 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13797
13798 ComPtr<IInternalSessionControl> directControl;
13799 {
13800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13801 if (mData->mSession.mLockType == LockType_VM)
13802 directControl = mData->mSession.mDirectControl;
13803 }
13804
13805 /* ignore notifications sent after #OnSessionEnd() is called */
13806 if (!directControl)
13807 return S_OK;
13808
13809 return directControl->OnCPUChange(aCPU, aRemove);
13810}
13811
13812HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13813{
13814 LogFlowThisFunc(("\n"));
13815
13816 AutoCaller autoCaller(this);
13817 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13818
13819 ComPtr<IInternalSessionControl> directControl;
13820 {
13821 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13822 if (mData->mSession.mLockType == LockType_VM)
13823 directControl = mData->mSession.mDirectControl;
13824 }
13825
13826 /* ignore notifications sent after #OnSessionEnd() is called */
13827 if (!directControl)
13828 return S_OK;
13829
13830 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13831}
13832
13833/**
13834 * @note Locks this object for reading.
13835 */
13836HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13837{
13838 LogFlowThisFunc(("\n"));
13839
13840 AutoCaller autoCaller(this);
13841 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13842
13843 ComPtr<IInternalSessionControl> directControl;
13844 {
13845 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13846 if (mData->mSession.mLockType == LockType_VM)
13847 directControl = mData->mSession.mDirectControl;
13848 }
13849
13850 /* ignore notifications sent after #OnSessionEnd() is called */
13851 if (!directControl)
13852 return S_OK;
13853
13854 return directControl->OnVRDEServerChange(aRestart);
13855}
13856
13857/**
13858 * @note Locks this object for reading.
13859 */
13860HRESULT SessionMachine::i_onVideoCaptureChange()
13861{
13862 LogFlowThisFunc(("\n"));
13863
13864 AutoCaller autoCaller(this);
13865 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13866
13867 ComPtr<IInternalSessionControl> directControl;
13868 {
13869 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13870 if (mData->mSession.mLockType == LockType_VM)
13871 directControl = mData->mSession.mDirectControl;
13872 }
13873
13874 /* ignore notifications sent after #OnSessionEnd() is called */
13875 if (!directControl)
13876 return S_OK;
13877
13878 return directControl->OnVideoCaptureChange();
13879}
13880
13881/**
13882 * @note Locks this object for reading.
13883 */
13884HRESULT SessionMachine::i_onUSBControllerChange()
13885{
13886 LogFlowThisFunc(("\n"));
13887
13888 AutoCaller autoCaller(this);
13889 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13890
13891 ComPtr<IInternalSessionControl> directControl;
13892 {
13893 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13894 if (mData->mSession.mLockType == LockType_VM)
13895 directControl = mData->mSession.mDirectControl;
13896 }
13897
13898 /* ignore notifications sent after #OnSessionEnd() is called */
13899 if (!directControl)
13900 return S_OK;
13901
13902 return directControl->OnUSBControllerChange();
13903}
13904
13905/**
13906 * @note Locks this object for reading.
13907 */
13908HRESULT SessionMachine::i_onSharedFolderChange()
13909{
13910 LogFlowThisFunc(("\n"));
13911
13912 AutoCaller autoCaller(this);
13913 AssertComRCReturnRC(autoCaller.rc());
13914
13915 ComPtr<IInternalSessionControl> directControl;
13916 {
13917 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13918 if (mData->mSession.mLockType == LockType_VM)
13919 directControl = mData->mSession.mDirectControl;
13920 }
13921
13922 /* ignore notifications sent after #OnSessionEnd() is called */
13923 if (!directControl)
13924 return S_OK;
13925
13926 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13927}
13928
13929/**
13930 * @note Locks this object for reading.
13931 */
13932HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13933{
13934 LogFlowThisFunc(("\n"));
13935
13936 AutoCaller autoCaller(this);
13937 AssertComRCReturnRC(autoCaller.rc());
13938
13939 ComPtr<IInternalSessionControl> directControl;
13940 {
13941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13942 if (mData->mSession.mLockType == LockType_VM)
13943 directControl = mData->mSession.mDirectControl;
13944 }
13945
13946 /* ignore notifications sent after #OnSessionEnd() is called */
13947 if (!directControl)
13948 return S_OK;
13949
13950 return directControl->OnClipboardModeChange(aClipboardMode);
13951}
13952
13953/**
13954 * @note Locks this object for reading.
13955 */
13956HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13957{
13958 LogFlowThisFunc(("\n"));
13959
13960 AutoCaller autoCaller(this);
13961 AssertComRCReturnRC(autoCaller.rc());
13962
13963 ComPtr<IInternalSessionControl> directControl;
13964 {
13965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13966 if (mData->mSession.mLockType == LockType_VM)
13967 directControl = mData->mSession.mDirectControl;
13968 }
13969
13970 /* ignore notifications sent after #OnSessionEnd() is called */
13971 if (!directControl)
13972 return S_OK;
13973
13974 return directControl->OnDnDModeChange(aDnDMode);
13975}
13976
13977/**
13978 * @note Locks this object for reading.
13979 */
13980HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13981{
13982 LogFlowThisFunc(("\n"));
13983
13984 AutoCaller autoCaller(this);
13985 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13986
13987 ComPtr<IInternalSessionControl> directControl;
13988 {
13989 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13990 if (mData->mSession.mLockType == LockType_VM)
13991 directControl = mData->mSession.mDirectControl;
13992 }
13993
13994 /* ignore notifications sent after #OnSessionEnd() is called */
13995 if (!directControl)
13996 return S_OK;
13997
13998 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
13999}
14000
14001/**
14002 * @note Locks this object for reading.
14003 */
14004HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14005{
14006 LogFlowThisFunc(("\n"));
14007
14008 AutoCaller autoCaller(this);
14009 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14010
14011 ComPtr<IInternalSessionControl> directControl;
14012 {
14013 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14014 if (mData->mSession.mLockType == LockType_VM)
14015 directControl = mData->mSession.mDirectControl;
14016 }
14017
14018 /* ignore notifications sent after #OnSessionEnd() is called */
14019 if (!directControl)
14020 return S_OK;
14021
14022 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14023}
14024
14025/**
14026 * Returns @c true if this machine's USB controller reports it has a matching
14027 * filter for the given USB device and @c false otherwise.
14028 *
14029 * @note locks this object for reading.
14030 */
14031bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14032{
14033 AutoCaller autoCaller(this);
14034 /* silently return if not ready -- this method may be called after the
14035 * direct machine session has been called */
14036 if (!autoCaller.isOk())
14037 return false;
14038
14039#ifdef VBOX_WITH_USB
14040 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14041
14042 switch (mData->mMachineState)
14043 {
14044 case MachineState_Starting:
14045 case MachineState_Restoring:
14046 case MachineState_TeleportingIn:
14047 case MachineState_Paused:
14048 case MachineState_Running:
14049 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14050 * elsewhere... */
14051 alock.release();
14052 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14053 default: break;
14054 }
14055#else
14056 NOREF(aDevice);
14057 NOREF(aMaskedIfs);
14058#endif
14059 return false;
14060}
14061
14062/**
14063 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14064 */
14065HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14066 IVirtualBoxErrorInfo *aError,
14067 ULONG aMaskedIfs,
14068 const com::Utf8Str &aCaptureFilename)
14069{
14070 LogFlowThisFunc(("\n"));
14071
14072 AutoCaller autoCaller(this);
14073
14074 /* This notification may happen after the machine object has been
14075 * uninitialized (the session was closed), so don't assert. */
14076 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14077
14078 ComPtr<IInternalSessionControl> directControl;
14079 {
14080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14081 if (mData->mSession.mLockType == LockType_VM)
14082 directControl = mData->mSession.mDirectControl;
14083 }
14084
14085 /* fail on notifications sent after #OnSessionEnd() is called, it is
14086 * expected by the caller */
14087 if (!directControl)
14088 return E_FAIL;
14089
14090 /* No locks should be held at this point. */
14091 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14092 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14093
14094 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14095}
14096
14097/**
14098 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14099 */
14100HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14101 IVirtualBoxErrorInfo *aError)
14102{
14103 LogFlowThisFunc(("\n"));
14104
14105 AutoCaller autoCaller(this);
14106
14107 /* This notification may happen after the machine object has been
14108 * uninitialized (the session was closed), so don't assert. */
14109 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14110
14111 ComPtr<IInternalSessionControl> directControl;
14112 {
14113 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14114 if (mData->mSession.mLockType == LockType_VM)
14115 directControl = mData->mSession.mDirectControl;
14116 }
14117
14118 /* fail on notifications sent after #OnSessionEnd() is called, it is
14119 * expected by the caller */
14120 if (!directControl)
14121 return E_FAIL;
14122
14123 /* No locks should be held at this point. */
14124 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14125 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14126
14127 return directControl->OnUSBDeviceDetach(aId, aError);
14128}
14129
14130// protected methods
14131/////////////////////////////////////////////////////////////////////////////
14132
14133/**
14134 * Deletes the given file if it is no longer in use by either the current machine state
14135 * (if the machine is "saved") or any of the machine's snapshots.
14136 *
14137 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14138 * but is different for each SnapshotMachine. When calling this, the order of calling this
14139 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14140 * is therefore critical. I know, it's all rather messy.
14141 *
14142 * @param strStateFile
14143 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14144 * the test for whether the saved state file is in use.
14145 */
14146void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14147 Snapshot *pSnapshotToIgnore)
14148{
14149 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14150 if ( (strStateFile.isNotEmpty())
14151 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14152 )
14153 // ... and it must also not be shared with other snapshots
14154 if ( !mData->mFirstSnapshot
14155 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14156 // this checks the SnapshotMachine's state file paths
14157 )
14158 RTFileDelete(strStateFile.c_str());
14159}
14160
14161/**
14162 * Locks the attached media.
14163 *
14164 * All attached hard disks are locked for writing and DVD/floppy are locked for
14165 * reading. Parents of attached hard disks (if any) are locked for reading.
14166 *
14167 * This method also performs accessibility check of all media it locks: if some
14168 * media is inaccessible, the method will return a failure and a bunch of
14169 * extended error info objects per each inaccessible medium.
14170 *
14171 * Note that this method is atomic: if it returns a success, all media are
14172 * locked as described above; on failure no media is locked at all (all
14173 * succeeded individual locks will be undone).
14174 *
14175 * The caller is responsible for doing the necessary state sanity checks.
14176 *
14177 * The locks made by this method must be undone by calling #unlockMedia() when
14178 * no more needed.
14179 */
14180HRESULT SessionMachine::i_lockMedia()
14181{
14182 AutoCaller autoCaller(this);
14183 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14184
14185 AutoMultiWriteLock2 alock(this->lockHandle(),
14186 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14187
14188 /* bail out if trying to lock things with already set up locking */
14189 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14190
14191 MultiResult mrc(S_OK);
14192
14193 /* Collect locking information for all medium objects attached to the VM. */
14194 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14195 it != mMediaData->mAttachments.end();
14196 ++it)
14197 {
14198 MediumAttachment* pAtt = *it;
14199 DeviceType_T devType = pAtt->i_getType();
14200 Medium *pMedium = pAtt->i_getMedium();
14201
14202 MediumLockList *pMediumLockList(new MediumLockList());
14203 // There can be attachments without a medium (floppy/dvd), and thus
14204 // it's impossible to create a medium lock list. It still makes sense
14205 // to have the empty medium lock list in the map in case a medium is
14206 // attached later.
14207 if (pMedium != NULL)
14208 {
14209 MediumType_T mediumType = pMedium->i_getType();
14210 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14211 || mediumType == MediumType_Shareable;
14212 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14213
14214 alock.release();
14215 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14216 !fIsReadOnlyLock /* fMediumLockWrite */,
14217 false /* fMediumLockWriteAll */,
14218 NULL,
14219 *pMediumLockList);
14220 alock.acquire();
14221 if (FAILED(mrc))
14222 {
14223 delete pMediumLockList;
14224 mData->mSession.mLockedMedia.Clear();
14225 break;
14226 }
14227 }
14228
14229 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14230 if (FAILED(rc))
14231 {
14232 mData->mSession.mLockedMedia.Clear();
14233 mrc = setError(rc,
14234 tr("Collecting locking information for all attached media failed"));
14235 break;
14236 }
14237 }
14238
14239 if (SUCCEEDED(mrc))
14240 {
14241 /* Now lock all media. If this fails, nothing is locked. */
14242 alock.release();
14243 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14244 alock.acquire();
14245 if (FAILED(rc))
14246 {
14247 mrc = setError(rc,
14248 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14249 }
14250 }
14251
14252 return mrc;
14253}
14254
14255/**
14256 * Undoes the locks made by by #lockMedia().
14257 */
14258HRESULT SessionMachine::i_unlockMedia()
14259{
14260 AutoCaller autoCaller(this);
14261 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14262
14263 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14264
14265 /* we may be holding important error info on the current thread;
14266 * preserve it */
14267 ErrorInfoKeeper eik;
14268
14269 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14270 AssertComRC(rc);
14271 return rc;
14272}
14273
14274/**
14275 * Helper to change the machine state (reimplementation).
14276 *
14277 * @note Locks this object for writing.
14278 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14279 * it can cause crashes in random places due to unexpectedly committing
14280 * the current settings. The caller is responsible for that. The call
14281 * to saveStateSettings is fine, because this method does not commit.
14282 */
14283HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14284{
14285 LogFlowThisFuncEnter();
14286 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14287
14288 AutoCaller autoCaller(this);
14289 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14290
14291 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14292
14293 MachineState_T oldMachineState = mData->mMachineState;
14294
14295 AssertMsgReturn(oldMachineState != aMachineState,
14296 ("oldMachineState=%s, aMachineState=%s\n",
14297 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14298 E_FAIL);
14299
14300 HRESULT rc = S_OK;
14301
14302 int stsFlags = 0;
14303 bool deleteSavedState = false;
14304
14305 /* detect some state transitions */
14306
14307 if ( ( oldMachineState == MachineState_Saved
14308 && aMachineState == MachineState_Restoring)
14309 || ( ( oldMachineState == MachineState_PoweredOff
14310 || oldMachineState == MachineState_Teleported
14311 || oldMachineState == MachineState_Aborted
14312 )
14313 && ( aMachineState == MachineState_TeleportingIn
14314 || aMachineState == MachineState_Starting
14315 )
14316 )
14317 )
14318 {
14319 /* The EMT thread is about to start */
14320
14321 /* Nothing to do here for now... */
14322
14323 /// @todo NEWMEDIA don't let mDVDDrive and other children
14324 /// change anything when in the Starting/Restoring state
14325 }
14326 else if ( ( oldMachineState == MachineState_Running
14327 || oldMachineState == MachineState_Paused
14328 || oldMachineState == MachineState_Teleporting
14329 || oldMachineState == MachineState_OnlineSnapshotting
14330 || oldMachineState == MachineState_LiveSnapshotting
14331 || oldMachineState == MachineState_Stuck
14332 || oldMachineState == MachineState_Starting
14333 || oldMachineState == MachineState_Stopping
14334 || oldMachineState == MachineState_Saving
14335 || oldMachineState == MachineState_Restoring
14336 || oldMachineState == MachineState_TeleportingPausedVM
14337 || oldMachineState == MachineState_TeleportingIn
14338 )
14339 && ( aMachineState == MachineState_PoweredOff
14340 || aMachineState == MachineState_Saved
14341 || aMachineState == MachineState_Teleported
14342 || aMachineState == MachineState_Aborted
14343 )
14344 )
14345 {
14346 /* The EMT thread has just stopped, unlock attached media. Note that as
14347 * opposed to locking that is done from Console, we do unlocking here
14348 * because the VM process may have aborted before having a chance to
14349 * properly unlock all media it locked. */
14350
14351 unlockMedia();
14352 }
14353
14354 if (oldMachineState == MachineState_Restoring)
14355 {
14356 if (aMachineState != MachineState_Saved)
14357 {
14358 /*
14359 * delete the saved state file once the machine has finished
14360 * restoring from it (note that Console sets the state from
14361 * Restoring to Saved if the VM couldn't restore successfully,
14362 * to give the user an ability to fix an error and retry --
14363 * we keep the saved state file in this case)
14364 */
14365 deleteSavedState = true;
14366 }
14367 }
14368 else if ( oldMachineState == MachineState_Saved
14369 && ( aMachineState == MachineState_PoweredOff
14370 || aMachineState == MachineState_Aborted
14371 || aMachineState == MachineState_Teleported
14372 )
14373 )
14374 {
14375 /*
14376 * delete the saved state after SessionMachine::ForgetSavedState() is called
14377 * or if the VM process (owning a direct VM session) crashed while the
14378 * VM was Saved
14379 */
14380
14381 /// @todo (dmik)
14382 // Not sure that deleting the saved state file just because of the
14383 // client death before it attempted to restore the VM is a good
14384 // thing. But when it crashes we need to go to the Aborted state
14385 // which cannot have the saved state file associated... The only
14386 // way to fix this is to make the Aborted condition not a VM state
14387 // but a bool flag: i.e., when a crash occurs, set it to true and
14388 // change the state to PoweredOff or Saved depending on the
14389 // saved state presence.
14390
14391 deleteSavedState = true;
14392 mData->mCurrentStateModified = TRUE;
14393 stsFlags |= SaveSTS_CurStateModified;
14394 }
14395
14396 if ( aMachineState == MachineState_Starting
14397 || aMachineState == MachineState_Restoring
14398 || aMachineState == MachineState_TeleportingIn
14399 )
14400 {
14401 /* set the current state modified flag to indicate that the current
14402 * state is no more identical to the state in the
14403 * current snapshot */
14404 if (!mData->mCurrentSnapshot.isNull())
14405 {
14406 mData->mCurrentStateModified = TRUE;
14407 stsFlags |= SaveSTS_CurStateModified;
14408 }
14409 }
14410
14411 if (deleteSavedState)
14412 {
14413 if (mRemoveSavedState)
14414 {
14415 Assert(!mSSData->strStateFilePath.isEmpty());
14416
14417 // it is safe to delete the saved state file if ...
14418 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14419 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14420 // ... none of the snapshots share the saved state file
14421 )
14422 RTFileDelete(mSSData->strStateFilePath.c_str());
14423 }
14424
14425 mSSData->strStateFilePath.setNull();
14426 stsFlags |= SaveSTS_StateFilePath;
14427 }
14428
14429 /* redirect to the underlying peer machine */
14430 mPeer->i_setMachineState(aMachineState);
14431
14432 if ( oldMachineState != MachineState_RestoringSnapshot
14433 && ( aMachineState == MachineState_PoweredOff
14434 || aMachineState == MachineState_Teleported
14435 || aMachineState == MachineState_Aborted
14436 || aMachineState == MachineState_Saved))
14437 {
14438 /* the machine has stopped execution
14439 * (or the saved state file was adopted) */
14440 stsFlags |= SaveSTS_StateTimeStamp;
14441 }
14442
14443 if ( ( oldMachineState == MachineState_PoweredOff
14444 || oldMachineState == MachineState_Aborted
14445 || oldMachineState == MachineState_Teleported
14446 )
14447 && aMachineState == MachineState_Saved)
14448 {
14449 /* the saved state file was adopted */
14450 Assert(!mSSData->strStateFilePath.isEmpty());
14451 stsFlags |= SaveSTS_StateFilePath;
14452 }
14453
14454#ifdef VBOX_WITH_GUEST_PROPS
14455 if ( aMachineState == MachineState_PoweredOff
14456 || aMachineState == MachineState_Aborted
14457 || aMachineState == MachineState_Teleported)
14458 {
14459 /* Make sure any transient guest properties get removed from the
14460 * property store on shutdown. */
14461 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14462
14463 /* remove it from the settings representation */
14464 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14465 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14466 it != llGuestProperties.end();
14467 /*nothing*/)
14468 {
14469 const settings::GuestProperty &prop = *it;
14470 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14471 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14472 {
14473 it = llGuestProperties.erase(it);
14474 fNeedsSaving = true;
14475 }
14476 else
14477 {
14478 ++it;
14479 }
14480 }
14481
14482 /* Additionally remove it from the HWData representation. Required to
14483 * keep everything in sync, as this is what the API keeps using. */
14484 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14485 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14486 it != llHWGuestProperties.end();
14487 /*nothing*/)
14488 {
14489 uint32_t fFlags = it->second.mFlags;
14490 if ( fFlags & guestProp::TRANSIENT
14491 || fFlags & guestProp::TRANSRESET)
14492 {
14493 /* iterator where we need to continue after the erase call
14494 * (C++03 is a fact still, and it doesn't return the iterator
14495 * which would allow continuing) */
14496 HWData::GuestPropertyMap::iterator it2 = it;
14497 ++it2;
14498 llHWGuestProperties.erase(it);
14499 it = it2;
14500 fNeedsSaving = true;
14501 }
14502 else
14503 {
14504 ++it;
14505 }
14506 }
14507
14508 if (fNeedsSaving)
14509 {
14510 mData->mCurrentStateModified = TRUE;
14511 stsFlags |= SaveSTS_CurStateModified;
14512 }
14513 }
14514#endif /* VBOX_WITH_GUEST_PROPS */
14515
14516 rc = i_saveStateSettings(stsFlags);
14517
14518 if ( ( oldMachineState != MachineState_PoweredOff
14519 && oldMachineState != MachineState_Aborted
14520 && oldMachineState != MachineState_Teleported
14521 )
14522 && ( aMachineState == MachineState_PoweredOff
14523 || aMachineState == MachineState_Aborted
14524 || aMachineState == MachineState_Teleported
14525 )
14526 )
14527 {
14528 /* we've been shut down for any reason */
14529 /* no special action so far */
14530 }
14531
14532 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14533 LogFlowThisFuncLeave();
14534 return rc;
14535}
14536
14537/**
14538 * Sends the current machine state value to the VM process.
14539 *
14540 * @note Locks this object for reading, then calls a client process.
14541 */
14542HRESULT SessionMachine::i_updateMachineStateOnClient()
14543{
14544 AutoCaller autoCaller(this);
14545 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14546
14547 ComPtr<IInternalSessionControl> directControl;
14548 {
14549 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14550 AssertReturn(!!mData, E_FAIL);
14551 if (mData->mSession.mLockType == LockType_VM)
14552 directControl = mData->mSession.mDirectControl;
14553
14554 /* directControl may be already set to NULL here in #OnSessionEnd()
14555 * called too early by the direct session process while there is still
14556 * some operation (like deleting the snapshot) in progress. The client
14557 * process in this case is waiting inside Session::close() for the
14558 * "end session" process object to complete, while #uninit() called by
14559 * #checkForDeath() on the Watcher thread is waiting for the pending
14560 * operation to complete. For now, we accept this inconsistent behavior
14561 * and simply do nothing here. */
14562
14563 if (mData->mSession.mState == SessionState_Unlocking)
14564 return S_OK;
14565 }
14566
14567 /* ignore notifications sent after #OnSessionEnd() is called */
14568 if (!directControl)
14569 return S_OK;
14570
14571 return directControl->UpdateMachineState(mData->mMachineState);
14572}
14573
14574
14575/**
14576 * Static Machine method that can get passed to RTThreadCreate to
14577 * have a thread started for a Task. See Machine::Task.
14578 */
14579/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14580{
14581 AssertReturn(pvUser, VERR_INVALID_POINTER);
14582
14583 Task *pTask = static_cast<Task *>(pvUser);
14584 pTask->handler();
14585 /** @todo r=klaus it would be safer to update the progress object here,
14586 * as it avoids possible races due to scoping issues/tricks in the handler */
14587 // it's our responsibility to delete the task
14588 delete pTask;
14589
14590 return 0;
14591}
14592
14593/*static*/
14594HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14595{
14596 va_list args;
14597 va_start(args, pcszMsg);
14598 HRESULT rc = setErrorInternal(aResultCode,
14599 getStaticClassIID(),
14600 getStaticComponentName(),
14601 Utf8Str(pcszMsg, args),
14602 false /* aWarning */,
14603 true /* aLogIt */);
14604 va_end(args);
14605 return rc;
14606}
14607
14608
14609HRESULT Machine::updateState(MachineState_T aState)
14610{
14611 NOREF(aState);
14612 ReturnComNotImplemented();
14613}
14614
14615HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14616{
14617 NOREF(aProgress);
14618 ReturnComNotImplemented();
14619}
14620
14621HRESULT Machine::endPowerUp(LONG aResult)
14622{
14623 NOREF(aResult);
14624 ReturnComNotImplemented();
14625}
14626
14627HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14628{
14629 NOREF(aProgress);
14630 ReturnComNotImplemented();
14631}
14632
14633HRESULT Machine::endPoweringDown(LONG aResult,
14634 const com::Utf8Str &aErrMsg)
14635{
14636 NOREF(aResult);
14637 NOREF(aErrMsg);
14638 ReturnComNotImplemented();
14639}
14640
14641HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14642 BOOL *aMatched,
14643 ULONG *aMaskedInterfaces)
14644{
14645 NOREF(aDevice);
14646 NOREF(aMatched);
14647 NOREF(aMaskedInterfaces);
14648 ReturnComNotImplemented();
14649
14650}
14651
14652HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14653{
14654 NOREF(aId); NOREF(aCaptureFilename);
14655 ReturnComNotImplemented();
14656}
14657
14658HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14659 BOOL aDone)
14660{
14661 NOREF(aId);
14662 NOREF(aDone);
14663 ReturnComNotImplemented();
14664}
14665
14666HRESULT Machine::autoCaptureUSBDevices()
14667{
14668 ReturnComNotImplemented();
14669}
14670
14671HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14672{
14673 NOREF(aDone);
14674 ReturnComNotImplemented();
14675}
14676
14677HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14678 ComPtr<IProgress> &aProgress)
14679{
14680 NOREF(aSession);
14681 NOREF(aProgress);
14682 ReturnComNotImplemented();
14683}
14684
14685HRESULT Machine::finishOnlineMergeMedium()
14686{
14687 ReturnComNotImplemented();
14688}
14689
14690HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14691 std::vector<com::Utf8Str> &aValues,
14692 std::vector<LONG64> &aTimestamps,
14693 std::vector<com::Utf8Str> &aFlags)
14694{
14695 NOREF(aNames);
14696 NOREF(aValues);
14697 NOREF(aTimestamps);
14698 NOREF(aFlags);
14699 ReturnComNotImplemented();
14700}
14701
14702HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14703 const com::Utf8Str &aValue,
14704 LONG64 aTimestamp,
14705 const com::Utf8Str &aFlags)
14706{
14707 NOREF(aName);
14708 NOREF(aValue);
14709 NOREF(aTimestamp);
14710 NOREF(aFlags);
14711 ReturnComNotImplemented();
14712}
14713
14714HRESULT Machine::lockMedia()
14715{
14716 ReturnComNotImplemented();
14717}
14718
14719HRESULT Machine::unlockMedia()
14720{
14721 ReturnComNotImplemented();
14722}
14723
14724HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14725 ComPtr<IMediumAttachment> &aNewAttachment)
14726{
14727 NOREF(aAttachment);
14728 NOREF(aNewAttachment);
14729 ReturnComNotImplemented();
14730}
14731
14732HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14733 ULONG aCpuUser,
14734 ULONG aCpuKernel,
14735 ULONG aCpuIdle,
14736 ULONG aMemTotal,
14737 ULONG aMemFree,
14738 ULONG aMemBalloon,
14739 ULONG aMemShared,
14740 ULONG aMemCache,
14741 ULONG aPagedTotal,
14742 ULONG aMemAllocTotal,
14743 ULONG aMemFreeTotal,
14744 ULONG aMemBalloonTotal,
14745 ULONG aMemSharedTotal,
14746 ULONG aVmNetRx,
14747 ULONG aVmNetTx)
14748{
14749 NOREF(aValidStats);
14750 NOREF(aCpuUser);
14751 NOREF(aCpuKernel);
14752 NOREF(aCpuIdle);
14753 NOREF(aMemTotal);
14754 NOREF(aMemFree);
14755 NOREF(aMemBalloon);
14756 NOREF(aMemShared);
14757 NOREF(aMemCache);
14758 NOREF(aPagedTotal);
14759 NOREF(aMemAllocTotal);
14760 NOREF(aMemFreeTotal);
14761 NOREF(aMemBalloonTotal);
14762 NOREF(aMemSharedTotal);
14763 NOREF(aVmNetRx);
14764 NOREF(aVmNetTx);
14765 ReturnComNotImplemented();
14766}
Note: See TracBrowser for help on using the repository browser.

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