VirtualBox

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

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

VBoxStartup.log -> VBoxHardening.log; option renamed likewise.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 509.9 KB
Line 
1/* $Id: MachineImpl.cpp 57823 2015-09-18 10:28:34Z 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.215389.xyz. 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\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", logFolder.c_str(), RTPATH_DELIMITER);
5342 RTFileDelete(log.c_str());
5343 log = Utf8StrFmt("%s%cVBoxHardening.log", logFolder.c_str(), RTPATH_DELIMITER);
5344 RTFileDelete(log.c_str());
5345#endif
5346
5347 RTDirRemove(logFolder.c_str());
5348 }
5349
5350 /* delete the Snapshots folder, nothing important should be left
5351 * there (we don't check for errors because the user might have
5352 * some private files there that we don't want to delete) */
5353 Utf8Str strFullSnapshotFolder;
5354 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
5355 Assert(!strFullSnapshotFolder.isEmpty());
5356 if (RTDirExists(strFullSnapshotFolder.c_str()))
5357 RTDirRemove(strFullSnapshotFolder.c_str());
5358
5359 // delete the directory that contains the settings file, but only
5360 // if it matches the VM name
5361 Utf8Str settingsDir;
5362 if (i_isInOwnDir(&settingsDir))
5363 RTDirRemove(settingsDir.c_str());
5364 }
5365
5366 alock.release();
5367
5368 mParent->i_saveModifiedRegistries();
5369 }
5370 catch (HRESULT aRC) { rc = aRC; }
5371
5372 task.m_pProgress->i_notifyComplete(rc);
5373
5374 LogFlowThisFuncLeave();
5375}
5376
5377HRESULT Machine::deleteConfig(const std::vector<ComPtr<IMedium> > &aMedia, ComPtr<IProgress> &aProgress)
5378{
5379 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5380
5381 HRESULT rc = i_checkStateDependency(MutableStateDep);
5382 if (FAILED(rc)) return rc;
5383
5384 if (mData->mRegistered)
5385 return setError(VBOX_E_INVALID_VM_STATE,
5386 tr("Cannot delete settings of a registered machine"));
5387
5388 // collect files to delete
5389 StringsList llFilesToDelete(mData->llFilesToDelete); // saved states pushed here by Unregister()
5390 if (mData->pMachineConfigFile->fileExists())
5391 llFilesToDelete.push_back(mData->m_strConfigFileFull);
5392
5393 RTCList<ComPtr<IMedium> > llMediums;
5394 for (size_t i = 0; i < aMedia.size(); ++i)
5395 {
5396 IMedium *pIMedium(aMedia[i]);
5397 ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
5398 if (pMedium.isNull())
5399 return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
5400 SafeArray<BSTR> ids;
5401 rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
5402 if (FAILED(rc)) return rc;
5403 /* At this point the medium should not have any back references
5404 * anymore. If it has it is attached to another VM and *must* not
5405 * deleted. */
5406 if (ids.size() < 1)
5407 llMediums.append(pMedium);
5408 }
5409
5410 ComObjPtr<Progress> pProgress;
5411 pProgress.createObject();
5412 rc = pProgress->init(i_getVirtualBox(),
5413 static_cast<IMachine*>(this) /* aInitiator */,
5414 Bstr(tr("Deleting files")).raw(),
5415 true /* fCancellable */,
5416 (ULONG)(llFilesToDelete.size() + llMediums.size() + 1), // cOperations
5417 BstrFmt(tr("Deleting '%s'"), llFilesToDelete.front().c_str()).raw());
5418 if (FAILED(rc))
5419 return rc;
5420
5421 /* create and start the task on a separate thread (note that it will not
5422 * start working until we release alock) */
5423 DeleteConfigTask *pTask = new DeleteConfigTask(this, pProgress, "DeleteVM", llMediums, llFilesToDelete);
5424 rc = pTask->createThread();
5425 if (FAILED(rc))
5426 return rc;
5427
5428 pProgress.queryInterfaceTo(aProgress.asOutParam());
5429
5430 LogFlowFuncLeave();
5431
5432 return S_OK;
5433}
5434
5435HRESULT Machine::findSnapshot(const com::Utf8Str &aNameOrId, ComPtr<ISnapshot> &aSnapshot)
5436{
5437 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5438
5439 ComObjPtr<Snapshot> pSnapshot;
5440 HRESULT rc;
5441
5442 if (aNameOrId.isEmpty())
5443 // null case (caller wants root snapshot): i_findSnapshotById() handles this
5444 rc = i_findSnapshotById(Guid(), pSnapshot, true /* aSetError */);
5445 else
5446 {
5447 Guid uuid(aNameOrId);
5448 if (uuid.isValid())
5449 rc = i_findSnapshotById(uuid, pSnapshot, true /* aSetError */);
5450 else
5451 rc = i_findSnapshotByName(aNameOrId, pSnapshot, true /* aSetError */);
5452 }
5453 pSnapshot.queryInterfaceTo(aSnapshot.asOutParam());
5454
5455 return rc;
5456}
5457
5458HRESULT Machine::createSharedFolder(const com::Utf8Str &aName, const com::Utf8Str &aHostPath, BOOL aWritable, BOOL aAutomount)
5459{
5460 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5461
5462 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5463 if (FAILED(rc)) return rc;
5464
5465 ComObjPtr<SharedFolder> sharedFolder;
5466 rc = i_findSharedFolder(aName, sharedFolder, false /* aSetError */);
5467 if (SUCCEEDED(rc))
5468 return setError(VBOX_E_OBJECT_IN_USE,
5469 tr("Shared folder named '%s' already exists"),
5470 aName.c_str());
5471
5472 sharedFolder.createObject();
5473 rc = sharedFolder->init(i_getMachine(),
5474 aName,
5475 aHostPath,
5476 !!aWritable,
5477 !!aAutomount,
5478 true /* fFailOnError */);
5479 if (FAILED(rc)) return rc;
5480
5481 i_setModified(IsModified_SharedFolders);
5482 mHWData.backup();
5483 mHWData->mSharedFolders.push_back(sharedFolder);
5484
5485 /* inform the direct session if any */
5486 alock.release();
5487 i_onSharedFolderChange();
5488
5489 return S_OK;
5490}
5491
5492HRESULT Machine::removeSharedFolder(const com::Utf8Str &aName)
5493{
5494 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5495
5496 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
5497 if (FAILED(rc)) return rc;
5498
5499 ComObjPtr<SharedFolder> sharedFolder;
5500 rc = i_findSharedFolder(aName, sharedFolder, true /* aSetError */);
5501 if (FAILED(rc)) return rc;
5502
5503 i_setModified(IsModified_SharedFolders);
5504 mHWData.backup();
5505 mHWData->mSharedFolders.remove(sharedFolder);
5506
5507 /* inform the direct session if any */
5508 alock.release();
5509 i_onSharedFolderChange();
5510
5511 return S_OK;
5512}
5513
5514HRESULT Machine::canShowConsoleWindow(BOOL *aCanShow)
5515{
5516 /* start with No */
5517 *aCanShow = FALSE;
5518
5519 ComPtr<IInternalSessionControl> directControl;
5520 {
5521 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5522
5523 if (mData->mSession.mState != SessionState_Locked)
5524 return setError(VBOX_E_INVALID_VM_STATE,
5525 tr("Machine is not locked for session (session state: %s)"),
5526 Global::stringifySessionState(mData->mSession.mState));
5527
5528 if (mData->mSession.mLockType == LockType_VM)
5529 directControl = mData->mSession.mDirectControl;
5530 }
5531
5532 /* ignore calls made after #OnSessionEnd() is called */
5533 if (!directControl)
5534 return S_OK;
5535
5536 LONG64 dummy;
5537 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy);
5538}
5539
5540HRESULT Machine::showConsoleWindow(LONG64 *aWinId)
5541{
5542 ComPtr<IInternalSessionControl> directControl;
5543 {
5544 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5545
5546 if (mData->mSession.mState != SessionState_Locked)
5547 return setError(E_FAIL,
5548 tr("Machine is not locked for session (session state: %s)"),
5549 Global::stringifySessionState(mData->mSession.mState));
5550
5551 if (mData->mSession.mLockType == LockType_VM)
5552 directControl = mData->mSession.mDirectControl;
5553 }
5554
5555 /* ignore calls made after #OnSessionEnd() is called */
5556 if (!directControl)
5557 return S_OK;
5558
5559 BOOL dummy;
5560 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId);
5561}
5562
5563#ifdef VBOX_WITH_GUEST_PROPS
5564/**
5565 * Look up a guest property in VBoxSVC's internal structures.
5566 */
5567HRESULT Machine::i_getGuestPropertyFromService(const com::Utf8Str &aName,
5568 com::Utf8Str &aValue,
5569 LONG64 *aTimestamp,
5570 com::Utf8Str &aFlags) const
5571{
5572 using namespace guestProp;
5573
5574 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5575 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(aName);
5576
5577 if (it != mHWData->mGuestProperties.end())
5578 {
5579 char szFlags[MAX_FLAGS_LEN + 1];
5580 aValue = it->second.strValue;
5581 *aTimestamp = it->second.mTimestamp;
5582 writeFlags(it->second.mFlags, szFlags);
5583 aFlags = Utf8Str(szFlags);
5584 }
5585
5586 return S_OK;
5587}
5588
5589/**
5590 * Query the VM that a guest property belongs to for the property.
5591 * @returns E_ACCESSDENIED if the VM process is not available or not
5592 * currently handling queries and the lookup should then be done in
5593 * VBoxSVC.
5594 */
5595HRESULT Machine::i_getGuestPropertyFromVM(const com::Utf8Str &aName,
5596 com::Utf8Str &aValue,
5597 LONG64 *aTimestamp,
5598 com::Utf8Str &aFlags) const
5599{
5600 HRESULT rc = S_OK;
5601 BSTR bValue = NULL;
5602 BSTR bFlags = NULL;
5603
5604 ComPtr<IInternalSessionControl> directControl;
5605 {
5606 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5607 if (mData->mSession.mLockType == LockType_VM)
5608 directControl = mData->mSession.mDirectControl;
5609 }
5610
5611 /* ignore calls made after #OnSessionEnd() is called */
5612 if (!directControl)
5613 rc = E_ACCESSDENIED;
5614 else
5615 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr("").raw(), Bstr("").raw(),
5616 0 /* accessMode */,
5617 &bValue, aTimestamp, &bFlags);
5618
5619 aValue = bValue;
5620 aFlags = bFlags;
5621
5622 return rc;
5623}
5624#endif // VBOX_WITH_GUEST_PROPS
5625
5626HRESULT Machine::getGuestProperty(const com::Utf8Str &aName,
5627 com::Utf8Str &aValue,
5628 LONG64 *aTimestamp,
5629 com::Utf8Str &aFlags)
5630{
5631#ifndef VBOX_WITH_GUEST_PROPS
5632 ReturnComNotImplemented();
5633#else // VBOX_WITH_GUEST_PROPS
5634
5635 HRESULT rc = i_getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags);
5636
5637 if (rc == E_ACCESSDENIED)
5638 /* The VM is not running or the service is not (yet) accessible */
5639 rc = i_getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags);
5640 return rc;
5641#endif // VBOX_WITH_GUEST_PROPS
5642}
5643
5644HRESULT Machine::getGuestPropertyValue(const com::Utf8Str &aProperty, com::Utf8Str &aValue)
5645{
5646 LONG64 dummyTimestamp;
5647 com::Utf8Str dummyFlags;
5648 HRESULT rc = getGuestProperty(aProperty, aValue, &dummyTimestamp, dummyFlags);
5649 return rc;
5650
5651}
5652HRESULT Machine::getGuestPropertyTimestamp(const com::Utf8Str &aProperty, LONG64 *aValue)
5653{
5654 com::Utf8Str dummyFlags;
5655 com::Utf8Str dummyValue;
5656 HRESULT rc = getGuestProperty(aProperty, dummyValue, aValue, dummyFlags);
5657 return rc;
5658}
5659
5660#ifdef VBOX_WITH_GUEST_PROPS
5661/**
5662 * Set a guest property in VBoxSVC's internal structures.
5663 */
5664HRESULT Machine::i_setGuestPropertyToService(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5665 const com::Utf8Str &aFlags, bool fDelete)
5666{
5667 using namespace guestProp;
5668
5669 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5670 HRESULT rc = S_OK;
5671
5672 rc = i_checkStateDependency(MutableOrSavedStateDep);
5673 if (FAILED(rc)) return rc;
5674
5675 try
5676 {
5677 uint32_t fFlags = NILFLAG;
5678 if (aFlags.length() && RT_FAILURE(validateFlags(aFlags.c_str(), &fFlags)))
5679 return setError(E_INVALIDARG, tr("Invalid guest property flag values: '%s'"), aFlags.c_str());
5680
5681 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
5682 if (it == mHWData->mGuestProperties.end())
5683 {
5684 if (!fDelete)
5685 {
5686 i_setModified(IsModified_MachineData);
5687 mHWData.backupEx();
5688
5689 RTTIMESPEC time;
5690 HWData::GuestProperty prop;
5691 prop.strValue = Bstr(aValue).raw();
5692 prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5693 prop.mFlags = fFlags;
5694 mHWData->mGuestProperties[aName] = prop;
5695 }
5696 }
5697 else
5698 {
5699 if (it->second.mFlags & (RDONLYHOST))
5700 {
5701 rc = setError(E_ACCESSDENIED, tr("The property '%s' cannot be changed by the host"), aName.c_str());
5702 }
5703 else
5704 {
5705 i_setModified(IsModified_MachineData);
5706 mHWData.backupEx();
5707
5708 /* The backupEx() operation invalidates our iterator,
5709 * so get a new one. */
5710 it = mHWData->mGuestProperties.find(aName);
5711 Assert(it != mHWData->mGuestProperties.end());
5712
5713 if (!fDelete)
5714 {
5715 RTTIMESPEC time;
5716 it->second.strValue = aValue;
5717 it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time));
5718 it->second.mFlags = fFlags;
5719 }
5720 else
5721 mHWData->mGuestProperties.erase(it);
5722 }
5723 }
5724
5725 if (SUCCEEDED(rc))
5726 {
5727 alock.release();
5728
5729 mParent->i_onGuestPropertyChange(mData->mUuid,
5730 Bstr(aName).raw(),
5731 Bstr(aValue).raw(),
5732 Bstr(aFlags).raw());
5733 }
5734 }
5735 catch (std::bad_alloc &)
5736 {
5737 rc = E_OUTOFMEMORY;
5738 }
5739
5740 return rc;
5741}
5742
5743/**
5744 * Set a property on the VM that that property belongs to.
5745 * @returns E_ACCESSDENIED if the VM process is not available or not
5746 * currently handling queries and the setting should then be done in
5747 * VBoxSVC.
5748 */
5749HRESULT Machine::i_setGuestPropertyToVM(const com::Utf8Str &aName, const com::Utf8Str &aValue,
5750 const com::Utf8Str &aFlags, bool fDelete)
5751{
5752 HRESULT rc;
5753
5754 try
5755 {
5756 ComPtr<IInternalSessionControl> directControl;
5757 {
5758 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5759 if (mData->mSession.mLockType == LockType_VM)
5760 directControl = mData->mSession.mDirectControl;
5761 }
5762
5763 BSTR dummy = NULL; /* will not be changed (setter) */
5764 LONG64 dummy64;
5765 if (!directControl)
5766 rc = E_ACCESSDENIED;
5767 else
5768 /** @todo Fix when adding DeleteGuestProperty(), see defect. */
5769 rc = directControl->AccessGuestProperty(Bstr(aName).raw(), Bstr(aValue).raw(), Bstr(aFlags).raw(),
5770 fDelete? 2: 1 /* accessMode */,
5771 &dummy, &dummy64, &dummy);
5772 }
5773 catch (std::bad_alloc &)
5774 {
5775 rc = E_OUTOFMEMORY;
5776 }
5777
5778 return rc;
5779}
5780#endif // VBOX_WITH_GUEST_PROPS
5781
5782HRESULT Machine::setGuestProperty(const com::Utf8Str &aProperty, const com::Utf8Str &aValue,
5783 const com::Utf8Str &aFlags)
5784{
5785#ifndef VBOX_WITH_GUEST_PROPS
5786 ReturnComNotImplemented();
5787#else // VBOX_WITH_GUEST_PROPS
5788 HRESULT rc = i_setGuestPropertyToVM(aProperty, aValue, aFlags, /* fDelete = */ false);
5789 if (rc == E_ACCESSDENIED)
5790 /* The VM is not running or the service is not (yet) accessible */
5791 rc = i_setGuestPropertyToService(aProperty, aValue, aFlags, /* fDelete = */ false);
5792 return rc;
5793#endif // VBOX_WITH_GUEST_PROPS
5794}
5795
5796HRESULT Machine::setGuestPropertyValue(const com::Utf8Str &aProperty, const com::Utf8Str &aValue)
5797{
5798 return setGuestProperty(aProperty, aValue, "");
5799}
5800
5801HRESULT Machine::deleteGuestProperty(const com::Utf8Str &aName)
5802{
5803#ifndef VBOX_WITH_GUEST_PROPS
5804 ReturnComNotImplemented();
5805#else // VBOX_WITH_GUEST_PROPS
5806 HRESULT rc = i_setGuestPropertyToVM(aName, "", "", /* fDelete = */ true);
5807 if (rc == E_ACCESSDENIED)
5808 /* The VM is not running or the service is not (yet) accessible */
5809 rc = i_setGuestPropertyToService(aName, "", "", /* fDelete = */ true);
5810 return rc;
5811#endif // VBOX_WITH_GUEST_PROPS
5812}
5813
5814#ifdef VBOX_WITH_GUEST_PROPS
5815/**
5816 * Enumerate the guest properties in VBoxSVC's internal structures.
5817 */
5818HRESULT Machine::i_enumerateGuestPropertiesInService(const com::Utf8Str &aPatterns,
5819 std::vector<com::Utf8Str> &aNames,
5820 std::vector<com::Utf8Str> &aValues,
5821 std::vector<LONG64> &aTimestamps,
5822 std::vector<com::Utf8Str> &aFlags)
5823{
5824 using namespace guestProp;
5825
5826 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5827 Utf8Str strPatterns(aPatterns);
5828
5829 HWData::GuestPropertyMap propMap;
5830
5831 /*
5832 * Look for matching patterns and build up a list.
5833 */
5834 HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
5835 while (it != mHWData->mGuestProperties.end())
5836 {
5837 if ( strPatterns.isEmpty()
5838 || RTStrSimplePatternMultiMatch(strPatterns.c_str(),
5839 RTSTR_MAX,
5840 it->first.c_str(),
5841 RTSTR_MAX,
5842 NULL)
5843 )
5844 propMap.insert(*it);
5845 ++it;
5846 }
5847
5848 alock.release();
5849
5850 /*
5851 * And build up the arrays for returning the property information.
5852 */
5853 size_t cEntries = propMap.size();
5854
5855 aNames.resize(cEntries);
5856 aValues.resize(cEntries);
5857 aTimestamps.resize(cEntries);
5858 aFlags.resize(cEntries);
5859
5860 char szFlags[MAX_FLAGS_LEN + 1];
5861 size_t i= 0;
5862 for (it = propMap.begin(); it != propMap.end(); ++i, ++it)
5863 {
5864 aNames[i] = it->first;
5865 aValues[i] = it->second.strValue;
5866 aTimestamps[i] = it->second.mTimestamp;
5867 writeFlags(it->second.mFlags, szFlags);
5868 aFlags[i] = Utf8Str(szFlags);
5869 }
5870
5871 return S_OK;
5872}
5873
5874/**
5875 * Enumerate the properties managed by a VM.
5876 * @returns E_ACCESSDENIED if the VM process is not available or not
5877 * currently handling queries and the setting should then be done in
5878 * VBoxSVC.
5879 */
5880HRESULT Machine::i_enumerateGuestPropertiesOnVM(const com::Utf8Str &aPatterns,
5881 std::vector<com::Utf8Str> &aNames,
5882 std::vector<com::Utf8Str> &aValues,
5883 std::vector<LONG64> &aTimestamps,
5884 std::vector<com::Utf8Str> &aFlags)
5885{
5886 HRESULT rc;
5887 ComPtr<IInternalSessionControl> directControl;
5888 {
5889 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5890 if (mData->mSession.mLockType == LockType_VM)
5891 directControl = mData->mSession.mDirectControl;
5892 }
5893
5894 com::SafeArray<BSTR> bNames;
5895 com::SafeArray<BSTR> bValues;
5896 com::SafeArray<LONG64> bTimestamps;
5897 com::SafeArray<BSTR> bFlags;
5898
5899 if (!directControl)
5900 rc = E_ACCESSDENIED;
5901 else
5902 rc = directControl->EnumerateGuestProperties(Bstr(aPatterns).raw(),
5903 ComSafeArrayAsOutParam(bNames),
5904 ComSafeArrayAsOutParam(bValues),
5905 ComSafeArrayAsOutParam(bTimestamps),
5906 ComSafeArrayAsOutParam(bFlags));
5907 size_t i;
5908 aNames.resize(bNames.size());
5909 for (i = 0; i < bNames.size(); ++i)
5910 aNames[i] = Utf8Str(bNames[i]);
5911 aValues.resize(bValues.size());
5912 for (i = 0; i < bValues.size(); ++i)
5913 aValues[i] = Utf8Str(bValues[i]);
5914 aTimestamps.resize(bTimestamps.size());
5915 for (i = 0; i < bTimestamps.size(); ++i)
5916 aTimestamps[i] = bTimestamps[i];
5917 aFlags.resize(bFlags.size());
5918 for (i = 0; i < bFlags.size(); ++i)
5919 aFlags[i] = Utf8Str(bFlags[i]);
5920
5921 return rc;
5922}
5923#endif // VBOX_WITH_GUEST_PROPS
5924HRESULT Machine::enumerateGuestProperties(const com::Utf8Str &aPatterns,
5925 std::vector<com::Utf8Str> &aNames,
5926 std::vector<com::Utf8Str> &aValues,
5927 std::vector<LONG64> &aTimestamps,
5928 std::vector<com::Utf8Str> &aFlags)
5929{
5930#ifndef VBOX_WITH_GUEST_PROPS
5931 ReturnComNotImplemented();
5932#else // VBOX_WITH_GUEST_PROPS
5933
5934 HRESULT rc = i_enumerateGuestPropertiesOnVM(aPatterns, aNames, aValues, aTimestamps, aFlags);
5935
5936 if (rc == E_ACCESSDENIED)
5937 /* The VM is not running or the service is not (yet) accessible */
5938 rc = i_enumerateGuestPropertiesInService(aPatterns, aNames, aValues, aTimestamps, aFlags);
5939 return rc;
5940#endif // VBOX_WITH_GUEST_PROPS
5941}
5942
5943HRESULT Machine::getMediumAttachmentsOfController(const com::Utf8Str &aName,
5944 std::vector<ComPtr<IMediumAttachment> > &aMediumAttachments)
5945{
5946 MediaData::AttachmentList atts;
5947
5948 HRESULT rc = i_getMediumAttachmentsOfController(aName, atts);
5949 if (FAILED(rc)) return rc;
5950
5951 size_t i = 0;
5952 aMediumAttachments.resize(atts.size());
5953 for (MediaData::AttachmentList::iterator it = atts.begin(); it != atts.end(); ++it, ++i)
5954 (*it).queryInterfaceTo(aMediumAttachments[i].asOutParam());
5955
5956 return S_OK;
5957}
5958
5959HRESULT Machine::getMediumAttachment(const com::Utf8Str &aName,
5960 LONG aControllerPort,
5961 LONG aDevice,
5962 ComPtr<IMediumAttachment> &aAttachment)
5963{
5964 LogFlowThisFunc(("aControllerName=\"%s\" aControllerPort=%d aDevice=%d\n",
5965 aName.c_str(), aControllerPort, aDevice));
5966
5967 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
5968
5969 aAttachment = NULL;
5970
5971 ComObjPtr<MediumAttachment> pAttach = i_findAttachment(mMediaData->mAttachments,
5972 Bstr(aName).raw(),
5973 aControllerPort,
5974 aDevice);
5975 if (pAttach.isNull())
5976 return setError(VBOX_E_OBJECT_NOT_FOUND,
5977 tr("No storage device attached to device slot %d on port %d of controller '%s'"),
5978 aDevice, aControllerPort, aName.c_str());
5979
5980 pAttach.queryInterfaceTo(aAttachment.asOutParam());
5981
5982 return S_OK;
5983}
5984
5985
5986HRESULT Machine::addStorageController(const com::Utf8Str &aName,
5987 StorageBus_T aConnectionType,
5988 ComPtr<IStorageController> &aController)
5989{
5990 if ( (aConnectionType <= StorageBus_Null)
5991 || (aConnectionType > StorageBus_USB))
5992 return setError(E_INVALIDARG,
5993 tr("Invalid connection type: %d"),
5994 aConnectionType);
5995
5996 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
5997
5998 HRESULT rc = i_checkStateDependency(MutableStateDep);
5999 if (FAILED(rc)) return rc;
6000
6001 /* try to find one with the name first. */
6002 ComObjPtr<StorageController> ctrl;
6003
6004 rc = i_getStorageControllerByName(aName, ctrl, false /* aSetError */);
6005 if (SUCCEEDED(rc))
6006 return setError(VBOX_E_OBJECT_IN_USE,
6007 tr("Storage controller named '%s' already exists"),
6008 aName.c_str());
6009
6010 ctrl.createObject();
6011
6012 /* get a new instance number for the storage controller */
6013 ULONG ulInstance = 0;
6014 bool fBootable = true;
6015 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6016 it != mStorageControllers->end();
6017 ++it)
6018 {
6019 if ((*it)->i_getStorageBus() == aConnectionType)
6020 {
6021 ULONG ulCurInst = (*it)->i_getInstance();
6022
6023 if (ulCurInst >= ulInstance)
6024 ulInstance = ulCurInst + 1;
6025
6026 /* Only one controller of each type can be marked as bootable. */
6027 if ((*it)->i_getBootable())
6028 fBootable = false;
6029 }
6030 }
6031
6032 rc = ctrl->init(this, aName, aConnectionType, ulInstance, fBootable);
6033 if (FAILED(rc)) return rc;
6034
6035 i_setModified(IsModified_Storage);
6036 mStorageControllers.backup();
6037 mStorageControllers->push_back(ctrl);
6038
6039 ctrl.queryInterfaceTo(aController.asOutParam());
6040
6041 /* inform the direct session if any */
6042 alock.release();
6043 i_onStorageControllerChange();
6044
6045 return S_OK;
6046}
6047
6048HRESULT Machine::getStorageControllerByName(const com::Utf8Str &aName,
6049 ComPtr<IStorageController> &aStorageController)
6050{
6051 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6052
6053 ComObjPtr<StorageController> ctrl;
6054
6055 HRESULT rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6056 if (SUCCEEDED(rc))
6057 ctrl.queryInterfaceTo(aStorageController.asOutParam());
6058
6059 return rc;
6060}
6061
6062HRESULT Machine::getStorageControllerByInstance(StorageBus_T aConnectionType,
6063 ULONG aInstance,
6064 ComPtr<IStorageController> &aStorageController)
6065{
6066 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6067
6068 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6069 it != mStorageControllers->end();
6070 ++it)
6071 {
6072 if ( (*it)->i_getStorageBus() == aConnectionType
6073 && (*it)->i_getInstance() == aInstance)
6074 {
6075 (*it).queryInterfaceTo(aStorageController.asOutParam());
6076 return S_OK;
6077 }
6078 }
6079
6080 return setError(VBOX_E_OBJECT_NOT_FOUND,
6081 tr("Could not find a storage controller with instance number '%lu'"),
6082 aInstance);
6083}
6084
6085HRESULT Machine::setStorageControllerBootable(const com::Utf8Str &aName, BOOL aBootable)
6086{
6087 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6088
6089 HRESULT rc = i_checkStateDependency(MutableStateDep);
6090 if (FAILED(rc)) return rc;
6091
6092 ComObjPtr<StorageController> ctrl;
6093
6094 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6095 if (SUCCEEDED(rc))
6096 {
6097 /* Ensure that only one controller of each type is marked as bootable. */
6098 if (aBootable == TRUE)
6099 {
6100 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
6101 it != mStorageControllers->end();
6102 ++it)
6103 {
6104 ComObjPtr<StorageController> aCtrl = (*it);
6105
6106 if ( (aCtrl->i_getName() != aName)
6107 && aCtrl->i_getBootable() == TRUE
6108 && aCtrl->i_getStorageBus() == ctrl->i_getStorageBus()
6109 && aCtrl->i_getControllerType() == ctrl->i_getControllerType())
6110 {
6111 aCtrl->i_setBootable(FALSE);
6112 break;
6113 }
6114 }
6115 }
6116
6117 if (SUCCEEDED(rc))
6118 {
6119 ctrl->i_setBootable(aBootable);
6120 i_setModified(IsModified_Storage);
6121 }
6122 }
6123
6124 if (SUCCEEDED(rc))
6125 {
6126 /* inform the direct session if any */
6127 alock.release();
6128 i_onStorageControllerChange();
6129 }
6130
6131 return rc;
6132}
6133
6134HRESULT Machine::removeStorageController(const com::Utf8Str &aName)
6135{
6136 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6137
6138 HRESULT rc = i_checkStateDependency(MutableStateDep);
6139 if (FAILED(rc)) return rc;
6140
6141 ComObjPtr<StorageController> ctrl;
6142 rc = i_getStorageControllerByName(aName, ctrl, true /* aSetError */);
6143 if (FAILED(rc)) return rc;
6144
6145 {
6146 /* find all attached devices to the appropriate storage controller and detach them all */
6147 // make a temporary list because detachDevice invalidates iterators into
6148 // mMediaData->mAttachments
6149 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
6150
6151 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();
6152 it != llAttachments2.end();
6153 ++it)
6154 {
6155 MediumAttachment *pAttachTemp = *it;
6156
6157 AutoCaller localAutoCaller(pAttachTemp);
6158 if (FAILED(localAutoCaller.rc())) return localAutoCaller.rc();
6159
6160 AutoReadLock local_alock(pAttachTemp COMMA_LOCKVAL_SRC_POS);
6161
6162 if (pAttachTemp->i_getControllerName() == aName)
6163 {
6164 rc = i_detachDevice(pAttachTemp, alock, NULL);
6165 if (FAILED(rc)) return rc;
6166 }
6167 }
6168 }
6169
6170 /* We can remove it now. */
6171 i_setModified(IsModified_Storage);
6172 mStorageControllers.backup();
6173
6174 ctrl->i_unshare();
6175
6176 mStorageControllers->remove(ctrl);
6177
6178 /* inform the direct session if any */
6179 alock.release();
6180 i_onStorageControllerChange();
6181
6182 return S_OK;
6183}
6184
6185HRESULT Machine::addUSBController(const com::Utf8Str &aName, USBControllerType_T aType,
6186 ComPtr<IUSBController> &aController)
6187{
6188 if ( (aType <= USBControllerType_Null)
6189 || (aType >= USBControllerType_Last))
6190 return setError(E_INVALIDARG,
6191 tr("Invalid USB controller type: %d"),
6192 aType);
6193
6194 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6195
6196 HRESULT rc = i_checkStateDependency(MutableStateDep);
6197 if (FAILED(rc)) return rc;
6198
6199 /* try to find one with the same type first. */
6200 ComObjPtr<USBController> ctrl;
6201
6202 rc = i_getUSBControllerByName(aName, ctrl, false /* aSetError */);
6203 if (SUCCEEDED(rc))
6204 return setError(VBOX_E_OBJECT_IN_USE,
6205 tr("USB controller named '%s' already exists"),
6206 aName.c_str());
6207
6208 /* Check that we don't exceed the maximum number of USB controllers for the given type. */
6209 ULONG maxInstances;
6210 rc = mParent->i_getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances);
6211 if (FAILED(rc))
6212 return rc;
6213
6214 ULONG cInstances = i_getUSBControllerCountByType(aType);
6215 if (cInstances >= maxInstances)
6216 return setError(E_INVALIDARG,
6217 tr("Too many USB controllers of this type"));
6218
6219 ctrl.createObject();
6220
6221 rc = ctrl->init(this, aName, aType);
6222 if (FAILED(rc)) return rc;
6223
6224 i_setModified(IsModified_USB);
6225 mUSBControllers.backup();
6226 mUSBControllers->push_back(ctrl);
6227
6228 ctrl.queryInterfaceTo(aController.asOutParam());
6229
6230 /* inform the direct session if any */
6231 alock.release();
6232 i_onUSBControllerChange();
6233
6234 return S_OK;
6235}
6236
6237HRESULT Machine::getUSBControllerByName(const com::Utf8Str &aName, ComPtr<IUSBController> &aController)
6238{
6239 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6240
6241 ComObjPtr<USBController> ctrl;
6242
6243 HRESULT rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6244 if (SUCCEEDED(rc))
6245 ctrl.queryInterfaceTo(aController.asOutParam());
6246
6247 return rc;
6248}
6249
6250HRESULT Machine::getUSBControllerCountByType(USBControllerType_T aType,
6251 ULONG *aControllers)
6252{
6253 if ( (aType <= USBControllerType_Null)
6254 || (aType >= USBControllerType_Last))
6255 return setError(E_INVALIDARG,
6256 tr("Invalid USB controller type: %d"),
6257 aType);
6258
6259 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6260
6261 ComObjPtr<USBController> ctrl;
6262
6263 *aControllers = i_getUSBControllerCountByType(aType);
6264
6265 return S_OK;
6266}
6267
6268HRESULT Machine::removeUSBController(const com::Utf8Str &aName)
6269{
6270
6271 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6272
6273 HRESULT rc = i_checkStateDependency(MutableStateDep);
6274 if (FAILED(rc)) return rc;
6275
6276 ComObjPtr<USBController> ctrl;
6277 rc = i_getUSBControllerByName(aName, ctrl, true /* aSetError */);
6278 if (FAILED(rc)) return rc;
6279
6280 i_setModified(IsModified_USB);
6281 mUSBControllers.backup();
6282
6283 ctrl->i_unshare();
6284
6285 mUSBControllers->remove(ctrl);
6286
6287 /* inform the direct session if any */
6288 alock.release();
6289 i_onUSBControllerChange();
6290
6291 return S_OK;
6292}
6293
6294HRESULT Machine::querySavedGuestScreenInfo(ULONG aScreenId,
6295 ULONG *aOriginX,
6296 ULONG *aOriginY,
6297 ULONG *aWidth,
6298 ULONG *aHeight,
6299 BOOL *aEnabled)
6300{
6301 uint32_t u32OriginX= 0;
6302 uint32_t u32OriginY= 0;
6303 uint32_t u32Width = 0;
6304 uint32_t u32Height = 0;
6305 uint16_t u16Flags = 0;
6306
6307 int vrc = readSavedGuestScreenInfo(mSSData->strStateFilePath, aScreenId,
6308 &u32OriginX, &u32OriginY, &u32Width, &u32Height, &u16Flags);
6309 if (RT_FAILURE(vrc))
6310 {
6311#ifdef RT_OS_WINDOWS
6312 /* HACK: GUI sets *pfEnabled to 'true' and expects it to stay so if the API fails.
6313 * This works with XPCOM. But Windows COM sets all output parameters to zero.
6314 * So just assign fEnable to TRUE again.
6315 * The right fix would be to change GUI API wrappers to make sure that parameters
6316 * are changed only if API succeeds.
6317 */
6318 *aEnabled = TRUE;
6319#endif
6320 return setError(VBOX_E_IPRT_ERROR,
6321 tr("Saved guest size is not available (%Rrc)"),
6322 vrc);
6323 }
6324
6325 *aOriginX = u32OriginX;
6326 *aOriginY = u32OriginY;
6327 *aWidth = u32Width;
6328 *aHeight = u32Height;
6329 *aEnabled = (u16Flags & VBVA_SCREEN_F_DISABLED) == 0;
6330
6331 return S_OK;
6332}
6333
6334HRESULT Machine::readSavedThumbnailToArray(ULONG aScreenId, BitmapFormat_T aBitmapFormat,
6335 ULONG *aWidth, ULONG *aHeight, std::vector<BYTE> &aData)
6336{
6337 if (aScreenId != 0)
6338 return E_NOTIMPL;
6339
6340 if ( aBitmapFormat != BitmapFormat_BGR0
6341 && aBitmapFormat != BitmapFormat_BGRA
6342 && aBitmapFormat != BitmapFormat_RGBA
6343 && aBitmapFormat != BitmapFormat_PNG)
6344 return setError(E_NOTIMPL,
6345 tr("Unsupported saved thumbnail format 0x%08X"), aBitmapFormat);
6346
6347 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6348
6349 uint8_t *pu8Data = NULL;
6350 uint32_t cbData = 0;
6351 uint32_t u32Width = 0;
6352 uint32_t u32Height = 0;
6353
6354 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6355
6356 if (RT_FAILURE(vrc))
6357 return setError(VBOX_E_IPRT_ERROR,
6358 tr("Saved thumbnail data is not available (%Rrc)"),
6359 vrc);
6360
6361 HRESULT hr = S_OK;
6362
6363 *aWidth = u32Width;
6364 *aHeight = u32Height;
6365
6366 if (cbData > 0)
6367 {
6368 /* Convert pixels to the format expected by the API caller. */
6369 if (aBitmapFormat == BitmapFormat_BGR0)
6370 {
6371 /* [0] B, [1] G, [2] R, [3] 0. */
6372 aData.resize(cbData);
6373 memcpy(&aData.front(), pu8Data, cbData);
6374 }
6375 else if (aBitmapFormat == BitmapFormat_BGRA)
6376 {
6377 /* [0] B, [1] G, [2] R, [3] A. */
6378 aData.resize(cbData);
6379 for (uint32_t i = 0; i < cbData; i += 4)
6380 {
6381 aData[i] = pu8Data[i];
6382 aData[i + 1] = pu8Data[i + 1];
6383 aData[i + 2] = pu8Data[i + 2];
6384 aData[i + 3] = 0xff;
6385 }
6386 }
6387 else if (aBitmapFormat == BitmapFormat_RGBA)
6388 {
6389 /* [0] R, [1] G, [2] B, [3] A. */
6390 aData.resize(cbData);
6391 for (uint32_t i = 0; i < cbData; i += 4)
6392 {
6393 aData[i] = pu8Data[i + 2];
6394 aData[i + 1] = pu8Data[i + 1];
6395 aData[i + 2] = pu8Data[i];
6396 aData[i + 3] = 0xff;
6397 }
6398 }
6399 else if (aBitmapFormat == BitmapFormat_PNG)
6400 {
6401 uint8_t *pu8PNG = NULL;
6402 uint32_t cbPNG = 0;
6403 uint32_t cxPNG = 0;
6404 uint32_t cyPNG = 0;
6405
6406 vrc = DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0);
6407
6408 if (RT_SUCCESS(vrc))
6409 {
6410 aData.resize(cbPNG);
6411 if (cbPNG)
6412 memcpy(&aData.front(), pu8PNG, cbPNG);
6413 }
6414 else
6415 hr = setError(VBOX_E_IPRT_ERROR,
6416 tr("Could not convert saved thumbnail to PNG (%Rrc)"),
6417 vrc);
6418
6419 RTMemFree(pu8PNG);
6420 }
6421 }
6422
6423 freeSavedDisplayScreenshot(pu8Data);
6424
6425 return hr;
6426}
6427
6428HRESULT Machine::querySavedScreenshotInfo(ULONG aScreenId,
6429 ULONG *aWidth,
6430 ULONG *aHeight,
6431 std::vector<BitmapFormat_T> &aBitmapFormats)
6432{
6433 if (aScreenId != 0)
6434 return E_NOTIMPL;
6435
6436 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6437
6438 uint8_t *pu8Data = NULL;
6439 uint32_t cbData = 0;
6440 uint32_t u32Width = 0;
6441 uint32_t u32Height = 0;
6442
6443 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6444
6445 if (RT_FAILURE(vrc))
6446 return setError(VBOX_E_IPRT_ERROR,
6447 tr("Saved screenshot data is not available (%Rrc)"),
6448 vrc);
6449
6450 *aWidth = u32Width;
6451 *aHeight = u32Height;
6452 aBitmapFormats.resize(1);
6453 aBitmapFormats[0] = BitmapFormat_PNG;
6454
6455 freeSavedDisplayScreenshot(pu8Data);
6456
6457 return S_OK;
6458}
6459
6460HRESULT Machine::readSavedScreenshotToArray(ULONG aScreenId,
6461 BitmapFormat_T aBitmapFormat,
6462 ULONG *aWidth,
6463 ULONG *aHeight,
6464 std::vector<BYTE> &aData)
6465{
6466 if (aScreenId != 0)
6467 return E_NOTIMPL;
6468
6469 if (aBitmapFormat != BitmapFormat_PNG)
6470 return E_NOTIMPL;
6471
6472 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6473
6474 uint8_t *pu8Data = NULL;
6475 uint32_t cbData = 0;
6476 uint32_t u32Width = 0;
6477 uint32_t u32Height = 0;
6478
6479 int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
6480
6481 if (RT_FAILURE(vrc))
6482 return setError(VBOX_E_IPRT_ERROR,
6483 tr("Saved screenshot thumbnail data is not available (%Rrc)"),
6484 vrc);
6485
6486 *aWidth = u32Width;
6487 *aHeight = u32Height;
6488
6489 aData.resize(cbData);
6490 if (cbData)
6491 memcpy(&aData.front(), pu8Data, cbData);
6492
6493 freeSavedDisplayScreenshot(pu8Data);
6494
6495 return S_OK;
6496}
6497
6498HRESULT Machine::hotPlugCPU(ULONG aCpu)
6499{
6500 HRESULT rc = S_OK;
6501 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6502
6503 if (!mHWData->mCPUHotPlugEnabled)
6504 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6505
6506 if (aCpu >= mHWData->mCPUCount)
6507 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1);
6508
6509 if (mHWData->mCPUAttached[aCpu])
6510 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu);
6511
6512 alock.release();
6513 rc = i_onCPUChange(aCpu, false);
6514 alock.acquire();
6515 if (FAILED(rc)) return rc;
6516
6517 i_setModified(IsModified_MachineData);
6518 mHWData.backup();
6519 mHWData->mCPUAttached[aCpu] = true;
6520
6521 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6522 if (Global::IsOnline(mData->mMachineState))
6523 i_saveSettings(NULL);
6524
6525 return S_OK;
6526}
6527
6528HRESULT Machine::hotUnplugCPU(ULONG aCpu)
6529{
6530 HRESULT rc = S_OK;
6531
6532 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6533
6534 if (!mHWData->mCPUHotPlugEnabled)
6535 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled"));
6536
6537 if (aCpu >= SchemaDefs::MaxCPUCount)
6538 return setError(E_INVALIDARG,
6539 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"),
6540 SchemaDefs::MaxCPUCount);
6541
6542 if (!mHWData->mCPUAttached[aCpu])
6543 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu);
6544
6545 /* CPU 0 can't be detached */
6546 if (aCpu == 0)
6547 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0"));
6548
6549 alock.release();
6550 rc = i_onCPUChange(aCpu, true);
6551 alock.acquire();
6552 if (FAILED(rc)) return rc;
6553
6554 i_setModified(IsModified_MachineData);
6555 mHWData.backup();
6556 mHWData->mCPUAttached[aCpu] = false;
6557
6558 /** Save settings if online - @todo why is this required? -- @bugref{6818} */
6559 if (Global::IsOnline(mData->mMachineState))
6560 i_saveSettings(NULL);
6561
6562 return S_OK;
6563}
6564
6565HRESULT Machine::getCPUStatus(ULONG aCpu, BOOL *aAttached)
6566{
6567 *aAttached = false;
6568
6569 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6570
6571 /* If hotplug is enabled the CPU is always enabled. */
6572 if (!mHWData->mCPUHotPlugEnabled)
6573 {
6574 if (aCpu < mHWData->mCPUCount)
6575 *aAttached = true;
6576 }
6577 else
6578 {
6579 if (aCpu < SchemaDefs::MaxCPUCount)
6580 *aAttached = mHWData->mCPUAttached[aCpu];
6581 }
6582
6583 return S_OK;
6584}
6585
6586HRESULT Machine::queryLogFilename(ULONG aIdx, com::Utf8Str &aFilename)
6587{
6588 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6589
6590 Utf8Str log = i_queryLogFilename(aIdx);
6591 if (!RTFileExists(log.c_str()))
6592 log.setNull();
6593 aFilename = log;
6594
6595 return S_OK;
6596}
6597
6598HRESULT Machine::readLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, std::vector<BYTE> &aData)
6599{
6600 if (aSize < 0)
6601 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize);
6602
6603 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6604
6605 HRESULT rc = S_OK;
6606 Utf8Str log = i_queryLogFilename(aIdx);
6607
6608 /* do not unnecessarily hold the lock while doing something which does
6609 * not need the lock and potentially takes a long time. */
6610 alock.release();
6611
6612 /* Limit the chunk size to 32K for now, as that gives better performance
6613 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice.
6614 * One byte expands to approx. 25 bytes of breathtaking XML. */
6615 size_t cbData = (size_t)RT_MIN(aSize, 32768);
6616 aData.resize(cbData);
6617
6618 RTFILE LogFile;
6619 int vrc = RTFileOpen(&LogFile, log.c_str(),
6620 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE);
6621 if (RT_SUCCESS(vrc))
6622 {
6623 vrc = RTFileReadAt(LogFile, aOffset, cbData? &aData.front(): NULL, cbData, &cbData);
6624 if (RT_SUCCESS(vrc))
6625 aData.resize(cbData);
6626 else
6627 rc = setError(VBOX_E_IPRT_ERROR,
6628 tr("Could not read log file '%s' (%Rrc)"),
6629 log.c_str(), vrc);
6630 RTFileClose(LogFile);
6631 }
6632 else
6633 rc = setError(VBOX_E_IPRT_ERROR,
6634 tr("Could not open log file '%s' (%Rrc)"),
6635 log.c_str(), vrc);
6636
6637 if (FAILED(rc))
6638 aData.resize(0);
6639
6640 return rc;
6641}
6642
6643
6644/**
6645 * Currently this method doesn't attach device to the running VM,
6646 * just makes sure it's plugged on next VM start.
6647 */
6648HRESULT Machine::attachHostPCIDevice(LONG aHostAddress, LONG aDesiredGuestAddress, BOOL /* aTryToUnbind */)
6649{
6650 // lock scope
6651 {
6652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6653
6654 HRESULT rc = i_checkStateDependency(MutableStateDep);
6655 if (FAILED(rc)) return rc;
6656
6657 ChipsetType_T aChipset = ChipsetType_PIIX3;
6658 COMGETTER(ChipsetType)(&aChipset);
6659
6660 if (aChipset != ChipsetType_ICH9)
6661 {
6662 return setError(E_INVALIDARG,
6663 tr("Host PCI attachment only supported with ICH9 chipset"));
6664 }
6665
6666 // check if device with this host PCI address already attached
6667 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6668 it != mHWData->mPCIDeviceAssignments.end();
6669 ++it)
6670 {
6671 LONG iHostAddress = -1;
6672 ComPtr<PCIDeviceAttachment> pAttach;
6673 pAttach = *it;
6674 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6675 if (iHostAddress == aHostAddress)
6676 return setError(E_INVALIDARG,
6677 tr("Device with host PCI address already attached to this VM"));
6678 }
6679
6680 ComObjPtr<PCIDeviceAttachment> pda;
6681 char name[32];
6682
6683 RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (aHostAddress>>8) & 0xff,
6684 (aHostAddress & 0xf8) >> 3, aHostAddress & 7);
6685 Bstr bname(name);
6686 pda.createObject();
6687 pda->init(this, bname, aHostAddress, aDesiredGuestAddress, TRUE);
6688 i_setModified(IsModified_MachineData);
6689 mHWData.backup();
6690 mHWData->mPCIDeviceAssignments.push_back(pda);
6691 }
6692
6693 return S_OK;
6694}
6695
6696/**
6697 * Currently this method doesn't detach device from the running VM,
6698 * just makes sure it's not plugged on next VM start.
6699 */
6700HRESULT Machine::detachHostPCIDevice(LONG aHostAddress)
6701{
6702 ComObjPtr<PCIDeviceAttachment> pAttach;
6703 bool fRemoved = false;
6704 HRESULT rc;
6705
6706 // lock scope
6707 {
6708 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6709
6710 rc = i_checkStateDependency(MutableStateDep);
6711 if (FAILED(rc)) return rc;
6712
6713 for (HWData::PCIDeviceAssignmentList::iterator it = mHWData->mPCIDeviceAssignments.begin();
6714 it != mHWData->mPCIDeviceAssignments.end();
6715 ++it)
6716 {
6717 LONG iHostAddress = -1;
6718 pAttach = *it;
6719 pAttach->COMGETTER(HostAddress)(&iHostAddress);
6720 if (iHostAddress != -1 && iHostAddress == aHostAddress)
6721 {
6722 i_setModified(IsModified_MachineData);
6723 mHWData.backup();
6724 mHWData->mPCIDeviceAssignments.remove(pAttach);
6725 fRemoved = true;
6726 break;
6727 }
6728 }
6729 }
6730
6731
6732 /* Fire event outside of the lock */
6733 if (fRemoved)
6734 {
6735 Assert(!pAttach.isNull());
6736 ComPtr<IEventSource> es;
6737 rc = mParent->COMGETTER(EventSource)(es.asOutParam());
6738 Assert(SUCCEEDED(rc));
6739 Bstr mid;
6740 rc = this->COMGETTER(Id)(mid.asOutParam());
6741 Assert(SUCCEEDED(rc));
6742 fireHostPCIDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
6743 }
6744
6745 return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
6746 tr("No host PCI device %08x attached"),
6747 aHostAddress
6748 );
6749}
6750
6751HRESULT Machine::getPCIDeviceAssignments(std::vector<ComPtr<IPCIDeviceAttachment> > &aPCIDeviceAssignments)
6752{
6753 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6754
6755 aPCIDeviceAssignments.resize(mHWData->mPCIDeviceAssignments.size());
6756
6757 size_t i = 0;
6758 for (std::list<ComObjPtr<PCIDeviceAttachment> >::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
6759 it != mHWData->mPCIDeviceAssignments.end();
6760 ++i, ++it)
6761 (*it).queryInterfaceTo(aPCIDeviceAssignments[i].asOutParam());
6762
6763 return S_OK;
6764}
6765
6766HRESULT Machine::getBandwidthControl(ComPtr<IBandwidthControl> &aBandwidthControl)
6767{
6768 mBandwidthControl.queryInterfaceTo(aBandwidthControl.asOutParam());
6769
6770 return S_OK;
6771}
6772
6773HRESULT Machine::getTracingEnabled(BOOL *aTracingEnabled)
6774{
6775 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6776
6777 *aTracingEnabled = mHWData->mDebugging.fTracingEnabled;
6778
6779 return S_OK;
6780}
6781
6782HRESULT Machine::setTracingEnabled(BOOL aTracingEnabled)
6783{
6784 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6785 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6786 if (SUCCEEDED(hrc))
6787 {
6788 hrc = mHWData.backupEx();
6789 if (SUCCEEDED(hrc))
6790 {
6791 i_setModified(IsModified_MachineData);
6792 mHWData->mDebugging.fTracingEnabled = aTracingEnabled != FALSE;
6793 }
6794 }
6795 return hrc;
6796}
6797
6798HRESULT Machine::getTracingConfig(com::Utf8Str &aTracingConfig)
6799{
6800 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6801 aTracingConfig = mHWData->mDebugging.strTracingConfig;
6802 return S_OK;
6803}
6804
6805HRESULT Machine::setTracingConfig(const com::Utf8Str &aTracingConfig)
6806{
6807 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6808 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6809 if (SUCCEEDED(hrc))
6810 {
6811 hrc = mHWData.backupEx();
6812 if (SUCCEEDED(hrc))
6813 {
6814 mHWData->mDebugging.strTracingConfig = aTracingConfig;
6815 if (SUCCEEDED(hrc))
6816 i_setModified(IsModified_MachineData);
6817 }
6818 }
6819 return hrc;
6820}
6821
6822HRESULT Machine::getAllowTracingToAccessVM(BOOL *aAllowTracingToAccessVM)
6823{
6824 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6825
6826 *aAllowTracingToAccessVM = mHWData->mDebugging.fAllowTracingToAccessVM;
6827
6828 return S_OK;
6829}
6830
6831HRESULT Machine::setAllowTracingToAccessVM(BOOL aAllowTracingToAccessVM)
6832{
6833 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6834 HRESULT hrc = i_checkStateDependency(MutableStateDep);
6835 if (SUCCEEDED(hrc))
6836 {
6837 hrc = mHWData.backupEx();
6838 if (SUCCEEDED(hrc))
6839 {
6840 i_setModified(IsModified_MachineData);
6841 mHWData->mDebugging.fAllowTracingToAccessVM = aAllowTracingToAccessVM != FALSE;
6842 }
6843 }
6844 return hrc;
6845}
6846
6847HRESULT Machine::getAutostartEnabled(BOOL *aAutostartEnabled)
6848{
6849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6850
6851 *aAutostartEnabled = mHWData->mAutostart.fAutostartEnabled;
6852
6853 return S_OK;
6854}
6855
6856HRESULT Machine::setAutostartEnabled(BOOL aAutostartEnabled)
6857{
6858 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6859
6860 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6861 if ( SUCCEEDED(hrc)
6862 && mHWData->mAutostart.fAutostartEnabled != !!aAutostartEnabled)
6863 {
6864 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6865 int vrc;
6866
6867 if (aAutostartEnabled)
6868 vrc = autostartDb->addAutostartVM(mUserData->s.strName.c_str());
6869 else
6870 vrc = autostartDb->removeAutostartVM(mUserData->s.strName.c_str());
6871
6872 if (RT_SUCCESS(vrc))
6873 {
6874 hrc = mHWData.backupEx();
6875 if (SUCCEEDED(hrc))
6876 {
6877 i_setModified(IsModified_MachineData);
6878 mHWData->mAutostart.fAutostartEnabled = aAutostartEnabled != FALSE;
6879 }
6880 }
6881 else if (vrc == VERR_NOT_SUPPORTED)
6882 hrc = setError(VBOX_E_NOT_SUPPORTED,
6883 tr("The VM autostart feature is not supported on this platform"));
6884 else if (vrc == VERR_PATH_NOT_FOUND)
6885 hrc = setError(E_FAIL,
6886 tr("The path to the autostart database is not set"));
6887 else
6888 hrc = setError(E_UNEXPECTED,
6889 tr("%s machine '%s' to the autostart database failed with %Rrc"),
6890 aAutostartEnabled ? "Adding" : "Removing",
6891 mUserData->s.strName.c_str(), vrc);
6892 }
6893 return hrc;
6894}
6895
6896HRESULT Machine::getAutostartDelay(ULONG *aAutostartDelay)
6897{
6898 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6899
6900 *aAutostartDelay = mHWData->mAutostart.uAutostartDelay;
6901
6902 return S_OK;
6903}
6904
6905HRESULT Machine::setAutostartDelay(ULONG aAutostartDelay)
6906{
6907 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6908 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6909 if (SUCCEEDED(hrc))
6910 {
6911 hrc = mHWData.backupEx();
6912 if (SUCCEEDED(hrc))
6913 {
6914 i_setModified(IsModified_MachineData);
6915 mHWData->mAutostart.uAutostartDelay = aAutostartDelay;
6916 }
6917 }
6918 return hrc;
6919}
6920
6921HRESULT Machine::getAutostopType(AutostopType_T *aAutostopType)
6922{
6923 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6924
6925 *aAutostopType = mHWData->mAutostart.enmAutostopType;
6926
6927 return S_OK;
6928}
6929
6930HRESULT Machine::setAutostopType(AutostopType_T aAutostopType)
6931{
6932 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6933 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
6934 if ( SUCCEEDED(hrc)
6935 && mHWData->mAutostart.enmAutostopType != aAutostopType)
6936 {
6937 AutostartDb *autostartDb = mParent->i_getAutostartDb();
6938 int vrc;
6939
6940 if (aAutostopType != AutostopType_Disabled)
6941 vrc = autostartDb->addAutostopVM(mUserData->s.strName.c_str());
6942 else
6943 vrc = autostartDb->removeAutostopVM(mUserData->s.strName.c_str());
6944
6945 if (RT_SUCCESS(vrc))
6946 {
6947 hrc = mHWData.backupEx();
6948 if (SUCCEEDED(hrc))
6949 {
6950 i_setModified(IsModified_MachineData);
6951 mHWData->mAutostart.enmAutostopType = aAutostopType;
6952 }
6953 }
6954 else if (vrc == VERR_NOT_SUPPORTED)
6955 hrc = setError(VBOX_E_NOT_SUPPORTED,
6956 tr("The VM autostop feature is not supported on this platform"));
6957 else if (vrc == VERR_PATH_NOT_FOUND)
6958 hrc = setError(E_FAIL,
6959 tr("The path to the autostart database is not set"));
6960 else
6961 hrc = setError(E_UNEXPECTED,
6962 tr("%s machine '%s' to the autostop database failed with %Rrc"),
6963 aAutostopType != AutostopType_Disabled ? "Adding" : "Removing",
6964 mUserData->s.strName.c_str(), vrc);
6965 }
6966 return hrc;
6967}
6968
6969HRESULT Machine::getDefaultFrontend(com::Utf8Str &aDefaultFrontend)
6970{
6971 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6972
6973 aDefaultFrontend = mHWData->mDefaultFrontend;
6974
6975 return S_OK;
6976}
6977
6978HRESULT Machine::setDefaultFrontend(const com::Utf8Str &aDefaultFrontend)
6979{
6980 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
6981 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
6982 if (SUCCEEDED(hrc))
6983 {
6984 hrc = mHWData.backupEx();
6985 if (SUCCEEDED(hrc))
6986 {
6987 i_setModified(IsModified_MachineData);
6988 mHWData->mDefaultFrontend = aDefaultFrontend;
6989 }
6990 }
6991 return hrc;
6992}
6993
6994HRESULT Machine::getIcon(std::vector<BYTE> &aIcon)
6995{
6996 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
6997 size_t cbIcon = mUserData->mIcon.size();
6998 aIcon.resize(cbIcon);
6999 if (cbIcon)
7000 memcpy(&aIcon.front(), &mUserData->mIcon[0], cbIcon);
7001 return S_OK;
7002}
7003
7004HRESULT Machine::setIcon(const std::vector<BYTE> &aIcon)
7005{
7006 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7007 HRESULT hrc = i_checkStateDependency(MutableOrSavedStateDep);
7008 if (SUCCEEDED(hrc))
7009 {
7010 i_setModified(IsModified_MachineData);
7011 mUserData.backup();
7012 size_t cbIcon = aIcon.size();
7013 mUserData->mIcon.resize(cbIcon);
7014 if (cbIcon)
7015 memcpy(&mUserData->mIcon[0], &aIcon.front(), cbIcon);
7016 }
7017 return hrc;
7018}
7019
7020HRESULT Machine::getUSBProxyAvailable(BOOL *aUSBProxyAvailable)
7021{
7022#ifdef VBOX_WITH_USB
7023 *aUSBProxyAvailable = true;
7024#else
7025 *aUSBProxyAvailable = false;
7026#endif
7027 return S_OK;
7028}
7029
7030HRESULT Machine::getVMProcessPriority(com::Utf8Str &aVMProcessPriority)
7031{
7032 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7033
7034 aVMProcessPriority = mUserData->s.strVMPriority;
7035
7036 return S_OK;
7037}
7038
7039HRESULT Machine::setVMProcessPriority(const com::Utf8Str &aVMProcessPriority)
7040{
7041 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7042 HRESULT hrc = i_checkStateDependency(MutableOrSavedOrRunningStateDep);
7043 if (SUCCEEDED(hrc))
7044 {
7045 /** @todo r=klaus: currently this is marked as not implemented, as
7046 * the code for setting the priority of the process is not there
7047 * (neither when starting the VM nor at runtime). */
7048 ReturnComNotImplemented();
7049 hrc = mUserData.backupEx();
7050 if (SUCCEEDED(hrc))
7051 {
7052 i_setModified(IsModified_MachineData);
7053 mUserData->s.strVMPriority = aVMProcessPriority;
7054 }
7055 }
7056 return hrc;
7057}
7058
7059HRESULT Machine::cloneTo(const ComPtr<IMachine> &aTarget, CloneMode_T aMode, const std::vector<CloneOptions_T> &aOptions,
7060 ComPtr<IProgress> &aProgress)
7061{
7062 ComObjPtr<Progress> pP;
7063 Progress *ppP = pP;
7064 IProgress *iP = static_cast<IProgress *>(ppP);
7065 IProgress **pProgress = &iP;
7066
7067 IMachine *pTarget = aTarget;
7068
7069 /* Convert the options. */
7070 RTCList<CloneOptions_T> optList;
7071 if (aOptions.size())
7072 for (size_t i = 0; i < aOptions.size(); ++i)
7073 optList.append(aOptions[i]);
7074
7075 if (optList.contains(CloneOptions_Link))
7076 {
7077 if (!i_isSnapshotMachine())
7078 return setError(E_INVALIDARG,
7079 tr("Linked clone can only be created from a snapshot"));
7080 if (aMode != CloneMode_MachineState)
7081 return setError(E_INVALIDARG,
7082 tr("Linked clone can only be created for a single machine state"));
7083 }
7084 AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
7085
7086 MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), aMode, optList);
7087
7088 HRESULT rc = pWorker->start(pProgress);
7089
7090 pP = static_cast<Progress *>(*pProgress);
7091 pP.queryInterfaceTo(aProgress.asOutParam());
7092
7093 return rc;
7094
7095}
7096
7097HRESULT Machine::saveState(ComPtr<IProgress> &aProgress)
7098{
7099 NOREF(aProgress);
7100 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7101
7102 // This check should always fail.
7103 HRESULT rc = i_checkStateDependency(MutableStateDep);
7104 if (FAILED(rc)) return rc;
7105
7106 AssertFailedReturn(E_NOTIMPL);
7107}
7108
7109HRESULT Machine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
7110{
7111 NOREF(aSavedStateFile);
7112 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7113
7114 // This check should always fail.
7115 HRESULT rc = i_checkStateDependency(MutableStateDep);
7116 if (FAILED(rc)) return rc;
7117
7118 AssertFailedReturn(E_NOTIMPL);
7119}
7120
7121HRESULT Machine::discardSavedState(BOOL aFRemoveFile)
7122{
7123 NOREF(aFRemoveFile);
7124 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7125
7126 // This check should always fail.
7127 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
7128 if (FAILED(rc)) return rc;
7129
7130 AssertFailedReturn(E_NOTIMPL);
7131}
7132
7133// public methods for internal purposes
7134/////////////////////////////////////////////////////////////////////////////
7135
7136/**
7137 * Adds the given IsModified_* flag to the dirty flags of the machine.
7138 * This must be called either during i_loadSettings or under the machine write lock.
7139 * @param fl
7140 */
7141void Machine::i_setModified(uint32_t fl, bool fAllowStateModification /* = true */)
7142{
7143 mData->flModifications |= fl;
7144 if (fAllowStateModification && i_isStateModificationAllowed())
7145 mData->mCurrentStateModified = true;
7146}
7147
7148/**
7149 * Adds the given IsModified_* flag to the dirty flags of the machine, taking
7150 * care of the write locking.
7151 *
7152 * @param fModifications The flag to add.
7153 */
7154void Machine::i_setModifiedLock(uint32_t fModification, bool fAllowStateModification /* = true */)
7155{
7156 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7157 i_setModified(fModification, fAllowStateModification);
7158}
7159
7160/**
7161 * Saves the registry entry of this machine to the given configuration node.
7162 *
7163 * @param aEntryNode Node to save the registry entry to.
7164 *
7165 * @note locks this object for reading.
7166 */
7167HRESULT Machine::i_saveRegistryEntry(settings::MachineRegistryEntry &data)
7168{
7169 AutoLimitedCaller autoCaller(this);
7170 AssertComRCReturnRC(autoCaller.rc());
7171
7172 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7173
7174 data.uuid = mData->mUuid;
7175 data.strSettingsFile = mData->m_strConfigFile;
7176
7177 return S_OK;
7178}
7179
7180/**
7181 * Calculates the absolute path of the given path taking the directory of the
7182 * machine settings file as the current directory.
7183 *
7184 * @param aPath Path to calculate the absolute path for.
7185 * @param aResult Where to put the result (used only on success, can be the
7186 * same Utf8Str instance as passed in @a aPath).
7187 * @return IPRT result.
7188 *
7189 * @note Locks this object for reading.
7190 */
7191int Machine::i_calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)
7192{
7193 AutoCaller autoCaller(this);
7194 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
7195
7196 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7197
7198 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);
7199
7200 Utf8Str strSettingsDir = mData->m_strConfigFileFull;
7201
7202 strSettingsDir.stripFilename();
7203 char folder[RTPATH_MAX];
7204 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));
7205 if (RT_SUCCESS(vrc))
7206 aResult = folder;
7207
7208 return vrc;
7209}
7210
7211/**
7212 * Copies strSource to strTarget, making it relative to the machine folder
7213 * if it is a subdirectory thereof, or simply copying it otherwise.
7214 *
7215 * @param strSource Path to evaluate and copy.
7216 * @param strTarget Buffer to receive target path.
7217 *
7218 * @note Locks this object for reading.
7219 */
7220void Machine::i_copyPathRelativeToMachine(const Utf8Str &strSource,
7221 Utf8Str &strTarget)
7222{
7223 AutoCaller autoCaller(this);
7224 AssertComRCReturn(autoCaller.rc(), (void)0);
7225
7226 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7227
7228 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());
7229 // use strTarget as a temporary buffer to hold the machine settings dir
7230 strTarget = mData->m_strConfigFileFull;
7231 strTarget.stripFilename();
7232 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))
7233 {
7234 // is relative: then append what's left
7235 strTarget = strSource.substr(strTarget.length() + 1); // skip '/'
7236 // for empty paths (only possible for subdirs) use "." to avoid
7237 // triggering default settings for not present config attributes.
7238 if (strTarget.isEmpty())
7239 strTarget = ".";
7240 }
7241 else
7242 // is not relative: then overwrite
7243 strTarget = strSource;
7244}
7245
7246/**
7247 * Returns the full path to the machine's log folder in the
7248 * \a aLogFolder argument.
7249 */
7250void Machine::i_getLogFolder(Utf8Str &aLogFolder)
7251{
7252 AutoCaller autoCaller(this);
7253 AssertComRCReturnVoid(autoCaller.rc());
7254
7255 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7256
7257 char szTmp[RTPATH_MAX];
7258 int vrc = RTEnvGetEx(RTENV_DEFAULT, "VBOX_USER_VMLOGDIR", szTmp, sizeof(szTmp), NULL);
7259 if (RT_SUCCESS(vrc))
7260 {
7261 if (szTmp[0] && !mUserData.isNull())
7262 {
7263 char szTmp2[RTPATH_MAX];
7264 vrc = RTPathAbs(szTmp, szTmp2, sizeof(szTmp2));
7265 if (RT_SUCCESS(vrc))
7266 aLogFolder = BstrFmt("%s%c%s",
7267 szTmp2,
7268 RTPATH_DELIMITER,
7269 mUserData->s.strName.c_str()); // path/to/logfolder/vmname
7270 }
7271 else
7272 vrc = VERR_PATH_IS_RELATIVE;
7273 }
7274
7275 if (RT_FAILURE(vrc))
7276 {
7277 // fallback if VBOX_USER_LOGHOME is not set or invalid
7278 aLogFolder = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7279 aLogFolder.stripFilename(); // path/to/machinesfolder/vmname
7280 aLogFolder.append(RTPATH_DELIMITER);
7281 aLogFolder.append("Logs"); // path/to/machinesfolder/vmname/Logs
7282 }
7283}
7284
7285/**
7286 * Returns the full path to the machine's log file for an given index.
7287 */
7288Utf8Str Machine::i_queryLogFilename(ULONG idx) /** @todo r=bird: Misnamed. Should be i_getLogFilename as it cannot fail.
7289 See VBox-CodingGuidelines.cpp, Compulsory seciont, line 79. */
7290{
7291 Utf8Str logFolder;
7292 getLogFolder(logFolder);
7293 Assert(logFolder.length());
7294 Utf8Str log;
7295 if (idx == 0)
7296 log = Utf8StrFmt("%s%cVBox.log",
7297 logFolder.c_str(), RTPATH_DELIMITER);
7298 else
7299 log = Utf8StrFmt("%s%cVBox.log.%d",
7300 logFolder.c_str(), RTPATH_DELIMITER, idx);
7301 return log;
7302}
7303
7304/**
7305 * Returns the full path to the machine's hardened log file.
7306 */
7307Utf8Str Machine::i_getHardeningLogFilename(void)
7308{
7309 Utf8Str strFilename;
7310 getLogFolder(strFilename);
7311 Assert(strFilename.length());
7312 strFilename.append(RTPATH_SLASH_STR "VBoxHardening.log");
7313 return strFilename;
7314}
7315
7316
7317/**
7318 * Composes a unique saved state filename based on the current system time. The filename is
7319 * granular to the second so this will work so long as no more than one snapshot is taken on
7320 * a machine per second.
7321 *
7322 * Before version 4.1, we used this formula for saved state files:
7323 * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
7324 * which no longer works because saved state files can now be shared between the saved state of the
7325 * "saved" machine and an online snapshot, and the following would cause problems:
7326 * 1) save machine
7327 * 2) create online snapshot from that machine state --> reusing saved state file
7328 * 3) save machine again --> filename would be reused, breaking the online snapshot
7329 *
7330 * So instead we now use a timestamp.
7331 *
7332 * @param str
7333 */
7334
7335void Machine::i_composeSavedStateFilename(Utf8Str &strStateFilePath)
7336{
7337 AutoCaller autoCaller(this);
7338 AssertComRCReturnVoid(autoCaller.rc());
7339
7340 {
7341 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7342 i_calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
7343 }
7344
7345 RTTIMESPEC ts;
7346 RTTimeNow(&ts);
7347 RTTIME time;
7348 RTTimeExplode(&time, &ts);
7349
7350 strStateFilePath += RTPATH_DELIMITER;
7351 strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
7352 time.i32Year, time.u8Month, time.u8MonthDay,
7353 time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
7354}
7355
7356/**
7357 * Returns the full path to the default video capture file.
7358 */
7359void Machine::i_getDefaultVideoCaptureFile(Utf8Str &strFile)
7360{
7361 AutoCaller autoCaller(this);
7362 AssertComRCReturnVoid(autoCaller.rc());
7363
7364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7365
7366 strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox
7367 strFile.stripSuffix(); // path/to/machinesfolder/vmname/vmname
7368 strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm
7369}
7370
7371/**
7372 * Returns whether at least one USB controller is present for the VM.
7373 */
7374bool Machine::i_isUSBControllerPresent()
7375{
7376 AutoCaller autoCaller(this);
7377 AssertComRCReturn(autoCaller.rc(), false);
7378
7379 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7380
7381 return (mUSBControllers->size() > 0);
7382}
7383
7384/**
7385 * @note Locks this object for writing, calls the client process
7386 * (inside the lock).
7387 */
7388HRESULT Machine::i_launchVMProcess(IInternalSessionControl *aControl,
7389 const Utf8Str &strFrontend,
7390 const Utf8Str &strEnvironment,
7391 ProgressProxy *aProgress)
7392{
7393 LogFlowThisFuncEnter();
7394
7395 AssertReturn(aControl, E_FAIL);
7396 AssertReturn(aProgress, E_FAIL);
7397 AssertReturn(!strFrontend.isEmpty(), E_FAIL);
7398
7399 AutoCaller autoCaller(this);
7400 if (FAILED(autoCaller.rc())) return autoCaller.rc();
7401
7402 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7403
7404 if (!mData->mRegistered)
7405 return setError(E_UNEXPECTED,
7406 tr("The machine '%s' is not registered"),
7407 mUserData->s.strName.c_str());
7408
7409 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));
7410
7411 /* The process started when launching a VM with separate UI/VM processes is always
7412 * the UI process, i.e. needs special handling as it won't claim the session. */
7413 bool fSeparate = strFrontend.endsWith("separate", Utf8Str::CaseInsensitive);
7414
7415 if (fSeparate)
7416 {
7417 if (mData->mSession.mState != SessionState_Unlocked && mData->mSession.mName != "headless")
7418 return setError(VBOX_E_INVALID_OBJECT_STATE,
7419 tr("The machine '%s' is in a state which is incompatible with launching a separate UI process"),
7420 mUserData->s.strName.c_str());
7421 }
7422 else
7423 {
7424 if ( mData->mSession.mState == SessionState_Locked
7425 || mData->mSession.mState == SessionState_Spawning
7426 || mData->mSession.mState == SessionState_Unlocking)
7427 return setError(VBOX_E_INVALID_OBJECT_STATE,
7428 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),
7429 mUserData->s.strName.c_str());
7430
7431 /* may not be busy */
7432 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);
7433 }
7434
7435 /* get the path to the executable */
7436 char szPath[RTPATH_MAX];
7437 RTPathAppPrivateArch(szPath, sizeof(szPath) - 1);
7438 size_t cchBufLeft = strlen(szPath);
7439 szPath[cchBufLeft++] = RTPATH_DELIMITER;
7440 szPath[cchBufLeft] = 0;
7441 char *pszNamePart = szPath + cchBufLeft;
7442 cchBufLeft = sizeof(szPath) - cchBufLeft;
7443
7444 int vrc = VINF_SUCCESS;
7445 RTPROCESS pid = NIL_RTPROCESS;
7446
7447 RTENV env = RTENV_DEFAULT;
7448
7449 if (!strEnvironment.isEmpty())
7450 {
7451 char *newEnvStr = NULL;
7452
7453 do
7454 {
7455 /* clone the current environment */
7456 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT);
7457 AssertRCBreakStmt(vrc2, vrc = vrc2);
7458
7459 newEnvStr = RTStrDup(strEnvironment.c_str());
7460 AssertPtrBreakStmt(newEnvStr, vrc = vrc2);
7461
7462 /* put new variables to the environment
7463 * (ignore empty variable names here since RTEnv API
7464 * intentionally doesn't do that) */
7465 char *var = newEnvStr;
7466 for (char *p = newEnvStr; *p; ++p)
7467 {
7468 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\'))
7469 {
7470 *p = '\0';
7471 if (*var)
7472 {
7473 char *val = strchr(var, '=');
7474 if (val)
7475 {
7476 *val++ = '\0';
7477 vrc2 = RTEnvSetEx(env, var, val);
7478 }
7479 else
7480 vrc2 = RTEnvUnsetEx(env, var);
7481 if (RT_FAILURE(vrc2))
7482 break;
7483 }
7484 var = p + 1;
7485 }
7486 }
7487 if (RT_SUCCESS(vrc2) && *var)
7488 vrc2 = RTEnvPutEx(env, var);
7489
7490 AssertRCBreakStmt(vrc2, vrc = vrc2);
7491 }
7492 while (0);
7493
7494 if (newEnvStr != NULL)
7495 RTStrFree(newEnvStr);
7496 }
7497
7498 /* Hardening logging */
7499#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7500 Utf8Str strSupHardeningLogArg("--sup-hardening-log=");
7501 {
7502 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7503 int vrc2 = RTFileDelete(strHardeningLogFile.c_str());
7504 if (vrc2 == VERR_PATH_NOT_FOUND || vrc2 == VERR_FILE_NOT_FOUND)
7505 {
7506 Utf8Str strStartupLogDir = strHardeningLogFile;
7507 strStartupLogDir.stripFilename();
7508 RTDirCreateFullPath(strStartupLogDir.c_str(), 0755); /** @todo add a variant for creating the path to a
7509 file without stripping the file. */
7510 }
7511 strSupHardeningLogArg.append(strHardeningLogFile);
7512
7513 /* Remove legacy log filename to avoid confusion. */
7514 Utf8Str strOldStartupLogFile;
7515 getLogFolder(strOldStartupLogFile);
7516 strOldStartupLogFile.append(RTPATH_SLASH_STR "VBoxStartup.log");
7517 RTFileDelete(strOldStartupLogFile.c_str());
7518 }
7519 const char *pszSupHardeningLogArg = strSupHardeningLogArg.c_str();
7520#else
7521 const char *pszSupHardeningLogArg = NULL;
7522#endif
7523
7524 Utf8Str strCanonicalName;
7525
7526#ifdef VBOX_WITH_QTGUI
7527 if ( !strFrontend.compare("gui", Utf8Str::CaseInsensitive)
7528 || !strFrontend.compare("GUI/Qt", Utf8Str::CaseInsensitive)
7529 || !strFrontend.compare("separate", Utf8Str::CaseInsensitive)
7530 || !strFrontend.compare("gui/separate", Utf8Str::CaseInsensitive)
7531 || !strFrontend.compare("GUI/Qt/separate", Utf8Str::CaseInsensitive))
7532 {
7533 strCanonicalName = "GUI/Qt";
7534# ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */
7535 /* Modify the base path so that we don't need to use ".." below. */
7536 RTPathStripTrailingSlash(szPath);
7537 RTPathStripFilename(szPath);
7538 cchBufLeft = strlen(szPath);
7539 pszNamePart = szPath + cchBufLeft;
7540 cchBufLeft = sizeof(szPath) - cchBufLeft;
7541
7542# define OSX_APP_NAME "VirtualBoxVM"
7543# define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM"
7544
7545 Utf8Str strAppOverride = i_getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride"));
7546 if ( strAppOverride.contains(".")
7547 || strAppOverride.contains("/")
7548 || strAppOverride.contains("\\")
7549 || strAppOverride.contains(":"))
7550 strAppOverride.setNull();
7551 Utf8Str strAppPath;
7552 if (!strAppOverride.isEmpty())
7553 {
7554 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str());
7555 Utf8Str strFullPath(szPath);
7556 strFullPath.append(strAppPath);
7557 /* there is a race, but people using this deserve the failure */
7558 if (!RTFileExists(strFullPath.c_str()))
7559 strAppOverride.setNull();
7560 }
7561 if (strAppOverride.isEmpty())
7562 strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME);
7563 AssertReturn(cchBufLeft > strAppPath.length(), E_UNEXPECTED);
7564 strcpy(pszNamePart, strAppPath.c_str());
7565# else
7566 static const char s_szVirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE;
7567 Assert(cchBufLeft >= sizeof(s_szVirtualBox_exe));
7568 strcpy(pszNamePart, s_szVirtualBox_exe);
7569# endif
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 "--no-startvm-errormsgbox",
7578 NULL, /* For "--separate". */
7579 NULL, /* For "--sup-startup-log". */
7580 NULL
7581 };
7582 unsigned iArg = 6;
7583 if (fSeparate)
7584 apszArgs[iArg++] = "--separate";
7585 apszArgs[iArg++] = pszSupHardeningLogArg;
7586
7587 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7588 }
7589#else /* !VBOX_WITH_QTGUI */
7590 if (0)
7591 ;
7592#endif /* VBOX_WITH_QTGUI */
7593
7594 else
7595
7596#ifdef VBOX_WITH_VBOXSDL
7597 if ( !strFrontend.compare("sdl", Utf8Str::CaseInsensitive)
7598 || !strFrontend.compare("GUI/SDL", Utf8Str::CaseInsensitive)
7599 || !strFrontend.compare("sdl/separate", Utf8Str::CaseInsensitive)
7600 || !strFrontend.compare("GUI/SDL/separate", Utf8Str::CaseInsensitive))
7601 {
7602 strCanonicalName = "GUI/SDL";
7603 static const char s_szVBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE;
7604 Assert(cchBufLeft >= sizeof(s_szVBoxSDL_exe));
7605 strcpy(pszNamePart, s_szVBoxSDL_exe);
7606
7607 Utf8Str idStr = mData->mUuid.toString();
7608 const char *apszArgs[] =
7609 {
7610 szPath,
7611 "--comment", mUserData->s.strName.c_str(),
7612 "--startvm", idStr.c_str(),
7613 NULL, /* For "--separate". */
7614 NULL, /* For "--sup-startup-log". */
7615 NULL
7616 };
7617 unsigned iArg = 5;
7618 if (fSeparate)
7619 apszArgs[iArg++] = "--separate";
7620 apszArgs[iArg++] = pszSupHardeningLogArg;
7621
7622 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7623 }
7624#else /* !VBOX_WITH_VBOXSDL */
7625 if (0)
7626 ;
7627#endif /* !VBOX_WITH_VBOXSDL */
7628
7629 else
7630
7631#ifdef VBOX_WITH_HEADLESS
7632 if ( !strFrontend.compare("headless", Utf8Str::CaseInsensitive)
7633 || !strFrontend.compare("capture", Utf8Str::CaseInsensitive)
7634 || !strFrontend.compare("vrdp", Utf8Str::CaseInsensitive) /* Deprecated. Same as headless. */
7635 )
7636 {
7637 strCanonicalName = "headless";
7638 /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE,
7639 * which did not contain VRDP server. In VBox 4.0 the remote desktop server (VRDE) is optional,
7640 * and a VM works even if the server has not been installed.
7641 * So in 4.0 the "headless" behavior remains the same for default VBox installations.
7642 * Only if a VRDE has been installed and the VM enables it, the "headless" will work
7643 * differently in 4.0 and 3.x.
7644 */
7645 static const char s_szVBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE;
7646 Assert(cchBufLeft >= sizeof(s_szVBoxHeadless_exe));
7647 strcpy(pszNamePart, s_szVBoxHeadless_exe);
7648
7649 Utf8Str idStr = mData->mUuid.toString();
7650 const char *apszArgs[] =
7651 {
7652 szPath,
7653 "--comment", mUserData->s.strName.c_str(),
7654 "--startvm", idStr.c_str(),
7655 "--vrde", "config",
7656 NULL, /* For "--capture". */
7657 NULL, /* For "--sup-startup-log". */
7658 NULL
7659 };
7660 unsigned iArg = 7;
7661 if (!strFrontend.compare("capture", Utf8Str::CaseInsensitive))
7662 apszArgs[iArg++] = "--capture";
7663 apszArgs[iArg++] = pszSupHardeningLogArg;
7664
7665# ifdef RT_OS_WINDOWS
7666 vrc = RTProcCreate(szPath, apszArgs, env, RTPROC_FLAGS_NO_WINDOW, &pid);
7667# else
7668 vrc = RTProcCreate(szPath, apszArgs, env, 0, &pid);
7669# endif
7670 }
7671#else /* !VBOX_WITH_HEADLESS */
7672 if (0)
7673 ;
7674#endif /* !VBOX_WITH_HEADLESS */
7675 else
7676 {
7677 RTEnvDestroy(env);
7678 return setError(E_INVALIDARG,
7679 tr("Invalid frontend name: '%s'"),
7680 strFrontend.c_str());
7681 }
7682
7683 RTEnvDestroy(env);
7684
7685 if (RT_FAILURE(vrc))
7686 return setError(VBOX_E_IPRT_ERROR,
7687 tr("Could not launch a process for the machine '%s' (%Rrc)"),
7688 mUserData->s.strName.c_str(), vrc);
7689
7690 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid));
7691
7692 if (!fSeparate)
7693 {
7694 /*
7695 * Note that we don't release the lock here before calling the client,
7696 * because it doesn't need to call us back if called with a NULL argument.
7697 * Releasing the lock here is dangerous because we didn't prepare the
7698 * launch data yet, but the client we've just started may happen to be
7699 * too fast and call LockMachine() that will fail (because of PID, etc.),
7700 * so that the Machine will never get out of the Spawning session state.
7701 */
7702
7703 /* inform the session that it will be a remote one */
7704 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n"));
7705#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
7706 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw());
7707#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7708 HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL);
7709#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
7710 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc));
7711
7712 if (FAILED(rc))
7713 {
7714 /* restore the session state */
7715 mData->mSession.mState = SessionState_Unlocked;
7716 alock.release();
7717 mParent->i_addProcessToReap(pid);
7718 /* The failure may occur w/o any error info (from RPC), so provide one */
7719 return setError(VBOX_E_VM_ERROR,
7720 tr("Failed to assign the machine to the session (%Rhrc)"), rc);
7721 }
7722
7723 /* attach launch data to the machine */
7724 Assert(mData->mSession.mPID == NIL_RTPROCESS);
7725 mData->mSession.mRemoteControls.push_back(aControl);
7726 mData->mSession.mProgress = aProgress;
7727 mData->mSession.mPID = pid;
7728 mData->mSession.mState = SessionState_Spawning;
7729 Assert(strCanonicalName.isNotEmpty());
7730 mData->mSession.mName = strCanonicalName;
7731 }
7732 else
7733 {
7734 /* For separate UI process we declare the launch as completed instantly, as the
7735 * actual headless VM start may or may not come. No point in remembering anything
7736 * yet, as what matters for us is when the headless VM gets started. */
7737 aProgress->i_notifyComplete(S_OK);
7738 }
7739
7740 alock.release();
7741 mParent->i_addProcessToReap(pid);
7742
7743 LogFlowThisFuncLeave();
7744 return S_OK;
7745}
7746
7747/**
7748 * Returns @c true if the given session machine instance has an open direct
7749 * session (and optionally also for direct sessions which are closing) and
7750 * returns the session control machine instance if so.
7751 *
7752 * Note that when the method returns @c false, the arguments remain unchanged.
7753 *
7754 * @param aMachine Session machine object.
7755 * @param aControl Direct session control object (optional).
7756 * @param aRequireVM If true then only allow VM sessions.
7757 * @param aAllowClosing If true then additionally a session which is currently
7758 * being closed will also be allowed.
7759 *
7760 * @note locks this object for reading.
7761 */
7762bool Machine::i_isSessionOpen(ComObjPtr<SessionMachine> &aMachine,
7763 ComPtr<IInternalSessionControl> *aControl /*= NULL*/,
7764 bool aRequireVM /*= false*/,
7765 bool aAllowClosing /*= false*/)
7766{
7767 AutoLimitedCaller autoCaller(this);
7768 AssertComRCReturn(autoCaller.rc(), false);
7769
7770 /* just return false for inaccessible machines */
7771 if (getObjectState().getState() != ObjectState::Ready)
7772 return false;
7773
7774 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7775
7776 if ( ( mData->mSession.mState == SessionState_Locked
7777 && (!aRequireVM || mData->mSession.mLockType == LockType_VM))
7778 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking)
7779 )
7780 {
7781 AssertReturn(!mData->mSession.mMachine.isNull(), false);
7782
7783 aMachine = mData->mSession.mMachine;
7784
7785 if (aControl != NULL)
7786 *aControl = mData->mSession.mDirectControl;
7787
7788 return true;
7789 }
7790
7791 return false;
7792}
7793
7794/**
7795 * Returns @c true if the given machine has an spawning direct session.
7796 *
7797 * @note locks this object for reading.
7798 */
7799bool Machine::i_isSessionSpawning()
7800{
7801 AutoLimitedCaller autoCaller(this);
7802 AssertComRCReturn(autoCaller.rc(), false);
7803
7804 /* just return false for inaccessible machines */
7805 if (getObjectState().getState() != ObjectState::Ready)
7806 return false;
7807
7808 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
7809
7810 if (mData->mSession.mState == SessionState_Spawning)
7811 return true;
7812
7813 return false;
7814}
7815
7816/**
7817 * Called from the client watcher thread to check for unexpected client process
7818 * death during Session_Spawning state (e.g. before it successfully opened a
7819 * direct session).
7820 *
7821 * On Win32 and on OS/2, this method is called only when we've got the
7822 * direct client's process termination notification, so it always returns @c
7823 * true.
7824 *
7825 * On other platforms, this method returns @c true if the client process is
7826 * terminated and @c false if it's still alive.
7827 *
7828 * @note Locks this object for writing.
7829 */
7830bool Machine::i_checkForSpawnFailure()
7831{
7832 AutoCaller autoCaller(this);
7833 if (!autoCaller.isOk())
7834 {
7835 /* nothing to do */
7836 LogFlowThisFunc(("Already uninitialized!\n"));
7837 return true;
7838 }
7839
7840 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7841
7842 if (mData->mSession.mState != SessionState_Spawning)
7843 {
7844 /* nothing to do */
7845 LogFlowThisFunc(("Not spawning any more!\n"));
7846 return true;
7847 }
7848
7849 HRESULT rc = S_OK;
7850
7851 /* PID not yet initialized, skip check. */
7852 if (mData->mSession.mPID == NIL_RTPROCESS)
7853 return false;
7854
7855 RTPROCSTATUS status;
7856 int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status);
7857
7858 if (vrc != VERR_PROCESS_RUNNING)
7859 {
7860 Utf8Str strExtraInfo;
7861
7862#if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_HARDENING)
7863 /* If the startup logfile exists and is of non-zero length, tell the
7864 user to look there for more details to encourage them to attach it
7865 when reporting startup issues. */
7866 Utf8Str strHardeningLogFile = i_getHardeningLogFilename();
7867 uint64_t cbStartupLogFile = 0;
7868 int vrc2 = RTFileQuerySize(strHardeningLogFile.c_str(), &cbStartupLogFile);
7869 if (RT_SUCCESS(vrc2) && cbStartupLogFile > 0)
7870 strExtraInfo.append(Utf8StrFmt(tr(". More details may be available in '%s'"), strHardeningLogFile.c_str()));
7871#endif
7872
7873 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL)
7874 rc = setError(E_FAIL,
7875 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d (%#x)%s"),
7876 i_getName().c_str(), status.iStatus, status.iStatus, strExtraInfo.c_str());
7877 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL)
7878 rc = setError(E_FAIL,
7879 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d%s"),
7880 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7881 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND)
7882 rc = setError(E_FAIL,
7883 tr("The virtual machine '%s' has terminated abnormally (iStatus=%#x)%s"),
7884 i_getName().c_str(), status.iStatus, strExtraInfo.c_str());
7885 else
7886 rc = setError(E_FAIL,
7887 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)%s"),
7888 i_getName().c_str(), vrc, strExtraInfo.c_str());
7889 }
7890
7891 if (FAILED(rc))
7892 {
7893 /* Close the remote session, remove the remote control from the list
7894 * and reset session state to Closed (@note keep the code in sync with
7895 * the relevant part in LockMachine()). */
7896
7897 Assert(mData->mSession.mRemoteControls.size() == 1);
7898 if (mData->mSession.mRemoteControls.size() == 1)
7899 {
7900 ErrorInfoKeeper eik;
7901 mData->mSession.mRemoteControls.front()->Uninitialize();
7902 }
7903
7904 mData->mSession.mRemoteControls.clear();
7905 mData->mSession.mState = SessionState_Unlocked;
7906
7907 /* finalize the progress after setting the state */
7908 if (!mData->mSession.mProgress.isNull())
7909 {
7910 mData->mSession.mProgress->notifyComplete(rc);
7911 mData->mSession.mProgress.setNull();
7912 }
7913
7914 mData->mSession.mPID = NIL_RTPROCESS;
7915
7916 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
7917 return true;
7918 }
7919
7920 return false;
7921}
7922
7923/**
7924 * Checks whether the machine can be registered. If so, commits and saves
7925 * all settings.
7926 *
7927 * @note Must be called from mParent's write lock. Locks this object and
7928 * children for writing.
7929 */
7930HRESULT Machine::i_prepareRegister()
7931{
7932 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL);
7933
7934 AutoLimitedCaller autoCaller(this);
7935 AssertComRCReturnRC(autoCaller.rc());
7936
7937 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
7938
7939 /* wait for state dependents to drop to zero */
7940 i_ensureNoStateDependencies();
7941
7942 if (!mData->mAccessible)
7943 return setError(VBOX_E_INVALID_OBJECT_STATE,
7944 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"),
7945 mUserData->s.strName.c_str(),
7946 mData->mUuid.toString().c_str());
7947
7948 AssertReturn(getObjectState().getState() == ObjectState::Ready, E_FAIL);
7949
7950 if (mData->mRegistered)
7951 return setError(VBOX_E_INVALID_OBJECT_STATE,
7952 tr("The machine '%s' with UUID {%s} is already registered"),
7953 mUserData->s.strName.c_str(),
7954 mData->mUuid.toString().c_str());
7955
7956 HRESULT rc = S_OK;
7957
7958 // Ensure the settings are saved. If we are going to be registered and
7959 // no config file exists yet, create it by calling i_saveSettings() too.
7960 if ( (mData->flModifications)
7961 || (!mData->pMachineConfigFile->fileExists())
7962 )
7963 {
7964 rc = i_saveSettings(NULL);
7965 // no need to check whether VirtualBox.xml needs saving too since
7966 // we can't have a machine XML file rename pending
7967 if (FAILED(rc)) return rc;
7968 }
7969
7970 /* more config checking goes here */
7971
7972 if (SUCCEEDED(rc))
7973 {
7974 /* we may have had implicit modifications we want to fix on success */
7975 i_commit();
7976
7977 mData->mRegistered = true;
7978 }
7979 else
7980 {
7981 /* we may have had implicit modifications we want to cancel on failure*/
7982 i_rollback(false /* aNotify */);
7983 }
7984
7985 return rc;
7986}
7987
7988/**
7989 * Increases the number of objects dependent on the machine state or on the
7990 * registered state. Guarantees that these two states will not change at least
7991 * until #releaseStateDependency() is called.
7992 *
7993 * Depending on the @a aDepType value, additional state checks may be made.
7994 * These checks will set extended error info on failure. See
7995 * #checkStateDependency() for more info.
7996 *
7997 * If this method returns a failure, the dependency is not added and the caller
7998 * is not allowed to rely on any particular machine state or registration state
7999 * value and may return the failed result code to the upper level.
8000 *
8001 * @param aDepType Dependency type to add.
8002 * @param aState Current machine state (NULL if not interested).
8003 * @param aRegistered Current registered state (NULL if not interested).
8004 *
8005 * @note Locks this object for writing.
8006 */
8007HRESULT Machine::i_addStateDependency(StateDependency aDepType /* = AnyStateDep */,
8008 MachineState_T *aState /* = NULL */,
8009 BOOL *aRegistered /* = NULL */)
8010{
8011 AutoCaller autoCaller(this);
8012 AssertComRCReturnRC(autoCaller.rc());
8013
8014 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8015
8016 HRESULT rc = i_checkStateDependency(aDepType);
8017 if (FAILED(rc)) return rc;
8018
8019 {
8020 if (mData->mMachineStateChangePending != 0)
8021 {
8022 /* i_ensureNoStateDependencies() is waiting for state dependencies to
8023 * drop to zero so don't add more. It may make sense to wait a bit
8024 * and retry before reporting an error (since the pending state
8025 * transition should be really quick) but let's just assert for
8026 * now to see if it ever happens on practice. */
8027
8028 AssertFailed();
8029
8030 return setError(E_ACCESSDENIED,
8031 tr("Machine state change is in progress. Please retry the operation later."));
8032 }
8033
8034 ++mData->mMachineStateDeps;
8035 Assert(mData->mMachineStateDeps != 0 /* overflow */);
8036 }
8037
8038 if (aState)
8039 *aState = mData->mMachineState;
8040 if (aRegistered)
8041 *aRegistered = mData->mRegistered;
8042
8043 return S_OK;
8044}
8045
8046/**
8047 * Decreases the number of objects dependent on the machine state.
8048 * Must always complete the #addStateDependency() call after the state
8049 * dependency is no more necessary.
8050 */
8051void Machine::i_releaseStateDependency()
8052{
8053 AutoCaller autoCaller(this);
8054 AssertComRCReturnVoid(autoCaller.rc());
8055
8056 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8057
8058 /* releaseStateDependency() w/o addStateDependency()? */
8059 AssertReturnVoid(mData->mMachineStateDeps != 0);
8060 -- mData->mMachineStateDeps;
8061
8062 if (mData->mMachineStateDeps == 0)
8063 {
8064 /* inform i_ensureNoStateDependencies() that there are no more deps */
8065 if (mData->mMachineStateChangePending != 0)
8066 {
8067 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI);
8068 RTSemEventMultiSignal (mData->mMachineStateDepsSem);
8069 }
8070 }
8071}
8072
8073Utf8Str Machine::i_getExtraData(const Utf8Str &strKey)
8074{
8075 /* start with nothing found */
8076 Utf8Str strResult("");
8077
8078 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
8079
8080 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey);
8081 if (it != mData->pMachineConfigFile->mapExtraDataItems.end())
8082 // found:
8083 strResult = it->second; // source is a Utf8Str
8084
8085 return strResult;
8086}
8087
8088// protected methods
8089/////////////////////////////////////////////////////////////////////////////
8090
8091/**
8092 * Performs machine state checks based on the @a aDepType value. If a check
8093 * fails, this method will set extended error info, otherwise it will return
8094 * S_OK. It is supposed, that on failure, the caller will immediately return
8095 * the return value of this method to the upper level.
8096 *
8097 * When @a aDepType is AnyStateDep, this method always returns S_OK.
8098 *
8099 * When @a aDepType is MutableStateDep, this method returns S_OK only if the
8100 * current state of this machine object allows to change settings of the
8101 * machine (i.e. the machine is not registered, or registered but not running
8102 * and not saved). It is useful to call this method from Machine setters
8103 * before performing any change.
8104 *
8105 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same
8106 * as for MutableStateDep except that if the machine is saved, S_OK is also
8107 * returned. This is useful in setters which allow changing machine
8108 * properties when it is in the saved state.
8109 *
8110 * When @a aDepType is MutableOrRunningStateDep, this method returns S_OK only
8111 * if the current state of this machine object allows to change runtime
8112 * changeable settings of the machine (i.e. the machine is not registered, or
8113 * registered but either running or not running and not saved). It is useful
8114 * to call this method from Machine setters before performing any changes to
8115 * runtime changeable settings.
8116 *
8117 * When @a aDepType is MutableOrSavedOrRunningStateDep, this method behaves
8118 * the same as for MutableOrRunningStateDep except that if the machine is
8119 * saved, S_OK is also returned. This is useful in setters which allow
8120 * changing runtime and saved state changeable machine properties.
8121 *
8122 * @param aDepType Dependency type to check.
8123 *
8124 * @note Non Machine based classes should use #addStateDependency() and
8125 * #releaseStateDependency() methods or the smart AutoStateDependency
8126 * template.
8127 *
8128 * @note This method must be called from under this object's read or write
8129 * lock.
8130 */
8131HRESULT Machine::i_checkStateDependency(StateDependency aDepType)
8132{
8133 switch (aDepType)
8134 {
8135 case AnyStateDep:
8136 {
8137 break;
8138 }
8139 case MutableStateDep:
8140 {
8141 if ( mData->mRegistered
8142 && ( !i_isSessionMachine()
8143 || ( mData->mMachineState != MachineState_Aborted
8144 && mData->mMachineState != MachineState_Teleported
8145 && mData->mMachineState != MachineState_PoweredOff
8146 )
8147 )
8148 )
8149 return setError(VBOX_E_INVALID_VM_STATE,
8150 tr("The machine is not mutable (state is %s)"),
8151 Global::stringifyMachineState(mData->mMachineState));
8152 break;
8153 }
8154 case MutableOrSavedStateDep:
8155 {
8156 if ( mData->mRegistered
8157 && ( !i_isSessionMachine()
8158 || ( mData->mMachineState != MachineState_Aborted
8159 && mData->mMachineState != MachineState_Teleported
8160 && mData->mMachineState != MachineState_Saved
8161 && mData->mMachineState != MachineState_PoweredOff
8162 )
8163 )
8164 )
8165 return setError(VBOX_E_INVALID_VM_STATE,
8166 tr("The machine is not mutable or saved (state is %s)"),
8167 Global::stringifyMachineState(mData->mMachineState));
8168 break;
8169 }
8170 case MutableOrRunningStateDep:
8171 {
8172 if ( mData->mRegistered
8173 && ( !i_isSessionMachine()
8174 || ( mData->mMachineState != MachineState_Aborted
8175 && mData->mMachineState != MachineState_Teleported
8176 && mData->mMachineState != MachineState_PoweredOff
8177 && !Global::IsOnline(mData->mMachineState)
8178 )
8179 )
8180 )
8181 return setError(VBOX_E_INVALID_VM_STATE,
8182 tr("The machine is not mutable or running (state is %s)"),
8183 Global::stringifyMachineState(mData->mMachineState));
8184 break;
8185 }
8186 case MutableOrSavedOrRunningStateDep:
8187 {
8188 if ( mData->mRegistered
8189 && ( !i_isSessionMachine()
8190 || ( mData->mMachineState != MachineState_Aborted
8191 && mData->mMachineState != MachineState_Teleported
8192 && mData->mMachineState != MachineState_Saved
8193 && mData->mMachineState != MachineState_PoweredOff
8194 && !Global::IsOnline(mData->mMachineState)
8195 )
8196 )
8197 )
8198 return setError(VBOX_E_INVALID_VM_STATE,
8199 tr("The machine is not mutable, saved or running (state is %s)"),
8200 Global::stringifyMachineState(mData->mMachineState));
8201 break;
8202 }
8203 }
8204
8205 return S_OK;
8206}
8207
8208/**
8209 * Helper to initialize all associated child objects and allocate data
8210 * structures.
8211 *
8212 * This method must be called as a part of the object's initialization procedure
8213 * (usually done in the #init() method).
8214 *
8215 * @note Must be called only from #init() or from #registeredInit().
8216 */
8217HRESULT Machine::initDataAndChildObjects()
8218{
8219 AutoCaller autoCaller(this);
8220 AssertComRCReturnRC(autoCaller.rc());
8221 AssertComRCReturn( getObjectState().getState() == ObjectState::InInit
8222 || getObjectState().getState() == ObjectState::Limited, E_FAIL);
8223
8224 AssertReturn(!mData->mAccessible, E_FAIL);
8225
8226 /* allocate data structures */
8227 mSSData.allocate();
8228 mUserData.allocate();
8229 mHWData.allocate();
8230 mMediaData.allocate();
8231 mStorageControllers.allocate();
8232 mUSBControllers.allocate();
8233
8234 /* initialize mOSTypeId */
8235 mUserData->s.strOsType = mParent->i_getUnknownOSType()->i_id();
8236
8237 /* create associated BIOS settings object */
8238 unconst(mBIOSSettings).createObject();
8239 mBIOSSettings->init(this);
8240
8241 /* create an associated VRDE object (default is disabled) */
8242 unconst(mVRDEServer).createObject();
8243 mVRDEServer->init(this);
8244
8245 /* create associated serial port objects */
8246 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8247 {
8248 unconst(mSerialPorts[slot]).createObject();
8249 mSerialPorts[slot]->init(this, slot);
8250 }
8251
8252 /* create associated parallel port objects */
8253 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8254 {
8255 unconst(mParallelPorts[slot]).createObject();
8256 mParallelPorts[slot]->init(this, slot);
8257 }
8258
8259 /* create the audio adapter object (always present, default is disabled) */
8260 unconst(mAudioAdapter).createObject();
8261 mAudioAdapter->init(this);
8262
8263 /* create the USB device filters object (always present) */
8264 unconst(mUSBDeviceFilters).createObject();
8265 mUSBDeviceFilters->init(this);
8266
8267 /* create associated network adapter objects */
8268 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
8269 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8270 {
8271 unconst(mNetworkAdapters[slot]).createObject();
8272 mNetworkAdapters[slot]->init(this, slot);
8273 }
8274
8275 /* create the bandwidth control */
8276 unconst(mBandwidthControl).createObject();
8277 mBandwidthControl->init(this);
8278
8279 return S_OK;
8280}
8281
8282/**
8283 * Helper to uninitialize all associated child objects and to free all data
8284 * structures.
8285 *
8286 * This method must be called as a part of the object's uninitialization
8287 * procedure (usually done in the #uninit() method).
8288 *
8289 * @note Must be called only from #uninit() or from #registeredInit().
8290 */
8291void Machine::uninitDataAndChildObjects()
8292{
8293 AutoCaller autoCaller(this);
8294 AssertComRCReturnVoid(autoCaller.rc());
8295 AssertComRCReturnVoid( getObjectState().getState() == ObjectState::InUninit
8296 || getObjectState().getState() == ObjectState::Limited);
8297
8298 /* tell all our other child objects we've been uninitialized */
8299 if (mBandwidthControl)
8300 {
8301 mBandwidthControl->uninit();
8302 unconst(mBandwidthControl).setNull();
8303 }
8304
8305 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
8306 {
8307 if (mNetworkAdapters[slot])
8308 {
8309 mNetworkAdapters[slot]->uninit();
8310 unconst(mNetworkAdapters[slot]).setNull();
8311 }
8312 }
8313
8314 if (mUSBDeviceFilters)
8315 {
8316 mUSBDeviceFilters->uninit();
8317 unconst(mUSBDeviceFilters).setNull();
8318 }
8319
8320 if (mAudioAdapter)
8321 {
8322 mAudioAdapter->uninit();
8323 unconst(mAudioAdapter).setNull();
8324 }
8325
8326 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
8327 {
8328 if (mParallelPorts[slot])
8329 {
8330 mParallelPorts[slot]->uninit();
8331 unconst(mParallelPorts[slot]).setNull();
8332 }
8333 }
8334
8335 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
8336 {
8337 if (mSerialPorts[slot])
8338 {
8339 mSerialPorts[slot]->uninit();
8340 unconst(mSerialPorts[slot]).setNull();
8341 }
8342 }
8343
8344 if (mVRDEServer)
8345 {
8346 mVRDEServer->uninit();
8347 unconst(mVRDEServer).setNull();
8348 }
8349
8350 if (mBIOSSettings)
8351 {
8352 mBIOSSettings->uninit();
8353 unconst(mBIOSSettings).setNull();
8354 }
8355
8356 /* Deassociate media (only when a real Machine or a SnapshotMachine
8357 * instance is uninitialized; SessionMachine instances refer to real
8358 * Machine media). This is necessary for a clean re-initialization of
8359 * the VM after successfully re-checking the accessibility state. Note
8360 * that in case of normal Machine or SnapshotMachine uninitialization (as
8361 * a result of unregistering or deleting the snapshot), outdated media
8362 * attachments will already be uninitialized and deleted, so this
8363 * code will not affect them. */
8364 if ( !!mMediaData
8365 && (!i_isSessionMachine())
8366 )
8367 {
8368 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
8369 it != mMediaData->mAttachments.end();
8370 ++it)
8371 {
8372 ComObjPtr<Medium> pMedium = (*it)->i_getMedium();
8373 if (pMedium.isNull())
8374 continue;
8375 HRESULT rc = pMedium->i_removeBackReference(mData->mUuid, i_getSnapshotId());
8376 AssertComRC(rc);
8377 }
8378 }
8379
8380 if (!i_isSessionMachine() && !i_isSnapshotMachine())
8381 {
8382 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively)
8383 if (mData->mFirstSnapshot)
8384 {
8385 // snapshots tree is protected by machine write lock; strictly
8386 // this isn't necessary here since we're deleting the entire
8387 // machine, but otherwise we assert in Snapshot::uninit()
8388 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8389 mData->mFirstSnapshot->uninit();
8390 mData->mFirstSnapshot.setNull();
8391 }
8392
8393 mData->mCurrentSnapshot.setNull();
8394 }
8395
8396 /* free data structures (the essential mData structure is not freed here
8397 * since it may be still in use) */
8398 mMediaData.free();
8399 mStorageControllers.free();
8400 mUSBControllers.free();
8401 mHWData.free();
8402 mUserData.free();
8403 mSSData.free();
8404}
8405
8406/**
8407 * Returns a pointer to the Machine object for this machine that acts like a
8408 * parent for complex machine data objects such as shared folders, etc.
8409 *
8410 * For primary Machine objects and for SnapshotMachine objects, returns this
8411 * object's pointer itself. For SessionMachine objects, returns the peer
8412 * (primary) machine pointer.
8413 */
8414Machine* Machine::i_getMachine()
8415{
8416 if (i_isSessionMachine())
8417 return (Machine*)mPeer;
8418 return this;
8419}
8420
8421/**
8422 * Makes sure that there are no machine state dependents. If necessary, waits
8423 * for the number of dependents to drop to zero.
8424 *
8425 * Make sure this method is called from under this object's write lock to
8426 * guarantee that no new dependents may be added when this method returns
8427 * control to the caller.
8428 *
8429 * @note Locks this object for writing. The lock will be released while waiting
8430 * (if necessary).
8431 *
8432 * @warning To be used only in methods that change the machine state!
8433 */
8434void Machine::i_ensureNoStateDependencies()
8435{
8436 AssertReturnVoid(isWriteLockOnCurrentThread());
8437
8438 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8439
8440 /* Wait for all state dependents if necessary */
8441 if (mData->mMachineStateDeps != 0)
8442 {
8443 /* lazy semaphore creation */
8444 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI)
8445 RTSemEventMultiCreate(&mData->mMachineStateDepsSem);
8446
8447 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n",
8448 mData->mMachineStateDeps));
8449
8450 ++mData->mMachineStateChangePending;
8451
8452 /* reset the semaphore before waiting, the last dependent will signal
8453 * it */
8454 RTSemEventMultiReset(mData->mMachineStateDepsSem);
8455
8456 alock.release();
8457
8458 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT);
8459
8460 alock.acquire();
8461
8462 -- mData->mMachineStateChangePending;
8463 }
8464}
8465
8466/**
8467 * Changes the machine state and informs callbacks.
8468 *
8469 * This method is not intended to fail so it either returns S_OK or asserts (and
8470 * returns a failure).
8471 *
8472 * @note Locks this object for writing.
8473 */
8474HRESULT Machine::i_setMachineState(MachineState_T aMachineState)
8475{
8476 LogFlowThisFuncEnter();
8477 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
8478 Assert(aMachineState != MachineState_Null);
8479
8480 AutoCaller autoCaller(this);
8481 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
8482
8483 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
8484
8485 /* wait for state dependents to drop to zero */
8486 i_ensureNoStateDependencies();
8487
8488 MachineState_T const enmOldState = mData->mMachineState;
8489 if (enmOldState != aMachineState)
8490 {
8491 mData->mMachineState = aMachineState;
8492 RTTimeNow(&mData->mLastStateChange);
8493
8494#ifdef VBOX_WITH_DTRACE_R3_MAIN
8495 VBOXAPI_MACHINE_STATE_CHANGED(this, aMachineState, enmOldState, mData->mUuid.toStringCurly().c_str());
8496#endif
8497 mParent->i_onMachineStateChange(mData->mUuid, aMachineState);
8498 }
8499
8500 LogFlowThisFuncLeave();
8501 return S_OK;
8502}
8503
8504/**
8505 * Searches for a shared folder with the given logical name
8506 * in the collection of shared folders.
8507 *
8508 * @param aName logical name of the shared folder
8509 * @param aSharedFolder where to return the found object
8510 * @param aSetError whether to set the error info if the folder is
8511 * not found
8512 * @return
8513 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found
8514 *
8515 * @note
8516 * must be called from under the object's lock!
8517 */
8518HRESULT Machine::i_findSharedFolder(const Utf8Str &aName,
8519 ComObjPtr<SharedFolder> &aSharedFolder,
8520 bool aSetError /* = false */)
8521{
8522 HRESULT rc = VBOX_E_OBJECT_NOT_FOUND;
8523 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
8524 it != mHWData->mSharedFolders.end();
8525 ++it)
8526 {
8527 SharedFolder *pSF = *it;
8528 AutoCaller autoCaller(pSF);
8529 if (pSF->i_getName() == aName)
8530 {
8531 aSharedFolder = pSF;
8532 rc = S_OK;
8533 break;
8534 }
8535 }
8536
8537 if (aSetError && FAILED(rc))
8538 setError(rc, tr("Could not find a shared folder named '%s'"), aName.c_str());
8539
8540 return rc;
8541}
8542
8543/**
8544 * Initializes all machine instance data from the given settings structures
8545 * from XML. The exception is the machine UUID which needs special handling
8546 * depending on the caller's use case, so the caller needs to set that herself.
8547 *
8548 * This gets called in several contexts during machine initialization:
8549 *
8550 * -- When machine XML exists on disk already and needs to be loaded into memory,
8551 * for example, from registeredInit() to load all registered machines on
8552 * VirtualBox startup. In this case, puuidRegistry is NULL because the media
8553 * attached to the machine should be part of some media registry already.
8554 *
8555 * -- During OVF import, when a machine config has been constructed from an
8556 * OVF file. In this case, puuidRegistry is set to the machine UUID to
8557 * ensure that the media listed as attachments in the config (which have
8558 * been imported from the OVF) receive the correct registry ID.
8559 *
8560 * -- During VM cloning.
8561 *
8562 * @param config Machine settings from XML.
8563 * @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID
8564 * for each attached medium in the config.
8565 * @return
8566 */
8567HRESULT Machine::i_loadMachineDataFromSettings(const settings::MachineConfigFile &config,
8568 const Guid *puuidRegistry)
8569{
8570 // copy name, description, OS type, teleporter, UTC etc.
8571 mUserData->s = config.machineUserData;
8572
8573 // Decode the Icon overide data from config userdata and set onto Machine.
8574 #define DECODE_STR_MAX _1M
8575 const char* pszStr = config.machineUserData.ovIcon.c_str();
8576 ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL);
8577 if (cbOut > DECODE_STR_MAX)
8578 return setError(E_FAIL,
8579 tr("Icon Data too long.'%d' > '%d'"),
8580 cbOut,
8581 DECODE_STR_MAX);
8582 mUserData->mIcon.resize(cbOut);
8583 int vrc = VINF_SUCCESS;
8584 if (cbOut)
8585 vrc = RTBase64Decode(pszStr, &mUserData->mIcon.front(), cbOut, NULL, NULL);
8586 if (RT_FAILURE(vrc))
8587 {
8588 mUserData->mIcon.resize(0);
8589 return setError(E_FAIL,
8590 tr("Failure to Decode Icon Data. '%s' (%Rrc)"),
8591 pszStr,
8592 vrc);
8593 }
8594
8595 // look up the object by Id to check it is valid
8596 ComPtr<IGuestOSType> guestOSType;
8597 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(),
8598 guestOSType.asOutParam());
8599 if (FAILED(rc)) return rc;
8600
8601 // stateFile (optional)
8602 if (config.strStateFile.isEmpty())
8603 mSSData->strStateFilePath.setNull();
8604 else
8605 {
8606 Utf8Str stateFilePathFull(config.strStateFile);
8607 vrc = i_calculateFullPath(stateFilePathFull, stateFilePathFull);
8608 if (RT_FAILURE(vrc))
8609 return setError(E_FAIL,
8610 tr("Invalid saved state file path '%s' (%Rrc)"),
8611 config.strStateFile.c_str(),
8612 vrc);
8613 mSSData->strStateFilePath = stateFilePathFull;
8614 }
8615
8616 // snapshot folder needs special processing so set it again
8617 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
8618 if (FAILED(rc)) return rc;
8619
8620 /* Copy the extra data items (Not in any case config is already the same as
8621 * mData->pMachineConfigFile, like when the xml files are read from disk. So
8622 * make sure the extra data map is copied). */
8623 mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
8624
8625 /* currentStateModified (optional, default is true) */
8626 mData->mCurrentStateModified = config.fCurrentStateModified;
8627
8628 mData->mLastStateChange = config.timeLastStateChange;
8629
8630 /*
8631 * note: all mUserData members must be assigned prior this point because
8632 * we need to commit changes in order to let mUserData be shared by all
8633 * snapshot machine instances.
8634 */
8635 mUserData.commitCopy();
8636
8637 // machine registry, if present (must be loaded before snapshots)
8638 if (config.canHaveOwnMediaRegistry())
8639 {
8640 // determine machine folder
8641 Utf8Str strMachineFolder = i_getSettingsFileFull();
8642 strMachineFolder.stripFilename();
8643 rc = mParent->initMedia(i_getId(), // media registry ID == machine UUID
8644 config.mediaRegistry,
8645 strMachineFolder);
8646 if (FAILED(rc)) return rc;
8647 }
8648
8649 /* Snapshot node (optional) */
8650 size_t cRootSnapshots;
8651 if ((cRootSnapshots = config.llFirstSnapshot.size()))
8652 {
8653 // there must be only one root snapshot
8654 Assert(cRootSnapshots == 1);
8655
8656 const settings::Snapshot &snap = config.llFirstSnapshot.front();
8657
8658 rc = i_loadSnapshot(snap,
8659 config.uuidCurrentSnapshot,
8660 NULL); // no parent == first snapshot
8661 if (FAILED(rc)) return rc;
8662 }
8663
8664 // hardware data
8665 rc = i_loadHardware(config.hardwareMachine, &config.debugging, &config.autostart);
8666 if (FAILED(rc)) return rc;
8667
8668 // load storage controllers
8669 rc = i_loadStorageControllers(config.storageMachine,
8670 puuidRegistry,
8671 NULL /* puuidSnapshot */);
8672 if (FAILED(rc)) return rc;
8673
8674 /*
8675 * NOTE: the assignment below must be the last thing to do,
8676 * otherwise it will be not possible to change the settings
8677 * somewhere in the code above because all setters will be
8678 * blocked by i_checkStateDependency(MutableStateDep).
8679 */
8680
8681 /* set the machine state to Aborted or Saved when appropriate */
8682 if (config.fAborted)
8683 {
8684 mSSData->strStateFilePath.setNull();
8685
8686 /* no need to use i_setMachineState() during init() */
8687 mData->mMachineState = MachineState_Aborted;
8688 }
8689 else if (!mSSData->strStateFilePath.isEmpty())
8690 {
8691 /* no need to use i_setMachineState() during init() */
8692 mData->mMachineState = MachineState_Saved;
8693 }
8694
8695 // after loading settings, we are no longer different from the XML on disk
8696 mData->flModifications = 0;
8697
8698 return S_OK;
8699}
8700
8701/**
8702 * Recursively loads all snapshots starting from the given.
8703 *
8704 * @param aNode <Snapshot> node.
8705 * @param aCurSnapshotId Current snapshot ID from the settings file.
8706 * @param aParentSnapshot Parent snapshot.
8707 */
8708HRESULT Machine::i_loadSnapshot(const settings::Snapshot &data,
8709 const Guid &aCurSnapshotId,
8710 Snapshot *aParentSnapshot)
8711{
8712 AssertReturn(!i_isSnapshotMachine(), E_FAIL);
8713 AssertReturn(!i_isSessionMachine(), E_FAIL);
8714
8715 HRESULT rc = S_OK;
8716
8717 Utf8Str strStateFile;
8718 if (!data.strStateFile.isEmpty())
8719 {
8720 /* optional */
8721 strStateFile = data.strStateFile;
8722 int vrc = i_calculateFullPath(strStateFile, strStateFile);
8723 if (RT_FAILURE(vrc))
8724 return setError(E_FAIL,
8725 tr("Invalid saved state file path '%s' (%Rrc)"),
8726 strStateFile.c_str(),
8727 vrc);
8728 }
8729
8730 /* create a snapshot machine object */
8731 ComObjPtr<SnapshotMachine> pSnapshotMachine;
8732 pSnapshotMachine.createObject();
8733 rc = pSnapshotMachine->initFromSettings(this,
8734 data.hardware,
8735 &data.debugging,
8736 &data.autostart,
8737 data.storage,
8738 data.uuid.ref(),
8739 strStateFile);
8740 if (FAILED(rc)) return rc;
8741
8742 /* create a snapshot object */
8743 ComObjPtr<Snapshot> pSnapshot;
8744 pSnapshot.createObject();
8745 /* initialize the snapshot */
8746 rc = pSnapshot->init(mParent, // VirtualBox object
8747 data.uuid,
8748 data.strName,
8749 data.strDescription,
8750 data.timestamp,
8751 pSnapshotMachine,
8752 aParentSnapshot);
8753 if (FAILED(rc)) return rc;
8754
8755 /* memorize the first snapshot if necessary */
8756 if (!mData->mFirstSnapshot)
8757 mData->mFirstSnapshot = pSnapshot;
8758
8759 /* memorize the current snapshot when appropriate */
8760 if ( !mData->mCurrentSnapshot
8761 && pSnapshot->i_getId() == aCurSnapshotId
8762 )
8763 mData->mCurrentSnapshot = pSnapshot;
8764
8765 // now create the children
8766 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin();
8767 it != data.llChildSnapshots.end();
8768 ++it)
8769 {
8770 const settings::Snapshot &childData = *it;
8771 // recurse
8772 rc = i_loadSnapshot(childData,
8773 aCurSnapshotId,
8774 pSnapshot); // parent = the one we created above
8775 if (FAILED(rc)) return rc;
8776 }
8777
8778 return rc;
8779}
8780
8781/**
8782 * Loads settings into mHWData.
8783 *
8784 * @param data Reference to the hardware settings.
8785 * @param pDbg Pointer to the debugging settings.
8786 * @param pAutostart Pointer to the autostart settings.
8787 */
8788HRESULT Machine::i_loadHardware(const settings::Hardware &data, const settings::Debugging *pDbg,
8789 const settings::Autostart *pAutostart)
8790{
8791 AssertReturn(!i_isSessionMachine(), E_FAIL);
8792
8793 HRESULT rc = S_OK;
8794
8795 try
8796 {
8797 /* The hardware version attribute (optional). */
8798 mHWData->mHWVersion = data.strVersion;
8799 mHWData->mHardwareUUID = data.uuid;
8800
8801 mHWData->mHWVirtExEnabled = data.fHardwareVirt;
8802 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging;
8803 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages;
8804 mHWData->mHWVirtExVPIDEnabled = data.fVPID;
8805 mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution;
8806 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce;
8807 mHWData->mPAEEnabled = data.fPAE;
8808 mHWData->mLongMode = data.enmLongMode;
8809 mHWData->mTripleFaultReset = data.fTripleFaultReset;
8810 mHWData->mCPUCount = data.cCPUs;
8811 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug;
8812 mHWData->mCpuExecutionCap = data.ulCpuExecutionCap;
8813 mHWData->mCpuIdPortabilityLevel = data.uCpuIdPortabilityLevel;
8814
8815 // cpu
8816 if (mHWData->mCPUHotPlugEnabled)
8817 {
8818 for (settings::CpuList::const_iterator it = data.llCpus.begin();
8819 it != data.llCpus.end();
8820 ++it)
8821 {
8822 const settings::Cpu &cpu = *it;
8823
8824 mHWData->mCPUAttached[cpu.ulId] = true;
8825 }
8826 }
8827
8828 // cpuid leafs
8829 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin();
8830 it != data.llCpuIdLeafs.end();
8831 ++it)
8832 {
8833 const settings::CpuIdLeaf &leaf = *it;
8834
8835 switch (leaf.ulId)
8836 {
8837 case 0x0:
8838 case 0x1:
8839 case 0x2:
8840 case 0x3:
8841 case 0x4:
8842 case 0x5:
8843 case 0x6:
8844 case 0x7:
8845 case 0x8:
8846 case 0x9:
8847 case 0xA:
8848 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf;
8849 break;
8850
8851 case 0x80000000:
8852 case 0x80000001:
8853 case 0x80000002:
8854 case 0x80000003:
8855 case 0x80000004:
8856 case 0x80000005:
8857 case 0x80000006:
8858 case 0x80000007:
8859 case 0x80000008:
8860 case 0x80000009:
8861 case 0x8000000A:
8862 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf;
8863 break;
8864
8865 default:
8866 /* just ignore */
8867 break;
8868 }
8869 }
8870
8871 mHWData->mMemorySize = data.ulMemorySizeMB;
8872 mHWData->mPageFusionEnabled = data.fPageFusionEnabled;
8873
8874 // boot order
8875 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
8876 {
8877 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i);
8878 if (it == data.mapBootOrder.end())
8879 mHWData->mBootOrder[i] = DeviceType_Null;
8880 else
8881 mHWData->mBootOrder[i] = it->second;
8882 }
8883
8884 mHWData->mGraphicsControllerType = data.graphicsControllerType;
8885 mHWData->mVRAMSize = data.ulVRAMSizeMB;
8886 mHWData->mMonitorCount = data.cMonitors;
8887 mHWData->mAccelerate3DEnabled = data.fAccelerate3D;
8888 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo;
8889 mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes;
8890 mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes;
8891 mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled;
8892 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); ++i)
8893 mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i);
8894 AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8);
8895 mHWData->mVideoCaptureRate = data.ulVideoCaptureRate;
8896 mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS;
8897 if (!data.strVideoCaptureFile.isEmpty())
8898 i_calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile);
8899 else
8900 mHWData->mVideoCaptureFile.setNull();
8901 mHWData->mFirmwareType = data.firmwareType;
8902 mHWData->mPointingHIDType = data.pointingHIDType;
8903 mHWData->mKeyboardHIDType = data.keyboardHIDType;
8904 mHWData->mChipsetType = data.chipsetType;
8905 mHWData->mParavirtProvider = data.paravirtProvider;
8906 mHWData->mEmulatedUSBCardReaderEnabled = data.fEmulatedUSBCardReader;
8907 mHWData->mHPETEnabled = data.fHPETEnabled;
8908
8909 /* VRDEServer */
8910 rc = mVRDEServer->i_loadSettings(data.vrdeSettings);
8911 if (FAILED(rc)) return rc;
8912
8913 /* BIOS */
8914 rc = mBIOSSettings->i_loadSettings(data.biosSettings);
8915 if (FAILED(rc)) return rc;
8916
8917 // Bandwidth control (must come before network adapters)
8918 rc = mBandwidthControl->i_loadSettings(data.ioSettings);
8919 if (FAILED(rc)) return rc;
8920
8921 /* Shared folders */
8922 for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin();
8923 it != data.usbSettings.llUSBControllers.end();
8924 ++it)
8925 {
8926 const settings::USBController &settingsCtrl = *it;
8927 ComObjPtr<USBController> newCtrl;
8928
8929 newCtrl.createObject();
8930 newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType);
8931 mUSBControllers->push_back(newCtrl);
8932 }
8933
8934 /* USB device filters */
8935 rc = mUSBDeviceFilters->i_loadSettings(data.usbSettings);
8936 if (FAILED(rc)) return rc;
8937
8938 // network adapters
8939 size_t newCount = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
8940 size_t oldCount = mNetworkAdapters.size();
8941 if (newCount > oldCount)
8942 {
8943 mNetworkAdapters.resize(newCount);
8944 for (size_t slot = oldCount; slot < mNetworkAdapters.size(); ++slot)
8945 {
8946 unconst(mNetworkAdapters[slot]).createObject();
8947 mNetworkAdapters[slot]->init(this, (ULONG)slot);
8948 }
8949 }
8950 else if (newCount < oldCount)
8951 mNetworkAdapters.resize(newCount);
8952 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin();
8953 it != data.llNetworkAdapters.end();
8954 ++it)
8955 {
8956 const settings::NetworkAdapter &nic = *it;
8957
8958 /* slot unicity is guaranteed by XML Schema */
8959 AssertBreak(nic.ulSlot < mNetworkAdapters.size());
8960 rc = mNetworkAdapters[nic.ulSlot]->i_loadSettings(mBandwidthControl, nic);
8961 if (FAILED(rc)) return rc;
8962 }
8963
8964 // serial ports
8965 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin();
8966 it != data.llSerialPorts.end();
8967 ++it)
8968 {
8969 const settings::SerialPort &s = *it;
8970
8971 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts));
8972 rc = mSerialPorts[s.ulSlot]->i_loadSettings(s);
8973 if (FAILED(rc)) return rc;
8974 }
8975
8976 // parallel ports (optional)
8977 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin();
8978 it != data.llParallelPorts.end();
8979 ++it)
8980 {
8981 const settings::ParallelPort &p = *it;
8982
8983 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts));
8984 rc = mParallelPorts[p.ulSlot]->i_loadSettings(p);
8985 if (FAILED(rc)) return rc;
8986 }
8987
8988 /* AudioAdapter */
8989 rc = mAudioAdapter->i_loadSettings(data.audioAdapter);
8990 if (FAILED(rc)) return rc;
8991
8992 /* Shared folders */
8993 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin();
8994 it != data.llSharedFolders.end();
8995 ++it)
8996 {
8997 const settings::SharedFolder &sf = *it;
8998
8999 ComObjPtr<SharedFolder> sharedFolder;
9000 /* Check for double entries. Not allowed! */
9001 rc = i_findSharedFolder(sf.strName, sharedFolder, false /* aSetError */);
9002 if (SUCCEEDED(rc))
9003 return setError(VBOX_E_OBJECT_IN_USE,
9004 tr("Shared folder named '%s' already exists"),
9005 sf.strName.c_str());
9006
9007 /* Create the new shared folder. Don't break on error. This will be
9008 * reported when the machine starts. */
9009 sharedFolder.createObject();
9010 rc = sharedFolder->init(i_getMachine(),
9011 sf.strName,
9012 sf.strHostPath,
9013 RT_BOOL(sf.fWritable),
9014 RT_BOOL(sf.fAutoMount),
9015 false /* fFailOnError */);
9016 if (FAILED(rc)) return rc;
9017 mHWData->mSharedFolders.push_back(sharedFolder);
9018 }
9019
9020 // Clipboard
9021 mHWData->mClipboardMode = data.clipboardMode;
9022
9023 // drag'n'drop
9024 mHWData->mDnDMode = data.dndMode;
9025
9026 // guest settings
9027 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize;
9028
9029 // IO settings
9030 mHWData->mIOCacheEnabled = data.ioSettings.fIOCacheEnabled;
9031 mHWData->mIOCacheSize = data.ioSettings.ulIOCacheSize;
9032
9033 // Host PCI devices
9034 for (settings::HostPCIDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
9035 it != data.pciAttachments.end();
9036 ++it)
9037 {
9038 const settings::HostPCIDeviceAttachment &hpda = *it;
9039 ComObjPtr<PCIDeviceAttachment> pda;
9040
9041 pda.createObject();
9042 pda->i_loadSettings(this, hpda);
9043 mHWData->mPCIDeviceAssignments.push_back(pda);
9044 }
9045
9046 /*
9047 * (The following isn't really real hardware, but it lives in HWData
9048 * for reasons of convenience.)
9049 */
9050
9051#ifdef VBOX_WITH_GUEST_PROPS
9052 /* Guest properties (optional) */
9053
9054 /* Only load transient guest properties for configs which have saved
9055 * state, because there shouldn't be any for powered off VMs. The same
9056 * logic applies for snapshots, as offline snapshots shouldn't have
9057 * any such properties. They confuse the code in various places.
9058 * Note: can't rely on the machine state, as it isn't set yet. */
9059 bool fSkipTransientGuestProperties = mSSData->strStateFilePath.isEmpty();
9060 /* apologies for the hacky unconst() usage, but this needs hacking
9061 * actually inconsistent settings into consistency, otherwise there
9062 * will be some corner cases where the inconsistency survives
9063 * surprisingly long without getting fixed, especially for snapshots
9064 * as there are no config changes. */
9065 settings::GuestPropertiesList &llGuestProperties = unconst(data.llGuestProperties);
9066 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
9067 it != llGuestProperties.end();
9068 /*nothing*/)
9069 {
9070 const settings::GuestProperty &prop = *it;
9071 uint32_t fFlags = guestProp::NILFLAG;
9072 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags);
9073 if ( fSkipTransientGuestProperties
9074 && ( fFlags & guestProp::TRANSIENT
9075 || fFlags & guestProp::TRANSRESET))
9076 {
9077 it = llGuestProperties.erase(it);
9078 continue;
9079 }
9080 HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags };
9081 mHWData->mGuestProperties[prop.strName] = property;
9082 ++it;
9083 }
9084#endif /* VBOX_WITH_GUEST_PROPS defined */
9085
9086 rc = i_loadDebugging(pDbg);
9087 if (FAILED(rc))
9088 return rc;
9089
9090 mHWData->mAutostart = *pAutostart;
9091
9092 /* default frontend */
9093 mHWData->mDefaultFrontend = data.strDefaultFrontend;
9094 }
9095 catch(std::bad_alloc &)
9096 {
9097 return E_OUTOFMEMORY;
9098 }
9099
9100 AssertComRC(rc);
9101 return rc;
9102}
9103
9104/**
9105 * Called from Machine::loadHardware() to load the debugging settings of the
9106 * machine.
9107 *
9108 * @param pDbg Pointer to the settings.
9109 */
9110HRESULT Machine::i_loadDebugging(const settings::Debugging *pDbg)
9111{
9112 mHWData->mDebugging = *pDbg;
9113 /* no more processing currently required, this will probably change. */
9114 return S_OK;
9115}
9116
9117/**
9118 * Called from i_loadMachineDataFromSettings() for the storage controller data, including media.
9119 *
9120 * @param data
9121 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9122 * @param puuidSnapshot
9123 * @return
9124 */
9125HRESULT Machine::i_loadStorageControllers(const settings::Storage &data,
9126 const Guid *puuidRegistry,
9127 const Guid *puuidSnapshot)
9128{
9129 AssertReturn(!i_isSessionMachine(), E_FAIL);
9130
9131 HRESULT rc = S_OK;
9132
9133 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin();
9134 it != data.llStorageControllers.end();
9135 ++it)
9136 {
9137 const settings::StorageController &ctlData = *it;
9138
9139 ComObjPtr<StorageController> pCtl;
9140 /* Try to find one with the name first. */
9141 rc = i_getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */);
9142 if (SUCCEEDED(rc))
9143 return setError(VBOX_E_OBJECT_IN_USE,
9144 tr("Storage controller named '%s' already exists"),
9145 ctlData.strName.c_str());
9146
9147 pCtl.createObject();
9148 rc = pCtl->init(this,
9149 ctlData.strName,
9150 ctlData.storageBus,
9151 ctlData.ulInstance,
9152 ctlData.fBootable);
9153 if (FAILED(rc)) return rc;
9154
9155 mStorageControllers->push_back(pCtl);
9156
9157 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType);
9158 if (FAILED(rc)) return rc;
9159
9160 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount);
9161 if (FAILED(rc)) return rc;
9162
9163 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache);
9164 if (FAILED(rc)) return rc;
9165
9166 /* Load the attached devices now. */
9167 rc = i_loadStorageDevices(pCtl,
9168 ctlData,
9169 puuidRegistry,
9170 puuidSnapshot);
9171 if (FAILED(rc)) return rc;
9172 }
9173
9174 return S_OK;
9175}
9176
9177/**
9178 * Called from i_loadStorageControllers for a controller's devices.
9179 *
9180 * @param aStorageController
9181 * @param data
9182 * @param puuidRegistry media registry ID to set media to or NULL; see Machine::i_loadMachineDataFromSettings()
9183 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine
9184 * @return
9185 */
9186HRESULT Machine::i_loadStorageDevices(StorageController *aStorageController,
9187 const settings::StorageController &data,
9188 const Guid *puuidRegistry,
9189 const Guid *puuidSnapshot)
9190{
9191 HRESULT rc = S_OK;
9192
9193 /* paranoia: detect duplicate attachments */
9194 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9195 it != data.llAttachedDevices.end();
9196 ++it)
9197 {
9198 const settings::AttachedDevice &ad = *it;
9199
9200 for (settings::AttachedDevicesList::const_iterator it2 = it;
9201 it2 != data.llAttachedDevices.end();
9202 ++it2)
9203 {
9204 if (it == it2)
9205 continue;
9206
9207 const settings::AttachedDevice &ad2 = *it2;
9208
9209 if ( ad.lPort == ad2.lPort
9210 && ad.lDevice == ad2.lDevice)
9211 {
9212 return setError(E_FAIL,
9213 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"),
9214 aStorageController->i_getName().c_str(),
9215 ad.lPort,
9216 ad.lDevice,
9217 mUserData->s.strName.c_str());
9218 }
9219 }
9220 }
9221
9222 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin();
9223 it != data.llAttachedDevices.end();
9224 ++it)
9225 {
9226 const settings::AttachedDevice &dev = *it;
9227 ComObjPtr<Medium> medium;
9228
9229 switch (dev.deviceType)
9230 {
9231 case DeviceType_Floppy:
9232 case DeviceType_DVD:
9233 if (dev.strHostDriveSrc.isNotEmpty())
9234 rc = mParent->i_host()->i_findHostDriveByName(dev.deviceType, dev.strHostDriveSrc,
9235 false /* fRefresh */, medium);
9236 else
9237 rc = mParent->i_findRemoveableMedium(dev.deviceType,
9238 dev.uuid,
9239 false /* fRefresh */,
9240 false /* aSetError */,
9241 medium);
9242 if (rc == VBOX_E_OBJECT_NOT_FOUND)
9243 // This is not an error. The host drive or UUID might have vanished, so just go
9244 // ahead without this removeable medium attachment
9245 rc = S_OK;
9246 break;
9247
9248 case DeviceType_HardDisk:
9249 {
9250 /* find a hard disk by UUID */
9251 rc = mParent->i_findHardDiskById(dev.uuid, true /* aDoSetError */, &medium);
9252 if (FAILED(rc))
9253 {
9254 if (i_isSnapshotMachine())
9255 {
9256 // wrap another error message around the "cannot find hard disk" set by findHardDisk
9257 // so the user knows that the bad disk is in a snapshot somewhere
9258 com::ErrorInfo info;
9259 return setError(E_FAIL,
9260 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"),
9261 puuidSnapshot->raw(),
9262 info.getText().raw());
9263 }
9264 else
9265 return rc;
9266 }
9267
9268 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS);
9269
9270 if (medium->i_getType() == MediumType_Immutable)
9271 {
9272 if (i_isSnapshotMachine())
9273 return setError(E_FAIL,
9274 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9275 "of the virtual machine '%s' ('%s')"),
9276 medium->i_getLocationFull().c_str(),
9277 dev.uuid.raw(),
9278 puuidSnapshot->raw(),
9279 mUserData->s.strName.c_str(),
9280 mData->m_strConfigFileFull.c_str());
9281
9282 return setError(E_FAIL,
9283 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9284 medium->i_getLocationFull().c_str(),
9285 dev.uuid.raw(),
9286 mUserData->s.strName.c_str(),
9287 mData->m_strConfigFileFull.c_str());
9288 }
9289
9290 if (medium->i_getType() == MediumType_MultiAttach)
9291 {
9292 if (i_isSnapshotMachine())
9293 return setError(E_FAIL,
9294 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} "
9295 "of the virtual machine '%s' ('%s')"),
9296 medium->i_getLocationFull().c_str(),
9297 dev.uuid.raw(),
9298 puuidSnapshot->raw(),
9299 mUserData->s.strName.c_str(),
9300 mData->m_strConfigFileFull.c_str());
9301
9302 return setError(E_FAIL,
9303 tr("Multi-attach hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"),
9304 medium->i_getLocationFull().c_str(),
9305 dev.uuid.raw(),
9306 mUserData->s.strName.c_str(),
9307 mData->m_strConfigFileFull.c_str());
9308 }
9309
9310 if ( !i_isSnapshotMachine()
9311 && medium->i_getChildren().size() != 0
9312 )
9313 return setError(E_FAIL,
9314 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') "
9315 "because it has %d differencing child hard disks"),
9316 medium->i_getLocationFull().c_str(),
9317 dev.uuid.raw(),
9318 mUserData->s.strName.c_str(),
9319 mData->m_strConfigFileFull.c_str(),
9320 medium->i_getChildren().size());
9321
9322 if (i_findAttachment(mMediaData->mAttachments,
9323 medium))
9324 return setError(E_FAIL,
9325 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"),
9326 medium->i_getLocationFull().c_str(),
9327 dev.uuid.raw(),
9328 mUserData->s.strName.c_str(),
9329 mData->m_strConfigFileFull.c_str());
9330
9331 break;
9332 }
9333
9334 default:
9335 return setError(E_FAIL,
9336 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"),
9337 medium->i_getLocationFull().c_str(),
9338 mUserData->s.strName.c_str(),
9339 mData->m_strConfigFileFull.c_str());
9340 }
9341
9342 if (FAILED(rc))
9343 break;
9344
9345 /* Bandwidth groups are loaded at this point. */
9346 ComObjPtr<BandwidthGroup> pBwGroup;
9347
9348 if (!dev.strBwGroup.isEmpty())
9349 {
9350 rc = mBandwidthControl->i_getBandwidthGroupByName(dev.strBwGroup, pBwGroup, false /* aSetError */);
9351 if (FAILED(rc))
9352 return setError(E_FAIL,
9353 tr("Device '%s' with unknown bandwidth group '%s' is attached to the virtual machine '%s' ('%s')"),
9354 medium->i_getLocationFull().c_str(),
9355 dev.strBwGroup.c_str(),
9356 mUserData->s.strName.c_str(),
9357 mData->m_strConfigFileFull.c_str());
9358 pBwGroup->i_reference();
9359 }
9360
9361 const Bstr controllerName = aStorageController->i_getName();
9362 ComObjPtr<MediumAttachment> pAttachment;
9363 pAttachment.createObject();
9364 rc = pAttachment->init(this,
9365 medium,
9366 controllerName,
9367 dev.lPort,
9368 dev.lDevice,
9369 dev.deviceType,
9370 false,
9371 dev.fPassThrough,
9372 dev.fTempEject,
9373 dev.fNonRotational,
9374 dev.fDiscard,
9375 dev.fHotPluggable,
9376 pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->i_getName());
9377 if (FAILED(rc)) break;
9378
9379 /* associate the medium with this machine and snapshot */
9380 if (!medium.isNull())
9381 {
9382 AutoCaller medCaller(medium);
9383 if (FAILED(medCaller.rc())) return medCaller.rc();
9384 AutoWriteLock mlock(medium COMMA_LOCKVAL_SRC_POS);
9385
9386 if (i_isSnapshotMachine())
9387 rc = medium->i_addBackReference(mData->mUuid, *puuidSnapshot);
9388 else
9389 rc = medium->i_addBackReference(mData->mUuid);
9390 /* If the medium->addBackReference fails it sets an appropriate
9391 * error message, so no need to do any guesswork here. */
9392
9393 if (puuidRegistry)
9394 // caller wants registry ID to be set on all attached media (OVF import case)
9395 medium->i_addRegistry(*puuidRegistry);
9396 }
9397
9398 if (FAILED(rc))
9399 break;
9400
9401 /* back up mMediaData to let registeredInit() properly rollback on failure
9402 * (= limited accessibility) */
9403 i_setModified(IsModified_Storage);
9404 mMediaData.backup();
9405 mMediaData->mAttachments.push_back(pAttachment);
9406 }
9407
9408 return rc;
9409}
9410
9411/**
9412 * Returns the snapshot with the given UUID or fails of no such snapshot exists.
9413 *
9414 * @param aId snapshot UUID to find (empty UUID refers the first snapshot)
9415 * @param aSnapshot where to return the found snapshot
9416 * @param aSetError true to set extended error info on failure
9417 */
9418HRESULT Machine::i_findSnapshotById(const Guid &aId,
9419 ComObjPtr<Snapshot> &aSnapshot,
9420 bool aSetError /* = false */)
9421{
9422 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9423
9424 if (!mData->mFirstSnapshot)
9425 {
9426 if (aSetError)
9427 return setError(E_FAIL, tr("This machine does not have any snapshots"));
9428 return E_FAIL;
9429 }
9430
9431 if (aId.isZero())
9432 aSnapshot = mData->mFirstSnapshot;
9433 else
9434 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(aId.ref());
9435
9436 if (!aSnapshot)
9437 {
9438 if (aSetError)
9439 return setError(E_FAIL,
9440 tr("Could not find a snapshot with UUID {%s}"),
9441 aId.toString().c_str());
9442 return E_FAIL;
9443 }
9444
9445 return S_OK;
9446}
9447
9448/**
9449 * Returns the snapshot with the given name or fails of no such snapshot.
9450 *
9451 * @param aName snapshot name to find
9452 * @param aSnapshot where to return the found snapshot
9453 * @param aSetError true to set extended error info on failure
9454 */
9455HRESULT Machine::i_findSnapshotByName(const Utf8Str &strName,
9456 ComObjPtr<Snapshot> &aSnapshot,
9457 bool aSetError /* = false */)
9458{
9459 AssertReturn(!strName.isEmpty(), E_INVALIDARG);
9460
9461 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS);
9462
9463 if (!mData->mFirstSnapshot)
9464 {
9465 if (aSetError)
9466 return setError(VBOX_E_OBJECT_NOT_FOUND,
9467 tr("This machine does not have any snapshots"));
9468 return VBOX_E_OBJECT_NOT_FOUND;
9469 }
9470
9471 aSnapshot = mData->mFirstSnapshot->i_findChildOrSelf(strName);
9472
9473 if (!aSnapshot)
9474 {
9475 if (aSetError)
9476 return setError(VBOX_E_OBJECT_NOT_FOUND,
9477 tr("Could not find a snapshot named '%s'"), strName.c_str());
9478 return VBOX_E_OBJECT_NOT_FOUND;
9479 }
9480
9481 return S_OK;
9482}
9483
9484/**
9485 * Returns a storage controller object with the given name.
9486 *
9487 * @param aName storage controller name to find
9488 * @param aStorageController where to return the found storage controller
9489 * @param aSetError true to set extended error info on failure
9490 */
9491HRESULT Machine::i_getStorageControllerByName(const Utf8Str &aName,
9492 ComObjPtr<StorageController> &aStorageController,
9493 bool aSetError /* = false */)
9494{
9495 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9496
9497 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
9498 it != mStorageControllers->end();
9499 ++it)
9500 {
9501 if ((*it)->i_getName() == aName)
9502 {
9503 aStorageController = (*it);
9504 return S_OK;
9505 }
9506 }
9507
9508 if (aSetError)
9509 return setError(VBOX_E_OBJECT_NOT_FOUND,
9510 tr("Could not find a storage controller named '%s'"),
9511 aName.c_str());
9512 return VBOX_E_OBJECT_NOT_FOUND;
9513}
9514
9515/**
9516 * Returns a USB controller object with the given name.
9517 *
9518 * @param aName USB controller name to find
9519 * @param aUSBController where to return the found USB controller
9520 * @param aSetError true to set extended error info on failure
9521 */
9522HRESULT Machine::i_getUSBControllerByName(const Utf8Str &aName,
9523 ComObjPtr<USBController> &aUSBController,
9524 bool aSetError /* = false */)
9525{
9526 AssertReturn(!aName.isEmpty(), E_INVALIDARG);
9527
9528 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9529 it != mUSBControllers->end();
9530 ++it)
9531 {
9532 if ((*it)->i_getName() == aName)
9533 {
9534 aUSBController = (*it);
9535 return S_OK;
9536 }
9537 }
9538
9539 if (aSetError)
9540 return setError(VBOX_E_OBJECT_NOT_FOUND,
9541 tr("Could not find a storage controller named '%s'"),
9542 aName.c_str());
9543 return VBOX_E_OBJECT_NOT_FOUND;
9544}
9545
9546/**
9547 * Returns the number of USB controller instance of the given type.
9548 *
9549 * @param enmType USB controller type.
9550 */
9551ULONG Machine::i_getUSBControllerCountByType(USBControllerType_T enmType)
9552{
9553 ULONG cCtrls = 0;
9554
9555 for (USBControllerList::const_iterator it = mUSBControllers->begin();
9556 it != mUSBControllers->end();
9557 ++it)
9558 {
9559 if ((*it)->i_getControllerType() == enmType)
9560 cCtrls++;
9561 }
9562
9563 return cCtrls;
9564}
9565
9566HRESULT Machine::i_getMediumAttachmentsOfController(const Utf8Str &aName,
9567 MediaData::AttachmentList &atts)
9568{
9569 AutoCaller autoCaller(this);
9570 if (FAILED(autoCaller.rc())) return autoCaller.rc();
9571
9572 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
9573
9574 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin();
9575 it != mMediaData->mAttachments.end();
9576 ++it)
9577 {
9578 const ComObjPtr<MediumAttachment> &pAtt = *it;
9579 // should never happen, but deal with NULL pointers in the list.
9580 AssertStmt(!pAtt.isNull(), continue);
9581
9582 // getControllerName() needs caller+read lock
9583 AutoCaller autoAttCaller(pAtt);
9584 if (FAILED(autoAttCaller.rc()))
9585 {
9586 atts.clear();
9587 return autoAttCaller.rc();
9588 }
9589 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS);
9590
9591 if (pAtt->i_getControllerName() == aName)
9592 atts.push_back(pAtt);
9593 }
9594
9595 return S_OK;
9596}
9597
9598
9599/**
9600 * Helper for #i_saveSettings. Cares about renaming the settings directory and
9601 * file if the machine name was changed and about creating a new settings file
9602 * if this is a new machine.
9603 *
9604 * @note Must be never called directly but only from #saveSettings().
9605 */
9606HRESULT Machine::i_prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
9607{
9608 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9609
9610 HRESULT rc = S_OK;
9611
9612 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists();
9613
9614 /// @todo need to handle primary group change, too
9615
9616 /* attempt to rename the settings file if machine name is changed */
9617 if ( mUserData->s.fNameSync
9618 && mUserData.isBackedUp()
9619 && ( mUserData.backedUpData()->s.strName != mUserData->s.strName
9620 || mUserData.backedUpData()->s.llGroups.front() != mUserData->s.llGroups.front())
9621 )
9622 {
9623 bool dirRenamed = false;
9624 bool fileRenamed = false;
9625
9626 Utf8Str configFile, newConfigFile;
9627 Utf8Str configFilePrev, newConfigFilePrev;
9628 Utf8Str configDir, newConfigDir;
9629
9630 do
9631 {
9632 int vrc = VINF_SUCCESS;
9633
9634 Utf8Str name = mUserData.backedUpData()->s.strName;
9635 Utf8Str newName = mUserData->s.strName;
9636 Utf8Str group = mUserData.backedUpData()->s.llGroups.front();
9637 if (group == "/")
9638 group.setNull();
9639 Utf8Str newGroup = mUserData->s.llGroups.front();
9640 if (newGroup == "/")
9641 newGroup.setNull();
9642
9643 configFile = mData->m_strConfigFileFull;
9644
9645 /* first, rename the directory if it matches the group and machine name */
9646 Utf8Str groupPlusName = Utf8StrFmt("%s%c%s",
9647 group.c_str(), RTPATH_DELIMITER, name.c_str());
9648 /** @todo hack, make somehow use of ComposeMachineFilename */
9649 if (mUserData->s.fDirectoryIncludesUUID)
9650 groupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9651 Utf8Str newGroupPlusName = Utf8StrFmt("%s%c%s",
9652 newGroup.c_str(), RTPATH_DELIMITER, newName.c_str());
9653 /** @todo hack, make somehow use of ComposeMachineFilename */
9654 if (mUserData->s.fDirectoryIncludesUUID)
9655 newGroupPlusName += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
9656 configDir = configFile;
9657 configDir.stripFilename();
9658 newConfigDir = configDir;
9659 if ( configDir.length() >= groupPlusName.length()
9660 && !RTPathCompare(configDir.substr(configDir.length() - groupPlusName.length(), groupPlusName.length()).c_str(),
9661 groupPlusName.c_str()))
9662 {
9663 newConfigDir = newConfigDir.substr(0, configDir.length() - groupPlusName.length());
9664 Utf8Str newConfigBaseDir(newConfigDir);
9665 newConfigDir.append(newGroupPlusName);
9666 /* consistency: use \ if appropriate on the platform */
9667 RTPathChangeToDosSlashes(newConfigDir.mutableRaw(), false);
9668 /* new dir and old dir cannot be equal here because of 'if'
9669 * above and because name != newName */
9670 Assert(configDir != newConfigDir);
9671 if (!fSettingsFileIsNew)
9672 {
9673 /* perform real rename only if the machine is not new */
9674 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9675 if ( vrc == VERR_FILE_NOT_FOUND
9676 || vrc == VERR_PATH_NOT_FOUND)
9677 {
9678 /* create the parent directory, then retry renaming */
9679 Utf8Str parent(newConfigDir);
9680 parent.stripFilename();
9681 (void)RTDirCreateFullPath(parent.c_str(), 0700);
9682 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0);
9683 }
9684 if (RT_FAILURE(vrc))
9685 {
9686 rc = setError(E_FAIL,
9687 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"),
9688 configDir.c_str(),
9689 newConfigDir.c_str(),
9690 vrc);
9691 break;
9692 }
9693 /* delete subdirectories which are no longer needed */
9694 Utf8Str dir(configDir);
9695 dir.stripFilename();
9696 while (dir != newConfigBaseDir && dir != ".")
9697 {
9698 vrc = RTDirRemove(dir.c_str());
9699 if (RT_FAILURE(vrc))
9700 break;
9701 dir.stripFilename();
9702 }
9703 dirRenamed = true;
9704 }
9705 }
9706
9707 newConfigFile = Utf8StrFmt("%s%c%s.vbox",
9708 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str());
9709
9710 /* then try to rename the settings file itself */
9711 if (newConfigFile != configFile)
9712 {
9713 /* get the path to old settings file in renamed directory */
9714 configFile = Utf8StrFmt("%s%c%s",
9715 newConfigDir.c_str(),
9716 RTPATH_DELIMITER,
9717 RTPathFilename(configFile.c_str()));
9718 if (!fSettingsFileIsNew)
9719 {
9720 /* perform real rename only if the machine is not new */
9721 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0);
9722 if (RT_FAILURE(vrc))
9723 {
9724 rc = setError(E_FAIL,
9725 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"),
9726 configFile.c_str(),
9727 newConfigFile.c_str(),
9728 vrc);
9729 break;
9730 }
9731 fileRenamed = true;
9732 configFilePrev = configFile;
9733 configFilePrev += "-prev";
9734 newConfigFilePrev = newConfigFile;
9735 newConfigFilePrev += "-prev";
9736 RTFileRename(configFilePrev.c_str(), newConfigFilePrev.c_str(), 0);
9737 }
9738 }
9739
9740 // update m_strConfigFileFull amd mConfigFile
9741 mData->m_strConfigFileFull = newConfigFile;
9742 // compute the relative path too
9743 mParent->i_copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile);
9744
9745 // store the old and new so that VirtualBox::i_saveSettings() can update
9746 // the media registry
9747 if ( mData->mRegistered
9748 && (configDir != newConfigDir || configFile != newConfigFile))
9749 {
9750 mParent->i_rememberMachineNameChangeForMedia(configDir, newConfigDir);
9751
9752 if (pfNeedsGlobalSaveSettings)
9753 *pfNeedsGlobalSaveSettings = true;
9754 }
9755
9756 // in the saved state file path, replace the old directory with the new directory
9757 if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
9758 mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
9759
9760 // and do the same thing for the saved state file paths of all the online snapshots
9761 if (mData->mFirstSnapshot)
9762 mData->mFirstSnapshot->i_updateSavedStatePaths(configDir.c_str(),
9763 newConfigDir.c_str());
9764 }
9765 while (0);
9766
9767 if (FAILED(rc))
9768 {
9769 /* silently try to rename everything back */
9770 if (fileRenamed)
9771 {
9772 RTFileRename(newConfigFilePrev.c_str(), configFilePrev.c_str(), 0);
9773 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0);
9774 }
9775 if (dirRenamed)
9776 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0);
9777 }
9778
9779 if (FAILED(rc)) return rc;
9780 }
9781
9782 if (fSettingsFileIsNew)
9783 {
9784 /* create a virgin config file */
9785 int vrc = VINF_SUCCESS;
9786
9787 /* ensure the settings directory exists */
9788 Utf8Str path(mData->m_strConfigFileFull);
9789 path.stripFilename();
9790 if (!RTDirExists(path.c_str()))
9791 {
9792 vrc = RTDirCreateFullPath(path.c_str(), 0700);
9793 if (RT_FAILURE(vrc))
9794 {
9795 return setError(E_FAIL,
9796 tr("Could not create a directory '%s' to save the settings file (%Rrc)"),
9797 path.c_str(),
9798 vrc);
9799 }
9800 }
9801
9802 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */
9803 path = Utf8Str(mData->m_strConfigFileFull);
9804 RTFILE f = NIL_RTFILE;
9805 vrc = RTFileOpen(&f, path.c_str(),
9806 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE);
9807 if (RT_FAILURE(vrc))
9808 return setError(E_FAIL,
9809 tr("Could not create the settings file '%s' (%Rrc)"),
9810 path.c_str(),
9811 vrc);
9812 RTFileClose(f);
9813 }
9814
9815 return rc;
9816}
9817
9818/**
9819 * Saves and commits machine data, user data and hardware data.
9820 *
9821 * Note that on failure, the data remains uncommitted.
9822 *
9823 * @a aFlags may combine the following flags:
9824 *
9825 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE.
9826 * Used when saving settings after an operation that makes them 100%
9827 * correspond to the settings from the current snapshot.
9828 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if
9829 * #isReallyModified() returns false. This is necessary for cases when we
9830 * change machine data directly, not through the backup()/commit() mechanism.
9831 * - SaveS_Force: settings will be saved without doing a deep compare of the
9832 * settings structures. This is used when this is called because snapshots
9833 * have changed to avoid the overhead of the deep compare.
9834 *
9835 * @note Must be called from under this object's write lock. Locks children for
9836 * writing.
9837 *
9838 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been
9839 * initialized to false and that will be set to true by this function if
9840 * the caller must invoke VirtualBox::i_saveSettings() because the global
9841 * settings have changed. This will happen if a machine rename has been
9842 * saved and the global machine and media registries will therefore need
9843 * updating.
9844 */
9845HRESULT Machine::i_saveSettings(bool *pfNeedsGlobalSaveSettings,
9846 int aFlags /*= 0*/)
9847{
9848 LogFlowThisFuncEnter();
9849
9850 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
9851
9852 /* make sure child objects are unable to modify the settings while we are
9853 * saving them */
9854 i_ensureNoStateDependencies();
9855
9856 AssertReturn(!i_isSnapshotMachine(),
9857 E_FAIL);
9858
9859 HRESULT rc = S_OK;
9860 bool fNeedsWrite = false;
9861
9862 /* First, prepare to save settings. It will care about renaming the
9863 * settings directory and file if the machine name was changed and about
9864 * creating a new settings file if this is a new machine. */
9865 rc = i_prepareSaveSettings(pfNeedsGlobalSaveSettings);
9866 if (FAILED(rc)) return rc;
9867
9868 // keep a pointer to the current settings structures
9869 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile;
9870 settings::MachineConfigFile *pNewConfig = NULL;
9871
9872 try
9873 {
9874 // make a fresh one to have everyone write stuff into
9875 pNewConfig = new settings::MachineConfigFile(NULL);
9876 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile);
9877
9878 // now go and copy all the settings data from COM to the settings structures
9879 // (this calles i_saveSettings() on all the COM objects in the machine)
9880 i_copyMachineDataToSettings(*pNewConfig);
9881
9882 if (aFlags & SaveS_ResetCurStateModified)
9883 {
9884 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot()
9885 mData->mCurrentStateModified = FALSE;
9886 fNeedsWrite = true; // always, no need to compare
9887 }
9888 else if (aFlags & SaveS_Force)
9889 {
9890 fNeedsWrite = true; // always, no need to compare
9891 }
9892 else
9893 {
9894 if (!mData->mCurrentStateModified)
9895 {
9896 // do a deep compare of the settings that we just saved with the settings
9897 // previously stored in the config file; this invokes MachineConfigFile::operator==
9898 // which does a deep compare of all the settings, which is expensive but less expensive
9899 // than writing out XML in vain
9900 bool fAnySettingsChanged = !(*pNewConfig == *pOldConfig);
9901
9902 // could still be modified if any settings changed
9903 mData->mCurrentStateModified = fAnySettingsChanged;
9904
9905 fNeedsWrite = fAnySettingsChanged;
9906 }
9907 else
9908 fNeedsWrite = true;
9909 }
9910
9911 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified;
9912
9913 if (fNeedsWrite)
9914 // now spit it all out!
9915 pNewConfig->write(mData->m_strConfigFileFull);
9916
9917 mData->pMachineConfigFile = pNewConfig;
9918 delete pOldConfig;
9919 i_commit();
9920
9921 // after saving settings, we are no longer different from the XML on disk
9922 mData->flModifications = 0;
9923 }
9924 catch (HRESULT err)
9925 {
9926 // we assume that error info is set by the thrower
9927 rc = err;
9928
9929 // restore old config
9930 delete pNewConfig;
9931 mData->pMachineConfigFile = pOldConfig;
9932 }
9933 catch (...)
9934 {
9935 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
9936 }
9937
9938 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway))
9939 {
9940 /* Fire the data change event, even on failure (since we've already
9941 * committed all data). This is done only for SessionMachines because
9942 * mutable Machine instances are always not registered (i.e. private
9943 * to the client process that creates them) and thus don't need to
9944 * inform callbacks. */
9945 if (i_isSessionMachine())
9946 mParent->i_onMachineDataChange(mData->mUuid);
9947 }
9948
9949 LogFlowThisFunc(("rc=%08X\n", rc));
9950 LogFlowThisFuncLeave();
9951 return rc;
9952}
9953
9954/**
9955 * Implementation for saving the machine settings into the given
9956 * settings::MachineConfigFile instance. This copies machine extradata
9957 * from the previous machine config file in the instance data, if any.
9958 *
9959 * This gets called from two locations:
9960 *
9961 * -- Machine::i_saveSettings(), during the regular XML writing;
9962 *
9963 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets
9964 * exported to OVF and we write the VirtualBox proprietary XML
9965 * into a <vbox:Machine> tag.
9966 *
9967 * This routine fills all the fields in there, including snapshots, *except*
9968 * for the following:
9969 *
9970 * -- fCurrentStateModified. There is some special logic associated with that.
9971 *
9972 * The caller can then call MachineConfigFile::write() or do something else
9973 * with it.
9974 *
9975 * Caller must hold the machine lock!
9976 *
9977 * This throws XML errors and HRESULT, so the caller must have a catch block!
9978 */
9979void Machine::i_copyMachineDataToSettings(settings::MachineConfigFile &config)
9980{
9981 // deep copy extradata
9982 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems;
9983
9984 config.uuid = mData->mUuid;
9985
9986 // copy name, description, OS type, teleport, UTC etc.
9987 config.machineUserData = mUserData->s;
9988
9989 // Encode the Icon Override data from Machine and store on config userdata.
9990 std::vector<BYTE> iconByte;
9991 getIcon(iconByte);
9992 ssize_t cbData = iconByte.size();
9993 if (cbData > 0)
9994 {
9995 ssize_t cchOut = RTBase64EncodedLength(cbData);
9996 Utf8Str strIconData;
9997 strIconData.reserve(cchOut+1);
9998 int vrc = RTBase64Encode(&iconByte.front(), cbData,
9999 strIconData.mutableRaw(), strIconData.capacity(),
10000 NULL);
10001 if (RT_FAILURE(vrc))
10002 throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc);
10003 strIconData.jolt();
10004 config.machineUserData.ovIcon = strIconData;
10005 }
10006 else
10007 config.machineUserData.ovIcon.setNull();
10008
10009 if ( mData->mMachineState == MachineState_Saved
10010 || mData->mMachineState == MachineState_Restoring
10011 // when doing certain snapshot operations we may or may not have
10012 // a saved state in the current state, so keep everything as is
10013 || ( ( mData->mMachineState == MachineState_Snapshotting
10014 || mData->mMachineState == MachineState_DeletingSnapshot
10015 || mData->mMachineState == MachineState_RestoringSnapshot)
10016 && (!mSSData->strStateFilePath.isEmpty())
10017 )
10018 )
10019 {
10020 Assert(!mSSData->strStateFilePath.isEmpty());
10021 /* try to make the file name relative to the settings file dir */
10022 i_copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
10023 }
10024 else
10025 {
10026 Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
10027 config.strStateFile.setNull();
10028 }
10029
10030 if (mData->mCurrentSnapshot)
10031 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->i_getId();
10032 else
10033 config.uuidCurrentSnapshot.clear();
10034
10035 config.timeLastStateChange = mData->mLastStateChange;
10036 config.fAborted = (mData->mMachineState == MachineState_Aborted);
10037 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported);
10038
10039 HRESULT rc = i_saveHardware(config.hardwareMachine, &config.debugging, &config.autostart);
10040 if (FAILED(rc)) throw rc;
10041
10042 rc = i_saveStorageControllers(config.storageMachine);
10043 if (FAILED(rc)) throw rc;
10044
10045 // save machine's media registry if this is VirtualBox 4.0 or later
10046 if (config.canHaveOwnMediaRegistry())
10047 {
10048 // determine machine folder
10049 Utf8Str strMachineFolder = i_getSettingsFileFull();
10050 strMachineFolder.stripFilename();
10051 mParent->i_saveMediaRegistry(config.mediaRegistry,
10052 i_getId(), // only media with registry ID == machine UUID
10053 strMachineFolder);
10054 // this throws HRESULT
10055 }
10056
10057 // save snapshots
10058 rc = i_saveAllSnapshots(config);
10059 if (FAILED(rc)) throw rc;
10060}
10061
10062/**
10063 * Saves all snapshots of the machine into the given machine config file. Called
10064 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler().
10065 * @param config
10066 * @return
10067 */
10068HRESULT Machine::i_saveAllSnapshots(settings::MachineConfigFile &config)
10069{
10070 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL);
10071
10072 HRESULT rc = S_OK;
10073
10074 try
10075 {
10076 config.llFirstSnapshot.clear();
10077
10078 if (mData->mFirstSnapshot)
10079 {
10080 // the settings use a list for "the first snapshot"
10081 config.llFirstSnapshot.push_back(settings::g_SnapshotEmpty);
10082
10083 // get reference to the snapshot on the list and work on that
10084 // element straight in the list to avoid excessive copying later
10085 rc = mData->mFirstSnapshot->i_saveSnapshot(config.llFirstSnapshot.back());
10086 if (FAILED(rc)) throw rc;
10087 }
10088
10089// if (mType == IsSessionMachine)
10090// mParent->onMachineDataChange(mData->mUuid); @todo is this necessary?
10091
10092 }
10093 catch (HRESULT err)
10094 {
10095 /* we assume that error info is set by the thrower */
10096 rc = err;
10097 }
10098 catch (...)
10099 {
10100 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10101 }
10102
10103 return rc;
10104}
10105
10106/**
10107 * Saves the VM hardware configuration. It is assumed that the
10108 * given node is empty.
10109 *
10110 * @param data Reference to the settings object for the hardware config.
10111 * @param pDbg Pointer to the settings object for the debugging config
10112 * which happens to live in mHWData.
10113 * @param pAutostart Pointer to the settings object for the autostart config
10114 * which happens to live in mHWData.
10115 */
10116HRESULT Machine::i_saveHardware(settings::Hardware &data, settings::Debugging *pDbg,
10117 settings::Autostart *pAutostart)
10118{
10119 HRESULT rc = S_OK;
10120
10121 try
10122 {
10123 /* The hardware version attribute (optional).
10124 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
10125 if ( mHWData->mHWVersion == "1"
10126 && mSSData->strStateFilePath.isEmpty()
10127 )
10128 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some
10129 other point needs to be found where this can be done. */
10130
10131 data.strVersion = mHWData->mHWVersion;
10132 data.uuid = mHWData->mHardwareUUID;
10133
10134 // CPU
10135 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled;
10136 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled;
10137 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled;
10138 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled;
10139 data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled;
10140 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled;
10141 data.fPAE = !!mHWData->mPAEEnabled;
10142 data.enmLongMode = mHWData->mLongMode;
10143 data.fTripleFaultReset = !!mHWData->mTripleFaultReset;
10144 data.cCPUs = mHWData->mCPUCount;
10145 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled;
10146 data.ulCpuExecutionCap = mHWData->mCpuExecutionCap;
10147 data.uCpuIdPortabilityLevel = mHWData->mCpuIdPortabilityLevel;
10148
10149 data.llCpus.clear();
10150 if (data.fCpuHotPlug)
10151 {
10152 for (unsigned idx = 0; idx < data.cCPUs; ++idx)
10153 {
10154 if (mHWData->mCPUAttached[idx])
10155 {
10156 settings::Cpu cpu;
10157 cpu.ulId = idx;
10158 data.llCpus.push_back(cpu);
10159 }
10160 }
10161 }
10162
10163 /* Standard and Extended CPUID leafs. */
10164 data.llCpuIdLeafs.clear();
10165 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); ++idx)
10166 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX)
10167 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]);
10168 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); ++idx)
10169 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX)
10170 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]);
10171
10172 // memory
10173 data.ulMemorySizeMB = mHWData->mMemorySize;
10174 data.fPageFusionEnabled = !!mHWData->mPageFusionEnabled;
10175
10176 // firmware
10177 data.firmwareType = mHWData->mFirmwareType;
10178
10179 // HID
10180 data.pointingHIDType = mHWData->mPointingHIDType;
10181 data.keyboardHIDType = mHWData->mKeyboardHIDType;
10182
10183 // chipset
10184 data.chipsetType = mHWData->mChipsetType;
10185
10186 // paravirt
10187 data.paravirtProvider = mHWData->mParavirtProvider;
10188
10189
10190 data.fEmulatedUSBCardReader = !!mHWData->mEmulatedUSBCardReaderEnabled;
10191
10192 // HPET
10193 data.fHPETEnabled = !!mHWData->mHPETEnabled;
10194
10195 // boot order
10196 data.mapBootOrder.clear();
10197 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mBootOrder); ++i)
10198 data.mapBootOrder[i] = mHWData->mBootOrder[i];
10199
10200 // display
10201 data.graphicsControllerType = mHWData->mGraphicsControllerType;
10202 data.ulVRAMSizeMB = mHWData->mVRAMSize;
10203 data.cMonitors = mHWData->mMonitorCount;
10204 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled;
10205 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled;
10206 data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth;
10207 data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight;
10208 data.ulVideoCaptureRate = mHWData->mVideoCaptureRate;
10209 data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS;
10210 data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled;
10211 for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; ++i)
10212 {
10213 if (mHWData->maVideoCaptureScreens[i])
10214 ASMBitSet(&data.u64VideoCaptureScreens, i);
10215 else
10216 ASMBitClear(&data.u64VideoCaptureScreens, i);
10217 }
10218 /* store relative video capture file if possible */
10219 i_copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile);
10220
10221 /* VRDEServer settings (optional) */
10222 rc = mVRDEServer->i_saveSettings(data.vrdeSettings);
10223 if (FAILED(rc)) throw rc;
10224
10225 /* BIOS (required) */
10226 rc = mBIOSSettings->i_saveSettings(data.biosSettings);
10227 if (FAILED(rc)) throw rc;
10228
10229 /* USB Controller (required) */
10230 data.usbSettings.llUSBControllers.clear();
10231 for (USBControllerList::const_iterator it = mUSBControllers->begin(); it != mUSBControllers->end(); ++it)
10232 {
10233 ComObjPtr<USBController> ctrl = *it;
10234 settings::USBController settingsCtrl;
10235
10236 settingsCtrl.strName = ctrl->i_getName();
10237 settingsCtrl.enmType = ctrl->i_getControllerType();
10238
10239 data.usbSettings.llUSBControllers.push_back(settingsCtrl);
10240 }
10241
10242 /* USB device filters (required) */
10243 rc = mUSBDeviceFilters->i_saveSettings(data.usbSettings);
10244 if (FAILED(rc)) throw rc;
10245
10246 /* Network adapters (required) */
10247 size_t uMaxNICs = RT_MIN(Global::getMaxNetworkAdapters(mHWData->mChipsetType), mNetworkAdapters.size());
10248 data.llNetworkAdapters.clear();
10249 /* Write out only the nominal number of network adapters for this
10250 * chipset type. Since Machine::commit() hasn't been called there
10251 * may be extra NIC settings in the vector. */
10252 for (size_t slot = 0; slot < uMaxNICs; ++slot)
10253 {
10254 settings::NetworkAdapter nic;
10255 nic.ulSlot = (uint32_t)slot;
10256 /* paranoia check... must not be NULL, but must not crash either. */
10257 if (mNetworkAdapters[slot])
10258 {
10259 rc = mNetworkAdapters[slot]->i_saveSettings(nic);
10260 if (FAILED(rc)) throw rc;
10261
10262 data.llNetworkAdapters.push_back(nic);
10263 }
10264 }
10265
10266 /* Serial ports */
10267 data.llSerialPorts.clear();
10268 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
10269 {
10270 settings::SerialPort s;
10271 s.ulSlot = slot;
10272 rc = mSerialPorts[slot]->i_saveSettings(s);
10273 if (FAILED(rc)) return rc;
10274
10275 data.llSerialPorts.push_back(s);
10276 }
10277
10278 /* Parallel ports */
10279 data.llParallelPorts.clear();
10280 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
10281 {
10282 settings::ParallelPort p;
10283 p.ulSlot = slot;
10284 rc = mParallelPorts[slot]->i_saveSettings(p);
10285 if (FAILED(rc)) return rc;
10286
10287 data.llParallelPorts.push_back(p);
10288 }
10289
10290 /* Audio adapter */
10291 rc = mAudioAdapter->i_saveSettings(data.audioAdapter);
10292 if (FAILED(rc)) return rc;
10293
10294 /* Shared folders */
10295 data.llSharedFolders.clear();
10296 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin();
10297 it != mHWData->mSharedFolders.end();
10298 ++it)
10299 {
10300 SharedFolder *pSF = *it;
10301 AutoCaller sfCaller(pSF);
10302 AutoReadLock sfLock(pSF COMMA_LOCKVAL_SRC_POS);
10303 settings::SharedFolder sf;
10304 sf.strName = pSF->i_getName();
10305 sf.strHostPath = pSF->i_getHostPath();
10306 sf.fWritable = !!pSF->i_isWritable();
10307 sf.fAutoMount = !!pSF->i_isAutoMounted();
10308
10309 data.llSharedFolders.push_back(sf);
10310 }
10311
10312 // clipboard
10313 data.clipboardMode = mHWData->mClipboardMode;
10314
10315 // drag'n'drop
10316 data.dndMode = mHWData->mDnDMode;
10317
10318 /* Guest */
10319 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize;
10320
10321 // IO settings
10322 data.ioSettings.fIOCacheEnabled = !!mHWData->mIOCacheEnabled;
10323 data.ioSettings.ulIOCacheSize = mHWData->mIOCacheSize;
10324
10325 /* BandwidthControl (required) */
10326 rc = mBandwidthControl->i_saveSettings(data.ioSettings);
10327 if (FAILED(rc)) throw rc;
10328
10329 /* Host PCI devices */
10330 for (HWData::PCIDeviceAssignmentList::const_iterator it = mHWData->mPCIDeviceAssignments.begin();
10331 it != mHWData->mPCIDeviceAssignments.end();
10332 ++it)
10333 {
10334 ComObjPtr<PCIDeviceAttachment> pda = *it;
10335 settings::HostPCIDeviceAttachment hpda;
10336
10337 rc = pda->i_saveSettings(hpda);
10338 if (FAILED(rc)) throw rc;
10339
10340 data.pciAttachments.push_back(hpda);
10341 }
10342
10343
10344 // guest properties
10345 data.llGuestProperties.clear();
10346#ifdef VBOX_WITH_GUEST_PROPS
10347 for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin();
10348 it != mHWData->mGuestProperties.end();
10349 ++it)
10350 {
10351 HWData::GuestProperty property = it->second;
10352
10353 /* Remove transient guest properties at shutdown unless we
10354 * are saving state. Note that restoring snapshot intentionally
10355 * keeps them, they will be removed if appropriate once the final
10356 * machine state is set (as crashes etc. need to work). */
10357 if ( ( mData->mMachineState == MachineState_PoweredOff
10358 || mData->mMachineState == MachineState_Aborted
10359 || mData->mMachineState == MachineState_Teleported)
10360 && ( property.mFlags & guestProp::TRANSIENT
10361 || property.mFlags & guestProp::TRANSRESET))
10362 continue;
10363 settings::GuestProperty prop;
10364 prop.strName = it->first;
10365 prop.strValue = property.strValue;
10366 prop.timestamp = property.mTimestamp;
10367 char szFlags[guestProp::MAX_FLAGS_LEN + 1];
10368 guestProp::writeFlags(property.mFlags, szFlags);
10369 prop.strFlags = szFlags;
10370
10371 data.llGuestProperties.push_back(prop);
10372 }
10373
10374 /* I presume this doesn't require a backup(). */
10375 mData->mGuestPropertiesModified = FALSE;
10376#endif /* VBOX_WITH_GUEST_PROPS defined */
10377
10378 *pDbg = mHWData->mDebugging;
10379 *pAutostart = mHWData->mAutostart;
10380
10381 data.strDefaultFrontend = mHWData->mDefaultFrontend;
10382 }
10383 catch(std::bad_alloc &)
10384 {
10385 return E_OUTOFMEMORY;
10386 }
10387
10388 AssertComRC(rc);
10389 return rc;
10390}
10391
10392/**
10393 * Saves the storage controller configuration.
10394 *
10395 * @param aNode <StorageControllers> node to save the VM hardware configuration to.
10396 */
10397HRESULT Machine::i_saveStorageControllers(settings::Storage &data)
10398{
10399 data.llStorageControllers.clear();
10400
10401 for (StorageControllerList::const_iterator it = mStorageControllers->begin();
10402 it != mStorageControllers->end();
10403 ++it)
10404 {
10405 HRESULT rc;
10406 ComObjPtr<StorageController> pCtl = *it;
10407
10408 settings::StorageController ctl;
10409 ctl.strName = pCtl->i_getName();
10410 ctl.controllerType = pCtl->i_getControllerType();
10411 ctl.storageBus = pCtl->i_getStorageBus();
10412 ctl.ulInstance = pCtl->i_getInstance();
10413 ctl.fBootable = pCtl->i_getBootable();
10414
10415 /* Save the port count. */
10416 ULONG portCount;
10417 rc = pCtl->COMGETTER(PortCount)(&portCount);
10418 ComAssertComRCRet(rc, rc);
10419 ctl.ulPortCount = portCount;
10420
10421 /* Save fUseHostIOCache */
10422 BOOL fUseHostIOCache;
10423 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);
10424 ComAssertComRCRet(rc, rc);
10425 ctl.fUseHostIOCache = !!fUseHostIOCache;
10426
10427 /* save the devices now. */
10428 rc = i_saveStorageDevices(pCtl, ctl);
10429 ComAssertComRCRet(rc, rc);
10430
10431 data.llStorageControllers.push_back(ctl);
10432 }
10433
10434 return S_OK;
10435}
10436
10437/**
10438 * Saves the hard disk configuration.
10439 */
10440HRESULT Machine::i_saveStorageDevices(ComObjPtr<StorageController> aStorageController,
10441 settings::StorageController &data)
10442{
10443 MediaData::AttachmentList atts;
10444
10445 HRESULT rc = i_getMediumAttachmentsOfController(aStorageController->i_getName(), atts);
10446 if (FAILED(rc)) return rc;
10447
10448 data.llAttachedDevices.clear();
10449 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10450 it != atts.end();
10451 ++it)
10452 {
10453 settings::AttachedDevice dev;
10454 IMediumAttachment *iA = *it;
10455 MediumAttachment *pAttach = static_cast<MediumAttachment *>(iA);
10456 Medium *pMedium = pAttach->i_getMedium();
10457
10458 dev.deviceType = pAttach->i_getType();
10459 dev.lPort = pAttach->i_getPort();
10460 dev.lDevice = pAttach->i_getDevice();
10461 dev.fPassThrough = pAttach->i_getPassthrough();
10462 dev.fHotPluggable = pAttach->i_getHotPluggable();
10463 if (pMedium)
10464 {
10465 if (pMedium->i_isHostDrive())
10466 dev.strHostDriveSrc = pMedium->i_getLocationFull();
10467 else
10468 dev.uuid = pMedium->i_getId();
10469 dev.fTempEject = pAttach->i_getTempEject();
10470 dev.fNonRotational = pAttach->i_getNonRotational();
10471 dev.fDiscard = pAttach->i_getDiscard();
10472 }
10473
10474 dev.strBwGroup = pAttach->i_getBandwidthGroup();
10475
10476 data.llAttachedDevices.push_back(dev);
10477 }
10478
10479 return S_OK;
10480}
10481
10482/**
10483 * Saves machine state settings as defined by aFlags
10484 * (SaveSTS_* values).
10485 *
10486 * @param aFlags Combination of SaveSTS_* flags.
10487 *
10488 * @note Locks objects for writing.
10489 */
10490HRESULT Machine::i_saveStateSettings(int aFlags)
10491{
10492 if (aFlags == 0)
10493 return S_OK;
10494
10495 AutoCaller autoCaller(this);
10496 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10497
10498 /* This object's write lock is also necessary to serialize file access
10499 * (prevent concurrent reads and writes) */
10500 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
10501
10502 HRESULT rc = S_OK;
10503
10504 Assert(mData->pMachineConfigFile);
10505
10506 try
10507 {
10508 if (aFlags & SaveSTS_CurStateModified)
10509 mData->pMachineConfigFile->fCurrentStateModified = true;
10510
10511 if (aFlags & SaveSTS_StateFilePath)
10512 {
10513 if (!mSSData->strStateFilePath.isEmpty())
10514 /* try to make the file name relative to the settings file dir */
10515 i_copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
10516 else
10517 mData->pMachineConfigFile->strStateFile.setNull();
10518 }
10519
10520 if (aFlags & SaveSTS_StateTimeStamp)
10521 {
10522 Assert( mData->mMachineState != MachineState_Aborted
10523 || mSSData->strStateFilePath.isEmpty());
10524
10525 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
10526
10527 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);
10528//@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);
10529 }
10530
10531 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);
10532 }
10533 catch (...)
10534 {
10535 rc = VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
10536 }
10537
10538 return rc;
10539}
10540
10541/**
10542 * Ensures that the given medium is added to a media registry. If this machine
10543 * was created with 4.0 or later, then the machine registry is used. Otherwise
10544 * the global VirtualBox media registry is used.
10545 *
10546 * Caller must NOT hold machine lock, media tree or any medium locks!
10547 *
10548 * @param pMedium
10549 */
10550void Machine::i_addMediumToRegistry(ComObjPtr<Medium> &pMedium)
10551{
10552 /* Paranoia checks: do not hold machine or media tree locks. */
10553 AssertReturnVoid(!isWriteLockOnCurrentThread());
10554 AssertReturnVoid(!mParent->i_getMediaTreeLockHandle().isWriteLockOnCurrentThread());
10555
10556 ComObjPtr<Medium> pBase;
10557 {
10558 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10559 pBase = pMedium->i_getBase();
10560 }
10561
10562 /* Paranoia checks: do not hold medium locks. */
10563 AssertReturnVoid(!pMedium->isWriteLockOnCurrentThread());
10564 AssertReturnVoid(!pBase->isWriteLockOnCurrentThread());
10565
10566 // decide which medium registry to use now that the medium is attached:
10567 Guid uuid;
10568 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry())
10569 // machine XML is VirtualBox 4.0 or higher:
10570 uuid = i_getId(); // machine UUID
10571 else
10572 uuid = mParent->i_getGlobalRegistryId(); // VirtualBox global registry UUID
10573
10574 if (pMedium->i_addRegistry(uuid))
10575 mParent->i_markRegistryModified(uuid);
10576
10577 /* For more complex hard disk structures it can happen that the base
10578 * medium isn't yet associated with any medium registry. Do that now. */
10579 if (pMedium != pBase)
10580 {
10581 /* Tree lock needed by Medium::addRegistry when recursing. */
10582 AutoReadLock treeLock(&mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10583 if (pBase->i_addRegistryRecursive(uuid))
10584 {
10585 treeLock.release();
10586 mParent->i_markRegistryModified(uuid);
10587 }
10588 }
10589}
10590
10591/**
10592 * Creates differencing hard disks for all normal hard disks attached to this
10593 * machine and a new set of attachments to refer to created disks.
10594 *
10595 * Used when taking a snapshot or when deleting the current state. Gets called
10596 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().
10597 *
10598 * This method assumes that mMediaData contains the original hard disk attachments
10599 * it needs to create diffs for. On success, these attachments will be replaced
10600 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly
10601 * called to delete created diffs which will also rollback mMediaData and restore
10602 * whatever was backed up before calling this method.
10603 *
10604 * Attachments with non-normal hard disks are left as is.
10605 *
10606 * If @a aOnline is @c false then the original hard disks that require implicit
10607 * diffs will be locked for reading. Otherwise it is assumed that they are
10608 * already locked for writing (when the VM was started). Note that in the latter
10609 * case it is responsibility of the caller to lock the newly created diffs for
10610 * writing if this method succeeds.
10611 *
10612 * @param aProgress Progress object to run (must contain at least as
10613 * many operations left as the number of hard disks
10614 * attached).
10615 * @param aOnline Whether the VM was online prior to this operation.
10616 *
10617 * @note The progress object is not marked as completed, neither on success nor
10618 * on failure. This is a responsibility of the caller.
10619 *
10620 * @note Locks this object and the media tree for writing.
10621 */
10622HRESULT Machine::i_createImplicitDiffs(IProgress *aProgress,
10623 ULONG aWeight,
10624 bool aOnline)
10625{
10626 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10627
10628 AutoCaller autoCaller(this);
10629 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10630
10631 AutoMultiWriteLock2 alock(this->lockHandle(),
10632 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10633
10634 /* must be in a protective state because we release the lock below */
10635 AssertReturn( mData->mMachineState == MachineState_Snapshotting
10636 || mData->mMachineState == MachineState_OnlineSnapshotting
10637 || mData->mMachineState == MachineState_LiveSnapshotting
10638 || mData->mMachineState == MachineState_RestoringSnapshot
10639 || mData->mMachineState == MachineState_DeletingSnapshot
10640 , E_FAIL);
10641
10642 HRESULT rc = S_OK;
10643
10644 // use appropriate locked media map (online or offline)
10645 MediumLockListMap lockedMediaOffline;
10646 MediumLockListMap *lockedMediaMap;
10647 if (aOnline)
10648 lockedMediaMap = &mData->mSession.mLockedMedia;
10649 else
10650 lockedMediaMap = &lockedMediaOffline;
10651
10652 try
10653 {
10654 if (!aOnline)
10655 {
10656 /* lock all attached hard disks early to detect "in use"
10657 * situations before creating actual diffs */
10658 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10659 it != mMediaData->mAttachments.end();
10660 ++it)
10661 {
10662 MediumAttachment* pAtt = *it;
10663 if (pAtt->i_getType() == DeviceType_HardDisk)
10664 {
10665 Medium* pMedium = pAtt->i_getMedium();
10666 Assert(pMedium);
10667
10668 MediumLockList *pMediumLockList(new MediumLockList());
10669 alock.release();
10670 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10671 false /* fMediumLockWrite */,
10672 false /* fMediumLockWriteAll */,
10673 NULL,
10674 *pMediumLockList);
10675 alock.acquire();
10676 if (FAILED(rc))
10677 {
10678 delete pMediumLockList;
10679 throw rc;
10680 }
10681 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10682 if (FAILED(rc))
10683 {
10684 throw setError(rc,
10685 tr("Collecting locking information for all attached media failed"));
10686 }
10687 }
10688 }
10689
10690 /* Now lock all media. If this fails, nothing is locked. */
10691 alock.release();
10692 rc = lockedMediaMap->Lock();
10693 alock.acquire();
10694 if (FAILED(rc))
10695 {
10696 throw setError(rc,
10697 tr("Locking of attached media failed"));
10698 }
10699 }
10700
10701 /* remember the current list (note that we don't use backup() since
10702 * mMediaData may be already backed up) */
10703 MediaData::AttachmentList atts = mMediaData->mAttachments;
10704
10705 /* start from scratch */
10706 mMediaData->mAttachments.clear();
10707
10708 /* go through remembered attachments and create diffs for normal hard
10709 * disks and attach them */
10710 for (MediaData::AttachmentList::const_iterator it = atts.begin();
10711 it != atts.end();
10712 ++it)
10713 {
10714 MediumAttachment* pAtt = *it;
10715
10716 DeviceType_T devType = pAtt->i_getType();
10717 Medium* pMedium = pAtt->i_getMedium();
10718
10719 if ( devType != DeviceType_HardDisk
10720 || pMedium == NULL
10721 || pMedium->i_getType() != MediumType_Normal)
10722 {
10723 /* copy the attachment as is */
10724
10725 /** @todo the progress object created in SessionMachine::TakeSnaphot
10726 * only expects operations for hard disks. Later other
10727 * device types need to show up in the progress as well. */
10728 if (devType == DeviceType_HardDisk)
10729 {
10730 if (pMedium == NULL)
10731 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")).raw(),
10732 aWeight); // weight
10733 else
10734 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),
10735 pMedium->i_getBase()->i_getName().c_str()).raw(),
10736 aWeight); // weight
10737 }
10738
10739 mMediaData->mAttachments.push_back(pAtt);
10740 continue;
10741 }
10742
10743 /* need a diff */
10744 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),
10745 pMedium->i_getBase()->i_getName().c_str()).raw(),
10746 aWeight); // weight
10747
10748 Utf8Str strFullSnapshotFolder;
10749 i_calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
10750
10751 ComObjPtr<Medium> diff;
10752 diff.createObject();
10753 // store the diff in the same registry as the parent
10754 // (this cannot fail here because we can't create implicit diffs for
10755 // unregistered images)
10756 Guid uuidRegistryParent;
10757 bool fInRegistry = pMedium->i_getFirstRegistryMachineId(uuidRegistryParent);
10758 Assert(fInRegistry); NOREF(fInRegistry);
10759 rc = diff->init(mParent,
10760 pMedium->i_getPreferredDiffFormat(),
10761 strFullSnapshotFolder.append(RTPATH_SLASH_STR),
10762 uuidRegistryParent,
10763 DeviceType_HardDisk);
10764 if (FAILED(rc)) throw rc;
10765
10766 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before
10767 * the push_back? Looks like we're going to release medium with the
10768 * wrong kind of lock (general issue with if we fail anywhere at all)
10769 * and an orphaned VDI in the snapshots folder. */
10770
10771 /* update the appropriate lock list */
10772 MediumLockList *pMediumLockList;
10773 rc = lockedMediaMap->Get(pAtt, pMediumLockList);
10774 AssertComRCThrowRC(rc);
10775 if (aOnline)
10776 {
10777 alock.release();
10778 /* The currently attached medium will be read-only, change
10779 * the lock type to read. */
10780 rc = pMediumLockList->Update(pMedium, false);
10781 alock.acquire();
10782 AssertComRCThrowRC(rc);
10783 }
10784
10785 /* release the locks before the potentially lengthy operation */
10786 alock.release();
10787 rc = pMedium->i_createDiffStorage(diff, MediumVariant_Standard,
10788 pMediumLockList,
10789 NULL /* aProgress */,
10790 true /* aWait */);
10791 alock.acquire();
10792 if (FAILED(rc)) throw rc;
10793
10794 /* actual lock list update is done in Medium::commitMedia */
10795
10796 rc = diff->i_addBackReference(mData->mUuid);
10797 AssertComRCThrowRC(rc);
10798
10799 /* add a new attachment */
10800 ComObjPtr<MediumAttachment> attachment;
10801 attachment.createObject();
10802 rc = attachment->init(this,
10803 diff,
10804 pAtt->i_getControllerName(),
10805 pAtt->i_getPort(),
10806 pAtt->i_getDevice(),
10807 DeviceType_HardDisk,
10808 true /* aImplicit */,
10809 false /* aPassthrough */,
10810 false /* aTempEject */,
10811 pAtt->i_getNonRotational(),
10812 pAtt->i_getDiscard(),
10813 pAtt->i_getHotPluggable(),
10814 pAtt->i_getBandwidthGroup());
10815 if (FAILED(rc)) throw rc;
10816
10817 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);
10818 AssertComRCThrowRC(rc);
10819 mMediaData->mAttachments.push_back(attachment);
10820 }
10821 }
10822 catch (HRESULT aRC) { rc = aRC; }
10823
10824 /* unlock all hard disks we locked when there is no VM */
10825 if (!aOnline)
10826 {
10827 ErrorInfoKeeper eik;
10828
10829 HRESULT rc1 = lockedMediaMap->Clear();
10830 AssertComRC(rc1);
10831 }
10832
10833 return rc;
10834}
10835
10836/**
10837 * Deletes implicit differencing hard disks created either by
10838 * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
10839 *
10840 * Note that to delete hard disks created by #AttachDevice() this method is
10841 * called from #fixupMedia() when the changes are rolled back.
10842 *
10843 * @note Locks this object and the media tree for writing.
10844 */
10845HRESULT Machine::i_deleteImplicitDiffs(bool aOnline)
10846{
10847 LogFlowThisFunc(("aOnline=%d\n", aOnline));
10848
10849 AutoCaller autoCaller(this);
10850 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
10851
10852 AutoMultiWriteLock2 alock(this->lockHandle(),
10853 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
10854
10855 /* We absolutely must have backed up state. */
10856 AssertReturn(mMediaData.isBackedUp(), E_FAIL);
10857
10858 /* Check if there are any implicitly created diff images. */
10859 bool fImplicitDiffs = false;
10860 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10861 it != mMediaData->mAttachments.end();
10862 ++it)
10863 {
10864 const ComObjPtr<MediumAttachment> &pAtt = *it;
10865 if (pAtt->i_isImplicit())
10866 {
10867 fImplicitDiffs = true;
10868 break;
10869 }
10870 }
10871 /* If there is nothing to do, leave early. This saves lots of image locking
10872 * effort. It also avoids a MachineStateChanged event without real reason.
10873 * This is important e.g. when loading a VM config, because there should be
10874 * no events. Otherwise API clients can become thoroughly confused for
10875 * inaccessible VMs (the code for loading VM configs uses this method for
10876 * cleanup if the config makes no sense), as they take such events as an
10877 * indication that the VM is alive, and they would force the VM config to
10878 * be reread, leading to an endless loop. */
10879 if (!fImplicitDiffs)
10880 return S_OK;
10881
10882 HRESULT rc = S_OK;
10883 MachineState_T oldState = mData->mMachineState;
10884
10885 /* will release the lock before the potentially lengthy operation,
10886 * so protect with the special state (unless already protected) */
10887 if ( oldState != MachineState_Snapshotting
10888 && oldState != MachineState_OnlineSnapshotting
10889 && oldState != MachineState_LiveSnapshotting
10890 && oldState != MachineState_RestoringSnapshot
10891 && oldState != MachineState_DeletingSnapshot
10892 && oldState != MachineState_DeletingSnapshotOnline
10893 && oldState != MachineState_DeletingSnapshotPaused
10894 )
10895 i_setMachineState(MachineState_SettingUp);
10896
10897 // use appropriate locked media map (online or offline)
10898 MediumLockListMap lockedMediaOffline;
10899 MediumLockListMap *lockedMediaMap;
10900 if (aOnline)
10901 lockedMediaMap = &mData->mSession.mLockedMedia;
10902 else
10903 lockedMediaMap = &lockedMediaOffline;
10904
10905 try
10906 {
10907 if (!aOnline)
10908 {
10909 /* lock all attached hard disks early to detect "in use"
10910 * situations before deleting actual diffs */
10911 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10912 it != mMediaData->mAttachments.end();
10913 ++it)
10914 {
10915 MediumAttachment* pAtt = *it;
10916 if (pAtt->i_getType() == DeviceType_HardDisk)
10917 {
10918 Medium* pMedium = pAtt->i_getMedium();
10919 Assert(pMedium);
10920
10921 MediumLockList *pMediumLockList(new MediumLockList());
10922 alock.release();
10923 rc = pMedium->i_createMediumLockList(true /* fFailIfInaccessible */,
10924 false /* fMediumLockWrite */,
10925 false /* fMediumLockWriteAll */,
10926 NULL,
10927 *pMediumLockList);
10928 alock.acquire();
10929
10930 if (FAILED(rc))
10931 {
10932 delete pMediumLockList;
10933 throw rc;
10934 }
10935
10936 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);
10937 if (FAILED(rc))
10938 throw rc;
10939 }
10940 }
10941
10942 if (FAILED(rc))
10943 throw rc;
10944 } // end of offline
10945
10946 /* Lock lists are now up to date and include implicitly created media */
10947
10948 /* Go through remembered attachments and delete all implicitly created
10949 * diffs and fix up the attachment information */
10950 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
10951 MediaData::AttachmentList implicitAtts;
10952 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
10953 it != mMediaData->mAttachments.end();
10954 ++it)
10955 {
10956 ComObjPtr<MediumAttachment> pAtt = *it;
10957 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
10958 if (pMedium.isNull())
10959 continue;
10960
10961 // Implicit attachments go on the list for deletion and back references are removed.
10962 if (pAtt->i_isImplicit())
10963 {
10964 /* Deassociate and mark for deletion */
10965 LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->i_getLogName()));
10966 rc = pMedium->i_removeBackReference(mData->mUuid);
10967 if (FAILED(rc))
10968 throw rc;
10969 implicitAtts.push_back(pAtt);
10970 continue;
10971 }
10972
10973 /* Was this medium attached before? */
10974 if (!i_findAttachment(oldAtts, pMedium))
10975 {
10976 /* no: de-associate */
10977 LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->i_getLogName()));
10978 rc = pMedium->i_removeBackReference(mData->mUuid);
10979 if (FAILED(rc))
10980 throw rc;
10981 continue;
10982 }
10983 LogFlowThisFunc(("Not detaching '%s'\n", pAtt->i_getLogName()));
10984 }
10985
10986 /* If there are implicit attachments to delete, throw away the lock
10987 * map contents (which will unlock all media) since the medium
10988 * attachments will be rolled back. Below we need to completely
10989 * recreate the lock map anyway since it is infinitely complex to
10990 * do this incrementally (would need reconstructing each attachment
10991 * change, which would be extremely hairy). */
10992 if (implicitAtts.size() != 0)
10993 {
10994 ErrorInfoKeeper eik;
10995
10996 HRESULT rc1 = lockedMediaMap->Clear();
10997 AssertComRC(rc1);
10998 }
10999
11000 /* rollback hard disk changes */
11001 mMediaData.rollback();
11002
11003 MultiResult mrc(S_OK);
11004
11005 // Delete unused implicit diffs.
11006 if (implicitAtts.size() != 0)
11007 {
11008 alock.release();
11009
11010 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); it != implicitAtts.end(); ++it)
11011 {
11012 // Remove medium associated with this attachment.
11013 ComObjPtr<MediumAttachment> pAtt = *it;
11014 Assert(pAtt);
11015 LogFlowThisFunc(("Deleting '%s'\n", pAtt->i_getLogName()));
11016 ComObjPtr<Medium> pMedium = pAtt->i_getMedium();
11017 Assert(pMedium);
11018
11019 rc = pMedium->i_deleteStorage(NULL /*aProgress*/, true /*aWait*/);
11020 // continue on delete failure, just collect error messages
11021 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->i_getLogName(),
11022 pMedium->i_getLocationFull().c_str() ));
11023 mrc = rc;
11024 }
11025 // Clear the list of deleted implicit attachments now, while not
11026 // holding the lock, as it will ultimately trigger Medium::uninit()
11027 // calls which assume that the media tree lock isn't held.
11028 implicitAtts.clear();
11029
11030 alock.acquire();
11031
11032 /* if there is a VM recreate media lock map as mentioned above,
11033 * otherwise it is a waste of time and we leave things unlocked */
11034 if (aOnline)
11035 {
11036 const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine;
11037 /* must never be NULL, but better safe than sorry */
11038 if (!pMachine.isNull())
11039 {
11040 alock.release();
11041 rc = mData->mSession.mMachine->i_lockMedia();
11042 alock.acquire();
11043 if (FAILED(rc))
11044 throw rc;
11045 }
11046 }
11047 }
11048 }
11049 catch (HRESULT aRC) {rc = aRC;}
11050
11051 if (mData->mMachineState == MachineState_SettingUp)
11052 i_setMachineState(oldState);
11053
11054 /* unlock all hard disks we locked when there is no VM */
11055 if (!aOnline)
11056 {
11057 ErrorInfoKeeper eik;
11058
11059 HRESULT rc1 = lockedMediaMap->Clear();
11060 AssertComRC(rc1);
11061 }
11062
11063 return rc;
11064}
11065
11066
11067/**
11068 * Looks through the given list of media attachments for one with the given parameters
11069 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11070 * can be searched as well if needed.
11071 *
11072 * @param list
11073 * @param aControllerName
11074 * @param aControllerPort
11075 * @param aDevice
11076 * @return
11077 */
11078MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11079 const Utf8Str &aControllerName,
11080 LONG aControllerPort,
11081 LONG aDevice)
11082{
11083 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11084 {
11085 MediumAttachment *pAttach = *it;
11086 if (pAttach->i_matches(aControllerName, aControllerPort, aDevice))
11087 return pAttach;
11088 }
11089
11090 return NULL;
11091}
11092
11093/**
11094 * Looks through the given list of media attachments for one with the given parameters
11095 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11096 * can be searched as well if needed.
11097 *
11098 * @param list
11099 * @param aControllerName
11100 * @param aControllerPort
11101 * @param aDevice
11102 * @return
11103 */
11104MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11105 ComObjPtr<Medium> pMedium)
11106{
11107 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11108 {
11109 MediumAttachment *pAttach = *it;
11110 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11111 if (pMediumThis == pMedium)
11112 return pAttach;
11113 }
11114
11115 return NULL;
11116}
11117
11118/**
11119 * Looks through the given list of media attachments for one with the given parameters
11120 * and returns it, or NULL if not found. The list is a parameter so that backup lists
11121 * can be searched as well if needed.
11122 *
11123 * @param list
11124 * @param aControllerName
11125 * @param aControllerPort
11126 * @param aDevice
11127 * @return
11128 */
11129MediumAttachment* Machine::i_findAttachment(const MediaData::AttachmentList &ll,
11130 Guid &id)
11131{
11132 for (MediaData::AttachmentList::const_iterator it = ll.begin(); it != ll.end(); ++it)
11133 {
11134 MediumAttachment *pAttach = *it;
11135 ComObjPtr<Medium> pMediumThis = pAttach->i_getMedium();
11136 if (pMediumThis->i_getId() == id)
11137 return pAttach;
11138 }
11139
11140 return NULL;
11141}
11142
11143/**
11144 * Main implementation for Machine::DetachDevice. This also gets called
11145 * from Machine::prepareUnregister() so it has been taken out for simplicity.
11146 *
11147 * @param pAttach Medium attachment to detach.
11148 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.
11149 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a
11150 * SnapshotMachine, and this must be its snapshot.
11151 * @return
11152 */
11153HRESULT Machine::i_detachDevice(MediumAttachment *pAttach,
11154 AutoWriteLock &writeLock,
11155 Snapshot *pSnapshot)
11156{
11157 ComObjPtr<Medium> oldmedium = pAttach->i_getMedium();
11158 DeviceType_T mediumType = pAttach->i_getType();
11159
11160 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->i_getLocationFull().c_str() : "NULL"));
11161
11162 if (pAttach->i_isImplicit())
11163 {
11164 /* attempt to implicitly delete the implicitly created diff */
11165
11166 /// @todo move the implicit flag from MediumAttachment to Medium
11167 /// and forbid any hard disk operation when it is implicit. Or maybe
11168 /// a special media state for it to make it even more simple.
11169
11170 Assert(mMediaData.isBackedUp());
11171
11172 /* will release the lock before the potentially lengthy operation, so
11173 * protect with the special state */
11174 MachineState_T oldState = mData->mMachineState;
11175 i_setMachineState(MachineState_SettingUp);
11176
11177 writeLock.release();
11178
11179 HRESULT rc = oldmedium->i_deleteStorage(NULL /*aProgress*/,
11180 true /*aWait*/);
11181
11182 writeLock.acquire();
11183
11184 i_setMachineState(oldState);
11185
11186 if (FAILED(rc)) return rc;
11187 }
11188
11189 i_setModified(IsModified_Storage);
11190 mMediaData.backup();
11191 mMediaData->mAttachments.remove(pAttach);
11192
11193 if (!oldmedium.isNull())
11194 {
11195 // if this is from a snapshot, do not defer detachment to commitMedia()
11196 if (pSnapshot)
11197 oldmedium->i_removeBackReference(mData->mUuid, pSnapshot->i_getId());
11198 // else if non-hard disk media, do not defer detachment to commitMedia() either
11199 else if (mediumType != DeviceType_HardDisk)
11200 oldmedium->i_removeBackReference(mData->mUuid);
11201 }
11202
11203 return S_OK;
11204}
11205
11206/**
11207 * Goes thru all media of the given list and
11208 *
11209 * 1) calls i_detachDevice() on each of them for this machine and
11210 * 2) adds all Medium objects found in the process to the given list,
11211 * depending on cleanupMode.
11212 *
11213 * If cleanupMode is CleanupMode_DetachAllReturnHardDisksOnly, this only
11214 * adds hard disks to the list. If it is CleanupMode_Full, this adds all
11215 * media to the list.
11216 *
11217 * This gets called from Machine::Unregister, both for the actual Machine and
11218 * the SnapshotMachine objects that might be found in the snapshots.
11219 *
11220 * Requires caller and locking. The machine lock must be passed in because it
11221 * will be passed on to i_detachDevice which needs it for temporary unlocking.
11222 *
11223 * @param writeLock Machine lock from top-level caller; this gets passed to i_detachDevice.
11224 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.
11225 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if
11226 * Full, then all media get added;
11227 * otherwise no media get added.
11228 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.
11229 * @return
11230 */
11231HRESULT Machine::i_detachAllMedia(AutoWriteLock &writeLock,
11232 Snapshot *pSnapshot,
11233 CleanupMode_T cleanupMode,
11234 MediaList &llMedia)
11235{
11236 Assert(isWriteLockOnCurrentThread());
11237
11238 HRESULT rc;
11239
11240 // make a temporary list because i_detachDevice invalidates iterators into
11241 // mMediaData->mAttachments
11242 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;
11243
11244 for (MediaData::AttachmentList::iterator it = llAttachments2.begin(); it != llAttachments2.end(); ++it)
11245 {
11246 ComObjPtr<MediumAttachment> &pAttach = *it;
11247 ComObjPtr<Medium> pMedium = pAttach->i_getMedium();
11248
11249 if (!pMedium.isNull())
11250 {
11251 AutoCaller mac(pMedium);
11252 if (FAILED(mac.rc())) return mac.rc();
11253 AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
11254 DeviceType_T devType = pMedium->i_getDeviceType();
11255 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
11256 && devType == DeviceType_HardDisk)
11257 || (cleanupMode == CleanupMode_Full)
11258 )
11259 {
11260 llMedia.push_back(pMedium);
11261 ComObjPtr<Medium> pParent = pMedium->i_getParent();
11262 /* Not allowed to keep this lock as below we need the parent
11263 * medium lock, and the lock order is parent to child. */
11264 lock.release();
11265 /*
11266 * Search for medias which are not attached to any machine, but
11267 * in the chain to an attached disk. Mediums are only consided
11268 * if they are:
11269 * - have only one child
11270 * - no references to any machines
11271 * - are of normal medium type
11272 */
11273 while (!pParent.isNull())
11274 {
11275 AutoCaller mac1(pParent);
11276 if (FAILED(mac1.rc())) return mac1.rc();
11277 AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
11278 if (pParent->i_getChildren().size() == 1)
11279 {
11280 if ( pParent->i_getMachineBackRefCount() == 0
11281 && pParent->i_getType() == MediumType_Normal
11282 && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
11283 llMedia.push_back(pParent);
11284 }
11285 else
11286 break;
11287 pParent = pParent->i_getParent();
11288 }
11289 }
11290 }
11291
11292 // real machine: then we need to use the proper method
11293 rc = i_detachDevice(pAttach, writeLock, pSnapshot);
11294
11295 if (FAILED(rc))
11296 return rc;
11297 }
11298
11299 return S_OK;
11300}
11301
11302/**
11303 * Perform deferred hard disk detachments.
11304 *
11305 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11306 * backed up).
11307 *
11308 * If @a aOnline is @c true then this method will also unlock the old hard disks
11309 * for which the new implicit diffs were created and will lock these new diffs for
11310 * writing.
11311 *
11312 * @param aOnline Whether the VM was online prior to this operation.
11313 *
11314 * @note Locks this object for writing!
11315 */
11316void Machine::i_commitMedia(bool aOnline /*= false*/)
11317{
11318 AutoCaller autoCaller(this);
11319 AssertComRCReturnVoid(autoCaller.rc());
11320
11321 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11322
11323 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));
11324
11325 HRESULT rc = S_OK;
11326
11327 /* no attach/detach operations -- nothing to do */
11328 if (!mMediaData.isBackedUp())
11329 return;
11330
11331 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;
11332 bool fMediaNeedsLocking = false;
11333
11334 /* enumerate new attachments */
11335 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11336 it != mMediaData->mAttachments.end();
11337 ++it)
11338 {
11339 MediumAttachment *pAttach = *it;
11340
11341 pAttach->i_commit();
11342
11343 Medium* pMedium = pAttach->i_getMedium();
11344 bool fImplicit = pAttach->i_isImplicit();
11345
11346 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",
11347 (pMedium) ? pMedium->i_getName().c_str() : "NULL",
11348 fImplicit));
11349
11350 /** @todo convert all this Machine-based voodoo to MediumAttachment
11351 * based commit logic. */
11352 if (fImplicit)
11353 {
11354 /* convert implicit attachment to normal */
11355 pAttach->i_setImplicit(false);
11356
11357 if ( aOnline
11358 && pMedium
11359 && pAttach->i_getType() == DeviceType_HardDisk
11360 )
11361 {
11362 /* update the appropriate lock list */
11363 MediumLockList *pMediumLockList;
11364 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11365 AssertComRC(rc);
11366 if (pMediumLockList)
11367 {
11368 /* unlock if there's a need to change the locking */
11369 if (!fMediaNeedsLocking)
11370 {
11371 rc = mData->mSession.mLockedMedia.Unlock();
11372 AssertComRC(rc);
11373 fMediaNeedsLocking = true;
11374 }
11375 rc = pMediumLockList->Update(pMedium->i_getParent(), false);
11376 AssertComRC(rc);
11377 rc = pMediumLockList->Append(pMedium, true);
11378 AssertComRC(rc);
11379 }
11380 }
11381
11382 continue;
11383 }
11384
11385 if (pMedium)
11386 {
11387 /* was this medium attached before? */
11388 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin(); oldIt != oldAtts.end(); ++oldIt)
11389 {
11390 MediumAttachment *pOldAttach = *oldIt;
11391 if (pOldAttach->i_getMedium() == pMedium)
11392 {
11393 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->i_getName().c_str()));
11394
11395 /* yes: remove from old to avoid de-association */
11396 oldAtts.erase(oldIt);
11397 break;
11398 }
11399 }
11400 }
11401 }
11402
11403 /* enumerate remaining old attachments and de-associate from the
11404 * current machine state */
11405 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); it != oldAtts.end(); ++it)
11406 {
11407 MediumAttachment *pAttach = *it;
11408 Medium* pMedium = pAttach->i_getMedium();
11409
11410 /* Detach only hard disks, since DVD/floppy media is detached
11411 * instantly in MountMedium. */
11412 if (pAttach->i_getType() == DeviceType_HardDisk && pMedium)
11413 {
11414 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->i_getName().c_str()));
11415
11416 /* now de-associate from the current machine state */
11417 rc = pMedium->i_removeBackReference(mData->mUuid);
11418 AssertComRC(rc);
11419
11420 if (aOnline)
11421 {
11422 /* unlock since medium is not used anymore */
11423 MediumLockList *pMediumLockList;
11424 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);
11425 if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE))
11426 {
11427 /* this happens for online snapshots, there the attachment
11428 * is changing, but only to a diff image created under
11429 * the old one, so there is no separate lock list */
11430 Assert(!pMediumLockList);
11431 }
11432 else
11433 {
11434 AssertComRC(rc);
11435 if (pMediumLockList)
11436 {
11437 rc = mData->mSession.mLockedMedia.Remove(pAttach);
11438 AssertComRC(rc);
11439 }
11440 }
11441 }
11442 }
11443 }
11444
11445 /* take media locks again so that the locking state is consistent */
11446 if (fMediaNeedsLocking)
11447 {
11448 Assert(aOnline);
11449 rc = mData->mSession.mLockedMedia.Lock();
11450 AssertComRC(rc);
11451 }
11452
11453 /* commit the hard disk changes */
11454 mMediaData.commit();
11455
11456 if (i_isSessionMachine())
11457 {
11458 /*
11459 * Update the parent machine to point to the new owner.
11460 * This is necessary because the stored parent will point to the
11461 * session machine otherwise and cause crashes or errors later
11462 * when the session machine gets invalid.
11463 */
11464 /** @todo Change the MediumAttachment class to behave like any other
11465 * class in this regard by creating peer MediumAttachment
11466 * objects for session machines and share the data with the peer
11467 * machine.
11468 */
11469 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11470 it != mMediaData->mAttachments.end();
11471 ++it)
11472 (*it)->i_updateParentMachine(mPeer);
11473
11474 /* attach new data to the primary machine and reshare it */
11475 mPeer->mMediaData.attach(mMediaData);
11476 }
11477
11478 return;
11479}
11480
11481/**
11482 * Perform deferred deletion of implicitly created diffs.
11483 *
11484 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not
11485 * backed up).
11486 *
11487 * @note Locks this object for writing!
11488 */
11489void Machine::i_rollbackMedia()
11490{
11491 AutoCaller autoCaller(this);
11492 AssertComRCReturnVoid(autoCaller.rc());
11493
11494 // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11495 LogFlowThisFunc(("Entering rollbackMedia\n"));
11496
11497 HRESULT rc = S_OK;
11498
11499 /* no attach/detach operations -- nothing to do */
11500 if (!mMediaData.isBackedUp())
11501 return;
11502
11503 /* enumerate new attachments */
11504 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
11505 it != mMediaData->mAttachments.end();
11506 ++it)
11507 {
11508 MediumAttachment *pAttach = *it;
11509 /* Fix up the backrefs for DVD/floppy media. */
11510 if (pAttach->i_getType() != DeviceType_HardDisk)
11511 {
11512 Medium* pMedium = pAttach->i_getMedium();
11513 if (pMedium)
11514 {
11515 rc = pMedium->i_removeBackReference(mData->mUuid);
11516 AssertComRC(rc);
11517 }
11518 }
11519
11520 (*it)->i_rollback();
11521
11522 pAttach = *it;
11523 /* Fix up the backrefs for DVD/floppy media. */
11524 if (pAttach->i_getType() != DeviceType_HardDisk)
11525 {
11526 Medium* pMedium = pAttach->i_getMedium();
11527 if (pMedium)
11528 {
11529 rc = pMedium->i_addBackReference(mData->mUuid);
11530 AssertComRC(rc);
11531 }
11532 }
11533 }
11534
11535 /** @todo convert all this Machine-based voodoo to MediumAttachment
11536 * based rollback logic. */
11537 i_deleteImplicitDiffs(Global::IsOnline(mData->mMachineState));
11538
11539 return;
11540}
11541
11542/**
11543 * Returns true if the settings file is located in the directory named exactly
11544 * as the machine; this means, among other things, that the machine directory
11545 * should be auto-renamed.
11546 *
11547 * @param aSettingsDir if not NULL, the full machine settings file directory
11548 * name will be assigned there.
11549 *
11550 * @note Doesn't lock anything.
11551 * @note Not thread safe (must be called from this object's lock).
11552 */
11553bool Machine::i_isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const
11554{
11555 Utf8Str strMachineDirName(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11556 strMachineDirName.stripFilename(); // path/to/machinesfolder/vmname
11557 if (aSettingsDir)
11558 *aSettingsDir = strMachineDirName;
11559 strMachineDirName.stripPath(); // vmname
11560 Utf8Str strConfigFileOnly(mData->m_strConfigFileFull); // path/to/machinesfolder/vmname/vmname.vbox
11561 strConfigFileOnly.stripPath() // vmname.vbox
11562 .stripSuffix(); // vmname
11563 /** @todo hack, make somehow use of ComposeMachineFilename */
11564 if (mUserData->s.fDirectoryIncludesUUID)
11565 strConfigFileOnly += Utf8StrFmt(" (%RTuuid)", mData->mUuid.raw());
11566
11567 AssertReturn(!strMachineDirName.isEmpty(), false);
11568 AssertReturn(!strConfigFileOnly.isEmpty(), false);
11569
11570 return strMachineDirName == strConfigFileOnly;
11571}
11572
11573/**
11574 * Discards all changes to machine settings.
11575 *
11576 * @param aNotify Whether to notify the direct session about changes or not.
11577 *
11578 * @note Locks objects for writing!
11579 */
11580void Machine::i_rollback(bool aNotify)
11581{
11582 AutoCaller autoCaller(this);
11583 AssertComRCReturn(autoCaller.rc(), (void)0);
11584
11585 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
11586
11587 if (!mStorageControllers.isNull())
11588 {
11589 if (mStorageControllers.isBackedUp())
11590 {
11591 /* unitialize all new devices (absent in the backed up list). */
11592 StorageControllerList::const_iterator it = mStorageControllers->begin();
11593 StorageControllerList *backedList = mStorageControllers.backedUpData();
11594 while (it != mStorageControllers->end())
11595 {
11596 if ( std::find(backedList->begin(), backedList->end(), *it)
11597 == backedList->end()
11598 )
11599 {
11600 (*it)->uninit();
11601 }
11602 ++it;
11603 }
11604
11605 /* restore the list */
11606 mStorageControllers.rollback();
11607 }
11608
11609 /* rollback any changes to devices after restoring the list */
11610 if (mData->flModifications & IsModified_Storage)
11611 {
11612 StorageControllerList::const_iterator it = mStorageControllers->begin();
11613 while (it != mStorageControllers->end())
11614 {
11615 (*it)->i_rollback();
11616 ++it;
11617 }
11618 }
11619 }
11620
11621 if (!mUSBControllers.isNull())
11622 {
11623 if (mUSBControllers.isBackedUp())
11624 {
11625 /* unitialize all new devices (absent in the backed up list). */
11626 USBControllerList::const_iterator it = mUSBControllers->begin();
11627 USBControllerList *backedList = mUSBControllers.backedUpData();
11628 while (it != mUSBControllers->end())
11629 {
11630 if ( std::find(backedList->begin(), backedList->end(), *it)
11631 == backedList->end()
11632 )
11633 {
11634 (*it)->uninit();
11635 }
11636 ++it;
11637 }
11638
11639 /* restore the list */
11640 mUSBControllers.rollback();
11641 }
11642
11643 /* rollback any changes to devices after restoring the list */
11644 if (mData->flModifications & IsModified_USB)
11645 {
11646 USBControllerList::const_iterator it = mUSBControllers->begin();
11647 while (it != mUSBControllers->end())
11648 {
11649 (*it)->i_rollback();
11650 ++it;
11651 }
11652 }
11653 }
11654
11655 mUserData.rollback();
11656
11657 mHWData.rollback();
11658
11659 if (mData->flModifications & IsModified_Storage)
11660 i_rollbackMedia();
11661
11662 if (mBIOSSettings)
11663 mBIOSSettings->i_rollback();
11664
11665 if (mVRDEServer && (mData->flModifications & IsModified_VRDEServer))
11666 mVRDEServer->i_rollback();
11667
11668 if (mAudioAdapter)
11669 mAudioAdapter->i_rollback();
11670
11671 if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB))
11672 mUSBDeviceFilters->i_rollback();
11673
11674 if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl))
11675 mBandwidthControl->i_rollback();
11676
11677 if (!mHWData.isNull())
11678 mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType));
11679 NetworkAdapterVector networkAdapters(mNetworkAdapters.size());
11680 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];
11681 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];
11682
11683 if (mData->flModifications & IsModified_NetworkAdapters)
11684 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
11685 if ( mNetworkAdapters[slot]
11686 && mNetworkAdapters[slot]->i_isModified())
11687 {
11688 mNetworkAdapters[slot]->i_rollback();
11689 networkAdapters[slot] = mNetworkAdapters[slot];
11690 }
11691
11692 if (mData->flModifications & IsModified_SerialPorts)
11693 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11694 if ( mSerialPorts[slot]
11695 && mSerialPorts[slot]->i_isModified())
11696 {
11697 mSerialPorts[slot]->i_rollback();
11698 serialPorts[slot] = mSerialPorts[slot];
11699 }
11700
11701 if (mData->flModifications & IsModified_ParallelPorts)
11702 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11703 if ( mParallelPorts[slot]
11704 && mParallelPorts[slot]->i_isModified())
11705 {
11706 mParallelPorts[slot]->i_rollback();
11707 parallelPorts[slot] = mParallelPorts[slot];
11708 }
11709
11710 if (aNotify)
11711 {
11712 /* inform the direct session about changes */
11713
11714 ComObjPtr<Machine> that = this;
11715 uint32_t flModifications = mData->flModifications;
11716 alock.release();
11717
11718 if (flModifications & IsModified_SharedFolders)
11719 that->i_onSharedFolderChange();
11720
11721 if (flModifications & IsModified_VRDEServer)
11722 that->i_onVRDEServerChange(/* aRestart */ TRUE);
11723 if (flModifications & IsModified_USB)
11724 that->i_onUSBControllerChange();
11725
11726 for (ULONG slot = 0; slot < networkAdapters.size(); ++slot)
11727 if (networkAdapters[slot])
11728 that->i_onNetworkAdapterChange(networkAdapters[slot], FALSE);
11729 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); ++slot)
11730 if (serialPorts[slot])
11731 that->i_onSerialPortChange(serialPorts[slot]);
11732 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); ++slot)
11733 if (parallelPorts[slot])
11734 that->i_onParallelPortChange(parallelPorts[slot]);
11735
11736 if (flModifications & IsModified_Storage)
11737 that->i_onStorageControllerChange();
11738
11739#if 0
11740 if (flModifications & IsModified_BandwidthControl)
11741 that->onBandwidthControlChange();
11742#endif
11743 }
11744}
11745
11746/**
11747 * Commits all the changes to machine settings.
11748 *
11749 * Note that this operation is supposed to never fail.
11750 *
11751 * @note Locks this object and children for writing.
11752 */
11753void Machine::i_commit()
11754{
11755 AutoCaller autoCaller(this);
11756 AssertComRCReturnVoid(autoCaller.rc());
11757
11758 AutoCaller peerCaller(mPeer);
11759 AssertComRCReturnVoid(peerCaller.rc());
11760
11761 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);
11762
11763 /*
11764 * use safe commit to ensure Snapshot machines (that share mUserData)
11765 * will still refer to a valid memory location
11766 */
11767 mUserData.commitCopy();
11768
11769 mHWData.commit();
11770
11771 if (mMediaData.isBackedUp())
11772 i_commitMedia(Global::IsOnline(mData->mMachineState));
11773
11774 mBIOSSettings->i_commit();
11775 mVRDEServer->i_commit();
11776 mAudioAdapter->i_commit();
11777 mUSBDeviceFilters->i_commit();
11778 mBandwidthControl->i_commit();
11779
11780 /* Since mNetworkAdapters is a list which might have been changed (resized)
11781 * without using the Backupable<> template we need to handle the copying
11782 * of the list entries manually, including the creation of peers for the
11783 * new objects. */
11784 bool commitNetworkAdapters = false;
11785 size_t newSize = Global::getMaxNetworkAdapters(mHWData->mChipsetType);
11786 if (mPeer)
11787 {
11788 /* commit everything, even the ones which will go away */
11789 for (size_t slot = 0; slot < mNetworkAdapters.size(); slot++)
11790 mNetworkAdapters[slot]->i_commit();
11791 /* copy over the new entries, creating a peer and uninit the original */
11792 mPeer->mNetworkAdapters.resize(RT_MAX(newSize, mPeer->mNetworkAdapters.size()));
11793 for (size_t slot = 0; slot < newSize; slot++)
11794 {
11795 /* look if this adapter has a peer device */
11796 ComObjPtr<NetworkAdapter> peer = mNetworkAdapters[slot]->i_getPeer();
11797 if (!peer)
11798 {
11799 /* no peer means the adapter is a newly created one;
11800 * create a peer owning data this data share it with */
11801 peer.createObject();
11802 peer->init(mPeer, mNetworkAdapters[slot], true /* aReshare */);
11803 }
11804 mPeer->mNetworkAdapters[slot] = peer;
11805 }
11806 /* uninit any no longer needed network adapters */
11807 for (size_t slot = newSize; slot < mNetworkAdapters.size(); ++slot)
11808 mNetworkAdapters[slot]->uninit();
11809 for (size_t slot = newSize; slot < mPeer->mNetworkAdapters.size(); ++slot)
11810 {
11811 if (mPeer->mNetworkAdapters[slot])
11812 mPeer->mNetworkAdapters[slot]->uninit();
11813 }
11814 /* Keep the original network adapter count until this point, so that
11815 * discarding a chipset type change will not lose settings. */
11816 mNetworkAdapters.resize(newSize);
11817 mPeer->mNetworkAdapters.resize(newSize);
11818 }
11819 else
11820 {
11821 /* we have no peer (our parent is the newly created machine);
11822 * just commit changes to the network adapters */
11823 commitNetworkAdapters = true;
11824 }
11825 if (commitNetworkAdapters)
11826 for (size_t slot = 0; slot < mNetworkAdapters.size(); ++slot)
11827 mNetworkAdapters[slot]->i_commit();
11828
11829 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
11830 mSerialPorts[slot]->i_commit();
11831 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
11832 mParallelPorts[slot]->i_commit();
11833
11834 bool commitStorageControllers = false;
11835
11836 if (mStorageControllers.isBackedUp())
11837 {
11838 mStorageControllers.commit();
11839
11840 if (mPeer)
11841 {
11842 /* Commit all changes to new controllers (this will reshare data with
11843 * peers for those who have peers) */
11844 StorageControllerList *newList = new StorageControllerList();
11845 StorageControllerList::const_iterator it = mStorageControllers->begin();
11846 while (it != mStorageControllers->end())
11847 {
11848 (*it)->i_commit();
11849
11850 /* look if this controller has a peer device */
11851 ComObjPtr<StorageController> peer = (*it)->i_getPeer();
11852 if (!peer)
11853 {
11854 /* no peer means the device is a newly created one;
11855 * create a peer owning data this device share it with */
11856 peer.createObject();
11857 peer->init(mPeer, *it, true /* aReshare */);
11858 }
11859 else
11860 {
11861 /* remove peer from the old list */
11862 mPeer->mStorageControllers->remove(peer);
11863 }
11864 /* and add it to the new list */
11865 newList->push_back(peer);
11866
11867 ++it;
11868 }
11869
11870 /* uninit old peer's controllers that are left */
11871 it = mPeer->mStorageControllers->begin();
11872 while (it != mPeer->mStorageControllers->end())
11873 {
11874 (*it)->uninit();
11875 ++it;
11876 }
11877
11878 /* attach new list of controllers to our peer */
11879 mPeer->mStorageControllers.attach(newList);
11880 }
11881 else
11882 {
11883 /* we have no peer (our parent is the newly created machine);
11884 * just commit changes to devices */
11885 commitStorageControllers = true;
11886 }
11887 }
11888 else
11889 {
11890 /* the list of controllers itself is not changed,
11891 * just commit changes to controllers themselves */
11892 commitStorageControllers = true;
11893 }
11894
11895 if (commitStorageControllers)
11896 {
11897 StorageControllerList::const_iterator it = mStorageControllers->begin();
11898 while (it != mStorageControllers->end())
11899 {
11900 (*it)->i_commit();
11901 ++it;
11902 }
11903 }
11904
11905 bool commitUSBControllers = false;
11906
11907 if (mUSBControllers.isBackedUp())
11908 {
11909 mUSBControllers.commit();
11910
11911 if (mPeer)
11912 {
11913 /* Commit all changes to new controllers (this will reshare data with
11914 * peers for those who have peers) */
11915 USBControllerList *newList = new USBControllerList();
11916 USBControllerList::const_iterator it = mUSBControllers->begin();
11917 while (it != mUSBControllers->end())
11918 {
11919 (*it)->i_commit();
11920
11921 /* look if this controller has a peer device */
11922 ComObjPtr<USBController> peer = (*it)->i_getPeer();
11923 if (!peer)
11924 {
11925 /* no peer means the device is a newly created one;
11926 * create a peer owning data this device share it with */
11927 peer.createObject();
11928 peer->init(mPeer, *it, true /* aReshare */);
11929 }
11930 else
11931 {
11932 /* remove peer from the old list */
11933 mPeer->mUSBControllers->remove(peer);
11934 }
11935 /* and add it to the new list */
11936 newList->push_back(peer);
11937
11938 ++it;
11939 }
11940
11941 /* uninit old peer's controllers that are left */
11942 it = mPeer->mUSBControllers->begin();
11943 while (it != mPeer->mUSBControllers->end())
11944 {
11945 (*it)->uninit();
11946 ++it;
11947 }
11948
11949 /* attach new list of controllers to our peer */
11950 mPeer->mUSBControllers.attach(newList);
11951 }
11952 else
11953 {
11954 /* we have no peer (our parent is the newly created machine);
11955 * just commit changes to devices */
11956 commitUSBControllers = true;
11957 }
11958 }
11959 else
11960 {
11961 /* the list of controllers itself is not changed,
11962 * just commit changes to controllers themselves */
11963 commitUSBControllers = true;
11964 }
11965
11966 if (commitUSBControllers)
11967 {
11968 USBControllerList::const_iterator it = mUSBControllers->begin();
11969 while (it != mUSBControllers->end())
11970 {
11971 (*it)->i_commit();
11972 ++it;
11973 }
11974 }
11975
11976 if (i_isSessionMachine())
11977 {
11978 /* attach new data to the primary machine and reshare it */
11979 mPeer->mUserData.attach(mUserData);
11980 mPeer->mHWData.attach(mHWData);
11981 /* mMediaData is reshared by fixupMedia */
11982 // mPeer->mMediaData.attach(mMediaData);
11983 Assert(mPeer->mMediaData.data() == mMediaData.data());
11984 }
11985}
11986
11987/**
11988 * Copies all the hardware data from the given machine.
11989 *
11990 * Currently, only called when the VM is being restored from a snapshot. In
11991 * particular, this implies that the VM is not running during this method's
11992 * call.
11993 *
11994 * @note This method must be called from under this object's lock.
11995 *
11996 * @note This method doesn't call #commit(), so all data remains backed up and
11997 * unsaved.
11998 */
11999void Machine::i_copyFrom(Machine *aThat)
12000{
12001 AssertReturnVoid(!i_isSnapshotMachine());
12002 AssertReturnVoid(aThat->i_isSnapshotMachine());
12003
12004 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));
12005
12006 mHWData.assignCopy(aThat->mHWData);
12007
12008 // create copies of all shared folders (mHWData after attaching a copy
12009 // contains just references to original objects)
12010 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();
12011 it != mHWData->mSharedFolders.end();
12012 ++it)
12013 {
12014 ComObjPtr<SharedFolder> folder;
12015 folder.createObject();
12016 HRESULT rc = folder->initCopy(i_getMachine(), *it);
12017 AssertComRC(rc);
12018 *it = folder;
12019 }
12020
12021 mBIOSSettings->i_copyFrom(aThat->mBIOSSettings);
12022 mVRDEServer->i_copyFrom(aThat->mVRDEServer);
12023 mAudioAdapter->i_copyFrom(aThat->mAudioAdapter);
12024 mUSBDeviceFilters->i_copyFrom(aThat->mUSBDeviceFilters);
12025 mBandwidthControl->i_copyFrom(aThat->mBandwidthControl);
12026
12027 /* create private copies of all controllers */
12028 mStorageControllers.backup();
12029 mStorageControllers->clear();
12030 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();
12031 it != aThat->mStorageControllers->end();
12032 ++it)
12033 {
12034 ComObjPtr<StorageController> ctrl;
12035 ctrl.createObject();
12036 ctrl->initCopy(this, *it);
12037 mStorageControllers->push_back(ctrl);
12038 }
12039
12040 /* create private copies of all USB controllers */
12041 mUSBControllers.backup();
12042 mUSBControllers->clear();
12043 for (USBControllerList::iterator it = aThat->mUSBControllers->begin();
12044 it != aThat->mUSBControllers->end();
12045 ++it)
12046 {
12047 ComObjPtr<USBController> ctrl;
12048 ctrl.createObject();
12049 ctrl->initCopy(this, *it);
12050 mUSBControllers->push_back(ctrl);
12051 }
12052
12053 mNetworkAdapters.resize(aThat->mNetworkAdapters.size());
12054 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12055 mNetworkAdapters[slot]->i_copyFrom(aThat->mNetworkAdapters[slot]);
12056 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12057 mSerialPorts[slot]->i_copyFrom(aThat->mSerialPorts[slot]);
12058 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12059 mParallelPorts[slot]->i_copyFrom(aThat->mParallelPorts[slot]);
12060}
12061
12062/**
12063 * Returns whether the given storage controller is hotplug capable.
12064 *
12065 * @returns true if the controller supports hotplugging
12066 * false otherwise.
12067 * @param enmCtrlType The controller type to check for.
12068 */
12069bool Machine::i_isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
12070{
12071 ComPtr<ISystemProperties> systemProperties;
12072 HRESULT rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
12073 if (FAILED(rc))
12074 return false;
12075
12076 BOOL aHotplugCapable = FALSE;
12077 systemProperties->GetStorageControllerHotplugCapable(enmCtrlType, &aHotplugCapable);
12078
12079 return RT_BOOL(aHotplugCapable);
12080}
12081
12082#ifdef VBOX_WITH_RESOURCE_USAGE_API
12083
12084void Machine::i_getDiskList(MediaList &list)
12085{
12086 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
12087 it != mMediaData->mAttachments.end();
12088 ++it)
12089 {
12090 MediumAttachment* pAttach = *it;
12091 /* just in case */
12092 AssertStmt(pAttach, continue);
12093
12094 AutoCaller localAutoCallerA(pAttach);
12095 if (FAILED(localAutoCallerA.rc())) continue;
12096
12097 AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS);
12098
12099 if (pAttach->i_getType() == DeviceType_HardDisk)
12100 list.push_back(pAttach->i_getMedium());
12101 }
12102}
12103
12104void Machine::i_registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
12105{
12106 AssertReturnVoid(isWriteLockOnCurrentThread());
12107 AssertPtrReturnVoid(aCollector);
12108
12109 pm::CollectorHAL *hal = aCollector->getHAL();
12110 /* Create sub metrics */
12111 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",
12112 "Percentage of processor time spent in user mode by the VM process.");
12113 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",
12114 "Percentage of processor time spent in kernel mode by the VM process.");
12115 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",
12116 "Size of resident portion of VM process in memory.");
12117 pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used",
12118 "Actual size of all VM disks combined.");
12119 pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx",
12120 "Network receive rate.");
12121 pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx",
12122 "Network transmit rate.");
12123 /* Create and register base metrics */
12124 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,
12125 cpuLoadUser, cpuLoadKernel);
12126 aCollector->registerBaseMetric(cpuLoad);
12127 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,
12128 ramUsageUsed);
12129 aCollector->registerBaseMetric(ramUsage);
12130 MediaList disks;
12131 i_getDiskList(disks);
12132 pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks,
12133 diskUsageUsed);
12134 aCollector->registerBaseMetric(diskUsage);
12135
12136 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));
12137 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12138 new pm::AggregateAvg()));
12139 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12140 new pm::AggregateMin()));
12141 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,
12142 new pm::AggregateMax()));
12143 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));
12144 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12145 new pm::AggregateAvg()));
12146 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12147 new pm::AggregateMin()));
12148 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,
12149 new pm::AggregateMax()));
12150
12151 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));
12152 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12153 new pm::AggregateAvg()));
12154 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12155 new pm::AggregateMin()));
12156 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,
12157 new pm::AggregateMax()));
12158
12159 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0));
12160 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12161 new pm::AggregateAvg()));
12162 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12163 new pm::AggregateMin()));
12164 aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed,
12165 new pm::AggregateMax()));
12166
12167
12168 /* Guest metrics collector */
12169 mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
12170 aCollector->registerGuest(mCollectorGuest);
12171 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12172
12173 /* Create sub metrics */
12174 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
12175 "Percentage of processor time spent in user mode as seen by the guest.");
12176 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",
12177 "Percentage of processor time spent in kernel mode as seen by the guest.");
12178 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",
12179 "Percentage of processor time spent idling as seen by the guest.");
12180
12181 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */
12182 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");
12183 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");
12184 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");
12185 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");
12186 pm::SubMetric *guestMemCache = new pm::SubMetric(
12187 "Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");
12188
12189 pm::SubMetric *guestPagedTotal = new pm::SubMetric(
12190 "Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
12191
12192 /* Create and register base metrics */
12193 pm::BaseMetric *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine,
12194 machineNetRx, machineNetTx);
12195 aCollector->registerBaseMetric(machineNetRate);
12196
12197 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
12198 guestLoadUser, guestLoadKernel, guestLoadIdle);
12199 aCollector->registerBaseMetric(guestCpuLoad);
12200
12201 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
12202 guestMemTotal, guestMemFree,
12203 guestMemBalloon, guestMemShared,
12204 guestMemCache, guestPagedTotal);
12205 aCollector->registerBaseMetric(guestCpuMem);
12206
12207 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0));
12208 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg()));
12209 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin()));
12210 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax()));
12211
12212 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0));
12213 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg()));
12214 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin()));
12215 aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax()));
12216
12217 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));
12218 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));
12219 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));
12220 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));
12221
12222 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));
12223 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));
12224 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));
12225 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));
12226
12227 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));
12228 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));
12229 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));
12230 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));
12231
12232 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));
12233 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));
12234 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));
12235 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));
12236
12237 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));
12238 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));
12239 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));
12240 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));
12241
12242 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));
12243 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));
12244 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));
12245 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));
12246
12247 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));
12248 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));
12249 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));
12250 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));
12251
12252 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));
12253 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));
12254 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));
12255 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));
12256
12257 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));
12258 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));
12259 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));
12260 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));
12261}
12262
12263void Machine::i_unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)
12264{
12265 AssertReturnVoid(isWriteLockOnCurrentThread());
12266
12267 if (aCollector)
12268 {
12269 aCollector->unregisterMetricsFor(aMachine);
12270 aCollector->unregisterBaseMetricsFor(aMachine);
12271 }
12272}
12273
12274#endif /* VBOX_WITH_RESOURCE_USAGE_API */
12275
12276
12277////////////////////////////////////////////////////////////////////////////////
12278
12279DEFINE_EMPTY_CTOR_DTOR(SessionMachine)
12280
12281HRESULT SessionMachine::FinalConstruct()
12282{
12283 LogFlowThisFunc(("\n"));
12284
12285 mClientToken = NULL;
12286
12287 return BaseFinalConstruct();
12288}
12289
12290void SessionMachine::FinalRelease()
12291{
12292 LogFlowThisFunc(("\n"));
12293
12294 Assert(!mClientToken);
12295 /* paranoia, should not hang around any more */
12296 if (mClientToken)
12297 {
12298 delete mClientToken;
12299 mClientToken = NULL;
12300 }
12301
12302 uninit(Uninit::Unexpected);
12303
12304 BaseFinalRelease();
12305}
12306
12307/**
12308 * @note Must be called only by Machine::LockMachine() from its own write lock.
12309 */
12310HRESULT SessionMachine::init(Machine *aMachine)
12311{
12312 LogFlowThisFuncEnter();
12313 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));
12314
12315 AssertReturn(aMachine, E_INVALIDARG);
12316
12317 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);
12318
12319 /* Enclose the state transition NotReady->InInit->Ready */
12320 AutoInitSpan autoInitSpan(this);
12321 AssertReturn(autoInitSpan.isOk(), E_FAIL);
12322
12323 HRESULT rc = S_OK;
12324
12325 /* create the machine client token */
12326 try
12327 {
12328 mClientToken = new ClientToken(aMachine, this);
12329 if (!mClientToken->isReady())
12330 {
12331 delete mClientToken;
12332 mClientToken = NULL;
12333 rc = E_FAIL;
12334 }
12335 }
12336 catch (std::bad_alloc &)
12337 {
12338 rc = E_OUTOFMEMORY;
12339 }
12340 if (FAILED(rc))
12341 return rc;
12342
12343 /* memorize the peer Machine */
12344 unconst(mPeer) = aMachine;
12345 /* share the parent pointer */
12346 unconst(mParent) = aMachine->mParent;
12347
12348 /* take the pointers to data to share */
12349 mData.share(aMachine->mData);
12350 mSSData.share(aMachine->mSSData);
12351
12352 mUserData.share(aMachine->mUserData);
12353 mHWData.share(aMachine->mHWData);
12354 mMediaData.share(aMachine->mMediaData);
12355
12356 mStorageControllers.allocate();
12357 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();
12358 it != aMachine->mStorageControllers->end();
12359 ++it)
12360 {
12361 ComObjPtr<StorageController> ctl;
12362 ctl.createObject();
12363 ctl->init(this, *it);
12364 mStorageControllers->push_back(ctl);
12365 }
12366
12367 mUSBControllers.allocate();
12368 for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin();
12369 it != aMachine->mUSBControllers->end();
12370 ++it)
12371 {
12372 ComObjPtr<USBController> ctl;
12373 ctl.createObject();
12374 ctl->init(this, *it);
12375 mUSBControllers->push_back(ctl);
12376 }
12377
12378 unconst(mBIOSSettings).createObject();
12379 mBIOSSettings->init(this, aMachine->mBIOSSettings);
12380 /* create another VRDEServer object that will be mutable */
12381 unconst(mVRDEServer).createObject();
12382 mVRDEServer->init(this, aMachine->mVRDEServer);
12383 /* create another audio adapter object that will be mutable */
12384 unconst(mAudioAdapter).createObject();
12385 mAudioAdapter->init(this, aMachine->mAudioAdapter);
12386 /* create a list of serial ports that will be mutable */
12387 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot)
12388 {
12389 unconst(mSerialPorts[slot]).createObject();
12390 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);
12391 }
12392 /* create a list of parallel ports that will be mutable */
12393 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); ++slot)
12394 {
12395 unconst(mParallelPorts[slot]).createObject();
12396 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);
12397 }
12398
12399 /* create another USB device filters object that will be mutable */
12400 unconst(mUSBDeviceFilters).createObject();
12401 mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters);
12402
12403 /* create a list of network adapters that will be mutable */
12404 mNetworkAdapters.resize(aMachine->mNetworkAdapters.size());
12405 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12406 {
12407 unconst(mNetworkAdapters[slot]).createObject();
12408 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);
12409 }
12410
12411 /* create another bandwidth control object that will be mutable */
12412 unconst(mBandwidthControl).createObject();
12413 mBandwidthControl->init(this, aMachine->mBandwidthControl);
12414
12415 /* default is to delete saved state on Saved -> PoweredOff transition */
12416 mRemoveSavedState = true;
12417
12418 /* Confirm a successful initialization when it's the case */
12419 autoInitSpan.setSucceeded();
12420
12421 miNATNetworksStarted = 0;
12422
12423 LogFlowThisFuncLeave();
12424 return rc;
12425}
12426
12427/**
12428 * Uninitializes this session object. If the reason is other than
12429 * Uninit::Unexpected, then this method MUST be called from #checkForDeath()
12430 * or the client watcher code.
12431 *
12432 * @param aReason uninitialization reason
12433 *
12434 * @note Locks mParent + this object for writing.
12435 */
12436void SessionMachine::uninit(Uninit::Reason aReason)
12437{
12438 LogFlowThisFuncEnter();
12439 LogFlowThisFunc(("reason=%d\n", aReason));
12440
12441 /*
12442 * Strongly reference ourselves to prevent this object deletion after
12443 * mData->mSession.mMachine.setNull() below (which can release the last
12444 * reference and call the destructor). Important: this must be done before
12445 * accessing any members (and before AutoUninitSpan that does it as well).
12446 * This self reference will be released as the very last step on return.
12447 */
12448 ComObjPtr<SessionMachine> selfRef = this;
12449
12450 /* Enclose the state transition Ready->InUninit->NotReady */
12451 AutoUninitSpan autoUninitSpan(this);
12452 if (autoUninitSpan.uninitDone())
12453 {
12454 LogFlowThisFunc(("Already uninitialized\n"));
12455 LogFlowThisFuncLeave();
12456 return;
12457 }
12458
12459 if (autoUninitSpan.initFailed())
12460 {
12461 /* We've been called by init() because it's failed. It's not really
12462 * necessary (nor it's safe) to perform the regular uninit sequence
12463 * below, the following is enough.
12464 */
12465 LogFlowThisFunc(("Initialization failed.\n"));
12466 /* destroy the machine client token */
12467 if (mClientToken)
12468 {
12469 delete mClientToken;
12470 mClientToken = NULL;
12471 }
12472 uninitDataAndChildObjects();
12473 mData.free();
12474 unconst(mParent) = NULL;
12475 unconst(mPeer) = NULL;
12476 LogFlowThisFuncLeave();
12477 return;
12478 }
12479
12480 MachineState_T lastState;
12481 {
12482 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);
12483 lastState = mData->mMachineState;
12484 }
12485 NOREF(lastState);
12486
12487#ifdef VBOX_WITH_USB
12488 // release all captured USB devices, but do this before requesting the locks below
12489 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))
12490 {
12491 /* Console::captureUSBDevices() is called in the VM process only after
12492 * setting the machine state to Starting or Restoring.
12493 * Console::detachAllUSBDevices() will be called upon successful
12494 * termination. So, we need to release USB devices only if there was
12495 * an abnormal termination of a running VM.
12496 *
12497 * This is identical to SessionMachine::DetachAllUSBDevices except
12498 * for the aAbnormal argument. */
12499 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
12500 AssertComRC(rc);
12501 NOREF(rc);
12502
12503 USBProxyService *service = mParent->i_host()->i_usbProxyService();
12504 if (service)
12505 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);
12506 }
12507#endif /* VBOX_WITH_USB */
12508
12509 // we need to lock this object in uninit() because the lock is shared
12510 // with mPeer (as well as data we modify below). mParent lock is needed
12511 // by several calls to it, and USB needs host lock.
12512 AutoMultiWriteLock3 multilock(mParent, mParent->i_host(), this COMMA_LOCKVAL_SRC_POS);
12513
12514#ifdef VBOX_WITH_RESOURCE_USAGE_API
12515 /*
12516 * It is safe to call Machine::i_unregisterMetrics() here because
12517 * PerformanceCollector::samplerCallback no longer accesses guest methods
12518 * holding the lock.
12519 */
12520 i_unregisterMetrics(mParent->i_performanceCollector(), mPeer);
12521 /* The guest must be unregistered after its metrics (@bugref{5949}). */
12522 Log7(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest));
12523 if (mCollectorGuest)
12524 {
12525 mParent->i_performanceCollector()->unregisterGuest(mCollectorGuest);
12526 // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
12527 mCollectorGuest = NULL;
12528 }
12529#endif
12530
12531 if (aReason == Uninit::Abnormal)
12532 {
12533 Log1WarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n", Global::IsOnlineOrTransient(lastState)));
12534
12535 /* reset the state to Aborted */
12536 if (mData->mMachineState != MachineState_Aborted)
12537 i_setMachineState(MachineState_Aborted);
12538 }
12539
12540 // any machine settings modified?
12541 if (mData->flModifications)
12542 {
12543 Log1WarningThisFunc(("Discarding unsaved settings changes!\n"));
12544 i_rollback(false /* aNotify */);
12545 }
12546
12547 mData->mSession.mPID = NIL_RTPROCESS;
12548
12549 if (aReason == Uninit::Unexpected)
12550 {
12551 /* Uninitialization didn't come from #checkForDeath(), so tell the
12552 * client watcher thread to update the set of machines that have open
12553 * sessions. */
12554 mParent->i_updateClientWatcher();
12555 }
12556
12557 /* uninitialize all remote controls */
12558 if (mData->mSession.mRemoteControls.size())
12559 {
12560 LogFlowThisFunc(("Closing remote sessions (%d):\n",
12561 mData->mSession.mRemoteControls.size()));
12562
12563 Data::Session::RemoteControlList::iterator it =
12564 mData->mSession.mRemoteControls.begin();
12565 while (it != mData->mSession.mRemoteControls.end())
12566 {
12567 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));
12568 HRESULT rc = (*it)->Uninitialize();
12569 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));
12570 if (FAILED(rc))
12571 Log1WarningThisFunc(("Forgot to close the remote session?\n"));
12572 ++it;
12573 }
12574 mData->mSession.mRemoteControls.clear();
12575 }
12576
12577 /* Remove all references to the NAT network service. The service will stop
12578 * if all references (also from other VMs) are removed. */
12579 for (; miNATNetworksStarted > 0; miNATNetworksStarted--)
12580 {
12581 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12582 {
12583 NetworkAttachmentType_T type;
12584 HRESULT hrc;
12585
12586 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12587 if ( SUCCEEDED(hrc)
12588 && type == NetworkAttachmentType_NATNetwork)
12589 {
12590 Bstr name;
12591 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12592 if (SUCCEEDED(hrc))
12593 {
12594 multilock.release();
12595 LogRel(("VM '%s' stops using NAT network '%ls'\n",
12596 mUserData->s.strName.c_str(), name.raw()));
12597 mParent->i_natNetworkRefDec(name.raw());
12598 multilock.acquire();
12599 }
12600 }
12601 }
12602 }
12603
12604 /*
12605 * An expected uninitialization can come only from #checkForDeath().
12606 * Otherwise it means that something's gone really wrong (for example,
12607 * the Session implementation has released the VirtualBox reference
12608 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,
12609 * etc). However, it's also possible, that the client releases the IPC
12610 * semaphore correctly (i.e. before it releases the VirtualBox reference),
12611 * but the VirtualBox release event comes first to the server process.
12612 * This case is practically possible, so we should not assert on an
12613 * unexpected uninit, just log a warning.
12614 */
12615
12616 if ((aReason == Uninit::Unexpected))
12617 Log1WarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));
12618
12619 if (aReason != Uninit::Normal)
12620 {
12621 mData->mSession.mDirectControl.setNull();
12622 }
12623 else
12624 {
12625 /* this must be null here (see #OnSessionEnd()) */
12626 Assert(mData->mSession.mDirectControl.isNull());
12627 Assert(mData->mSession.mState == SessionState_Unlocking);
12628 Assert(!mData->mSession.mProgress.isNull());
12629 }
12630 if (mData->mSession.mProgress)
12631 {
12632 if (aReason == Uninit::Normal)
12633 mData->mSession.mProgress->i_notifyComplete(S_OK);
12634 else
12635 mData->mSession.mProgress->i_notifyComplete(E_FAIL,
12636 COM_IIDOF(ISession),
12637 getComponentName(),
12638 tr("The VM session was aborted"));
12639 mData->mSession.mProgress.setNull();
12640 }
12641
12642 /* remove the association between the peer machine and this session machine */
12643 Assert( (SessionMachine*)mData->mSession.mMachine == this
12644 || aReason == Uninit::Unexpected);
12645
12646 /* reset the rest of session data */
12647 mData->mSession.mLockType = LockType_Null;
12648 mData->mSession.mMachine.setNull();
12649 mData->mSession.mState = SessionState_Unlocked;
12650 mData->mSession.mName.setNull();
12651
12652 /* destroy the machine client token before leaving the exclusive lock */
12653 if (mClientToken)
12654 {
12655 delete mClientToken;
12656 mClientToken = NULL;
12657 }
12658
12659 /* fire an event */
12660 mParent->i_onSessionStateChange(mData->mUuid, SessionState_Unlocked);
12661
12662 uninitDataAndChildObjects();
12663
12664 /* free the essential data structure last */
12665 mData.free();
12666
12667 /* release the exclusive lock before setting the below two to NULL */
12668 multilock.release();
12669
12670 unconst(mParent) = NULL;
12671 unconst(mPeer) = NULL;
12672
12673 LogFlowThisFuncLeave();
12674}
12675
12676// util::Lockable interface
12677////////////////////////////////////////////////////////////////////////////////
12678
12679/**
12680 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle
12681 * with the primary Machine instance (mPeer).
12682 */
12683RWLockHandle *SessionMachine::lockHandle() const
12684{
12685 AssertReturn(mPeer != NULL, NULL);
12686 return mPeer->lockHandle();
12687}
12688
12689// IInternalMachineControl methods
12690////////////////////////////////////////////////////////////////////////////////
12691
12692/**
12693 * Passes collected guest statistics to performance collector object
12694 */
12695HRESULT SessionMachine::reportVmStatistics(ULONG aValidStats, ULONG aCpuUser,
12696 ULONG aCpuKernel, ULONG aCpuIdle,
12697 ULONG aMemTotal, ULONG aMemFree,
12698 ULONG aMemBalloon, ULONG aMemShared,
12699 ULONG aMemCache, ULONG aPageTotal,
12700 ULONG aAllocVMM, ULONG aFreeVMM,
12701 ULONG aBalloonedVMM, ULONG aSharedVMM,
12702 ULONG aVmNetRx, ULONG aVmNetTx)
12703{
12704#ifdef VBOX_WITH_RESOURCE_USAGE_API
12705 if (mCollectorGuest)
12706 mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle,
12707 aMemTotal, aMemFree, aMemBalloon, aMemShared,
12708 aMemCache, aPageTotal, aAllocVMM, aFreeVMM,
12709 aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx);
12710
12711 return S_OK;
12712#else
12713 NOREF(aValidStats);
12714 NOREF(aCpuUser);
12715 NOREF(aCpuKernel);
12716 NOREF(aCpuIdle);
12717 NOREF(aMemTotal);
12718 NOREF(aMemFree);
12719 NOREF(aMemBalloon);
12720 NOREF(aMemShared);
12721 NOREF(aMemCache);
12722 NOREF(aPageTotal);
12723 NOREF(aAllocVMM);
12724 NOREF(aFreeVMM);
12725 NOREF(aBalloonedVMM);
12726 NOREF(aSharedVMM);
12727 NOREF(aVmNetRx);
12728 NOREF(aVmNetTx);
12729 return E_NOTIMPL;
12730#endif
12731}
12732
12733////////////////////////////////////////////////////////////////////////////////
12734//
12735// SessionMachine task records
12736//
12737////////////////////////////////////////////////////////////////////////////////
12738
12739/**
12740 * Task record for saving the machine state.
12741 */
12742struct SessionMachine::SaveStateTask
12743 : public Machine::Task
12744{
12745 SaveStateTask(SessionMachine *m,
12746 Progress *p,
12747 const Utf8Str &t,
12748 Reason_T enmReason,
12749 const Utf8Str &strStateFilePath)
12750 : Task(m, p, t),
12751 m_enmReason(enmReason),
12752 m_strStateFilePath(strStateFilePath)
12753 {}
12754
12755 void handler()
12756 {
12757 ((SessionMachine *)(Machine *)m_pMachine)->i_saveStateHandler(*this);
12758 }
12759
12760 Reason_T m_enmReason;
12761 Utf8Str m_strStateFilePath;
12762};
12763
12764/**
12765 * Task thread implementation for SessionMachine::SaveState(), called from
12766 * SessionMachine::taskHandler().
12767 *
12768 * @note Locks this object for writing.
12769 *
12770 * @param task
12771 * @return
12772 */
12773void SessionMachine::i_saveStateHandler(SaveStateTask &task)
12774{
12775 LogFlowThisFuncEnter();
12776
12777 AutoCaller autoCaller(this);
12778 LogFlowThisFunc(("state=%d\n", getObjectState().getState()));
12779 if (FAILED(autoCaller.rc()))
12780 {
12781 /* we might have been uninitialized because the session was accidentally
12782 * closed by the client, so don't assert */
12783 HRESULT rc = setError(E_FAIL,
12784 tr("The session has been accidentally closed"));
12785 task.m_pProgress->i_notifyComplete(rc);
12786 LogFlowThisFuncLeave();
12787 return;
12788 }
12789
12790 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12791
12792 HRESULT rc = S_OK;
12793
12794 try
12795 {
12796 ComPtr<IInternalSessionControl> directControl;
12797 if (mData->mSession.mLockType == LockType_VM)
12798 directControl = mData->mSession.mDirectControl;
12799 if (directControl.isNull())
12800 throw setError(VBOX_E_INVALID_VM_STATE,
12801 tr("Trying to save state without a running VM"));
12802 alock.release();
12803 BOOL fSuspendedBySave;
12804 rc = directControl->SaveStateWithReason(task.m_enmReason, task.m_pProgress, Bstr(task.m_strStateFilePath).raw(), task.m_machineStateBackup != MachineState_Paused, &fSuspendedBySave);
12805 Assert(!fSuspendedBySave);
12806 alock.acquire();
12807
12808 AssertStmt( (SUCCEEDED(rc) && mData->mMachineState == MachineState_Saved)
12809 || (FAILED(rc) && mData->mMachineState == MachineState_Saving),
12810 throw E_FAIL);
12811
12812 if (SUCCEEDED(rc))
12813 {
12814 mSSData->strStateFilePath = task.m_strStateFilePath;
12815
12816 /* save all VM settings */
12817 rc = i_saveSettings(NULL);
12818 // no need to check whether VirtualBox.xml needs saving also since
12819 // we can't have a name change pending at this point
12820 }
12821 else
12822 {
12823 // On failure, set the state to the state we had at the beginning.
12824 i_setMachineState(task.m_machineStateBackup);
12825 i_updateMachineStateOnClient();
12826
12827 // Delete the saved state file (might have been already created).
12828 // No need to check whether this is shared with a snapshot here
12829 // because we certainly created a fresh saved state file here.
12830 RTFileDelete(task.m_strStateFilePath.c_str());
12831 }
12832 }
12833 catch (HRESULT aRC) { rc = aRC; }
12834
12835 task.m_pProgress->i_notifyComplete(rc);
12836
12837 LogFlowThisFuncLeave();
12838}
12839
12840/**
12841 * @note Locks this object for writing.
12842 */
12843HRESULT SessionMachine::saveState(ComPtr<IProgress> &aProgress)
12844{
12845 return i_saveStateWithReason(Reason_Unspecified, aProgress);
12846}
12847
12848HRESULT SessionMachine::i_saveStateWithReason(Reason_T aReason, ComPtr<IProgress> &aProgress)
12849{
12850 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12851
12852 HRESULT rc = i_checkStateDependency(MutableOrRunningStateDep);
12853 if (FAILED(rc)) return rc;
12854
12855 if ( mData->mMachineState != MachineState_Running
12856 && mData->mMachineState != MachineState_Paused
12857 )
12858 return setError(VBOX_E_INVALID_VM_STATE,
12859 tr("Cannot save the execution state as the machine is not running or paused (machine state: %s)"),
12860 Global::stringifyMachineState(mData->mMachineState));
12861
12862 ComObjPtr<Progress> pProgress;
12863 pProgress.createObject();
12864 rc = pProgress->init(i_getVirtualBox(),
12865 static_cast<IMachine *>(this) /* aInitiator */,
12866 Bstr(tr("Saving the execution state of the virtual machine")).raw(),
12867 FALSE /* aCancelable */);
12868 if (FAILED(rc))
12869 return rc;
12870
12871 Utf8Str strStateFilePath;
12872 i_composeSavedStateFilename(strStateFilePath);
12873
12874 /* create and start the task on a separate thread (note that it will not
12875 * start working until we release alock) */
12876 SaveStateTask *pTask = new SaveStateTask(this, pProgress, "SaveState", aReason, strStateFilePath);
12877 rc = pTask->createThread();
12878 if (FAILED(rc))
12879 return rc;
12880
12881 /* set the state to Saving (expected by Session::SaveStateWithReason()) */
12882 i_setMachineState(MachineState_Saving);
12883 i_updateMachineStateOnClient();
12884
12885 pProgress.queryInterfaceTo(aProgress.asOutParam());
12886
12887 return S_OK;
12888}
12889
12890/**
12891 * @note Locks this object for writing.
12892 */
12893HRESULT SessionMachine::adoptSavedState(const com::Utf8Str &aSavedStateFile)
12894{
12895 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12896
12897 HRESULT rc = i_checkStateDependency(MutableStateDep);
12898 if (FAILED(rc)) return rc;
12899
12900 if ( mData->mMachineState != MachineState_PoweredOff
12901 && mData->mMachineState != MachineState_Teleported
12902 && mData->mMachineState != MachineState_Aborted
12903 )
12904 return setError(VBOX_E_INVALID_VM_STATE,
12905 tr("Cannot adopt the saved machine state as the machine is not in Powered Off, Teleported or Aborted state (machine state: %s)"),
12906 Global::stringifyMachineState(mData->mMachineState));
12907
12908 com::Utf8Str stateFilePathFull;
12909 int vrc = i_calculateFullPath(aSavedStateFile, stateFilePathFull);
12910 if (RT_FAILURE(vrc))
12911 return setError(VBOX_E_FILE_ERROR,
12912 tr("Invalid saved state file path '%s' (%Rrc)"),
12913 aSavedStateFile.c_str(),
12914 vrc);
12915
12916 mSSData->strStateFilePath = stateFilePathFull;
12917
12918 /* The below i_setMachineState() will detect the state transition and will
12919 * update the settings file */
12920
12921 return i_setMachineState(MachineState_Saved);
12922}
12923
12924/**
12925 * @note Locks this object for writing.
12926 */
12927HRESULT SessionMachine::discardSavedState(BOOL aFRemoveFile)
12928{
12929 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12930
12931 HRESULT rc = i_checkStateDependency(MutableOrSavedStateDep);
12932 if (FAILED(rc)) return rc;
12933
12934 if (mData->mMachineState != MachineState_Saved)
12935 return setError(VBOX_E_INVALID_VM_STATE,
12936 tr("Cannot delete the machine state as the machine is not in the saved state (machine state: %s)"),
12937 Global::stringifyMachineState(mData->mMachineState));
12938
12939 mRemoveSavedState = RT_BOOL(aFRemoveFile);
12940
12941 /*
12942 * Saved -> PoweredOff transition will be detected in the SessionMachine
12943 * and properly handled.
12944 */
12945 rc = i_setMachineState(MachineState_PoweredOff);
12946 return rc;
12947}
12948
12949
12950/**
12951 * @note Locks the same as #i_setMachineState() does.
12952 */
12953HRESULT SessionMachine::updateState(MachineState_T aState)
12954{
12955 return i_setMachineState(aState);
12956}
12957
12958/**
12959 * @note Locks this object for writing.
12960 */
12961HRESULT SessionMachine::beginPowerUp(const ComPtr<IProgress> &aProgress)
12962{
12963 IProgress* pProgress(aProgress);
12964
12965 LogFlowThisFunc(("aProgress=%p\n", pProgress));
12966
12967 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
12968
12969 if (mData->mSession.mState != SessionState_Locked)
12970 return VBOX_E_INVALID_OBJECT_STATE;
12971
12972 if (!mData->mSession.mProgress.isNull())
12973 mData->mSession.mProgress->setOtherProgressObject(pProgress);
12974
12975 /* If we didn't reference the NAT network service yet, add a reference to
12976 * force a start */
12977 if (miNATNetworksStarted < 1)
12978 {
12979 for (ULONG slot = 0; slot < mNetworkAdapters.size(); ++slot)
12980 {
12981 NetworkAttachmentType_T type;
12982 HRESULT hrc;
12983 hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type);
12984 if ( SUCCEEDED(hrc)
12985 && type == NetworkAttachmentType_NATNetwork)
12986 {
12987 Bstr name;
12988 hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam());
12989 if (SUCCEEDED(hrc))
12990 {
12991 LogRel(("VM '%s' starts using NAT network '%ls'\n",
12992 mUserData->s.strName.c_str(), name.raw()));
12993 mPeer->lockHandle()->unlockWrite();
12994 mParent->i_natNetworkRefInc(name.raw());
12995#ifdef RT_LOCK_STRICT
12996 mPeer->lockHandle()->lockWrite(RT_SRC_POS);
12997#else
12998 mPeer->lockHandle()->lockWrite();
12999#endif
13000 }
13001 }
13002 }
13003 miNATNetworksStarted++;
13004 }
13005
13006 LogFlowThisFunc(("returns S_OK.\n"));
13007 return S_OK;
13008}
13009
13010/**
13011 * @note Locks this object for writing.
13012 */
13013HRESULT SessionMachine::endPowerUp(LONG aResult)
13014{
13015 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13016
13017 if (mData->mSession.mState != SessionState_Locked)
13018 return VBOX_E_INVALID_OBJECT_STATE;
13019
13020 /* Finalize the LaunchVMProcess progress object. */
13021 if (mData->mSession.mProgress)
13022 {
13023 mData->mSession.mProgress->notifyComplete((HRESULT)aResult);
13024 mData->mSession.mProgress.setNull();
13025 }
13026
13027 if (SUCCEEDED((HRESULT)aResult))
13028 {
13029#ifdef VBOX_WITH_RESOURCE_USAGE_API
13030 /* The VM has been powered up successfully, so it makes sense
13031 * now to offer the performance metrics for a running machine
13032 * object. Doing it earlier wouldn't be safe. */
13033 i_registerMetrics(mParent->i_performanceCollector(), mPeer,
13034 mData->mSession.mPID);
13035#endif /* VBOX_WITH_RESOURCE_USAGE_API */
13036 }
13037
13038 return S_OK;
13039}
13040
13041/**
13042 * @note Locks this object for writing.
13043 */
13044HRESULT SessionMachine::beginPoweringDown(ComPtr<IProgress> &aProgress)
13045{
13046 LogFlowThisFuncEnter();
13047
13048 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13049
13050 AssertReturn(mConsoleTaskData.mLastState == MachineState_Null,
13051 E_FAIL);
13052
13053 /* create a progress object to track operation completion */
13054 ComObjPtr<Progress> pProgress;
13055 pProgress.createObject();
13056 pProgress->init(i_getVirtualBox(),
13057 static_cast<IMachine *>(this) /* aInitiator */,
13058 Bstr(tr("Stopping the virtual machine")).raw(),
13059 FALSE /* aCancelable */);
13060
13061 /* fill in the console task data */
13062 mConsoleTaskData.mLastState = mData->mMachineState;
13063 mConsoleTaskData.mProgress = pProgress;
13064
13065 /* set the state to Stopping (this is expected by Console::PowerDown()) */
13066 i_setMachineState(MachineState_Stopping);
13067
13068 pProgress.queryInterfaceTo(aProgress.asOutParam());
13069
13070 return S_OK;
13071}
13072
13073/**
13074 * @note Locks this object for writing.
13075 */
13076HRESULT SessionMachine::endPoweringDown(LONG aResult,
13077 const com::Utf8Str &aErrMsg)
13078{
13079 LogFlowThisFuncEnter();
13080
13081 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13082
13083 AssertReturn( ( (SUCCEEDED(aResult) && mData->mMachineState == MachineState_PoweredOff)
13084 || (FAILED(aResult) && mData->mMachineState == MachineState_Stopping))
13085 && mConsoleTaskData.mLastState != MachineState_Null,
13086 E_FAIL);
13087
13088 /*
13089 * On failure, set the state to the state we had when BeginPoweringDown()
13090 * was called (this is expected by Console::PowerDown() and the associated
13091 * task). On success the VM process already changed the state to
13092 * MachineState_PoweredOff, so no need to do anything.
13093 */
13094 if (FAILED(aResult))
13095 i_setMachineState(mConsoleTaskData.mLastState);
13096
13097 /* notify the progress object about operation completion */
13098 Assert(mConsoleTaskData.mProgress);
13099 if (SUCCEEDED(aResult))
13100 mConsoleTaskData.mProgress->i_notifyComplete(S_OK);
13101 else
13102 {
13103 if (aErrMsg.length())
13104 mConsoleTaskData.mProgress->i_notifyComplete(aResult,
13105 COM_IIDOF(ISession),
13106 getComponentName(),
13107 aErrMsg.c_str());
13108 else
13109 mConsoleTaskData.mProgress->i_notifyComplete(aResult);
13110 }
13111
13112 /* clear out the temporary saved state data */
13113 mConsoleTaskData.mLastState = MachineState_Null;
13114 mConsoleTaskData.mProgress.setNull();
13115
13116 LogFlowThisFuncLeave();
13117 return S_OK;
13118}
13119
13120
13121/**
13122 * Goes through the USB filters of the given machine to see if the given
13123 * device matches any filter or not.
13124 *
13125 * @note Locks the same as USBController::hasMatchingFilter() does.
13126 */
13127HRESULT SessionMachine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
13128 BOOL *aMatched,
13129 ULONG *aMaskedInterfaces)
13130{
13131 LogFlowThisFunc(("\n"));
13132
13133#ifdef VBOX_WITH_USB
13134 *aMatched = mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedInterfaces);
13135#else
13136 NOREF(aDevice);
13137 NOREF(aMaskedInterfaces);
13138 *aMatched = FALSE;
13139#endif
13140
13141 return S_OK;
13142}
13143
13144/**
13145 * @note Locks the same as Host::captureUSBDevice() does.
13146 */
13147HRESULT SessionMachine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
13148{
13149 LogFlowThisFunc(("\n"));
13150
13151#ifdef VBOX_WITH_USB
13152 /* if captureDeviceForVM() fails, it must have set extended error info */
13153 clearError();
13154 MultiResult rc = mParent->i_host()->i_checkUSBProxyService();
13155 if (FAILED(rc)) return rc;
13156
13157 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13158 AssertReturn(service, E_FAIL);
13159 return service->captureDeviceForVM(this, aId.ref(), aCaptureFilename);
13160#else
13161 NOREF(aId);
13162 return E_NOTIMPL;
13163#endif
13164}
13165
13166/**
13167 * @note Locks the same as Host::detachUSBDevice() does.
13168 */
13169HRESULT SessionMachine::detachUSBDevice(const com::Guid &aId,
13170 BOOL aDone)
13171{
13172 LogFlowThisFunc(("\n"));
13173
13174#ifdef VBOX_WITH_USB
13175 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13176 AssertReturn(service, E_FAIL);
13177 return service->detachDeviceFromVM(this, aId.ref(), !!aDone);
13178#else
13179 NOREF(aId);
13180 NOREF(aDone);
13181 return E_NOTIMPL;
13182#endif
13183}
13184
13185/**
13186 * Inserts all machine filters to the USB proxy service and then calls
13187 * Host::autoCaptureUSBDevices().
13188 *
13189 * Called by Console from the VM process upon VM startup.
13190 *
13191 * @note Locks what called methods lock.
13192 */
13193HRESULT SessionMachine::autoCaptureUSBDevices()
13194{
13195 LogFlowThisFunc(("\n"));
13196
13197#ifdef VBOX_WITH_USB
13198 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(true /* aInsertFilters */);
13199 AssertComRC(rc);
13200 NOREF(rc);
13201
13202 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13203 AssertReturn(service, E_FAIL);
13204 return service->autoCaptureDevicesForVM(this);
13205#else
13206 return S_OK;
13207#endif
13208}
13209
13210/**
13211 * Removes all machine filters from the USB proxy service and then calls
13212 * Host::detachAllUSBDevices().
13213 *
13214 * Called by Console from the VM process upon normal VM termination or by
13215 * SessionMachine::uninit() upon abnormal VM termination (from under the
13216 * Machine/SessionMachine lock).
13217 *
13218 * @note Locks what called methods lock.
13219 */
13220HRESULT SessionMachine::detachAllUSBDevices(BOOL aDone)
13221{
13222 LogFlowThisFunc(("\n"));
13223
13224#ifdef VBOX_WITH_USB
13225 HRESULT rc = mUSBDeviceFilters->i_notifyProxy(false /* aInsertFilters */);
13226 AssertComRC(rc);
13227 NOREF(rc);
13228
13229 USBProxyService *service = mParent->i_host()->i_usbProxyService();
13230 AssertReturn(service, E_FAIL);
13231 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);
13232#else
13233 NOREF(aDone);
13234 return S_OK;
13235#endif
13236}
13237
13238/**
13239 * @note Locks this object for writing.
13240 */
13241HRESULT SessionMachine::onSessionEnd(const ComPtr<ISession> &aSession,
13242 ComPtr<IProgress> &aProgress)
13243{
13244 LogFlowThisFuncEnter();
13245
13246 LogFlowThisFunc(("callerstate=%d\n", getObjectState().getState()));
13247 /*
13248 * We don't assert below because it might happen that a non-direct session
13249 * informs us it is closed right after we've been uninitialized -- it's ok.
13250 */
13251
13252 /* get IInternalSessionControl interface */
13253 ComPtr<IInternalSessionControl> control(aSession);
13254
13255 ComAssertRet(!control.isNull(), E_INVALIDARG);
13256
13257 /* Creating a Progress object requires the VirtualBox lock, and
13258 * thus locking it here is required by the lock order rules. */
13259 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);
13260
13261 if (control == mData->mSession.mDirectControl)
13262 {
13263 /* The direct session is being normally closed by the client process
13264 * ----------------------------------------------------------------- */
13265
13266 /* go to the closing state (essential for all open*Session() calls and
13267 * for #checkForDeath()) */
13268 Assert(mData->mSession.mState == SessionState_Locked);
13269 mData->mSession.mState = SessionState_Unlocking;
13270
13271 /* set direct control to NULL to release the remote instance */
13272 mData->mSession.mDirectControl.setNull();
13273 LogFlowThisFunc(("Direct control is set to NULL\n"));
13274
13275 if (mData->mSession.mProgress)
13276 {
13277 /* finalize the progress, someone might wait if a frontend
13278 * closes the session before powering on the VM. */
13279 mData->mSession.mProgress->notifyComplete(E_FAIL,
13280 COM_IIDOF(ISession),
13281 getComponentName(),
13282 tr("The VM session was closed before any attempt to power it on"));
13283 mData->mSession.mProgress.setNull();
13284 }
13285
13286 /* Create the progress object the client will use to wait until
13287 * #checkForDeath() is called to uninitialize this session object after
13288 * it releases the IPC semaphore.
13289 * Note! Because we're "reusing" mProgress here, this must be a proxy
13290 * object just like for LaunchVMProcess. */
13291 Assert(mData->mSession.mProgress.isNull());
13292 ComObjPtr<ProgressProxy> progress;
13293 progress.createObject();
13294 ComPtr<IUnknown> pPeer(mPeer);
13295 progress->init(mParent, pPeer,
13296 Bstr(tr("Closing session")).raw(),
13297 FALSE /* aCancelable */);
13298 progress.queryInterfaceTo(aProgress.asOutParam());
13299 mData->mSession.mProgress = progress;
13300 }
13301 else
13302 {
13303 /* the remote session is being normally closed */
13304 Data::Session::RemoteControlList::iterator it =
13305 mData->mSession.mRemoteControls.begin();
13306 while (it != mData->mSession.mRemoteControls.end())
13307 {
13308 if (control == *it)
13309 break;
13310 ++it;
13311 }
13312 BOOL found = it != mData->mSession.mRemoteControls.end();
13313 ComAssertMsgRet(found, ("The session is not found in the session list!"),
13314 E_INVALIDARG);
13315 // This MUST be erase(it), not remove(*it) as the latter triggers a
13316 // very nasty use after free due to the place where the value "lives".
13317 mData->mSession.mRemoteControls.erase(it);
13318 }
13319
13320 /* signal the client watcher thread, because the client is going away */
13321 mParent->i_updateClientWatcher();
13322
13323 LogFlowThisFuncLeave();
13324 return S_OK;
13325}
13326
13327HRESULT SessionMachine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
13328 std::vector<com::Utf8Str> &aValues,
13329 std::vector<LONG64> &aTimestamps,
13330 std::vector<com::Utf8Str> &aFlags)
13331{
13332 LogFlowThisFunc(("\n"));
13333
13334#ifdef VBOX_WITH_GUEST_PROPS
13335 using namespace guestProp;
13336
13337 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13338
13339 size_t cEntries = mHWData->mGuestProperties.size();
13340 aNames.resize(cEntries);
13341 aValues.resize(cEntries);
13342 aTimestamps.resize(cEntries);
13343 aFlags.resize(cEntries);
13344
13345 size_t i = 0;
13346 for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin();
13347 it != mHWData->mGuestProperties.end();
13348 ++it, ++i)
13349 {
13350 char szFlags[MAX_FLAGS_LEN + 1];
13351 aNames[i] = it->first;
13352 aValues[i] = it->second.strValue;
13353 aTimestamps[i] = it->second.mTimestamp;
13354
13355 /* If it is NULL, keep it NULL. */
13356 if (it->second.mFlags)
13357 {
13358 writeFlags(it->second.mFlags, szFlags);
13359 aFlags[i] = szFlags;
13360 }
13361 else
13362 aFlags[i] = "";
13363 }
13364 return S_OK;
13365#else
13366 ReturnComNotImplemented();
13367#endif
13368}
13369
13370HRESULT SessionMachine::pushGuestProperty(const com::Utf8Str &aName,
13371 const com::Utf8Str &aValue,
13372 LONG64 aTimestamp,
13373 const com::Utf8Str &aFlags)
13374{
13375 LogFlowThisFunc(("\n"));
13376
13377#ifdef VBOX_WITH_GUEST_PROPS
13378 using namespace guestProp;
13379
13380 try
13381 {
13382 /*
13383 * Convert input up front.
13384 */
13385 uint32_t fFlags = NILFLAG;
13386 if (aFlags.length())
13387 {
13388 int vrc = validateFlags(aFlags.c_str(), &fFlags);
13389 AssertRCReturn(vrc, E_INVALIDARG);
13390 }
13391
13392 /*
13393 * Now grab the object lock, validate the state and do the update.
13394 */
13395
13396 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13397
13398 if (!Global::IsOnline(mData->mMachineState))
13399 {
13400 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),
13401 VBOX_E_INVALID_VM_STATE);
13402 }
13403
13404 i_setModified(IsModified_MachineData);
13405 mHWData.backup();
13406
13407 bool fDelete = !aValue.length();
13408 HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(aName);
13409 if (it != mHWData->mGuestProperties.end())
13410 {
13411 if (!fDelete)
13412 {
13413 it->second.strValue = aValue;
13414 it->second.mTimestamp = aTimestamp;
13415 it->second.mFlags = fFlags;
13416 }
13417 else
13418 mHWData->mGuestProperties.erase(it);
13419
13420 mData->mGuestPropertiesModified = TRUE;
13421 }
13422 else if (!fDelete)
13423 {
13424 HWData::GuestProperty prop;
13425 prop.strValue = aValue;
13426 prop.mTimestamp = aTimestamp;
13427 prop.mFlags = fFlags;
13428
13429 mHWData->mGuestProperties[aName] = prop;
13430 mData->mGuestPropertiesModified = TRUE;
13431 }
13432
13433 alock.release();
13434
13435 mParent->i_onGuestPropertyChange(mData->mUuid,
13436 Bstr(aName).raw(),
13437 Bstr(aValue).raw(),
13438 Bstr(aFlags).raw());
13439 }
13440 catch (...)
13441 {
13442 return VirtualBoxBase::handleUnexpectedExceptions(this, RT_SRC_POS);
13443 }
13444 return S_OK;
13445#else
13446 ReturnComNotImplemented();
13447#endif
13448}
13449
13450
13451HRESULT SessionMachine::lockMedia()
13452{
13453 AutoMultiWriteLock2 alock(this->lockHandle(),
13454 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13455
13456 AssertReturn( mData->mMachineState == MachineState_Starting
13457 || mData->mMachineState == MachineState_Restoring
13458 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);
13459
13460 clearError();
13461 alock.release();
13462 return i_lockMedia();
13463}
13464
13465HRESULT SessionMachine::unlockMedia()
13466{
13467 HRESULT hrc = i_unlockMedia();
13468 return hrc;
13469}
13470
13471HRESULT SessionMachine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
13472 ComPtr<IMediumAttachment> &aNewAttachment)
13473{
13474 // request the host lock first, since might be calling Host methods for getting host drives;
13475 // next, protect the media tree all the while we're in here, as well as our member variables
13476 AutoMultiWriteLock3 multiLock(mParent->i_host()->lockHandle(),
13477 this->lockHandle(),
13478 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
13479
13480 IMediumAttachment *iAttach = aAttachment;
13481 ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(iAttach);
13482
13483 Bstr ctrlName;
13484 LONG lPort;
13485 LONG lDevice;
13486 bool fTempEject;
13487 {
13488 AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13489
13490 /* Need to query the details first, as the IMediumAttachment reference
13491 * might be to the original settings, which we are going to change. */
13492 ctrlName = pAttach->i_getControllerName();
13493 lPort = pAttach->i_getPort();
13494 lDevice = pAttach->i_getDevice();
13495 fTempEject = pAttach->i_getTempEject();
13496 }
13497
13498 if (!fTempEject)
13499 {
13500 /* Remember previously mounted medium. The medium before taking the
13501 * backup is not necessarily the same thing. */
13502 ComObjPtr<Medium> oldmedium;
13503 oldmedium = pAttach->i_getMedium();
13504
13505 i_setModified(IsModified_Storage);
13506 mMediaData.backup();
13507
13508 // The backup operation makes the pAttach reference point to the
13509 // old settings. Re-get the correct reference.
13510 pAttach = i_findAttachment(mMediaData->mAttachments,
13511 ctrlName.raw(),
13512 lPort,
13513 lDevice);
13514
13515 {
13516 AutoCaller autoAttachCaller(this);
13517 if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
13518
13519 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13520 if (!oldmedium.isNull())
13521 oldmedium->i_removeBackReference(mData->mUuid);
13522
13523 pAttach->i_updateMedium(NULL);
13524 pAttach->i_updateEjected();
13525 }
13526
13527 i_setModified(IsModified_Storage);
13528 }
13529 else
13530 {
13531 {
13532 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
13533 pAttach->i_updateEjected();
13534 }
13535 }
13536
13537 pAttach.queryInterfaceTo(aNewAttachment.asOutParam());
13538
13539 return S_OK;
13540}
13541
13542// public methods only for internal purposes
13543/////////////////////////////////////////////////////////////////////////////
13544
13545#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER
13546/**
13547 * Called from the client watcher thread to check for expected or unexpected
13548 * death of the client process that has a direct session to this machine.
13549 *
13550 * On Win32 and on OS/2, this method is called only when we've got the
13551 * mutex (i.e. the client has either died or terminated normally) so it always
13552 * returns @c true (the client is terminated, the session machine is
13553 * uninitialized).
13554 *
13555 * On other platforms, the method returns @c true if the client process has
13556 * terminated normally or abnormally and the session machine was uninitialized,
13557 * and @c false if the client process is still alive.
13558 *
13559 * @note Locks this object for writing.
13560 */
13561bool SessionMachine::i_checkForDeath()
13562{
13563 Uninit::Reason reason;
13564 bool terminated = false;
13565
13566 /* Enclose autoCaller with a block because calling uninit() from under it
13567 * will deadlock. */
13568 {
13569 AutoCaller autoCaller(this);
13570 if (!autoCaller.isOk())
13571 {
13572 /* return true if not ready, to cause the client watcher to exclude
13573 * the corresponding session from watching */
13574 LogFlowThisFunc(("Already uninitialized!\n"));
13575 return true;
13576 }
13577
13578 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
13579
13580 /* Determine the reason of death: if the session state is Closing here,
13581 * everything is fine. Otherwise it means that the client did not call
13582 * OnSessionEnd() before it released the IPC semaphore. This may happen
13583 * either because the client process has abnormally terminated, or
13584 * because it simply forgot to call ISession::Close() before exiting. We
13585 * threat the latter also as an abnormal termination (see
13586 * Session::uninit() for details). */
13587 reason = mData->mSession.mState == SessionState_Unlocking ?
13588 Uninit::Normal :
13589 Uninit::Abnormal;
13590
13591 if (mClientToken)
13592 terminated = mClientToken->release();
13593 } /* AutoCaller block */
13594
13595 if (terminated)
13596 uninit(reason);
13597
13598 return terminated;
13599}
13600
13601void SessionMachine::i_getTokenId(Utf8Str &strTokenId)
13602{
13603 LogFlowThisFunc(("\n"));
13604
13605 strTokenId.setNull();
13606
13607 AutoCaller autoCaller(this);
13608 AssertComRCReturnVoid(autoCaller.rc());
13609
13610 Assert(mClientToken);
13611 if (mClientToken)
13612 mClientToken->getId(strTokenId);
13613}
13614#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13615IToken *SessionMachine::i_getToken()
13616{
13617 LogFlowThisFunc(("\n"));
13618
13619 AutoCaller autoCaller(this);
13620 AssertComRCReturn(autoCaller.rc(), NULL);
13621
13622 Assert(mClientToken);
13623 if (mClientToken)
13624 return mClientToken->getToken();
13625 else
13626 return NULL;
13627}
13628#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */
13629
13630Machine::ClientToken *SessionMachine::i_getClientToken()
13631{
13632 LogFlowThisFunc(("\n"));
13633
13634 AutoCaller autoCaller(this);
13635 AssertComRCReturn(autoCaller.rc(), NULL);
13636
13637 return mClientToken;
13638}
13639
13640
13641/**
13642 * @note Locks this object for reading.
13643 */
13644HRESULT SessionMachine::i_onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)
13645{
13646 LogFlowThisFunc(("\n"));
13647
13648 AutoCaller autoCaller(this);
13649 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13650
13651 ComPtr<IInternalSessionControl> directControl;
13652 {
13653 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13654 if (mData->mSession.mLockType == LockType_VM)
13655 directControl = mData->mSession.mDirectControl;
13656 }
13657
13658 /* ignore notifications sent after #OnSessionEnd() is called */
13659 if (!directControl)
13660 return S_OK;
13661
13662 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);
13663}
13664
13665/**
13666 * @note Locks this object for reading.
13667 */
13668HRESULT SessionMachine::i_onNATRedirectRuleChange(ULONG ulSlot, BOOL aNatRuleRemove, IN_BSTR aRuleName,
13669 NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort,
13670 IN_BSTR aGuestIp, LONG aGuestPort)
13671{
13672 LogFlowThisFunc(("\n"));
13673
13674 AutoCaller autoCaller(this);
13675 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13676
13677 ComPtr<IInternalSessionControl> directControl;
13678 {
13679 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13680 if (mData->mSession.mLockType == LockType_VM)
13681 directControl = mData->mSession.mDirectControl;
13682 }
13683
13684 /* ignore notifications sent after #OnSessionEnd() is called */
13685 if (!directControl)
13686 return S_OK;
13687 /*
13688 * instead acting like callback we ask IVirtualBox deliver corresponding event
13689 */
13690
13691 mParent->i_onNatRedirectChange(i_getId(), ulSlot, RT_BOOL(aNatRuleRemove), aRuleName, aProto, aHostIp,
13692 (uint16_t)aHostPort, aGuestIp, (uint16_t)aGuestPort);
13693 return S_OK;
13694}
13695
13696/**
13697 * @note Locks this object for reading.
13698 */
13699HRESULT SessionMachine::i_onSerialPortChange(ISerialPort *serialPort)
13700{
13701 LogFlowThisFunc(("\n"));
13702
13703 AutoCaller autoCaller(this);
13704 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13705
13706 ComPtr<IInternalSessionControl> directControl;
13707 {
13708 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13709 if (mData->mSession.mLockType == LockType_VM)
13710 directControl = mData->mSession.mDirectControl;
13711 }
13712
13713 /* ignore notifications sent after #OnSessionEnd() is called */
13714 if (!directControl)
13715 return S_OK;
13716
13717 return directControl->OnSerialPortChange(serialPort);
13718}
13719
13720/**
13721 * @note Locks this object for reading.
13722 */
13723HRESULT SessionMachine::i_onParallelPortChange(IParallelPort *parallelPort)
13724{
13725 LogFlowThisFunc(("\n"));
13726
13727 AutoCaller autoCaller(this);
13728 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13729
13730 ComPtr<IInternalSessionControl> directControl;
13731 {
13732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13733 if (mData->mSession.mLockType == LockType_VM)
13734 directControl = mData->mSession.mDirectControl;
13735 }
13736
13737 /* ignore notifications sent after #OnSessionEnd() is called */
13738 if (!directControl)
13739 return S_OK;
13740
13741 return directControl->OnParallelPortChange(parallelPort);
13742}
13743
13744/**
13745 * @note Locks this object for reading.
13746 */
13747HRESULT SessionMachine::i_onStorageControllerChange()
13748{
13749 LogFlowThisFunc(("\n"));
13750
13751 AutoCaller autoCaller(this);
13752 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13753
13754 ComPtr<IInternalSessionControl> directControl;
13755 {
13756 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13757 if (mData->mSession.mLockType == LockType_VM)
13758 directControl = mData->mSession.mDirectControl;
13759 }
13760
13761 /* ignore notifications sent after #OnSessionEnd() is called */
13762 if (!directControl)
13763 return S_OK;
13764
13765 return directControl->OnStorageControllerChange();
13766}
13767
13768/**
13769 * @note Locks this object for reading.
13770 */
13771HRESULT SessionMachine::i_onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)
13772{
13773 LogFlowThisFunc(("\n"));
13774
13775 AutoCaller autoCaller(this);
13776 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13777
13778 ComPtr<IInternalSessionControl> directControl;
13779 {
13780 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13781 if (mData->mSession.mLockType == LockType_VM)
13782 directControl = mData->mSession.mDirectControl;
13783 }
13784
13785 /* ignore notifications sent after #OnSessionEnd() is called */
13786 if (!directControl)
13787 return S_OK;
13788
13789 return directControl->OnMediumChange(aAttachment, aForce);
13790}
13791
13792/**
13793 * @note Locks this object for reading.
13794 */
13795HRESULT SessionMachine::i_onCPUChange(ULONG aCPU, BOOL aRemove)
13796{
13797 LogFlowThisFunc(("\n"));
13798
13799 AutoCaller autoCaller(this);
13800 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13801
13802 ComPtr<IInternalSessionControl> directControl;
13803 {
13804 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13805 if (mData->mSession.mLockType == LockType_VM)
13806 directControl = mData->mSession.mDirectControl;
13807 }
13808
13809 /* ignore notifications sent after #OnSessionEnd() is called */
13810 if (!directControl)
13811 return S_OK;
13812
13813 return directControl->OnCPUChange(aCPU, aRemove);
13814}
13815
13816HRESULT SessionMachine::i_onCPUExecutionCapChange(ULONG aExecutionCap)
13817{
13818 LogFlowThisFunc(("\n"));
13819
13820 AutoCaller autoCaller(this);
13821 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13822
13823 ComPtr<IInternalSessionControl> directControl;
13824 {
13825 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13826 if (mData->mSession.mLockType == LockType_VM)
13827 directControl = mData->mSession.mDirectControl;
13828 }
13829
13830 /* ignore notifications sent after #OnSessionEnd() is called */
13831 if (!directControl)
13832 return S_OK;
13833
13834 return directControl->OnCPUExecutionCapChange(aExecutionCap);
13835}
13836
13837/**
13838 * @note Locks this object for reading.
13839 */
13840HRESULT SessionMachine::i_onVRDEServerChange(BOOL aRestart)
13841{
13842 LogFlowThisFunc(("\n"));
13843
13844 AutoCaller autoCaller(this);
13845 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13846
13847 ComPtr<IInternalSessionControl> directControl;
13848 {
13849 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13850 if (mData->mSession.mLockType == LockType_VM)
13851 directControl = mData->mSession.mDirectControl;
13852 }
13853
13854 /* ignore notifications sent after #OnSessionEnd() is called */
13855 if (!directControl)
13856 return S_OK;
13857
13858 return directControl->OnVRDEServerChange(aRestart);
13859}
13860
13861/**
13862 * @note Locks this object for reading.
13863 */
13864HRESULT SessionMachine::i_onVideoCaptureChange()
13865{
13866 LogFlowThisFunc(("\n"));
13867
13868 AutoCaller autoCaller(this);
13869 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13870
13871 ComPtr<IInternalSessionControl> directControl;
13872 {
13873 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13874 if (mData->mSession.mLockType == LockType_VM)
13875 directControl = mData->mSession.mDirectControl;
13876 }
13877
13878 /* ignore notifications sent after #OnSessionEnd() is called */
13879 if (!directControl)
13880 return S_OK;
13881
13882 return directControl->OnVideoCaptureChange();
13883}
13884
13885/**
13886 * @note Locks this object for reading.
13887 */
13888HRESULT SessionMachine::i_onUSBControllerChange()
13889{
13890 LogFlowThisFunc(("\n"));
13891
13892 AutoCaller autoCaller(this);
13893 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13894
13895 ComPtr<IInternalSessionControl> directControl;
13896 {
13897 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13898 if (mData->mSession.mLockType == LockType_VM)
13899 directControl = mData->mSession.mDirectControl;
13900 }
13901
13902 /* ignore notifications sent after #OnSessionEnd() is called */
13903 if (!directControl)
13904 return S_OK;
13905
13906 return directControl->OnUSBControllerChange();
13907}
13908
13909/**
13910 * @note Locks this object for reading.
13911 */
13912HRESULT SessionMachine::i_onSharedFolderChange()
13913{
13914 LogFlowThisFunc(("\n"));
13915
13916 AutoCaller autoCaller(this);
13917 AssertComRCReturnRC(autoCaller.rc());
13918
13919 ComPtr<IInternalSessionControl> directControl;
13920 {
13921 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13922 if (mData->mSession.mLockType == LockType_VM)
13923 directControl = mData->mSession.mDirectControl;
13924 }
13925
13926 /* ignore notifications sent after #OnSessionEnd() is called */
13927 if (!directControl)
13928 return S_OK;
13929
13930 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);
13931}
13932
13933/**
13934 * @note Locks this object for reading.
13935 */
13936HRESULT SessionMachine::i_onClipboardModeChange(ClipboardMode_T aClipboardMode)
13937{
13938 LogFlowThisFunc(("\n"));
13939
13940 AutoCaller autoCaller(this);
13941 AssertComRCReturnRC(autoCaller.rc());
13942
13943 ComPtr<IInternalSessionControl> directControl;
13944 {
13945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13946 if (mData->mSession.mLockType == LockType_VM)
13947 directControl = mData->mSession.mDirectControl;
13948 }
13949
13950 /* ignore notifications sent after #OnSessionEnd() is called */
13951 if (!directControl)
13952 return S_OK;
13953
13954 return directControl->OnClipboardModeChange(aClipboardMode);
13955}
13956
13957/**
13958 * @note Locks this object for reading.
13959 */
13960HRESULT SessionMachine::i_onDnDModeChange(DnDMode_T aDnDMode)
13961{
13962 LogFlowThisFunc(("\n"));
13963
13964 AutoCaller autoCaller(this);
13965 AssertComRCReturnRC(autoCaller.rc());
13966
13967 ComPtr<IInternalSessionControl> directControl;
13968 {
13969 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13970 if (mData->mSession.mLockType == LockType_VM)
13971 directControl = mData->mSession.mDirectControl;
13972 }
13973
13974 /* ignore notifications sent after #OnSessionEnd() is called */
13975 if (!directControl)
13976 return S_OK;
13977
13978 return directControl->OnDnDModeChange(aDnDMode);
13979}
13980
13981/**
13982 * @note Locks this object for reading.
13983 */
13984HRESULT SessionMachine::i_onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
13985{
13986 LogFlowThisFunc(("\n"));
13987
13988 AutoCaller autoCaller(this);
13989 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
13990
13991 ComPtr<IInternalSessionControl> directControl;
13992 {
13993 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
13994 if (mData->mSession.mLockType == LockType_VM)
13995 directControl = mData->mSession.mDirectControl;
13996 }
13997
13998 /* ignore notifications sent after #OnSessionEnd() is called */
13999 if (!directControl)
14000 return S_OK;
14001
14002 return directControl->OnBandwidthGroupChange(aBandwidthGroup);
14003}
14004
14005/**
14006 * @note Locks this object for reading.
14007 */
14008HRESULT SessionMachine::i_onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent)
14009{
14010 LogFlowThisFunc(("\n"));
14011
14012 AutoCaller autoCaller(this);
14013 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14014
14015 ComPtr<IInternalSessionControl> directControl;
14016 {
14017 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14018 if (mData->mSession.mLockType == LockType_VM)
14019 directControl = mData->mSession.mDirectControl;
14020 }
14021
14022 /* ignore notifications sent after #OnSessionEnd() is called */
14023 if (!directControl)
14024 return S_OK;
14025
14026 return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent);
14027}
14028
14029/**
14030 * Returns @c true if this machine's USB controller reports it has a matching
14031 * filter for the given USB device and @c false otherwise.
14032 *
14033 * @note locks this object for reading.
14034 */
14035bool SessionMachine::i_hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)
14036{
14037 AutoCaller autoCaller(this);
14038 /* silently return if not ready -- this method may be called after the
14039 * direct machine session has been called */
14040 if (!autoCaller.isOk())
14041 return false;
14042
14043#ifdef VBOX_WITH_USB
14044 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14045
14046 switch (mData->mMachineState)
14047 {
14048 case MachineState_Starting:
14049 case MachineState_Restoring:
14050 case MachineState_TeleportingIn:
14051 case MachineState_Paused:
14052 case MachineState_Running:
14053 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of
14054 * elsewhere... */
14055 alock.release();
14056 return mUSBDeviceFilters->i_hasMatchingFilter(aDevice, aMaskedIfs);
14057 default: break;
14058 }
14059#else
14060 NOREF(aDevice);
14061 NOREF(aMaskedIfs);
14062#endif
14063 return false;
14064}
14065
14066/**
14067 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14068 */
14069HRESULT SessionMachine::i_onUSBDeviceAttach(IUSBDevice *aDevice,
14070 IVirtualBoxErrorInfo *aError,
14071 ULONG aMaskedIfs,
14072 const com::Utf8Str &aCaptureFilename)
14073{
14074 LogFlowThisFunc(("\n"));
14075
14076 AutoCaller autoCaller(this);
14077
14078 /* This notification may happen after the machine object has been
14079 * uninitialized (the session was closed), so don't assert. */
14080 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14081
14082 ComPtr<IInternalSessionControl> directControl;
14083 {
14084 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14085 if (mData->mSession.mLockType == LockType_VM)
14086 directControl = mData->mSession.mDirectControl;
14087 }
14088
14089 /* fail on notifications sent after #OnSessionEnd() is called, it is
14090 * expected by the caller */
14091 if (!directControl)
14092 return E_FAIL;
14093
14094 /* No locks should be held at this point. */
14095 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14096 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14097
14098 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs, Bstr(aCaptureFilename).raw());
14099}
14100
14101/**
14102 * @note The calls shall hold no locks. Will temporarily lock this object for reading.
14103 */
14104HRESULT SessionMachine::i_onUSBDeviceDetach(IN_BSTR aId,
14105 IVirtualBoxErrorInfo *aError)
14106{
14107 LogFlowThisFunc(("\n"));
14108
14109 AutoCaller autoCaller(this);
14110
14111 /* This notification may happen after the machine object has been
14112 * uninitialized (the session was closed), so don't assert. */
14113 if (FAILED(autoCaller.rc())) return autoCaller.rc();
14114
14115 ComPtr<IInternalSessionControl> directControl;
14116 {
14117 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14118 if (mData->mSession.mLockType == LockType_VM)
14119 directControl = mData->mSession.mDirectControl;
14120 }
14121
14122 /* fail on notifications sent after #OnSessionEnd() is called, it is
14123 * expected by the caller */
14124 if (!directControl)
14125 return E_FAIL;
14126
14127 /* No locks should be held at this point. */
14128 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));
14129 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));
14130
14131 return directControl->OnUSBDeviceDetach(aId, aError);
14132}
14133
14134// protected methods
14135/////////////////////////////////////////////////////////////////////////////
14136
14137/**
14138 * Deletes the given file if it is no longer in use by either the current machine state
14139 * (if the machine is "saved") or any of the machine's snapshots.
14140 *
14141 * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
14142 * but is different for each SnapshotMachine. When calling this, the order of calling this
14143 * function on the one hand and changing that variable OR the snapshots tree on the other hand
14144 * is therefore critical. I know, it's all rather messy.
14145 *
14146 * @param strStateFile
14147 * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in
14148 * the test for whether the saved state file is in use.
14149 */
14150void SessionMachine::i_releaseSavedStateFile(const Utf8Str &strStateFile,
14151 Snapshot *pSnapshotToIgnore)
14152{
14153 // it is safe to delete this saved state file if it is not currently in use by the machine ...
14154 if ( (strStateFile.isNotEmpty())
14155 && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
14156 )
14157 // ... and it must also not be shared with other snapshots
14158 if ( !mData->mFirstSnapshot
14159 || !mData->mFirstSnapshot->i_sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
14160 // this checks the SnapshotMachine's state file paths
14161 )
14162 RTFileDelete(strStateFile.c_str());
14163}
14164
14165/**
14166 * Locks the attached media.
14167 *
14168 * All attached hard disks are locked for writing and DVD/floppy are locked for
14169 * reading. Parents of attached hard disks (if any) are locked for reading.
14170 *
14171 * This method also performs accessibility check of all media it locks: if some
14172 * media is inaccessible, the method will return a failure and a bunch of
14173 * extended error info objects per each inaccessible medium.
14174 *
14175 * Note that this method is atomic: if it returns a success, all media are
14176 * locked as described above; on failure no media is locked at all (all
14177 * succeeded individual locks will be undone).
14178 *
14179 * The caller is responsible for doing the necessary state sanity checks.
14180 *
14181 * The locks made by this method must be undone by calling #unlockMedia() when
14182 * no more needed.
14183 */
14184HRESULT SessionMachine::i_lockMedia()
14185{
14186 AutoCaller autoCaller(this);
14187 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14188
14189 AutoMultiWriteLock2 alock(this->lockHandle(),
14190 &mParent->i_getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
14191
14192 /* bail out if trying to lock things with already set up locking */
14193 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);
14194
14195 MultiResult mrc(S_OK);
14196
14197 /* Collect locking information for all medium objects attached to the VM. */
14198 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
14199 it != mMediaData->mAttachments.end();
14200 ++it)
14201 {
14202 MediumAttachment* pAtt = *it;
14203 DeviceType_T devType = pAtt->i_getType();
14204 Medium *pMedium = pAtt->i_getMedium();
14205
14206 MediumLockList *pMediumLockList(new MediumLockList());
14207 // There can be attachments without a medium (floppy/dvd), and thus
14208 // it's impossible to create a medium lock list. It still makes sense
14209 // to have the empty medium lock list in the map in case a medium is
14210 // attached later.
14211 if (pMedium != NULL)
14212 {
14213 MediumType_T mediumType = pMedium->i_getType();
14214 bool fIsReadOnlyLock = mediumType == MediumType_Readonly
14215 || mediumType == MediumType_Shareable;
14216 bool fIsVitalImage = (devType == DeviceType_HardDisk);
14217
14218 alock.release();
14219 mrc = pMedium->i_createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,
14220 !fIsReadOnlyLock /* fMediumLockWrite */,
14221 false /* fMediumLockWriteAll */,
14222 NULL,
14223 *pMediumLockList);
14224 alock.acquire();
14225 if (FAILED(mrc))
14226 {
14227 delete pMediumLockList;
14228 mData->mSession.mLockedMedia.Clear();
14229 break;
14230 }
14231 }
14232
14233 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);
14234 if (FAILED(rc))
14235 {
14236 mData->mSession.mLockedMedia.Clear();
14237 mrc = setError(rc,
14238 tr("Collecting locking information for all attached media failed"));
14239 break;
14240 }
14241 }
14242
14243 if (SUCCEEDED(mrc))
14244 {
14245 /* Now lock all media. If this fails, nothing is locked. */
14246 alock.release();
14247 HRESULT rc = mData->mSession.mLockedMedia.Lock();
14248 alock.acquire();
14249 if (FAILED(rc))
14250 {
14251 mrc = setError(rc,
14252 tr("Locking of attached media failed. A possible reason is that one of the media is attached to a running VM"));
14253 }
14254 }
14255
14256 return mrc;
14257}
14258
14259/**
14260 * Undoes the locks made by by #lockMedia().
14261 */
14262HRESULT SessionMachine::i_unlockMedia()
14263{
14264 AutoCaller autoCaller(this);
14265 AssertComRCReturn(autoCaller.rc(),autoCaller.rc());
14266
14267 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14268
14269 /* we may be holding important error info on the current thread;
14270 * preserve it */
14271 ErrorInfoKeeper eik;
14272
14273 HRESULT rc = mData->mSession.mLockedMedia.Clear();
14274 AssertComRC(rc);
14275 return rc;
14276}
14277
14278/**
14279 * Helper to change the machine state (reimplementation).
14280 *
14281 * @note Locks this object for writing.
14282 * @note This method must not call i_saveSettings or SaveSettings, otherwise
14283 * it can cause crashes in random places due to unexpectedly committing
14284 * the current settings. The caller is responsible for that. The call
14285 * to saveStateSettings is fine, because this method does not commit.
14286 */
14287HRESULT SessionMachine::i_setMachineState(MachineState_T aMachineState)
14288{
14289 LogFlowThisFuncEnter();
14290 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));
14291
14292 AutoCaller autoCaller(this);
14293 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14294
14295 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
14296
14297 MachineState_T oldMachineState = mData->mMachineState;
14298
14299 AssertMsgReturn(oldMachineState != aMachineState,
14300 ("oldMachineState=%s, aMachineState=%s\n",
14301 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),
14302 E_FAIL);
14303
14304 HRESULT rc = S_OK;
14305
14306 int stsFlags = 0;
14307 bool deleteSavedState = false;
14308
14309 /* detect some state transitions */
14310
14311 if ( ( oldMachineState == MachineState_Saved
14312 && aMachineState == MachineState_Restoring)
14313 || ( ( oldMachineState == MachineState_PoweredOff
14314 || oldMachineState == MachineState_Teleported
14315 || oldMachineState == MachineState_Aborted
14316 )
14317 && ( aMachineState == MachineState_TeleportingIn
14318 || aMachineState == MachineState_Starting
14319 )
14320 )
14321 )
14322 {
14323 /* The EMT thread is about to start */
14324
14325 /* Nothing to do here for now... */
14326
14327 /// @todo NEWMEDIA don't let mDVDDrive and other children
14328 /// change anything when in the Starting/Restoring state
14329 }
14330 else if ( ( oldMachineState == MachineState_Running
14331 || oldMachineState == MachineState_Paused
14332 || oldMachineState == MachineState_Teleporting
14333 || oldMachineState == MachineState_OnlineSnapshotting
14334 || oldMachineState == MachineState_LiveSnapshotting
14335 || oldMachineState == MachineState_Stuck
14336 || oldMachineState == MachineState_Starting
14337 || oldMachineState == MachineState_Stopping
14338 || oldMachineState == MachineState_Saving
14339 || oldMachineState == MachineState_Restoring
14340 || oldMachineState == MachineState_TeleportingPausedVM
14341 || oldMachineState == MachineState_TeleportingIn
14342 )
14343 && ( aMachineState == MachineState_PoweredOff
14344 || aMachineState == MachineState_Saved
14345 || aMachineState == MachineState_Teleported
14346 || aMachineState == MachineState_Aborted
14347 )
14348 )
14349 {
14350 /* The EMT thread has just stopped, unlock attached media. Note that as
14351 * opposed to locking that is done from Console, we do unlocking here
14352 * because the VM process may have aborted before having a chance to
14353 * properly unlock all media it locked. */
14354
14355 unlockMedia();
14356 }
14357
14358 if (oldMachineState == MachineState_Restoring)
14359 {
14360 if (aMachineState != MachineState_Saved)
14361 {
14362 /*
14363 * delete the saved state file once the machine has finished
14364 * restoring from it (note that Console sets the state from
14365 * Restoring to Saved if the VM couldn't restore successfully,
14366 * to give the user an ability to fix an error and retry --
14367 * we keep the saved state file in this case)
14368 */
14369 deleteSavedState = true;
14370 }
14371 }
14372 else if ( oldMachineState == MachineState_Saved
14373 && ( aMachineState == MachineState_PoweredOff
14374 || aMachineState == MachineState_Aborted
14375 || aMachineState == MachineState_Teleported
14376 )
14377 )
14378 {
14379 /*
14380 * delete the saved state after SessionMachine::ForgetSavedState() is called
14381 * or if the VM process (owning a direct VM session) crashed while the
14382 * VM was Saved
14383 */
14384
14385 /// @todo (dmik)
14386 // Not sure that deleting the saved state file just because of the
14387 // client death before it attempted to restore the VM is a good
14388 // thing. But when it crashes we need to go to the Aborted state
14389 // which cannot have the saved state file associated... The only
14390 // way to fix this is to make the Aborted condition not a VM state
14391 // but a bool flag: i.e., when a crash occurs, set it to true and
14392 // change the state to PoweredOff or Saved depending on the
14393 // saved state presence.
14394
14395 deleteSavedState = true;
14396 mData->mCurrentStateModified = TRUE;
14397 stsFlags |= SaveSTS_CurStateModified;
14398 }
14399
14400 if ( aMachineState == MachineState_Starting
14401 || aMachineState == MachineState_Restoring
14402 || aMachineState == MachineState_TeleportingIn
14403 )
14404 {
14405 /* set the current state modified flag to indicate that the current
14406 * state is no more identical to the state in the
14407 * current snapshot */
14408 if (!mData->mCurrentSnapshot.isNull())
14409 {
14410 mData->mCurrentStateModified = TRUE;
14411 stsFlags |= SaveSTS_CurStateModified;
14412 }
14413 }
14414
14415 if (deleteSavedState)
14416 {
14417 if (mRemoveSavedState)
14418 {
14419 Assert(!mSSData->strStateFilePath.isEmpty());
14420
14421 // it is safe to delete the saved state file if ...
14422 if ( !mData->mFirstSnapshot // ... we have no snapshots or
14423 || !mData->mFirstSnapshot->i_sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
14424 // ... none of the snapshots share the saved state file
14425 )
14426 RTFileDelete(mSSData->strStateFilePath.c_str());
14427 }
14428
14429 mSSData->strStateFilePath.setNull();
14430 stsFlags |= SaveSTS_StateFilePath;
14431 }
14432
14433 /* redirect to the underlying peer machine */
14434 mPeer->i_setMachineState(aMachineState);
14435
14436 if ( oldMachineState != MachineState_RestoringSnapshot
14437 && ( aMachineState == MachineState_PoweredOff
14438 || aMachineState == MachineState_Teleported
14439 || aMachineState == MachineState_Aborted
14440 || aMachineState == MachineState_Saved))
14441 {
14442 /* the machine has stopped execution
14443 * (or the saved state file was adopted) */
14444 stsFlags |= SaveSTS_StateTimeStamp;
14445 }
14446
14447 if ( ( oldMachineState == MachineState_PoweredOff
14448 || oldMachineState == MachineState_Aborted
14449 || oldMachineState == MachineState_Teleported
14450 )
14451 && aMachineState == MachineState_Saved)
14452 {
14453 /* the saved state file was adopted */
14454 Assert(!mSSData->strStateFilePath.isEmpty());
14455 stsFlags |= SaveSTS_StateFilePath;
14456 }
14457
14458#ifdef VBOX_WITH_GUEST_PROPS
14459 if ( aMachineState == MachineState_PoweredOff
14460 || aMachineState == MachineState_Aborted
14461 || aMachineState == MachineState_Teleported)
14462 {
14463 /* Make sure any transient guest properties get removed from the
14464 * property store on shutdown. */
14465 BOOL fNeedsSaving = mData->mGuestPropertiesModified;
14466
14467 /* remove it from the settings representation */
14468 settings::GuestPropertiesList &llGuestProperties = mData->pMachineConfigFile->hardwareMachine.llGuestProperties;
14469 for (settings::GuestPropertiesList::iterator it = llGuestProperties.begin();
14470 it != llGuestProperties.end();
14471 /*nothing*/)
14472 {
14473 const settings::GuestProperty &prop = *it;
14474 if ( prop.strFlags.contains("TRANSRESET", Utf8Str::CaseInsensitive)
14475 || prop.strFlags.contains("TRANSIENT", Utf8Str::CaseInsensitive))
14476 {
14477 it = llGuestProperties.erase(it);
14478 fNeedsSaving = true;
14479 }
14480 else
14481 {
14482 ++it;
14483 }
14484 }
14485
14486 /* Additionally remove it from the HWData representation. Required to
14487 * keep everything in sync, as this is what the API keeps using. */
14488 HWData::GuestPropertyMap &llHWGuestProperties = mHWData->mGuestProperties;
14489 for (HWData::GuestPropertyMap::iterator it = llHWGuestProperties.begin();
14490 it != llHWGuestProperties.end();
14491 /*nothing*/)
14492 {
14493 uint32_t fFlags = it->second.mFlags;
14494 if ( fFlags & guestProp::TRANSIENT
14495 || fFlags & guestProp::TRANSRESET)
14496 {
14497 /* iterator where we need to continue after the erase call
14498 * (C++03 is a fact still, and it doesn't return the iterator
14499 * which would allow continuing) */
14500 HWData::GuestPropertyMap::iterator it2 = it;
14501 ++it2;
14502 llHWGuestProperties.erase(it);
14503 it = it2;
14504 fNeedsSaving = true;
14505 }
14506 else
14507 {
14508 ++it;
14509 }
14510 }
14511
14512 if (fNeedsSaving)
14513 {
14514 mData->mCurrentStateModified = TRUE;
14515 stsFlags |= SaveSTS_CurStateModified;
14516 }
14517 }
14518#endif /* VBOX_WITH_GUEST_PROPS */
14519
14520 rc = i_saveStateSettings(stsFlags);
14521
14522 if ( ( oldMachineState != MachineState_PoweredOff
14523 && oldMachineState != MachineState_Aborted
14524 && oldMachineState != MachineState_Teleported
14525 )
14526 && ( aMachineState == MachineState_PoweredOff
14527 || aMachineState == MachineState_Aborted
14528 || aMachineState == MachineState_Teleported
14529 )
14530 )
14531 {
14532 /* we've been shut down for any reason */
14533 /* no special action so far */
14534 }
14535
14536 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));
14537 LogFlowThisFuncLeave();
14538 return rc;
14539}
14540
14541/**
14542 * Sends the current machine state value to the VM process.
14543 *
14544 * @note Locks this object for reading, then calls a client process.
14545 */
14546HRESULT SessionMachine::i_updateMachineStateOnClient()
14547{
14548 AutoCaller autoCaller(this);
14549 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
14550
14551 ComPtr<IInternalSessionControl> directControl;
14552 {
14553 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
14554 AssertReturn(!!mData, E_FAIL);
14555 if (mData->mSession.mLockType == LockType_VM)
14556 directControl = mData->mSession.mDirectControl;
14557
14558 /* directControl may be already set to NULL here in #OnSessionEnd()
14559 * called too early by the direct session process while there is still
14560 * some operation (like deleting the snapshot) in progress. The client
14561 * process in this case is waiting inside Session::close() for the
14562 * "end session" process object to complete, while #uninit() called by
14563 * #checkForDeath() on the Watcher thread is waiting for the pending
14564 * operation to complete. For now, we accept this inconsistent behavior
14565 * and simply do nothing here. */
14566
14567 if (mData->mSession.mState == SessionState_Unlocking)
14568 return S_OK;
14569 }
14570
14571 /* ignore notifications sent after #OnSessionEnd() is called */
14572 if (!directControl)
14573 return S_OK;
14574
14575 return directControl->UpdateMachineState(mData->mMachineState);
14576}
14577
14578
14579/**
14580 * Static Machine method that can get passed to RTThreadCreate to
14581 * have a thread started for a Task. See Machine::Task.
14582 */
14583/* static */ DECLCALLBACK(int) Machine::taskHandler(RTTHREAD /* thread */, void *pvUser)
14584{
14585 AssertReturn(pvUser, VERR_INVALID_POINTER);
14586
14587 Task *pTask = static_cast<Task *>(pvUser);
14588 pTask->handler();
14589 /** @todo r=klaus it would be safer to update the progress object here,
14590 * as it avoids possible races due to scoping issues/tricks in the handler */
14591 // it's our responsibility to delete the task
14592 delete pTask;
14593
14594 return 0;
14595}
14596
14597/*static*/
14598HRESULT Machine::i_setErrorStatic(HRESULT aResultCode, const char *pcszMsg, ...)
14599{
14600 va_list args;
14601 va_start(args, pcszMsg);
14602 HRESULT rc = setErrorInternal(aResultCode,
14603 getStaticClassIID(),
14604 getStaticComponentName(),
14605 Utf8Str(pcszMsg, args),
14606 false /* aWarning */,
14607 true /* aLogIt */);
14608 va_end(args);
14609 return rc;
14610}
14611
14612
14613HRESULT Machine::updateState(MachineState_T aState)
14614{
14615 NOREF(aState);
14616 ReturnComNotImplemented();
14617}
14618
14619HRESULT Machine::beginPowerUp(const ComPtr<IProgress> &aProgress)
14620{
14621 NOREF(aProgress);
14622 ReturnComNotImplemented();
14623}
14624
14625HRESULT Machine::endPowerUp(LONG aResult)
14626{
14627 NOREF(aResult);
14628 ReturnComNotImplemented();
14629}
14630
14631HRESULT Machine::beginPoweringDown(ComPtr<IProgress> &aProgress)
14632{
14633 NOREF(aProgress);
14634 ReturnComNotImplemented();
14635}
14636
14637HRESULT Machine::endPoweringDown(LONG aResult,
14638 const com::Utf8Str &aErrMsg)
14639{
14640 NOREF(aResult);
14641 NOREF(aErrMsg);
14642 ReturnComNotImplemented();
14643}
14644
14645HRESULT Machine::runUSBDeviceFilters(const ComPtr<IUSBDevice> &aDevice,
14646 BOOL *aMatched,
14647 ULONG *aMaskedInterfaces)
14648{
14649 NOREF(aDevice);
14650 NOREF(aMatched);
14651 NOREF(aMaskedInterfaces);
14652 ReturnComNotImplemented();
14653
14654}
14655
14656HRESULT Machine::captureUSBDevice(const com::Guid &aId, const com::Utf8Str &aCaptureFilename)
14657{
14658 NOREF(aId); NOREF(aCaptureFilename);
14659 ReturnComNotImplemented();
14660}
14661
14662HRESULT Machine::detachUSBDevice(const com::Guid &aId,
14663 BOOL aDone)
14664{
14665 NOREF(aId);
14666 NOREF(aDone);
14667 ReturnComNotImplemented();
14668}
14669
14670HRESULT Machine::autoCaptureUSBDevices()
14671{
14672 ReturnComNotImplemented();
14673}
14674
14675HRESULT Machine::detachAllUSBDevices(BOOL aDone)
14676{
14677 NOREF(aDone);
14678 ReturnComNotImplemented();
14679}
14680
14681HRESULT Machine::onSessionEnd(const ComPtr<ISession> &aSession,
14682 ComPtr<IProgress> &aProgress)
14683{
14684 NOREF(aSession);
14685 NOREF(aProgress);
14686 ReturnComNotImplemented();
14687}
14688
14689HRESULT Machine::finishOnlineMergeMedium()
14690{
14691 ReturnComNotImplemented();
14692}
14693
14694HRESULT Machine::pullGuestProperties(std::vector<com::Utf8Str> &aNames,
14695 std::vector<com::Utf8Str> &aValues,
14696 std::vector<LONG64> &aTimestamps,
14697 std::vector<com::Utf8Str> &aFlags)
14698{
14699 NOREF(aNames);
14700 NOREF(aValues);
14701 NOREF(aTimestamps);
14702 NOREF(aFlags);
14703 ReturnComNotImplemented();
14704}
14705
14706HRESULT Machine::pushGuestProperty(const com::Utf8Str &aName,
14707 const com::Utf8Str &aValue,
14708 LONG64 aTimestamp,
14709 const com::Utf8Str &aFlags)
14710{
14711 NOREF(aName);
14712 NOREF(aValue);
14713 NOREF(aTimestamp);
14714 NOREF(aFlags);
14715 ReturnComNotImplemented();
14716}
14717
14718HRESULT Machine::lockMedia()
14719{
14720 ReturnComNotImplemented();
14721}
14722
14723HRESULT Machine::unlockMedia()
14724{
14725 ReturnComNotImplemented();
14726}
14727
14728HRESULT Machine::ejectMedium(const ComPtr<IMediumAttachment> &aAttachment,
14729 ComPtr<IMediumAttachment> &aNewAttachment)
14730{
14731 NOREF(aAttachment);
14732 NOREF(aNewAttachment);
14733 ReturnComNotImplemented();
14734}
14735
14736HRESULT Machine::reportVmStatistics(ULONG aValidStats,
14737 ULONG aCpuUser,
14738 ULONG aCpuKernel,
14739 ULONG aCpuIdle,
14740 ULONG aMemTotal,
14741 ULONG aMemFree,
14742 ULONG aMemBalloon,
14743 ULONG aMemShared,
14744 ULONG aMemCache,
14745 ULONG aPagedTotal,
14746 ULONG aMemAllocTotal,
14747 ULONG aMemFreeTotal,
14748 ULONG aMemBalloonTotal,
14749 ULONG aMemSharedTotal,
14750 ULONG aVmNetRx,
14751 ULONG aVmNetTx)
14752{
14753 NOREF(aValidStats);
14754 NOREF(aCpuUser);
14755 NOREF(aCpuKernel);
14756 NOREF(aCpuIdle);
14757 NOREF(aMemTotal);
14758 NOREF(aMemFree);
14759 NOREF(aMemBalloon);
14760 NOREF(aMemShared);
14761 NOREF(aMemCache);
14762 NOREF(aPagedTotal);
14763 NOREF(aMemAllocTotal);
14764 NOREF(aMemFreeTotal);
14765 NOREF(aMemBalloonTotal);
14766 NOREF(aMemSharedTotal);
14767 NOREF(aVmNetRx);
14768 NOREF(aVmNetTx);
14769 ReturnComNotImplemented();
14770}
14771
14772HRESULT Machine::applyDefaults(const com::Utf8Str &aFlags)
14773{
14774 NOREF(aFlags);
14775 ReturnComNotImplemented();
14776}
14777
14778/* This isn't handled entirely by the wrapper generator yet. */
14779#ifdef VBOX_WITH_XPCOM
14780NS_DECL_CLASSINFO(SessionMachine)
14781NS_IMPL_THREADSAFE_ISUPPORTS2_CI(SessionMachine, IMachine, IInternalMachineControl)
14782
14783NS_DECL_CLASSINFO(SnapshotMachine)
14784NS_IMPL_THREADSAFE_ISUPPORTS1_CI(SnapshotMachine, IMachine)
14785#endif
Note: See TracBrowser for help on using the repository browser.

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